Structured Rectangular Meshes
dream.mesh.get_rectangular_mesh builds a structured Cartesian mesh from explicit nodal coordinate arrays. The caller supplies one 1-D array per spatial direction for each domain region; the function takes their union as the global set of mesh nodes and connects them into quad (default) or triangle elements.
Boundaries are specified the same way: passing a single-valued array in one direction selects all nodes on that edge, while the other direction spans its full extent.
The helper get_nodal_points generates 1-D node distributions with several clustering options ('uniform', 'cosine', 'tanh', 'exponential') that concentrate grid points near boundaries.
[1]:
import numpy as np
from ngsolve.webgui import Draw
from dream.mesh import get_rectangular_mesh, get_nodal_points
Uniform channel mesh
We first generate a simple channel \([0, 4] \times [-0.5, 0.5]\) with uniform node spacing in both directions and four named boundaries.
[2]:
nx = np.linspace(0.0, 4.0, 21) # 20 uniform intervals in x
ny = np.linspace(-0.5, 0.5, 11) # 10 uniform intervals in y
domains = [
("channel", (nx, ny)),
]
boundaries = [
("inflow", (np.array([0.0]), ny)),
("outflow", (np.array([4.0]), ny)),
("wall", (nx, np.array([-0.5]))),
("wall", (nx, np.array([0.5]))),
]
mesh = get_rectangular_mesh(domains, boundaries)
print("Materials: ", mesh.GetMaterials())
print("Boundaries:", mesh.GetBoundaries())
Draw(mesh)
Materials: ('channel',)
Boundaries: ('inflow', 'outflow', 'wall')
[2]:
WebGLScene
Wall-clustered channel mesh
For boundary-layer-resolving simulations it is useful to cluster nodes near the walls. Here we use get_nodal_points with a hyperbolic-tangent distribution and stretching factor \(\beta = 3\) to concentrate the grid points near \(y = \pm 0.5\).
[3]:
nx = np.linspace(0.0, 4.0, 21)
# tanh distribution in [0, 1], mapped to [-0.5, 0.5]
ny = get_nodal_points(21, distribution='tanh', beta=3) - 0.5
domains = [
("channel", (nx, ny)),
]
boundaries = [
("inflow", (np.array([0.0]), ny)),
("outflow", (np.array([4.0]), ny)),
("wall", (nx, np.array([ny[0]]))),
("wall", (nx, np.array([ny[-1]]))),
]
mesh = get_rectangular_mesh(domains, boundaries)
print("Materials: ", mesh.GetMaterials())
print("Boundaries:", mesh.GetBoundaries())
Draw(mesh)
Materials: ('channel',)
Boundaries: ('inflow', 'outflow', 'wall')
[3]:
WebGLScene
Two-domain channel with a downstream sponge region
Multi-domain meshes are constructed by passing several entries to domains. The node arrays from all domains are merged, so the two regions share their common boundary at \(x = 4\). We use a coarser spacing in the downstream sponge zone.
Both wall boundaries span the full channel length, so we pass the union of the x-coordinate arrays from both domains.
[4]:
nx_channel = np.linspace(0.0, 4.0, 41) # fine grid in main channel
nx_sponge = np.linspace(4.0, 5.0, 6) # coarser grid in sponge region
nx_all = np.union1d(nx_channel, nx_sponge)
ny = np.linspace(-0.5, 0.5, 11)
domains = [
("channel", (nx_channel, ny)),
("sponge", (nx_sponge, ny)),
]
boundaries = [
("inflow", (np.array([0.0]), ny)),
("outflow", (np.array([5.0]), ny)),
("wall", (nx_all, np.array([-0.5]))),
("wall", (nx_all, np.array([0.5]))),
]
mesh = get_rectangular_mesh(domains, boundaries)
print("Materials: ", mesh.GetMaterials())
print("Boundaries:", mesh.GetBoundaries())
Draw(mesh)
Materials: ('channel', 'sponge')
Boundaries: ('inflow', 'outflow', 'wall')
[4]:
WebGLScene