Skip to content

Commit 6acd793

Browse files
committed
Merge branch 'master' of github.com:zingale/hydro_examples
2 parents 2973bf9 + e0905ee commit 6acd793

3 files changed

Lines changed: 234 additions & 10 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ https://github.com/zingale/pyro2
2727
- `fdadvect.py`: a 1-d first-order explicit finite-difference linear
2828
advection solver using upwinded differencing.
2929

30+
- `fv_mol.py`: a 1-d method-of-lines second-order accurate advection
31+
solver.
32+
3033
- `Fortran/`:
3134

3235
* `advect.f90`: a Fortran implementation of second-order linear

advection/advection.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def __init__(self, nx, ng, xmin=0.0, xmax=1.0):
6464
# physical coords -- cell-centered, left and right edges
6565
self.dx = (xmax - xmin)/(nx)
6666
self.x = xmin + (np.arange(nx+2*ng)-ng+0.5)*self.dx
67+
self.xl = xmin + (np.arange(nx+2*ng)-ng)*self.dx
68+
self.xr = xmin + (np.arange(nx+2*ng)+1.0)*self.dx
6769

6870
# storage for the solution
6971
self.a = np.zeros((nx+2*ng), dtype=np.float64)
@@ -89,7 +91,8 @@ def norm(self, e):
8991
if len(e) != 2*self.ng + self.nx:
9092
return None
9193

92-
return np.sqrt(self.dx*np.sum(e[self.ilo:self.ihi+1]**2))
94+
#return np.sqrt(self.dx*np.sum(e[self.ilo:self.ihi+1]**2))
95+
return np.max(abs(e[self.ilo:self.ihi+1]))
9396

9497

9598
class Simulation(object):
@@ -113,7 +116,11 @@ def init_cond(self, type="tophat"):
113116
self.grid.a[:] = np.sin(2.0*np.pi*self.grid.x/(self.grid.xmax-self.grid.xmin))
114117

115118
elif type == "gaussian":
116-
self.grid.a[:] = 1.0 + np.exp(-60.0*(self.grid.x - 0.5)**2)
119+
al = 1.0 + np.exp(-60.0*(self.grid.xl - 0.5)**2)
120+
ar = 1.0 + np.exp(-60.0*(self.grid.xr - 0.5)**2)
121+
ac = 1.0 + np.exp(-60.0*(self.grid.x - 0.5)**2)
122+
123+
self.grid.a[:] = (1./6.)*(al + 4*ac + ar)
117124

118125

119126
def timestep(self):
@@ -302,6 +309,7 @@ def evolve(self, num_periods=1):
302309
ng = 2
303310
N = [32, 64, 128, 256, 512]
304311

312+
err_god = []
305313
err_nolim = []
306314
err_lim = []
307315
err_lim2 = []
@@ -310,13 +318,20 @@ def evolve(self, num_periods=1):
310318

311319
for nx in N:
312320

321+
# no limiting
322+
gg = Grid1d(nx, ng, xmin=xmin, xmax=xmax)
323+
sg = Simulation(gg, u, C=0.8, slope_type="godunov")
324+
sg.init_cond("gaussian")
325+
ainit = sg.grid.a.copy()
326+
sg.evolve(num_periods=5)
327+
328+
err_god.append(gg.norm(gg.a - ainit))
329+
313330
# no limiting
314331
gu = Grid1d(nx, ng, xmin=xmin, xmax=xmax)
315332
su = Simulation(gu, u, C=0.8, slope_type="centered")
316-
317333
su.init_cond("gaussian")
318334
ainit = su.grid.a.copy()
319-
320335
su.evolve(num_periods=5)
321336

322337
err_nolim.append(gu.norm(gu.a - ainit))
@@ -349,11 +364,14 @@ def evolve(self, num_periods=1):
349364
err_lim = np.array(err_lim)
350365
err_lim2 = np.array(err_lim2)
351366

352-
plt.scatter(N, err_nolim, label="unlimited center")
353-
plt.scatter(N, err_lim, label="MC")
354-
plt.scatter(N, err_lim2, label="minmod")
367+
plt.scatter(N, err_god, label="Godunov", color="C0")
368+
plt.scatter(N, err_nolim, label="unlimited center", color="C1")
369+
plt.scatter(N, err_lim, label="MC", color="C2")
370+
plt.scatter(N, err_lim2, label="minmod", color="C3")
371+
plt.plot(N, err_god[len(N)-1]*(N[len(N)-1]/N),
372+
color="k", label=r"$\mathcal{O}(\Delta x)$")
355373
plt.plot(N, err_nolim[len(N)-1]*(N[len(N)-1]/N)**2,
356-
color="k", label=r"$\mathcal{O}(\Delta x^2)$")
374+
color="0.5", label=r"$\mathcal{O}(\Delta x^2)$")
357375

