This page was generated from docs/notebooks/radiation/grid_3d.ipynb.
Interactive online version: Binder badge. Download notebook.

3D Grid VisualizationΒΆ

This notebook demonstrates how to load and visualize a 3-D grid from Generalized Grid Description (GGD) in IMAS.

The example test data was calculated by JOREK for an ITER disruption scenario.

[1]:
import numpy as np
import ultraplot as uplt
from imas import DBEntry
from rich import print as rprint
from rich.table import Table

from cherab.imas.datasets import iter_jorek
from cherab.imas.ids.common import get_ids_time_slice
from cherab.imas.ids.common.ggd import load_grid

# Set dark background for plots
uplt.rc.style = "dark_background"

Retrieve ITER JOREK sample dataΒΆ

[2]:
path = iter_jorek()
Downloading file 'iter_disruption_113112_1.nc' from 'doi:10.5281/zenodo.17062699/iter_disruption_113112_1.nc' to '/home/runner/.cache/cherab/imas'.

Retrieve the GGD data belonging to the radiation IDS.

[3]:
with DBEntry(path, "r") as entry:
    ids = get_ids_time_slice(entry, "radiation")
    grid = load_grid(ids.grid_ggd[0])
17:12:33 INFO     Parsing data dictionary version 4.1.1 @dd_zip.py:89
17:12:33 INFO     Parsing data dictionary version 4.0.0 @dd_zip.py:89
/tmp/ipykernel_3725/3961500530.py:2: RuntimeWarning: The 'get_slice' method is not implemented for the URI '/home/runner/.cache/cherab/imas/iter_disruption_113112_1.nc'. Falling back to 'get' and re-slicing via the IMAS memory backend.
  ids = get_ids_time_slice(entry, "radiation")

Show the grid specification

[4]:
table = Table(show_header=False, title="Grid specification")
table.add_row("Grid name", str(grid.name))
table.add_row("Number Faces", str(grid.num_faces))
table.add_row("Number Toroidal", str(grid.num_toroidal))
table.add_row("Number Cell", str(grid.num_cell))
table.add_row("Shape of vertices array", str(grid.vertices.shape))
table.add_row("Shape of cells array", str(grid.cells.shape))
rprint(table)
           Grid specification            
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Grid name               β”‚ JOREK mesh  β”‚
β”‚ Number Faces            β”‚ 6398        β”‚
β”‚ Number Toroidal         β”‚ 64          β”‚
β”‚ Number Cell             β”‚ 409472      β”‚
β”‚ Shape of vertices array β”‚ (418304, 3) β”‚
β”‚ Shape of cells array    β”‚ (409472, 8) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Visualize the cross-section of the gridΒΆ

Show the grid lines and cell volume map in the poloidal cross-section of the grid.

[5]:
fig, axs = uplt.subplots(ncols=2)
# Plot the grid mesh in 2D cross-section
grid.plot_mesh(ax=axs[0], edgecolor="red")

# Plot the cell volume map in 2D cross-section
ax = grid.plot_mesh(ax=axs[1], data=grid.cell_volume[: grid.num_faces])
ax.collections[0].set_cmap("magma")

ax.colorbar(
    ax.collections[0],
    tickdir="out",
    loc="lr",
    orientation="vertical",
    ticklabelsize="small",
    length=5,
    frame=False,
)
ax.format(
    urtitle="Cell volume [m$^3$]",
    titleborder=False,
)

# Format the axes
axs.format(xlocator=1, ylocator=1)
../../_images/notebooks_radiation_grid_3d_9_0.png

Plot center points of cells

[6]:
# Extract cell center points for one toroidal slice
cell_centers = grid.cell_centre[: grid.num_faces, :]

# Calculate the (r, z) coordinates
r_coords = np.hypot(cell_centers[:, 0], cell_centers[:, 1])
z_coords = cell_centers[:, 2]

fig, ax = uplt.subplots()

# Plot grid and cell centers in the poloidal cross-section
grid.plot_mesh(
    ax=ax,
    edgecolor="red",
)
ax.scatter(r_coords, z_coords, s=1, c="C0")
ax.format(
    xlocator=1,
    ylocator=1,
)

