Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bc1cc38
🎨 Fix grid subset handling in plasma loading functions
munechika-koyo Jun 20, 2026
fedfdf3
📝 Add 3D grid visualization notebook for ITER JOREK data
munechika-koyo Jun 22, 2026
dd5fde6
🔧 Update environment.yml to include additional dependencies for binder
munechika-koyo Jun 22, 2026
7f07f1f
🎨 Ensure coefficients array is C-contiguous in FourierBezierConstructor
munechika-koyo Jun 22, 2026
08d719e
🎨 Implement re-slicing of IDS via IMAS memory backend with fallback h…
munechika-koyo Jun 22, 2026
93a94f7
✅ Add unit tests for get_ids_time_slice function with fallback handling
munechika-koyo Jun 22, 2026
1db5ba5
🏷️ Fix charge type conversion in load_core_plasma function
munechika-koyo Jun 22, 2026
700a4b0
📝 Fix markdown format
munechika-koyo Jun 23, 2026
896b2ef
🔧 Add PyVista to intersphinx mapping in documentation configuration
munechika-koyo Jun 23, 2026
cfdee51
🎨 Remove unused numpy import and fix docstrings
munechika-koyo Jun 23, 2026
b43f1e0
📝 Update docstrings for interpolator cache parameters in grid classes
munechika-koyo Jun 23, 2026
95f82cf
✨ Add radiation data loading modules and emitter initialization
munechika-koyo Jun 23, 2026
5042ce2
➕ Add python-kaleido and trame-pyvista dependencies for 3D visualization
munechika-koyo Jun 23, 2026
ab14644
✨ Add JOREK-based 3D radiation emitter visualization notebook
munechika-koyo Jun 23, 2026
267ac0e
📝 Update docstring for 'phis' parameter in load_radiation_emitter fun…
munechika-koyo Jun 23, 2026
f214645
✅ Add test for radiation and update conftest
munechika-koyo Jun 23, 2026
68e6268
📝 Update changelog for version 0.5.0 release
munechika-koyo Jun 23, 2026
4de788e
🐛 Fix _FakeIDS class to remove unnecessary inheritance from IDSToplevel
munechika-koyo Jun 24, 2026
e4f5d56
📝 Update release date for version 0.5.0 in changelog
munechika-koyo Jun 24, 2026
5f2902e
🎨 Fix what copilot reviewed
munechika-koyo Jun 24, 2026
0207ec1
🐛 Fix type casting for fourier_periodicity in FourierBezierConstructor
munechika-koyo Jun 24, 2026
7d0b2ee
🐛 Fix type casting for fourier_periodicity in FourierBezierConstructor
munechika-koyo Jun 24, 2026
5274bad
➖ Remove pyvista
munechika-koyo Jun 24, 2026
b2ac3d5
📝 Update grid 3d notebook to use plotly not pyvista
munechika-koyo Jun 24, 2026
816de95
Remove pyvista stuff
munechika-koyo Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ channels:
dependencies:
- cherab-imas
- ultraplot
- plotly
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.5.0] - Unreleased
## [0.5.0] - 2026-06-24

### Added

Expand All @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add notebook.link environment configuration for interactive notebook demos for future use in documentation.
- Add Fourier-Bezier reconstruction module and integrate it into math utilities
- Add JINTRAC/JOREK dataset fixtures and interpolator cache tests
- Add radiation data loading modules and emitter initialization
- Add JOREK-based 3D radiation emitter visualization notebook
- Add 3D grid visualization notebook for ITER JOREK data
- Add unit tests for `get_ids_time_slice` function with fallback handling

### Changed

Expand All @@ -25,11 +29,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Enhance interpolator caching behavior in grid classes and add subset validation in `load_unstruct_grid_2d_extended`
- Update pixi channels and `doc-serve` task to support normal conda-forge settings and dynamic port arguments
- Refactor dataset handling to make `pooch` a required dependency and simplify cache directory handling
- Implement re-slicing of IDS via IMAS memory backend with fallback handling
- Update `environment.yml` to include additional dependencies for binder
- Update docstrings for interpolator cache parameters in grid classes

### Fixed

- Ensure bolometer dataset cache directories are created before use
- Fix total power calculation and exception handling in the emission notebook workflow
- Fix charge type conversion in `load_core_plasma` function
- Fix grid subset handling in plasma loading functions
- Ensure coefficients array is C-contiguous in `FourierBezierConstructor`

### Removed