358376
ax = plt.gca()
359377
ax.set_xscale('log')
@@ -363,7 +381,7 @@ def evolve(self, num_periods=1):
363381
plt.ylabel(r"$\| a^\mathrm{final} - a^\mathrm{init} \|_2$",
364382
fontsize=16)
365383

366-
plt.legend(frameon=False)
384+
plt.legend(frameon=False, loc="best", fontsize="small")
367385

368386
plt.savefig("plm-converge.pdf")
369387

@@ -373,7 +391,7 @@ def evolve(self, num_periods=1):
373391

374392
xmin = 0.0
375393
xmax = 1.0
376-
nx = 128
394+
nx = 32
377395
ng = 2
378396

379397
u = 1.0

advection/fv_mol.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
class FVGrid(object):
5+
6+
def __init__(self, nx, ng, xmin=0.0, xmax=1.0):
7+
8+
self.xmin = xmin
9+
self.xmax = xmax
10+
self.ng = ng
11+
self.nx = nx
12+
13+
# python is zero-based. Make easy intergers to know where the
14+
# real data lives
15+
self.ilo = ng
16+
self.ihi = ng+nx-1
17+
18+
# physical coords -- cell-centered, left and right edges
19+
self.dx = (xmax - xmin)/(nx)
20+
self.x = xmin + (np.arange(nx+2*ng)-ng+0.5)*self.dx
21+
self.xl = xmin + (np.arange(nx+2*ng)-ng)*self.dx
22+
self.xr = xmin + (np.arange(nx+2*ng)-ng+1.0)*self.dx
23+
24+
# storage for the solution
25+
self.a = self.scratch_array()
26+
self.ainit = self.scratch_array()
27+
28+
def period(self, u):
29+
""" return the period for advection with velocity u """
30+
return (self.xmax - self.xmin)/u
31+
32+
def scratch_array(self):
33+
""" return a scratch array dimensioned for our grid """
34+
return np.zeros((self.nx+2*self.ng), dtype=np.float64)
35+
36+
def fill_BCs(self, atmp):
37+
""" fill all single ghostcell with periodic boundary conditions """
38+
39+
# left boundary
40+
for n in range(self.ng):
41+
atmp[self.ilo-1-n] = atmp[self.ihi-n]
42+
43+
# right boundary
44+
for n in range(self.ng):
45+
atmp[self.ihi+1+n] = atmp[self.ilo+n]
46+
47+
def init_cond(self, ic):
48+
49+
if ic == "tophat":
50+
self.a[np.logical_and(self.x >= 0.333, self.x <= 0.666)] = 1.0
51+
elif ic == "sine":
52+
self.a[:] = np.sin(2.0*np.pi*self.x/(self.xmax-self.xmin))
53+
elif ic == "gaussian":
54+
self.a[:] = 1.0 + np.exp(-60.0*(self.x - 0.5)**2)
55+
56+
self.ainit[:] = self.a[:]
57+
58+
def norm(self, e):
59+
""" return the norm of quantity e which lives on the grid """
60+
if not len(e) == (2*self.ng + self.nx):
61+
return None
62+
63+
return np.sqrt(self.dx*np.sum(e[self.ilo:self.ihi+1]**2))
64+
65+
66+
def flux_update(gr, u, a, limit=False):
67+
68+
if not limit:
69+
# slope
70+
da = gr.scratch_array()
71+
da[gr.ilo-1:gr.ihi+2] = 0.5*(a[gr.ilo:gr.ihi+3] - a[gr.ilo-2:gr.ihi+1])
72+
73+
else:
74+
# MC slope
75+
ib = gr.ilo-1
76+
ie = gr.ihi+1
77+
78+
dc = gr.scratch_array()
79+
dl = gr.scratch_array()
80+
dr = gr.scratch_array()
81+
82+
dc[ib:ie+1] = 0.5*(a[ib+1:ie+2] - a[ib-1:ie ])
83+
dl[ib:ie+1] = a[ib+1:ie+2] - a[ib :ie+1]
84+
dr[ib:ie+1] = a[ib :ie+1] - a[ib-1:ie ]
85+
86+
# these where's do a minmod()
87+
d1 = 2.0*np.where(np.fabs(dl) < np.fabs(dr), dl, dr)
88+
d2 = np.where(np.fabs(dc) < np.fabs(d1), dc, d1)
89+
da = np.where(dl*dr > 0.0, d2, 0.0)
90+
91+
# upwinding means that we take the left state always
92+
aint = gr.scratch_array()
93+
aint[gr.ilo:gr.ihi+2] = a[gr.ilo-1:gr.ihi+1] + 0.5*da[gr.ilo-1:gr.ihi+1]
94+
95+
flux_diff = gr.scratch_array()
96+
flux_diff[gr.ilo:gr.ihi+1] = u*(aint[gr.ilo:gr.ihi+1] - aint[gr.ilo+1:gr.ihi+2])/gr.dx
97+
98+
return flux_diff
99+
100+
101+
def mol_update(C, u, nx, num_periods=1, init_cond="gaussian", limit=False):
102+
103+
# create a grid
104+
gr = FVGrid(nx, ng=2)
105+
106+
tmax = num_periods*gr.period(u)
107+
108+
# setup initial conditions
109+
gr.init_cond(init_cond)
110+
111+
# compute the timestep
112+
dt = C*gr.dx/u
113+
114+
t = 0.0
115+
while t < tmax:
116+
117+
if t + dt > tmax:
118+
dt = tmax - t
119+
120+
# second-order RK integration
121+
gr.fill_BCs(gr.a)
122+
k1 = flux_update(gr, u, gr.a, limit=limit)
123+
124+
atmp = gr.scratch_array()
125+
atmp[:] = gr.a[:] + 0.5*dt*k1[:]
126+
127+
gr.fill_BCs(atmp)
128+
k2 = flux_update(gr, u, atmp, limit=limit)
129+
130+
gr.a[:] += dt*k2[:]
131+
132+
t += dt
133+
134+
return gr
135+
136+
if __name__ == "__main__":
137+
138+
C = 0.8
139+
u = 1.0
140+
nx = 64
141+
142+
# without limiting
143+
gr = mol_update(C, u, nx, num_periods=1.0, init_cond="gaussian")
144+
145+
plt.clf()
146+
plt.plot(gr.x, gr.a)
147+
plt.plot(gr.x, gr.ainit, ls=":")
148+
plt.savefig("advection_gaussian.png", dpi=150)
149+
150+
gr = mol_update(C, u, nx, num_periods=1.0, init_cond="tophat")
151+
152+
plt.clf()
153+
plt.plot(gr.x, gr.a)
154+
plt.plot(gr.x, gr.ainit, ls=":")
155+
plt.savefig("advection_tophat.png", dpi=150)
156+
157+
158+
# with limiting
159+
gr = mol_update(C, u, nx, num_periods=1.0, init_cond="gaussian", limit=True)
160+
161+
plt.clf()
162+
plt.plot(gr.x, gr.a)
163+
plt.plot(gr.x, gr.ainit, ls=":")
164+
plt.savefig("advection_gaussian_limit.png", dpi=150)
165+
166+
gr = mol_update(C, u, nx, num_periods=1.0, init_cond="tophat", limit=True)
167+
168+
plt.clf()
169+
plt.plot(gr.x, gr.a)
170+
plt.plot(gr.x, gr.ainit, ls=":")
171+
plt.savefig("advection_tophat_limit.png", dpi=150)
172+
173+
174+
# convergence
175+
plt.clf()
176+
177+
Ns = [32, 64, 128, 256, 512]
178+
err_gauss = []
179+
err_nolimit = []
180+
for nx in Ns:
181+
gr = mol_update(C, u, nx, num_periods=1.0, init_cond="gaussian", limit=True)
182+
gr_nolimit = mol_update(C, u, nx, num_periods=1.0, init_cond="gaussian", limit=False)
183+
err_gauss.append(gr.norm(gr.a - gr.ainit))
184+
err_nolimit.append(gr.norm(gr_nolimit.a - gr_nolimit.ainit))
185+
print(nx, err_gauss[-1], err_nolimit[-1])
186+
187+
plt.scatter(Ns, err_gauss, label="gaussian")
188+
plt.scatter(Ns, err_nolimit, label="gaussian (no limit)")
189+
190+
# plot a trend line
191+
N = np.asarray(Ns)
192+
err = np.asarray(err_gauss)
193+
plt.plot(N, err[-1]*(N[-1]/N), ls=":", color="0.5", label=r"$\mathcal{O}(\Delta x)$")
194+
plt.plot(N, err[-1]*(N[-1]/N)**2, ls="-", color="0.5", label=r"$\mathcal{O}(\Delta x^2)$")
195+
196+
ax = plt.gca()
197+
ax.set_xscale("log")
198+
ax.set_yscale("log")
199+
plt.legend(frameon=False, fontsize="small", loc=3)
200+
plt.xlim(10, 1000)
201+
plt.ylim(1.e-4, 0.1)
202+
203+
plt.savefig("convergence_limited.png", dpi=150)

0 commit comments

Comments
 (0)