Skip to content

feat: maps of GGD values on unstructured grids - #17

Merged
icyspirit merged 4 commits into
mainfrom
feat/unstructured-ggd
Sep 30, 2026
Merged

icyspirit merged 4 commits into
mainfrom
feat/unstructured-ggd

Conversation

@icyspirit

@icyspirit icyspirit commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

Maps on unstructured GGD grids.

  • Which grids: an unstructured grid in the (R, Z) plane, that is, one space whose nodes are in R and Z, with 2D cells, as edge codes such as SOLPS write them.
  • What is drawn: the values of a GGD quantity on such a grid are a map of its cells. Each cell is filled with the color of its value, and there is a color bar.
    • Example path: edge_profiles/ggd/electrons/temperature[0]/values.
    • Panels of kind auto pick the map by themselves, so a path from the IDS browser works too.
  • Values on nodes or cells:
    • Values on the cells are theirs.
    • Values on the nodes give each cell the mean of its nodes.
    • The grid subset of the values says what they are on: from its elements, or, for all the nodes (1) and all the cells (5), from its identifier, since the Data Dictionary declares those implicitly.
    • Values on other subsets (e.g. edges), or values whose size does not fit, are drawn along their index, as before.
  • Which grid:
    • the grid of the GGD itself (as in distribution_sources);
    • or, in IDSs such as edge_profiles, the grid_ggd slice at the time of the values.
    • R and Z are read in the order the coordinates of the space give, in DD 4 (identifiers) and DD 3 (indices).
  • Cell order: a cell whose nodes are not in order around it (e.g. the corners of a quadrilateral row by row) is put in order.
  • Speed:
    • Lazy loading reads tens of thousands of grid objects one read each: 6.7 s for 8000 cells.
    • When the IDS was read lazily from a netCDF file, the grid arrays are now read at once: 0.08 s for 8000 cells, 0.57 s for 40 000. Other sources are read one by one, as before.
    • The last few meshes are kept.
  • Drawing:
    • The cells of each color are drawn as one path, and the drawing is kept until the values, the levels or the colors change: 0.5 s for 40 000 cells.
    • The color bar controls it as it does an image (levels by dragging).
    • The status bar, and the crosshair of the Point mode, give the value of the cell under the mouse.
    • Several sources or subsets go in the Show selector, and the time bar steps through the slices.
    • The contours option does not apply: cells are always filled.

