import matplotlib.pyplot as plt
import matplotlib.tri as mtri
from mpl_toolkits.axes_grid1 import make_axes_locatable
from basix.ufl import element
tdim = msh.topology.dim
msh.topology.create_connectivity(tdim, 0)
msh.topology.create_connectivity(0, tdim)
cells = msh.topology.connectivity(tdim, 0).array.reshape(-1, 3)
nverts = (
msh.topology.index_map(0).size_local
+ msh.topology.index_map(0).num_ghosts
)
coords = msh.geometry.x[:nverts, :2]
vertex_ids = np.arange(nverts, dtype=np.int32)
tri = mtri.Triangulation(coords[:, 0], coords[:, 1], triangles=cells)
xmin, xmax = coords[:, 0].min(), coords[:, 0].max()
ymin, ymax = coords[:, 1].min(), coords[:, 1].max()
pad = 0.01 * max(xmax - xmin, ymax - ymin)
def format_geometry_axes(ax):
ax.set_xlim(xmin - pad, xmax + pad)
ax.set_ylim(ymin - pad, ymax + pad)
ax.set_aspect("equal", adjustable="box")
ax.set_xlabel("x [m]")
ax.set_ylabel("y [m]")
def boundary_edges_from_triangles(cells):
edge_count = {}
for cell in cells:
for edge in (
(cell[0], cell[1]), (cell[1], cell[2]), (cell[2], cell[0])
):
edge = tuple(sorted(edge))
edge_count[edge] = edge_count.get(edge, 0) + 1
return np.array(
[edge for edge, count in edge_count.items() if count == 1],
dtype=np.int32)
boundary_edges = boundary_edges_from_triangles(cells)
def add_mesh_boundary(ax):
for edge in boundary_edges:
points = coords[edge]
ax.plot(
points[:, 0], points[:, 1], color="black", linewidth=0.9, zorder=5
)
def scalar_at_vertices(f, Vh):
dofs = fem.locate_dofs_topological(Vh, 0, vertex_ids)
return f.x.array[dofs].real
def vector_at_vertices(f):
Vvec = fem.functionspace(
msh,
element("Lagrange", msh.basix_cell(), 1, shape=(msh.geometry.dim,)),
)
f_p1 = fem.Function(Vvec)
f_p1.interpolate(f)
dofs = fem.locate_dofs_topological(Vvec, 0, vertex_ids)
bs = Vvec.dofmap.bs
return f_p1.x.array.reshape(-1, bs)[dofs, :].real
def add_matched_colorbar(fig, ax, mappable, label):
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.08)
cbar = fig.colorbar(mappable, cax=cax)
cbar.set_label(label)
return cbar
def plot_scalar_panel(data, title, cmap, colorbar_label):
fig, ax = plt.subplots(
figsize=(6.0, 3.1), dpi=180, constrained_layout=False
)
pcm = ax.tripcolor(tri, data, shading="gouraud", cmap=cmap)
ax.set_title(title)
format_geometry_axes(ax)
add_mesh_boundary(ax)
add_matched_colorbar(fig, ax, pcm, colorbar_label)
plt.show()
def plot_streamline_panel(u_vertex):
fig, ax = plt.subplots(
figsize=(6.0, 3.1), dpi=180, constrained_layout=False
)
interp_ux = mtri.LinearTriInterpolator(tri, u_vertex[:, 0])
interp_uy = mtri.LinearTriInterpolator(tri, u_vertex[:, 1])
x_grid = np.linspace(xmin, xmax, 240)
y_grid = np.linspace(ymin, ymax, 120)
X, Y = np.meshgrid(x_grid, y_grid)
U = interp_ux(X, Y)
W = interp_uy(X, Y)
speed = np.sqrt(U**2 + W**2)
lines = ax.streamplot(
X,
Y,
U,
W,
color=speed,
cmap="viridis",
density=1.9,
linewidth=0.9,
arrowsize=0.75,
)
add_mesh_boundary(ax)
ax.set_title(r"Velocity streamlines")
format_geometry_axes(ax)
add_matched_colorbar(fig, ax, lines.lines, "m/s")
plt.show()
T_vertex = scalar_at_vertices(Th, V)
p_vertex = scalar_at_vertices(ph, ph.function_space)
u_vertex = vector_at_vertices(uh)
Vmag = fem.functionspace(msh, ("Lagrange", 1))
u_mag = fem.Function(Vmag)
interp_points = Vmag.element.interpolation_points
if callable(interp_points):
interp_points = interp_points()
u_mag_expr = fem.Expression(ufl.sqrt(ufl.inner(uh, uh)), interp_points)
u_mag.interpolate(u_mag_expr)
u_mag_vertex = scalar_at_vertices(u_mag, Vmag)
plot_scalar_panel(T_vertex, r"Temperature $T_h$", "inferno", "K")
plot_scalar_panel(p_vertex, r"Pressure $p_h$", "coolwarm", "pressure")
plot_scalar_panel(u_mag_vertex, r"Velocity magnitude $|u_h|$", "viridis", "m/s")
plot_streamline_panel(u_vertex)