# Zoom in around divertor region
ix = ax.inset(
    [7.0, -9, 6, 6],
    transform="data",
    zoom_kw={"ec": "grape3", "ls": "--", "lw": 2},
)
grid.plot_mesh(ax=ix, edgecolor="red")
ix.scatter(r_coords, z_coords, s=1, c="C0")
ix.format(
    xlim=(ax.get_xlim()[0], 6.2),
    ylim=(ax.get_ylim()[0], -3.0),
    aspect="equal",
    color="grape9",
    linewidth=1.5,
    ticklabelweight="bold",
    xlocator=1,
    ylocator=1,
    xformatter="none",
    yformatter="none",
    xlabel="",
    ylabel="",
)
../../_images/notebooks_radiation_grid_3d_11_0.png

Cut out quarter of the torusΒΆ

[7]:
grid_cut = grid.subset(np.arange(grid.num_faces * grid.num_toroidal // 4))

Visualize the 3D grid in a quarter of the torus. plotly is used to visualize the tetrahedral mesh in the notebook.

[8]:
import plotly.graph_objects as go
[9]:
tetra = grid_cut.tetrahedra
faces = np.vstack(
    [
        tetra[:, [0, 1, 2]],
        tetra[:, [0, 1, 3]],
        tetra[:, [0, 2, 3]],
        tetra[:, [1, 2, 3]],
    ]
)

# Keep only boundary faces (faces appearing exactly once).
faces_sorted = np.sort(faces, axis=1)
_, inverse, counts = np.unique(faces_sorted, axis=0, return_inverse=True, return_counts=True)
surface_faces = faces[counts[inverse] == 1]

# Build unique boundary edges from boundary triangles.
tri_edges = np.vstack(
    [
        surface_faces[:, [0, 1]],
        surface_faces[:, [1, 2]],
        surface_faces[:, [2, 0]],
    ]
)
boundary_edges = np.unique(np.sort(tri_edges, axis=1), axis=0)

verts = grid_cut.vertices
x, y, z = verts[:, 0], verts[:, 1], verts[:, 2]

# Customize edge appearance here.
edge_color = "black"
edge_width = 1.5

# Convert edge index pairs to line segments separated by NaN for Plotly.
edge_xyz = verts[boundary_edges]
xe = np.column_stack(
    [edge_xyz[:, 0, 0], edge_xyz[:, 1, 0], np.full(len(boundary_edges), np.nan)]
).ravel()
ye = np.column_stack(
    [edge_xyz[:, 0, 1], edge_xyz[:, 1, 1], np.full(len(boundary_edges), np.nan)]
).ravel()
ze = np.column_stack(
    [edge_xyz[:, 0, 2], edge_xyz[:, 1, 2], np.full(len(boundary_edges), np.nan)]
).ravel()

fig = go.Figure(
    data=[
        go.Mesh3d(
            x=x,
            y=y,
            z=z,
            i=surface_faces[:, 0],
            j=surface_faces[:, 1],
            k=surface_faces[:, 2],
            color="deepskyblue",
            flatshading=True,
            hoverinfo="skip",
            showscale=False,
            name="Surface",
        ),
        go.Scatter3d(
            x=xe,
            y=ye,
            z=ze,
            mode="lines",
            line=dict(color=edge_color, width=edge_width),
            name="Surface boundary",
            hoverinfo="skip",
        ),
    ]
)

fig.update_layout(
    title="Quarter-torus tetrahedral mesh",
    width=500,
    height=500,
    scene=dict(
        xaxis_title="X [m]",
        yaxis_title="Y [m]",
        zaxis_title="Z [m]",
        aspectmode="data",
    ),
    margin=dict(l=0, r=0, b=0, t=30),
    scene_camera=dict(
        eye=dict(x=0.0, y=-2.0, z=0.25),
    ),
    template="plotly_dark",
)
fig.show(
    renderer="png",
)
../../_images/notebooks_radiation_grid_3d_16_0.png