Expand Down
356 changes: 356 additions & 0 deletions docs/notebooks/radiation/3d_radiation.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# 3D Radiation Emitter\n",
"\n",
"This notebook demonstrates how to create a 3D radiation emitter from IMAS radiation IDS.\n",
"\n",
"The example test data was calculated by JOREK for an ITER disruption scenario."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import ultraplot as uplt\n",
"from imas import DBEntry\n",
"from raysect.optical import World\n",
"from rich import print as rprint\n",
"from rich.table import Table\n",
"\n",
"from cherab.core.math import sample3d_grid\n",
"from cherab.imas.datasets import iter_jorek\n",
"from cherab.imas.emitter import load_radiation_emitter\n",
"\n",
"# Set dark background for plots\n",
"uplt.rc.style = \"dark_background\"\n",
"\n",
"TOGW = 1e-9 # [W] -> [GW]"
]
},
{
"cell_type": "markdown",
"id": "2",
"metadata": {},
"source": [
"## Retrieve ITER JOREK sample data"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3",
"metadata": {},
"outputs": [],
"source": [
"path = iter_jorek()"
]
},
{
"cell_type": "markdown",
"id": "4",
"metadata": {},
"source": [
"## Create 3D radiation emitter from IMAS IDS"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5",
"metadata": {},
"outputs": [],
"source": [
"world = World()\n",
"emitter = load_radiation_emitter(\n",
" path,\n",
" \"r\",\n",
" time=0.0042853,\n",
" parent=world,\n",
" interpolator_cache=\"disk\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6",
"metadata": {},
"source": [
"## Visualize the emitter in 2D\n",
"\n",
"Sample 2D visualization of the radiation function at all toroidal angles."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"R_MIN, R_MAX = 4.0, 8.5\n",
"Z_MIN, Z_MAX = -4.5, 4.4\n",
"RES = 0.01 # resolution of grid in [m]\n",
"n_r = round((R_MAX - R_MIN) / RES) + 1\n",
"n_z = round((Z_MAX - Z_MIN) / RES) + 1\n",
"dr, dz = (R_MAX - R_MIN) / (n_r - 1), (Z_MAX - Z_MIN) / (n_z - 1)\n",
"\n",
"# (r, z) coordinates at phi=0\n",
"r_pts = np.linspace(R_MIN, R_MAX, n_r, endpoint=True)\n",
"z_pts = np.linspace(Z_MIN, Z_MAX, n_z, endpoint=True)\n",
"\n",
"# Toroidal angles to sample the radiation function at\n",
"n_phi = 64\n",
"d_phi = 360 / n_phi\n",
"eps = 1e-6 # small offset to avoid sampling at exactly 0 and 360 degrees\n",
"phis = np.linspace(0 + d_phi + eps, 360 + d_phi - eps, n_phi + 1, endpoint=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8",
"metadata": {},
"outputs": [],
"source": [
"n_phi = len(phis)\n",
"rad = np.zeros((n_r, n_z, n_phi), dtype=float)\n",
"for i, j, k in np.ndindex(n_r, n_z, n_phi):\n",
" rad[i, j, k] = emitter.material.radiation_function(\n",
" r_pts[i] * np.cos(np.deg2rad(phis[k])),\n",
" r_pts[i] * np.sin(np.deg2rad(phis[k])),\n",
" z_pts[j],\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "9",
"metadata": {},
"source": [
"### Cross-sectional view of the radiation\n",
"\n",
"2D poloidal cross-sections of the radiation function at two toroidal angles."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10",
"metadata": {},
"outputs": [],
"source": [
"phi_selected = [95, 330] # toroidal angles to visualize\n",
"\n",
"# Get the maximum value\n",
"vmax = -np.inf\n",
"for phi in phi_selected:\n",
" i_phi = np.argmin(np.abs(phis - phi))\n",
" vmax = max(vmax, np.max(rad[:, :, i_phi] * TOGW))\n",
"\n",
"\n",
"fig, axs = uplt.subplots(ncols=len(phi_selected), spanx=False)\n",
"for i, phi in enumerate(phi_selected):\n",
" i_phi = np.argmin(np.abs(phis - phi))\n",
"\n",
" im = axs[i].pcolormesh(\n",
" r_pts,\n",
" z_pts,\n",
" rad[:, :, i_phi].T * TOGW,\n",
" shading=\"auto\",\n",
" cmap=\"inferno\",\n",
" discrete=False,\n",
" vmax=vmax,\n",
" vmin=0,\n",
" )\n",
" axs[i].format(\n",
" axesedgecolor=f\"C{i}\",\n",
" urtitle=f\"{phis[i_phi]:.1f}°\",\n",
" titleborder=False,\n",
" aspect=\"equal\",\n",
" xlabel=\"$R$ [m]\",\n",
" ylabel=\"$Z$ [m]\",\n",
" )\n",
"\n",
"fig.colorbar(\n",
" im,\n",
" ax=axs,\n",
" loc=\"b\",\n",
" label=\"[GW/m³]\",\n",
" shrink=0.5,\n",
" tickminor=True,\n",
");"
]
},
{
"cell_type": "markdown",
"id": "11",
"metadata": {},
"source": [
"### Z-integrated X-Y projection\n",
"\n",
"The radiation distribution is integrated along the z-axis and projected onto the X-Y plane.\n",
"\n",
"Each toroidal angle at which the cross-section was plotted above is also shown as a colored line in the X-Y projection."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "12",
"metadata": {
"tags": [
"nbsphinx-thumbnail"
]
},
"outputs": [],
"source": [
"fig, ax = uplt.subplots(\n",
" proj=\"polar\",\n",
" dpi=150,\n",
" refwidth=4,\n",
")\n",
"\n",
"im = ax.pcolormesh(\n",
" np.deg2rad(phis),\n",
" r_pts,\n",
" rad.sum(axis=1) * TOGW * dz,\n",
" shading=\"auto\",\n",
" cmap=\"inferno\",\n",
" discrete=False,\n",
" vmin=0,\n",
")\n",
"fig.colorbar(\n",
" im,\n",
" ax=ax,\n",
" label=\"[GW/m²]\",\n",
" loc=\"b\",\n",
" shrink=0.35,\n",
" ticks=2,\n",
" tickminor=True,\n",
" orientation=\"horizontal\",\n",
" pad=-15,\n",
")\n",
"\n",
"for i, phi in enumerate(phi_selected):\n",
" ax.axvline(\n",
" np.deg2rad(phis[np.argmin(np.abs(phis - phi))]),\n",
" color=uplt.set_alpha(f\"C{i}\", 0.5),\n",
" lw=4,\n",
" )\n",
"ax.format(\n",
" rmin=r_pts.min(),\n",
" rmax=r_pts.max(),\n",
" rformatter=\"none\",\n",
" r0=r_pts.min() / r_pts.max(),\n",
" thetalocator=30,\n",
" rlabelpos=0,\n",
" rlines=1,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "13",
"metadata": {},
"source": [
"## Visualize the emitter in 3D\n",
"\n",
"Resample the radiation function between the toroidal angle 270° and 360° (quarter of the torus) in (X, Y, Z) coordinates and visualize the result in 3D.\n",
"\n",
"To plot 3D data, we use the [plotly](https://plotly.com/python/) library. The 3D radiation distribution is visualized as a volume rendering."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "14",
"metadata": {},
"outputs": [],
"source": [
"import plotly.graph_objects as go\n",
"from plotly import io\n",
"\n",
"io.renderers.default = \"notebook\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15",
"metadata": {},
"outputs": [],
"source": [
"dr = dz = 10e-2\n",
"x_pts = np.arange(0, R_MAX, dr)\n",
"y_pts = np.arange(0, -R_MAX, -dr)\n",
"z_pts = np.arange(Z_MIN, Z_MAX, dz)\n",
"X, Y, Z = np.meshgrid(x_pts, y_pts, z_pts)\n",
"\n",
"rad_xyz = np.zeros((len(x_pts), len(y_pts), len(z_pts)), dtype=float)\n",
"for i, j, k in np.ndindex(len(x_pts), len(y_pts), len(z_pts)):\n",
" rad_xyz[i, j, k] = emitter.material.radiation_function(x_pts[i], y_pts[j], z_pts[k])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16",
"metadata": {},
"outputs": [],
"source": [
"fig = go.Figure(\n",
" data=go.Volume(\n",
" x=X.flatten(),\n",
" y=Y.flatten(),\n",
" z=Z.flatten(),\n",
" value=rad_xyz.flatten(),\n",
" isomin=1e8,\n",
" isomax=rad_xyz.max(),\n",
" opacity=0.1, # needs to be small to see through all surfaces\n",
" surface_count=21, # needs to be a large number for good volume rendering\n",
" )\n",
")\n",
"fig.update_layout(\n",
" scene=dict(\n",
" xaxis_title=\"X [m]\",\n",
" yaxis_title=\"Y [m]\",\n",
" zaxis_title=\"Z [m]\",\n",
" aspectmode=\"data\",\n",
" ),\n",
" margin=dict(l=0, r=0, b=0, t=0),\n",
" template=\"plotly_dark\",\n",
")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "docs",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading
Loading