Run this tutorial
Click here to run this tutorial on mybinder.org:Example: structured 1d grids (YASP_1D vs OneDGrid)¶
Until now the Python bindings exposed exactly one one-dimensional grid, Dune::OneDGrid
(make_cube_grid(Dim(1), ...)). WP2 of
issue #320 adds the structured
YaspGrid<1, EquidistantOffsetCoordinates> next to it – the 1d sibling of the YASP_2D /
YASP_3D cube grids that already back the 2d/3d examples, and the grid the C++ SimplicialGrids
typed-test list uses in 1d. This notebook
builds the same 1d domain as both an
OneDGridand aYASP_1Dgrid,solves the stationary heat equation on each and checks the two discretizations agree, and
demonstrates the equidistant-offset feature (a structured grid on an arbitrary interval
[a, b],a != 0) that motivates having a structured 1d grid at all.
# wurlitzer: display dune's output in the notebook
%load_ext wurlitzer
import numpy as np
Two ways to mesh the unit interval¶
OneDGrid is selected through the dimension-only make_cube_grid overload, while the structured
YASP_1D grid is selected through the same (Dimension, Cube) overload as its 2d/3d counterparts
– so both one-dimensional grids can coexist without an ambiguous factory signature.
from dune.xt.grid import Cube, Dim, make_cube_grid
n = 8 # number of intervals
lower_left, upper_right = [0.0], [1.0]
oned_grid = make_cube_grid(Dim(1), lower_left=lower_left, upper_right=upper_right,
num_elements=[n])
yasp_grid = make_cube_grid(Dim(1), Cube(), lower_left=lower_left, upper_right=upper_right,
num_elements=[n])
for name, grid in (("OneDGrid", oned_grid), ("YASP_1D", yasp_grid)):
print(f'{name:9s}: {grid.size(0)} elements, {grid.size(1)} vertices')
OneDGrid : 8 elements, 9 vertices
YASP_1D : 8 elements, 9 vertices
Both provide the identical structured skeleton (num_elements equal intervals of width
h = 1/num_elements); the element centers coincide up to ordering:
oned_centers = np.sort(np.array(oned_grid.centers(0), copy=False).ravel())
yasp_centers = np.sort(np.array(yasp_grid.centers(0), copy=False).ravel())
assert np.allclose(oned_centers, yasp_centers)
print('element centers:', yasp_centers)
element centers: [0.0625 0.1875 0.3125 0.4375 0.5625 0.6875 0.8125 0.9375]
The stationary heat equation on both grids¶
We reuse the continuous-Lagrange discretization from discretize_elliptic_cg.py (the same helper
the 2d CG tutorial uses) to solve
whose exact solution is \(u(x) = \tfrac{1}{2}\,x\,(1 - x)\).
from discretize_elliptic_cg import discretize_elliptic_cg_dirichlet_zero
def solve(grid):
# constant unit diffusion and unit source, homogeneous Dirichlet everywhere
u_h = discretize_elliptic_cg_dirichlet_zero(grid, diffusion=1.0, source=1.0)
return np.array(u_h.dofs.vector, copy=False)
oned_dofs = solve(oned_grid)
yasp_dofs = solve(yasp_grid)
The two grids give rise to the identical P1 linear system (up to a DoF permutation), so the sorted DoF vectors must match to solver accuracy:
assert oned_dofs.shape == yasp_dofs.shape == (n + 1,)
grid_to_grid = np.max(np.abs(np.sort(oned_dofs) - np.sort(yasp_dofs)))
print(f'max |OneDGrid - YASP_1D| over the DoFs: {grid_to_grid:.2e}')
assert grid_to_grid < 1e-6
max |OneDGrid - YASP_1D| over the DoFs: 0.00e+00
In one dimension the P1 Galerkin solution of the Poisson problem is nodally exact, so the sorted DoF values reproduce the exact solution sampled on the nodes \(x_i = i/N\):
nodes = np.linspace(0.0, 1.0, n + 1)
exact_nodal = np.sort(0.5 * nodes * (1.0 - nodes))
nodal_error = np.max(np.abs(np.sort(yasp_dofs) - exact_nodal))
print(f'max nodal error vs. exact solution: {nodal_error:.2e}')
assert nodal_error < 1e-6
max nodal error vs. exact solution: 4.73e-15
The equidistant-offset feature: a structured grid on [a, b]¶
The whole point of EquidistantOffsetCoordinates is that the structured grid need not start at the
origin. We put a YASP_1D grid on the offset interval [2, 5] and solve the same problem there,
with exact solution \(u(x) = \tfrac{1}{2}\,(x - 2)\,(5 - x)\).
a, b = 2.0, 5.0
offset_grid = make_cube_grid(Dim(1), Cube(), lower_left=[a], upper_right=[b],
num_elements=[n])
centers = np.sort(np.array(offset_grid.centers(0), copy=False).ravel())
h = (b - a) / n
print(f'offset grid spans [{centers.min() - 0.5 * h:.1f}, {centers.max() + 0.5 * h:.1f}]')
offset_dofs = solve(offset_grid)
offset_nodes = np.linspace(a, b, n + 1)
offset_exact = np.sort(0.5 * (offset_nodes - a) * (b - offset_nodes))
offset_error = np.max(np.abs(np.sort(offset_dofs) - offset_exact))
print(f'max nodal error on [{a}, {b}]: {offset_error:.2e}')
assert offset_error < 1e-6
offset grid spans [2.0, 5.0]
max nodal error on [2.0, 5.0]: 4.26e-14
The structured 1d grid solves the shifted problem exactly, without any special handling of the
non-zero offset – the same behaviour the 2d/3d YASP cube grids already provide, now available in
one dimension for property testing and structured-grid experiments alongside OneDGrid.
Download the code:
example__structured_1d_grids.md
example__structured_1d_grids.ipynb