Arithmetic and comparison of sources (with #16, now on main):

  • Values on the cells of a grid combine:
    • with numbers, and abs(), cell by cell;
    • with values on the same cells (the same points and cells, e.g. two runs of an edge code), at the closest slice of the second within its time range. A slice on another grid gives NaN, and grids with different cells are an error;
    • with a time trace, at the time of each slice (and a static grid then takes the times of the trace).
  • deriv(), other maps and profiles are refused with an error.
  • A panel showing sources relative to the first gives B − A or B / A of such values. Before this, it failed with an AttributeError. The boundaries drawn over the values keep the line style of their sources (A, B).

Color bars: they now show only the major ticks, with compact exponents (2e19 for 2e+19), and they are 8 px wider. Values such as densities were cut off at the right edge before.

Demo:

  • The demo gains edge_profiles: the electron temperature on the cells and the density on the nodes of a ring of 96 × 12 cells, from the plasma boundary to the first wall.
  • The ring uses heterogeneous time: one grid for five time slices.

Notes

  • MeshSeries is a map (kind == "map"), so panels treat it like other maps. It gives the Mesh (points, cells in order, centers) and a value per cell for each time slice.
  • Fast path on internals: the netCDF fast path uses imas-python's lazy netCDF context (IDSStructArray._lazy_ctx, nc2ids._lazy_map).
    • It is guarded: any failure falls back to reading element by element.
    • The file pads such arrays to the longest one of the same kind, so only the first len(array) rows are taken.
    • A test checks that both paths give the same grid.
  • Main merged in:
  • Comparing two grids of 40 000 cells from two sources takes 12 ms, and B − A of a slice 21 ms.

Test plan

  • QT_QPA_PLATFORM=offscreen pytest: 242 passed in the dev environment, a clean PySide6 6.11 environment and a PyQt5 5.15 environment, after merging main.
  • New tests:
    • the demo in DD 4: values on cells (sizes, times, units, axes, the hottest cell), on nodes (the mean over each cell's nodes), and at one slice; cell_at in the plasma and beyond the wall;
    • the demo in DD 3: integer coordinate types;
    • the netCDF fast path: equal to reading one by one, for node geometry, cell nodes and subset elements;
    • synthetic grids:
      • cells given row by row, reordered, next to cells already in order and a triangle;
      • Z-first coordinates;
      • an explicit subset of one cell;
      • node values averaged per cell;
      • edges drawn as a profile;
      • a grid per time slice, each with its own values;
      • values that do not fit;
    • arithmetic on cells:
      • cell by cell, with numbers, and with the same cells on points of their own;
      • the closest slice of the second, within its time range;
      • a static grid at the times of a trace;
      • grids of different cells (error), a grid that moves (NaN), and a structured map and deriv() (refused);
      • on the demo: density over temperature, abs() over a trace, and B − A of the two demo sources;
      • the three new arithmetic tests and the new GUI test fail without the fix (AttributeError);
    • GUI:
      • a MeshItem with the color bar levels of the values, the status bar value of the cell, the color of the hottest cell on screen, the color bar setting the levels, the time changing the values, and the crosshair giving the value of the cell (and none in the plasma, where there is no cell);
      • B − A and B / A of the demo sources, with the color bar title, and the line-style legend of the boundaries (A, B);
    • compact exponents.
  • Timings measured on 8000- and 40 000-cell grids. Screenshots checked:
    • DD 4 and DD 3 sources with the boundary and the wall over the temperature;
    • the density on nodes;
    • the crosshair over the cells;
    • B − A of the temperature, and the temperature per ampere.
  • pyflakes src tests reports nothing.

🤖 Generated with Claude Code

https://claude.ai/code/session_018aVekYzCRLPE71Mm6UH8Zp


Generated by Claude Code

The values of a GGD quantity on an unstructured grid in the (R, Z) plane -- one
space whose nodes are in R and Z, with 2D cells, as edge codes such as SOLPS
write them -- are a map of the cells of the grid, each filled with the color of
its value, with a color bar.

- Values on the cells are theirs; values on the nodes give each cell the mean
  of its nodes. What the values are on comes from their grid subset: from its
  elements, or, for all the nodes (1) and all the cells (5), from its identifier,
  which the Data Dictionary declares implicitly. Values on other subsets (e.g.
  edges), or that do not fit, are drawn along their index as before.
- The grid is that of the GGD (e.g. distribution_sources), or, in IDSs such as
  edge_profiles, the one of grid_ggd at the time of the values. The nodes are R
  and Z in the order the coordinates of the space say.
- The nodes of a cell are put in order around it when their order crosses
  itself (e.g. the corners of a quadrilateral row by row).
- Reading tens of thousands of objects one by one, as lazy loading does, takes a
  read each: from a netCDF file, the arrays of the grid are read at once (an
  8000-cell grid in 0.08 s instead of 6.7 s), else one by one as before. The
  last meshes are kept.
- The cells of a color are drawn as one path, kept until the values, the levels
  or the colors change; the color bar controls them as it does an image. The
  status bar gives the value of the cell under the mouse.

Color bars: their ticks show only the major values, and exponents compactly
(2e19 for 2e+19), and they are 8 px wider, so that values such as densities are
not cut off.

The demo gains edge_profiles: the electron temperature on the cells and the
density on the nodes of a ring of 96 x 12 cells from the plasma boundary to the
first wall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aVekYzCRLPE71Mm6UH8Zp
Main now has the crosshair (#14) and the device template (#15). The
crosshair gives the value of the image through Panel._image_value_at,
so it also says the value of the cell of an unstructured grid under it,
and the status bar keeps the coordinates rounded to the view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aVekYzCRLPE71Mm6UH8Zp
Main now has the arithmetic of signals and the sources relative to the
first (#16). The conflicts were side by side: imports, helpers of the
panel, and tests appended at the same place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aVekYzCRLPE71Mm6UH8Zp
With the arithmetic of signals (#16) on main, values on the cells of an
unstructured grid can now be computed with:

- numbers and abs(), cell by cell;
- values on the same cells (the same points and cells, e.g. two runs of
  an edge code), at the closest slice of the second within its time
  range, and NaN for a slice on another grid; grids of different cells
  are an error;
- a time trace, at the time of each slice, and a static grid at the
  times of a trace.

deriv() and other maps or profiles are refused with an error. A panel
showing sources relative to the first thus gives B − A or B / A of such
values, where it failed with an AttributeError. Its line-style legend
now counts these values as an image, as it does maps, so the boundaries
over them are named after their sources (A, B), not "B − A".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aVekYzCRLPE71Mm6UH8Zp
@icyspirit
icyspirit merged commit 5d17d23 into main Sep 30, 2026
2 checks passed
@icyspirit
icyspirit deleted the feat/unstructured-ggd branch September 30, 2026 00:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants