From 13456597bfcd03294c80c3de7e18a4e4cfaa1e2e Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Sat, 11 Apr 2015 19:12:53 -0500
Subject: [PATCH] Add generate_box_mesh

---
 meshmode/mesh/generation.py | 131 +++++++++++++++++++++++++++---------
 test/test_meshmode.py       |   5 ++
 2 files changed, 105 insertions(+), 31 deletions(-)

diff --git a/meshmode/mesh/generation.py b/meshmode/mesh/generation.py
index 5b925dcb..9a72c537 100644
--- a/meshmode/mesh/generation.py
+++ b/meshmode/mesh/generation.py
@@ -53,6 +53,11 @@ Surfaces
 .. autofunction:: generate_icosahedron
 .. autofunction:: generate_icosphere
 .. autofunction:: generate_torus
+
+Volumes
+-------
+
+.. autofunction:: generate_box_mesh
 .. autofunction:: generate_regular_rect_mesh
 
 """
@@ -373,51 +378,95 @@ def generate_torus(r_outer, r_inner, n_outer=20, n_inner=10, order=1):
     return mesh
 
 
-# {{{ generate_regular_rect_mesh
+# {{{ generate_box_mesh
 
-def generate_regular_rect_mesh(a=(0, 0), b=(1, 1), n=(5, 5), order=1):
-    """Create a semi-structured rectangular mesh.
+def generate_box_mesh(axis_coords, order=1, coord_dtype=np.float64):
+    """Create a semi-structured mesh.
 
-    :param a: the lower left hand point of the rectangle
-    :param b: the upper right hand point of the rectangle
-    :param n: a tuple of integers indicating the total number of points
-      on [a,b].
+    :param axis_coords: a tuple with a number of entries corresponding
+        to the number of dimensions, with each entry a numpy array
+        specifying the coordinates to be used along that axis.
     """
-    if min(n) < 2:
-        raise ValueError("need at least two points in each direction")
 
-    node_dict = {}
-    vertices = []
-    points_1d = [np.linspace(a_i, b_i, n_i)
-            for a_i, b_i, n_i in zip(a, b, n)]
+    for iaxis, axc in enumerate(axis_coords):
+        if len(axc) < 2:
+            raise ValueError("need at least two points along axis %d"
+                    % (iaxis+1))
 
-    for j in range(n[1]):
-        for i in range(n[0]):
-            node_dict[i, j] = len(vertices)
-            vertices.append(np.array([points_1d[0][i], points_1d[1][j]]))
+    dim = len(axis_coords)
 
-    vertices = np.array(vertices).T.copy()
+    shape = tuple(len(axc) for axc in axis_coords)
 
-    vertex_indices = []
+    from pytools import product
+    nvertices = product(shape)
 
-    for i in range(n[0]-1):
-        for j in range(n[1]-1):
+    vertex_indices = np.arange(nvertices).reshape(*shape, order="F")
 
-            # c--d
-            # |  |
+    vertices = np.empty((dim,)+shape, dtype=coord_dtype)
+    for idim in range(dim):
+        vshape = (shape[idim],) + (1,)*idim
+        vertices[idim] = axis_coords[idim].reshape(*vshape)
+
+    vertices = vertices.reshape(dim, -1)
+
+    el_vertices = []
+
+    if dim == 1:
+        for i in range(shape[0]-1):
             # a--b
 
-            a = node_dict[i, j]
-            b = node_dict[i+1, j]
-            c = node_dict[i, j+1]
-            d = node_dict[i+1, j+1]
+            a = vertex_indices[i]
+            b = vertex_indices[i+1]
 
-            vertex_indices.append((a, b, c))
-            vertex_indices.append((d, c, b))
+            el_vertices.append((a, b,))
 
-    vertex_indices = np.array(vertex_indices, dtype=np.int32)
+    elif dim == 2:
+        for i in range(shape[0]-1):
+            for j in range(shape[1]-1):
 
-    grp = make_group_from_vertices(vertices, vertex_indices, order)
+                # c--d
+                # |  |
+                # a--b
+
+                a = vertex_indices[i, j]
+                b = vertex_indices[i+1, j]
+                c = vertex_indices[i, j+1]
+                d = vertex_indices[i+1, j+1]
+
+                el_vertices.append((a, b, c))
+                el_vertices.append((d, c, b))
+
+    elif dim == 3:
+        for i in range(shape[0]-1):
+            for j in range(shape[1]-1):
+                for k in range(shape[2]-1):
+
+                    a000 = vertex_indices[i, j, k]
+                    a001 = vertex_indices[i, j, k+1]
+                    a010 = vertex_indices[i, j+1, k]
+                    a011 = vertex_indices[i, j+1, k+1]
+
+                    a100 = vertex_indices[i+1, j, k]
+                    a101 = vertex_indices[i+1, j, k+1]
+                    a110 = vertex_indices[i+1, j+1, k]
+                    a111 = vertex_indices[i+1, j+1, k+1]
+
+                    el_vertices.append((a000, a100, a010, a001))
+                    el_vertices.append((a101, a100, a001, a010))
+                    el_vertices.append((a101, a011, a010, a001))
+
+                    el_vertices.append((a100, a010, a101, a110))
+                    el_vertices.append((a011, a010, a110, a101))
+                    el_vertices.append((a011, a111, a101, a110))
+
+    else:
+        raise NotImplementedError("box meshes of dimension %d"
+                % dim)
+
+    el_vertices = np.array(el_vertices, dtype=np.int32)
+
+    grp = make_group_from_vertices(
+            vertices.reshape(dim, -1), el_vertices, order)
 
     from meshmode.mesh import Mesh
     return Mesh(vertices, [grp],
@@ -426,4 +475,24 @@ def generate_regular_rect_mesh(a=(0, 0), b=(1, 1), n=(5, 5), order=1):
 # }}}
 
 
+# {{{ generate_regular_rect_mesh
+
+def generate_regular_rect_mesh(a=(0, 0), b=(1, 1), n=(5, 5), order=1):
+    """Create a semi-structured rectangular mesh.
+
+    :param a: the lower left hand point of the rectangle
+    :param b: the upper right hand point of the rectangle
+    :param n: a tuple of integers indicating the total number of points
+      on [a,b].
+    """
+    if min(n) < 2:
+        raise ValueError("need at least two points in each direction")
+
+    axis_coords = [np.linspace(a_i, b_i, n_i)
+            for a_i, b_i, n_i in zip(a, b, n)]
+
+    return generate_box_mesh(axis_coords, order=order)
+
+# }}}
+
 # vim: fdm=marker
diff --git a/test/test_meshmode.py b/test/test_meshmode.py
index 25b26dab..e7d3e941 100644
--- a/test/test_meshmode.py
+++ b/test/test_meshmode.py
@@ -354,6 +354,11 @@ def test_rect_mesh(do_plot=False):
         pt.show()
 
 
+def test_box_mesh():
+    from meshmode.mesh.generation import generate_box_mesh
+    generate_box_mesh(3*(np.linspace(0, 1, 5),))
+
+
 def test_as_python():
     from meshmode.mesh.generation import make_curve_mesh, cloverleaf
     mesh = make_curve_mesh(cloverleaf, np.linspace(0, 1, 100), order=3)
-- 
GitLab