Skip to content

Commit 6c84b5e

Browse files
authored
add support for spherical polar grid: add geometric terms (volume/area) (#201)
Add volume and area terms for spherical polar grid and generalize it for cartesian as well.
1 parent 493774e commit 6c84b5e

7 files changed

Lines changed: 343 additions & 25 deletions

File tree

examples/examples.ipynb

Lines changed: 9 additions & 8 deletions
Large diffs are not rendered by default.

pyro/_defaults

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ store_images = 0 ; store vis images to files (1=yes, 0=no)
2525

2626
[mesh]
2727

28+
grid_type = Cartesian2d ; Geometry of the Grid ('Cartesian2d' or 'SphericalPolar')
2829
xmin = 0.0 ; domain minimum x-coordinate
2930
xmax = 1.0 ; domain maximum x-coordinate
3031
ymin = 0.0 ; domain minimum y-coordinate

pyro/compressible/interface.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ def states(idir, ng, dx, dt,
201201

202202
# construct the states
203203
for m in range(nvar):
204-
sum_l = np.dot(betal, rvec[:, m])
205-
sum_r = np.dot(betar, rvec[:, m])
204+
sum_l = np.dot(betal, np.ascontiguousarray(rvec[:, m]))
205+
sum_r = np.dot(betar, np.ascontiguousarray(rvec[:, m]))
206206

207207
if idir == 1:
208208
q_l[i + 1, j, m] = q_l[i + 1, j, m] + sum_l

pyro/mesh/mesh-examples.ipynb

Lines changed: 118 additions & 3 deletions
Large diffs are not rendered by default.

pyro/mesh/patch.py

Lines changed: 120 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,18 @@ def __init__(self, nx, ny, ng=1,
133133
self.yr = (np.arange(self.qy) + 1.0 - ng)*self.dy + ymin
134134
self.y = 0.5*(self.yl + self.yr)
135135

136-
# 2-d versions of the zone coordinates (replace with meshgrid?)
137-
x2d = np.repeat(self.x, self.qy)
138-
x2d.shape = (self.qx, self.qy)
139-
self.x2d = x2d
136+
# 2-d versions of the zone coordinates
137+
x2d, y2d = np.meshgrid(self.x, self.y, indexing='ij')
138+
self.x2d = ArrayIndexer(d=x2d, grid=self)
139+
self.y2d = ArrayIndexer(d=y2d, grid=self)
140140

141-
y2d = np.repeat(self.y, self.qx)
142-
y2d.shape = (self.qy, self.qx)
143-
y2d = np.transpose(y2d)
144-
self.y2d = y2d
141+
xl2d, yl2d = np.meshgrid(self.xl, self.yl, indexing='ij')
142+
self.xl2d = ArrayIndexer(d=xl2d, grid=self)
143+
self.yl2d = ArrayIndexer(d=yl2d, grid=self)
144+
145+
xr2d, yr2d = np.meshgrid(self.xr, self.yr, indexing='ij')
146+
self.xr2d = ArrayIndexer(d=xr2d, grid=self)
147+
self.yr2d = ArrayIndexer(d=yr2d, grid=self)
145148

146149
def scratch_array(self, nvar=1):
147150
"""
@@ -186,6 +189,115 @@ def __eq__(self, other):
186189
return result
187190

188191

192+
class Cartesian2d(Grid2d):
193+
"""
194+
This class defines a 2D Cartesian Grid.
195+
196+
Define:
197+
x = x
198+
y = y
199+
"""
200+
201+
def __init__(self, nx, ny, ng=1,
202+
xmin=0.0, xmax=1.0, ymin=0.0, ymax=1.0):
203+
204+
super().__init__(nx, ny, ng, xmin, xmax, ymin, ymax)
205+
206+
self.coord_type = 0
207+
208+
# Length of the side in x- and y-direction
209+
210+
self.Lx = ArrayIndexer(np.full((self.qx, self.qy), self.dx),
211+
grid=self)
212+
self.Ly = ArrayIndexer(np.full((self.qx, self.qy), self.dy),
213+
grid=self)
214+
215+
# This is area of the side that is perpendicular to x.
216+
217+
self.Ax = self.Ly
218+
219+
# This is area of the side that is perpendicular to y.
220+
221+
self.Ay = self.Lx
222+
223+
# Volume of the cell.
224+
225+
self.V = ArrayIndexer(np.full((self.qx, self.qy), self.dx * self.dy),
226+
grid=self)
227+
228+
def __str__(self):
229+
""" print out some basic information about the grid object """
230+
return f"Cartesian 2D Grid: xmin = {self.xmin}, xmax = {self.xmax}, " + \
231+
f"ymin = {self.ymin}, ymax = {self.ymax}, " + \
232+
f"nx = {self.nx}, ny = {self.ny}, ng = {self.ng}"
233+
234+
235+
class SphericalPolar(Grid2d):
236+
"""
237+
This class defines a spherical polar grid.
238+
This is technically a 2D geometry but assumes azimuthal symmetry.
239+
240+
Define:
241+
r = x
242+
theta = y
243+
"""
244+
245+
def __init__(self, nx, ny, ng=1,
246+
xmin=0.2, xmax=1.0, ymin=0.0, ymax=1.0):
247+
248+
# Make sure theta is within [0, PI]
249+
assert ymin >= 0.0 and ymax <= np.pi, "y or \u03b8 should be within [0, \u03c0]."
250+
251+
# Make sure the ghost cells doesn't extend out negative x(r)
252+
assert xmin - ng*(xmax-xmin)/nx >= 0.0, \
253+
"xmin (r-direction), must be large enough so ghost cell doesn't have negative x."
254+
255+
super().__init__(nx, ny, ng, xmin, xmax, ymin, ymax)
256+
257+
self.coord_type = 1
258+
259+
# Length of the side along r-direction, dr
260+
261+
self.Lx = ArrayIndexer(np.full((self.qx, self.qy), self.dx),
262+
grid=self)
263+
264+
# Length of the side along theta-direction, r*dtheta
265+
266+
self.Ly = ArrayIndexer(np.full((self.qx, self.qy), self.x2d*self.dy),
267+
grid=self)
268+
269+
# Returns an array of the face area that points in the r(x) direction.
270+
# dL_theta x dL_phi = r^2 * sin(theta) * dtheta * dphi
271+
272+
# dAr_l = - r{i-1/2}^2 * 2pi * cos(theta{i+1/2}) - cos(theta{i-1/2})
273+
self.Ax = np.abs(-2.0 * np.pi * self.xl2d**2 *
274+
(np.cos(self.yr2d) - np.cos(self.yl2d)))
275+
276+
# Returns an array of the face area that points in the theta(y) direction.
277+
# dL_phi x dL_r = dr * r * sin(theta) * dphi
278+
279+
# dAtheta_l = pi * sin(theta{i-1/2}) * (r{i+1/2}^2 - r{i-1/2}^2)
280+
self.Ay = np.abs(np.pi * np.sin(self.yl2d) *
281+
(self.xr2d**2 - self.xl2d**2))
282+
283+
# Returns an array of the volume of each cell.
284+
# dV = dL_r * dL_theta * dL_phi
285+
# = (dr) * (r * dtheta) * (r * sin(theta) * dphi)
286+
# dV = - 2*np.pi / 3 * (cos(theta{i+1/2}) - cos(theta{i-1/2})) * (r{i+1/2}^3 - r{i-1/2}^3)
287+
288+
self.V = np.abs(-2.0 * np.pi / 3.0 *
289+
(np.cos(self.yr2d) - np.cos(self.yl2d)) *
290+
(self.xr2d - self.xl2d) *
291+
(self.xr2d**2 + self.xl2d**2 + self.xr2d*self.xl2d))
292+
293+
def __str__(self):
294+
""" print out some basic information about the grid object """
295+
return "Spherical Polar 2D Grid: Define x : r, y : \u03b8. " + \
296+
f"xmin (r) = {self.xmin}, xmax= {self.xmax}, " + \
297+
f"ymin = {self.ymin}, ymax = {self.ymax}, " + \
298+
f"nx = {self.nx}, ny = {self.ny}, ng = {self.ng}"
299+
300+
189301
class CellCenterData2d:
190302
"""
191303
A class to define cell-centered data that lives on a grid. A

pyro/mesh/tests/test_patch.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,80 @@ def test_equality(self):
6868
assert g2 != self.g
6969

7070

71+
# Cartesian2d tests
72+
class TestCartesian2d:
73+
@classmethod
74+
def setup_class(cls):
75+
""" this is run once for each class before any tests """
76+
77+
@classmethod
78+
def teardown_class(cls):
79+
""" this is run once for each class after all tests """
80+
81+
def setup_method(self):
82+
""" this is run before each test """
83+
self.g = patch.Cartesian2d(4, 10, ng=2)
84+
85+
def teardown_method(self):
86+
""" this is run after each test """
87+
self.g = None
88+
89+
def test_Ax(self):
90+
assert np.all(self.g.Ax.v() == 0.1)
91+
92+
def test_Ay(self):
93+
assert np.all(self.g.Ay.v() == 0.25)
94+
95+
def test_V(self):
96+
assert np.all(self.g.V.v() == 0.1 * 0.25)
97+
98+
99+
# SphericalPolar Grid tests
100+
class TestSphericalPolar:
101+
@classmethod
102+
def setup_class(cls):
103+
""" this is run once for each class before any tests """
104+
105+
@classmethod
106+
def teardown_class(cls):
107+
""" this is run once for each class after all tests """
108+
109+
def setup_method(self):
110+
""" this is run before each test """
111+
self.g = patch.SphericalPolar(4, 10, xmin=1.0, xmax=2.0,
112+
ymax=np.pi, ng=2)
113+
114+
def teardown_method(self):
115+
""" this is run after each test """
116+
self.g = None
117+
118+
def test_Ax(self):
119+
area_x_l = np.abs(-2.0 * np.pi * self.g.xl2d.v()**2 *
120+
(np.cos(self.g.yr2d.v()) - np.cos(self.g.yl2d.v())))
121+
assert_array_equal(self.g.Ax.v(), area_x_l)
122+
123+
area_x_r = np.abs(-2.0 * np.pi * self.g.xr2d.v()**2 *
124+
(np.cos(self.g.yr2d.v()) - np.cos(self.g.yl2d.v())))
125+
assert_array_equal(self.g.Ax.ip(1), area_x_r)
126+
127+
def test_Ay(self):
128+
area_y_l = np.abs(np.pi *
129+
np.sin(self.g.yl2d.v()) *
130+
(self.g.xr2d.v()**2 - self.g.xl2d.v()**2))
131+
assert_array_equal(self.g.Ay.v(), area_y_l)
132+
133+
area_y_r = np.abs(np.pi *
134+
np.sin(self.g.yr2d.v()) *
135+
(self.g.xr2d.v()**2 - self.g.xl2d.v()**2))
136+
assert_array_equal(self.g.Ay.jp(1), area_y_r)
137+
138+
def test_V(self):
139+
volume = np.abs(-2.0 * np.pi / 3.0 *
140+
(np.cos(self.g.yr2d.v()) - np.cos(self.g.yl2d.v())) *
141+
(self.g.xr2d.v()**3 - self.g.xl2d.v()**3))
142+
assert_array_equal(self.g.V.v(), volume)
143+
144+
71145
# CellCenterData2d tests
72146
class TestCellCenterData2d:
73147
@classmethod

pyro/simulation_null.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,26 @@ def grid_setup(rp, ng=1):
3434
ymax = rp.get_param("mesh.ymax")
3535
except KeyError:
3636
ymax = 1.0
37-
msg.warning("mesh.ynax not set, defaulting to 1.0")
37+
msg.warning("mesh.ymax not set, defaulting to 1.0")
38+
39+
try:
40+
grid_type = rp.get_param("mesh.grid_type")
41+
except KeyError:
42+
grid_type = "Cartesian2d"
43+
msg.warning("mesh.grid_type not set, defaulting to Cartesian2D")
44+
45+
if grid_type == "Cartesian2d":
46+
create_grid = patch.Cartesian2d
47+
elif grid_type == "SphericalPolar":
48+
create_grid = patch.SphericalPolar
49+
else:
50+
raise ValueError("Unsupported grid type!")
51+
52+
my_grid = create_grid(nx, ny,
53+
xmin=xmin, xmax=xmax,
54+
ymin=ymin, ymax=ymax,
55+
ng=ng)
3856

39-
my_grid = patch.Grid2d(nx, ny,
40-
xmin=xmin, xmax=xmax,
41-
ymin=ymin, ymax=ymax, ng=ng)
4257
return my_grid
4358

4459

0 commit comments

Comments
 (0)