Skip to content

✨ Update Python version and enhance plasma loading features#27

Open
munechika-koyo wants to merge 89 commits intomasterfrom
develop
Open

✨ Update Python version and enhance plasma loading features#27
munechika-koyo wants to merge 89 commits intomasterfrom
develop

Conversation

@munechika-koyo
Copy link
Member

Key changes include:

  • Support for Python 3.14 and update CI configurations.

  • Addition of plotly dependency for 3D visualization of bolometer cameras.

  • Introduction of new data models (SpeciesData, ProfileData, SpeciesComposition, VelocityData) for improved plasma data handling.

  • Refactoring of core and edge profile loading functions to utilize dataclasses for better structure and clarity.

  • Enhancement of documentation and Sphinx configuration for improved clarity and consistency.

  • Fixes for regression in magnetic field loading and path handling in dataset workflows.

Use latest python version for CI
…ta, and SpeciesComposition dataclasses; refactor get_ion_state and get_neutral_state functions for improved clarity and functionality.
…ion calculation in coronal equilibrium; update `__init__.py` to include the new function.
…oved structure and clarity; enhance documentation and streamline data handling.
…tion` dataclass for improved type safety and clarity; enhance parameter documentation.
…ators; enhance `load_core_plasma` function to support atomic data and improve species distribution handling.
…aise a RuntimeError if the toroidal component of the magnetic field is not found; add fallback logic to DDv3
…es; enhance `ProfileData` to include velocity data.
…eper structure retrieval; improve handling of `IDSNumericArray` and add return type checks.
…e handling; remove unused `ProfileInterporater` dataclass and improve error checks for electron properties.
…ture, and velocity interpolating functions; improve warning messages for unsupported species.
…utilize `ProfileData` and `SpeciesComposition` dataclasses; enhance profile loading logic and streamline data handling for species and velocities.
…g and streamline species distribution logic; enhance profile interpolation with `ProfileInterporater` dataclass.
Use data from dataclasses implemented before
To improve plotting functions and add functionality for splitting bundled species profiles
munechika-koyo and others added 26 commits March 20, 2026 19:25
Add test for calling magnetic field function
To link aginst the latest version's cherab
@munechika-koyo munechika-koyo requested a review from Copilot March 23, 2026 22:31
@codecov
Copy link

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 58.39002% with 367 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.49%. Comparing base (4475266) to head (f855d91).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/cherab/imas/observer/bolometer.py 0.00% 116 Missing ⚠️
src/cherab/imas/ids/core_profiles/load_profiles.py 64.42% 40 Missing and 13 partials ⚠️
src/cherab/imas/ids/edge_profiles/load_profiles.py 63.30% 32 Missing and 19 partials ⚠️
src/cherab/imas/ids/common/species.py 74.30% 32 Missing and 5 partials ⚠️
src/cherab/imas/plasma/edge.py 55.55% 24 Missing and 12 partials ⚠️
src/cherab/imas/plasma/blend.py 62.19% 20 Missing and 11 partials ⚠️
src/cherab/imas/plasma/core.py 57.89% 14 Missing and 10 partials ⚠️
src/cherab/imas/ids/common/_model.py 69.69% 5 Missing and 5 partials ⚠️
src/cherab/imas/ids/equilibrium/load_field.py 28.57% 4 Missing and 1 partial ⚠️
src/cherab/imas/plasma/utility.py 91.89% 1 Missing and 2 partials ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #27      +/-   ##
==========================================
+ Coverage   49.50%   50.49%   +0.98%     
==========================================
  Files          43       45       +2     
  Lines        2137     2646     +509     
  Branches      349      452     +103     
==========================================
+ Hits         1058     1336     +278     
- Misses        953     1133     +180     
- Partials      126      177      +51     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the IMAS plasma loading stack by introducing structured datamodels for species/profile data, adding ion-bundle splitting via a coronal-equilibrium solver, and updating tooling/docs/CI for newer Python and notebook workflows.

Changes:

  • Add dataclass-based species/profile models and refactor core/edge/full plasma loaders to use them (incl. optional ion-bundle splitting).
  • Add coronal-equilibrium solver utility plus dataset patching for JINTRAC sample data.
  • Add Plotly-based 3D bolometer camera visualization and update docs/notebooks, CI, and Pixi environments for Python 3.14.

Reviewed changes

Copilot reviewed 40 out of 40 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/plasma/test_equilibrium.py Update magnetic-field return-type expectation and add basic callability checks.
tests/plasma/test_edge.py Consolidate edge plasma tests and add assertions for ion-bundle splitting behavior.
tests/plasma/test_core.py Consolidate core plasma tests and add assertions for ion-bundle splitting behavior.
tests/plasma/test_blend.py Consolidate blended plasma tests and add assertions for ion-bundle splitting behavior.
tests/plasma/conftest.py Add fixtures for additional sample datasets (SOLPS/JOREK).
tests/conftest.py Add autouse OpenADAS repository population hook for tests.
src/cherab/imas/plasma/utility.py Introduce ProfileInterpolator + ZERO_VELOCITY and update species-warning utility for new datamodels.
src/cherab/imas/plasma/equilibrium.py Adjust Raysect interpolator imports/types; update load_magnetic_field() return type hint.
src/cherab/imas/plasma/edge.py Refactor edge plasma loading to new dataclasses, add ion-bundle splitting controls + atomic data injection.
src/cherab/imas/plasma/core.py Refactor core plasma loading to new dataclasses, add ion-bundle splitting controls + atomic data injection.
src/cherab/imas/plasma/blend.py Refactor core/edge blending pipeline to dataclass interpolators and new composition handling.
src/cherab/imas/observer/bolometer.py Add Plotly-based 3D camera visualize() helper and supporting geometry extraction utilities.
src/cherab/imas/math/functions/vector_functions.pyx Minor docstring grammar tweaks.
src/cherab/imas/math/functions/vector_functions.pyi Minor docstring grammar tweaks.
src/cherab/imas/ids/wall/load3d.py Improve docstring formatting with proper Sphinx code-block.
src/cherab/imas/ids/wall/load2d.py Improve docstring typing markup consistency.
src/cherab/imas/ids/equilibrium/load_field.py Add b_field_phi/b_field_tor fallback logic and deprecation warning.
src/cherab/imas/ids/edge_profiles/load_profiles.py Replace dict-based profiles with dataclasses; implement optional ion-bundle splitting via coronal equilibrium.
src/cherab/imas/ids/core_profiles/load_profiles.py Add GridData dataclass; replace dict-based profiles with dataclasses; implement optional ion-bundle splitting.
src/cherab/imas/ids/core_profiles/init.py Export GridData in package API.
src/cherab/imas/ids/common/species.py Add species/profile/composition dataclasses and replace legacy ID frozenset logic.
src/cherab/imas/ids/common/_model.py Add coronal-equilibrium solver implementation.
src/cherab/imas/ids/common/init.py Export solve_coronal_equilibrium.
src/cherab/imas/ggd/unstruct_2d_extend_mesh.py Docstring section ordering tweak.
src/cherab/imas/datasets/_registry.py Update dataset registry entries (incl. patched JINTRAC variant).
src/cherab/imas/datasets/_patch.py Add JINTRAC patching utility to fix bundled ion z-range metadata.
src/cherab/imas/datasets/_fetchers.py Apply JINTRAC patch automatically after fetch; tighten fetcher dependency guard.
pyproject.toml Add Python 3.13/3.14 classifiers.
pixi.toml Add Python 3.14 build variant, add plotly + nbstripout, restructure tasks/environments.
docs/source/intro.md Update citation DOI.
docs/source/examples.md Adjust examples globbing path.
docs/source/conf.py Sphinx napoleon config tweak; simplify HTML title.
docs/notebooks/plasma/full_plasma.ipynb Update narrative, add ADAS download guidance, adjust plotting utilities, add Zeff example.
docs/notebooks/plasma/emission.ipynb Rework OpenADAS setup guidance and emission workflow; update plotting and sampling strategy.
docs/notebooks/plasma/edge_plasma.ipynb Update plotting stack (ultraplot) and add ion-bundle splitting demonstration.
docs/notebooks/misc/fractional_abundances.ipynb New notebook demonstrating coronal-equilibrium fractional abundances.
CHANGELOG.md Add 0.4.0 release notes entry.
.lefthook.yaml Update pre-commit job structure; add nbstripout; adjust CI skips.
.github/workflows/docs.yml Remove Pixi version pin.
.github/workflows/ci.yaml Switch matrix env from py313 to py314; remove Pixi version pin.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +172 to +173
if z_min >= z_max:
raise ValueError("z_min cannot be greater than or equal to z_max.")
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

solve_coronal_equilibrium() currently rejects z_min == z_max (raises when z_min >= z_max). A single charge state (or a bundle of exactly one state) is a valid edge case and should return an array with one row equal to density (or at least be allowed by changing the validation to z_min > z_max).

Suggested change
if z_min >= z_max:
raise ValueError("z_min cannot be greater than or equal to z_max.")
if z_min > z_max:
raise ValueError("z_min cannot be greater than z_max.")

Copilot uses AI. Check for mistakes.
Comment on lines +650 to +652
(local_x_axis, "X Axis", "rgb(256, 0, 0)"),
(local_y_axis, "Y Axis", "rgb(0, 256, 0)"),
(local_z_axis, "Z Axis", "rgb(0, 0, 256)"),
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plotly RGB strings should use channel values in the 0–255 range. The current axis colors use 256, which can lead to invalid color parsing; use 255 instead (or hex colors).

Suggested change
(local_x_axis, "X Axis", "rgb(256, 0, 0)"),
(local_y_axis, "Y Axis", "rgb(0, 256, 0)"),
(local_z_axis, "Z Axis", "rgb(0, 0, 256)"),
(local_x_axis, "X Axis", "rgb(255, 0, 0)"),
(local_y_axis, "Y Axis", "rgb(0, 255, 0)"),
(local_z_axis, "Z Axis", "rgb(0, 0, 255)"),

Copilot uses AI. Check for mistakes.
Comment on lines +523 to +538
camera.parent = world

# === Local Axis ===
local_origin = ORIGIN + sum(
[ORIGIN.vector_to(foil.slit.centre_point) for foil in camera.foil_detectors],
Vector3D(0, 0, 0),
) / len(camera.foil_detectors)
local_z_axis: Vector3D = sum(
[foil.slit.normal_vector.normalise() for foil in camera.foil_detectors], Vector3D(0, 0, 0)
).normalise()
local_x_axis: Vector3D = sum(
[foil.slit.basis_x.normalise() for foil in camera.foil_detectors], Vector3D(0, 0, 0)
).normalise()

local_y_axis = local_z_axis.cross(local_x_axis).normalise()

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visualize() mutates camera.parent to attach it to a temporary World, but restoration happens only on the normal return path. If any exception is raised during plotting/ray tracing, camera.parent will remain modified. Wrap the temporary parent assignment in a try/finally to guarantee restoration (and consider similarly scoping temporary scene objects).

Suggested change
camera.parent = world
# === Local Axis ===
local_origin = ORIGIN + sum(
[ORIGIN.vector_to(foil.slit.centre_point) for foil in camera.foil_detectors],
Vector3D(0, 0, 0),
) / len(camera.foil_detectors)
local_z_axis: Vector3D = sum(
[foil.slit.normal_vector.normalise() for foil in camera.foil_detectors], Vector3D(0, 0, 0)
).normalise()
local_x_axis: Vector3D = sum(
[foil.slit.basis_x.normalise() for foil in camera.foil_detectors], Vector3D(0, 0, 0)
).normalise()
local_y_axis = local_z_axis.cross(local_x_axis).normalise()
try:
camera.parent = world
# === Local Axis ===
local_origin = ORIGIN + sum(
[ORIGIN.vector_to(foil.slit.centre_point) for foil in camera.foil_detectors],
Vector3D(0, 0, 0),
) / len(camera.foil_detectors)
local_z_axis: Vector3D = sum(
[foil.slit.normal_vector.normalise() for foil in camera.foil_detectors],
Vector3D(0, 0, 0),
).normalise()
local_x_axis: Vector3D = sum(
[foil.slit.basis_x.normalise() for foil in camera.foil_detectors],
Vector3D(0, 0, 0),
).normalise()
local_y_axis = local_z_axis.cross(local_x_axis).normalise()
finally:
camera.parent = prev_parent

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +16
@pytest.fixture(scope="session", autouse=True)
def populate_openadas_repository():
"""Fixture to populate the OpenADAS repository before running tests."""
adas = OpenADAS(missing_rates_return_null=False)
try:
adas.ionisation_rate(neon, 1)
except Exception:
print("Populating OpenADAS repository...")
populate()
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This autouse session fixture can trigger cherab.openadas.repository.populate() during test collection, which may download data from the network and make the test suite flaky/offline-unfriendly. Consider avoiding network access in tests (e.g., ship minimal ADAS test data, use a pre-populated cache in CI, or skip these tests when data is unavailable), and catch a narrower exception than Exception so unexpected errors aren’t silently turned into downloads.

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +80

The ion bundle species are split into their constituent charge states using `.solve_coronal_equilibrium`.
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says ion bundle species “are split into their constituent charge states”, but the implementation is best-effort: splitting depends on split_ion_bundles and available atomic data, and failures fall back to leaving bundles unsplit with a warning. Please clarify the docstring to reflect the conditional/partial behavior.

Copilot uses AI. Check for mistakes.
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