Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8b9c741
Update dependencies: upgrade Cython to 3.1 and numpy to >=2; adjust r…
munechika-koyo Oct 13, 2025
d059ed8
Update CI configuration: switch to ubuntu-latest, adjust Python and n…
munechika-koyo Oct 13, 2025
3ec32a1
Refactor: Correct spelling of 'Targetted' to 'Targeted' in bolometry …
munechika-koyo Oct 13, 2025
10d690f
Fix remaining typo `targetted` to `targeted`
munechika-koyo Oct 13, 2025
662eb04
Rename filename
munechika-koyo Oct 13, 2025
af3d5c2
Fix numpy v2 error related thing
munechika-koyo Oct 13, 2025
3d53694
Update ci.yml
munechika-koyo Oct 13, 2025
58708e0
Remove Python 3.14 from CI matrix until pyopencl support is available
munechika-koyo Oct 13, 2025
db60923
Merge branch 'development' into feature/raysect-0.9-compat
munechika-koyo Oct 13, 2025
1a63b6d
Update CHANGELOG.md for Release 1.6.0: add API changes and new features
munechika-koyo Oct 14, 2025
fde76c4
Revert "Fix remaining typo `targetted` to `targeted`"
munechika-koyo Oct 16, 2025
97a634c
Revert "Rename filename"
munechika-koyo Oct 16, 2025
60d76bf
Fix typo: change `targetted` to `targeted` in the internal code of Bo…
munechika-koyo Oct 16, 2025
ab0b0fd
Bump version to 1.6.0.dev2
munechika-koyo Oct 16, 2025
3dbad49
Fix typo: change `targetted` to `targeted` in generate_segmented_cyli…
munechika-koyo Oct 16, 2025
503aa22
Restore `targetted_path_prob` property
munechika-koyo Oct 16, 2025
5182d8b
Restore `targetted_path_prob` in test
munechika-koyo Oct 16, 2025
362a6e3
Remove API changes section
munechika-koyo Oct 16, 2025
21d6449
Fix typo
munechika-koyo Oct 16, 2025
9b20d37
Revert import lines in setup.py
munechika-koyo Oct 17, 2025
780364f
Update numpy version requirement to 2.0 in setup.py
munechika-koyo Oct 17, 2025
cae5114
Update numpy version requirement to 2.0 in requirements.txt
munechika-koyo Oct 17, 2025
8d48d54
Add deprecation warnings for `targetted_path_prob` property in Bolome…
munechika-koyo Oct 27, 2025
5810951
Re-enable Python 3.14 in CI workflow matrix
munechika-koyo Nov 3, 2025
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
9 changes: 4 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ on:
jobs:
tests:
name: Run tests
runs-on: ubuntu-22.04 # Needed for Python 3.7 compatibility
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
numpy-version: ["oldest-supported-numpy", "'numpy<2'"]
Copy link
Member

Choose a reason for hiding this comment

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

I think we should continue supporting (and therefore testing against) the oldest Numpy available for each Python version. Not all environments (HPC or other experiment analysis clusters for example) may be amenable to upgrading numpy, possibly due to other packages, so Cherab should where possible not require features in very recent Numpy versions. Testing against old versions is to guard against accidentally using new Numpy functions where they're not always required.

oldest-supported-numpy was a convenient way of doing this, but for now the matrix should be updated with the oldest numpy version supported by each Python release. Awkward the first time, but should be quick to modify on each new Python release.

python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout code
uses: actions/checkout@v2
Expand All @@ -23,9 +22,9 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies
run: python -m pip install --prefer-binary cython~=3.0 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4"
run: python -m pip install --prefer-binary setuptools cython~=3.1 numpy>=2 scipy matplotlib "pyopencl[pocl]>=2022.2.4"
- name: Install Raysect from pypi
run: pip install raysect==0.8.1.*
run: pip install raysect==0.9.*
Copy link
Member

Choose a reason for hiding this comment

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

I don't think there's a need any longer to install raysect separate from the rest of the dependencies. A long time ago it had to come from Github sometimes and PyPI other times, so made sense to deal with it differently, but it's about time this was cleaned up. Put it in the "Install Python dependencies" step.

Also, should be raysect==0.9.1 since that's what we explicitly depend on in pyproject.toml and setup.py.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree with you.
Moreover, we might be better off utilizing the dependency info that is configured in only one place (such as setup.py in this case).
Should we use the following command?:

python -m pip install -e ".[opencl]"

- name: Build cherab
run: dev/build.sh
- name: Run tests
Expand Down
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ New:
* Add Function6D framework. (#478)
* Add e_field attribute to Plasma object for electric field vector. (#465)
* Add Integrator2D base class for integration of two-dimensional functions. (#472)
* Support Raysect 0.9. (#486)
* Test against Python 3.9, 3.10, 3.11, 3.12, 3.13 and latest released Numpy. Drop Python 3.7, 3.8 and older Numpy from tests. (#486)

Release 1.5.0 (27 Aug 2024)
-------------------
Expand Down Expand Up @@ -134,7 +136,7 @@ API changes:

New:
* Merged cherab-openadas package into the core cherab package to simplify installation.
* Beam object uses a cone primitive instead of a cylinder for the bounding volume of divergent beams.
* Beam object uses a cone primitive instead of a cylinder for the bounding volume of divergent beams.
* Added Clamp functions.
* Added ThermalCXRate.
* Added optimised ray transfer grid calculation tools.
Expand Down Expand Up @@ -162,7 +164,7 @@ New:

Bug fixes:
* Improved handling on non c-order arrays in various methods.
* Numerous minor bug fixes (see commit history)
* Numerous minor bug fixes (see commit history)


Release 1.0.1 (1 Oct 2018)
Expand Down
2 changes: 1 addition & 1 deletion cherab/core/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.6.0.dev1
1.6.0.dev2
2 changes: 1 addition & 1 deletion cherab/core/model/laser/profile.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ def generate_segmented_cylinder(radius, length):
Generates a segmented cylindrical laser geometry

Approximates a long cylinder with a cylindrical segments to optimize
targetted and importance sampling. The height of a cylinder segments is roughly
targeted and importance sampling. The height of a cylinder segments is roughly
2 * cylinder radius.

:return: List of cylinders
Expand Down
61 changes: 49 additions & 12 deletions cherab/tools/observers/bolometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
# under the Licence.

from enum import Enum
from warnings import warn
import functools
import numpy as np

from raysect.core import Node, translate, rotate_basis, Point3D, Vector3D, Ray as CoreRay, Primitive, World
from raysect.core.math.sampler import TargettedHemisphereSampler, RectangleSampler3D
from raysect.core.math.sampler import TargetedHemisphereSampler, RectangleSampler3D
from raysect.primitive import Box, Cylinder, Subtract, Union
from raysect.optical.observer import PowerPipeline0D, RadiancePipeline0D, \
SpectralPowerPipeline0D, SpectralRadiancePipeline0D, SightLine, TargettedPixel
SpectralPowerPipeline0D, SpectralRadiancePipeline0D, SightLine, TargetedPixel
from raysect.optical.observer import PowerPipeline2D, RadiancePipeline2D, \
SpectralPowerPipeline2D, SpectralRadiancePipeline2D, TargettedCCDArray
SpectralPowerPipeline2D, SpectralRadiancePipeline2D, TargetedCCDArray
from raysect.optical.material.material import NullMaterial
from raysect.optical.material import AbsorbingSurface

Expand Down Expand Up @@ -213,7 +214,7 @@ class BolometerSlit(Node):
larger than the slit dx and dy, which can cause partial occlusion of
nearby primitives. It also relies on no rays being launched with directions
outside the solid angle of the aperture's bounding sphere: depending on the
foil-slit distance and slit size, and also the foil's targetted_path_prob,
foil-slit distance and slit size, and also the foil's targeted_path_prob,
this may not be guaranteed. Supplying a proper mesh geometry for the camera
is recommended instead of using a CSG aperture.

Expand Down Expand Up @@ -351,7 +352,7 @@ def curvature_radius(self):
return self._curvature_radius


class BolometerFoil(TargettedPixel):
class BolometerFoil(TargetedPixel):
"""
A rectangular foil bolometer detector.

Expand Down Expand Up @@ -447,7 +448,7 @@ def __init__(self, detector_id, centre_point, basis_x, dx, basis_y, dy, slit,
translation = translate(centre_point.x, centre_point.y, centre_point.z)
rotation = rotate_basis(normal_vec, basis_y)

super().__init__([slit.target], targetted_path_prob=1.0,
super().__init__([slit.target], targeted_path_prob=1.0,
pixel_samples=1000, x_width=dx, y_width=dy, spectral_bins=1, quiet=True,
parent=parent, transform=translation * rotation, name=detector_id)

Expand Down Expand Up @@ -516,6 +517,24 @@ def accumulate(self, value):
# Discard any samples from previous accumulate behaviour
pipeline.value.clear()

@property
def targetted_path_prob(self):
warn(
"The 'targetted_path_prob' property is deprecated, use 'targeted_path_prob' instead.",
DeprecationWarning,
stacklevel=2
)
return self._targeted_path_prob

@targetted_path_prob.setter
def targetted_path_prob(self, value):
warn(
"The 'targetted_path_prob' property is deprecated, use 'targeted_path_prob' instead.",
DeprecationWarning,
stacklevel=2
)
self.targeted_path_prob = value

def as_sightline(self):
"""
Constructs a SightLine observer for this bolometer.
Expand Down Expand Up @@ -661,8 +680,8 @@ def calculate_etendue(self, ray_count=10000, batches=10, max_distance=1e999):
# generate bounding sphere and convert to local coordinate system
sphere = target.bounding_sphere()
spheres = [(sphere.centre.transform(self.to_local()), sphere.radius, 1.0)]
# instance targetted pixel sampler to sample directions
targetted_sampler = TargettedHemisphereSampler(spheres)
# instance targeted pixel sampler to sample directions
targeted_sampler = TargetedHemisphereSampler(spheres)
# instance rectangle pixel sampler to sample origins
point_sampler = RectangleSampler3D(width=self.x_width, height=self.y_width)

Expand All @@ -671,8 +690,8 @@ def etendue_single_run(_):
origins = point_sampler(samples=ray_count)
passed = 0.0
for origin in origins:
# obtain targetted vector sample
direction, pdf = targetted_sampler(origin, pdf=True)
# obtain targeted vector sample
direction, pdf = targeted_sampler(origin, pdf=True)
path_weight = R_2_PI * direction.z / pdf
# Transform to world space
origin = origin.transform(detector_transform)
Expand Down Expand Up @@ -701,7 +720,7 @@ def etendue_single_run(_):
return etendue, etendue_error


class BolometerIRVB(TargettedCCDArray):
class BolometerIRVB(TargetedCCDArray):
"""
A rectangular infra red video bolometer (IRVB).

Expand Down Expand Up @@ -784,7 +803,7 @@ def __init__(self, name, width, pixels, slit, transform, parent=None,
self._accumulate = None # Will be set after pipeline is created.

super().__init__([slit.target], pixels=pixels, width=width,
targetted_path_prob=0.99, parent=parent, pipelines=[],
targeted_path_prob=0.99, parent=parent, pipelines=[],
transform=transform, name=name)
self.pixel_samples = 1000
self.spectral_bins = 1
Expand Down Expand Up @@ -896,6 +915,24 @@ def accumulate(self, value):
if pipeline.frame is not None:
pipeline.frame.clear()

@property
def targetted_path_prob(self):
warn(
"The 'targetted_path_prob' property is deprecated, use 'targeted_path_prob' instead.",
DeprecationWarning,
stacklevel=2,
)
return self._targeted_path_prob

@targetted_path_prob.setter
def targetted_path_prob(self, value):
warn(
"The 'targetted_path_prob' property is deprecated, use 'targeted_path_prob' instead.",
DeprecationWarning,
stacklevel=2,
)
self.targeted_path_prob = value

def as_sightlines(self):
"""
Constructs a SightLine observer for each pixel in this bolometer.
Expand Down
20 changes: 10 additions & 10 deletions cherab/tools/observers/group/targettedpixel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
# under the Licence.

from numpy import ndarray
from raysect.optical.observer import TargettedPixel
from raysect.optical.observer import TargetedPixel

from .base import Observer0DGroup


class TargettedPixelGroup(Observer0DGroup):
"""
A group of targetted pixel under a single scene-graph node.
A group of targeted pixel under a single scene-graph node.

A scene-graph object regrouping a series of 'TargettedPixel'
observers as a scene-graph parent. Allows combined observation and display
Expand All @@ -35,7 +35,8 @@ class TargettedPixelGroup(Observer0DGroup):
:ivar list targets: Targets for preferential sampling
:ivar list targetted_path_prob: Probability of ray being casted at the target
"""
_OBSERVER_TYPE = TargettedPixel

_OBSERVER_TYPE = TargetedPixel

@property
def x_width(self):
Expand Down Expand Up @@ -76,7 +77,7 @@ def targets(self):
"""
List of target lists used by pixels for preferential sampling

:param list value: List of primitives to be set to each pixel or
:param list value: List of primitives to be set to each pixel or
list of lists containing targets specific for each pixel
in this case the number of lists must match number of pixels

Expand All @@ -100,17 +101,16 @@ def targets(self, value):

@property
def targetted_path_prob(self):
return [pixel.targetted_path_prob for pixel in self._observers]
return [pixel.targeted_path_prob for pixel in self._observers]

@targetted_path_prob.setter
def targetted_path_prob(self, value):
if isinstance(value, (list, tuple)):
if len(value) == len(self._observers):
for pixel, v in zip(self._observers, value):
pixel.targetted_path_prob = v
pixel.targeted_path_prob = v
else:
raise ValueError("The length of 'value' ({}) "
"mismatches the number of pixels ({}).".format(len(value), len(self._observers)))
raise ValueError("The length of 'value' ({}) mismatches the number of pixels ({}).".format(len(value), len(self._observers)))
else:
for pixel in self._observers:
pixel.targetted_path_prob = value
pixel.targeted_path_prob = value
20 changes: 10 additions & 10 deletions cherab/tools/tests/test_observer_groups.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unittest

from raysect.core.workflow import RenderEngine
from raysect.optical.observer import Observer0D, SightLine, FibreOptic, Pixel, TargettedPixel, PowerPipeline0D, SpectralPowerPipeline0D
from raysect.optical.observer import Observer0D, SightLine, FibreOptic, Pixel, TargetedPixel, PowerPipeline0D, SpectralPowerPipeline0D
from raysect.primitive import Sphere

from cherab.tools.observers.group.base import Observer0DGroup
Expand All @@ -26,7 +26,7 @@ def test_get_item(self):
idx = slice(1, 3, 1)
for observer, input_observer in zip(group[idx], self.observers[idx]):
self.assertIs(observer, input_observer)

for i, name in enumerate(names):
self.assertIs(group[name], self.observers[i])

Expand Down Expand Up @@ -83,7 +83,7 @@ def test_assignments(self):
with self.assertRaises(ValueError):
group.pipelines = [ppln_0]

# render_engine
# render_engine
engine = RenderEngine()
group.render_engine = engine
for group_engine in group.render_engine:
Expand All @@ -102,7 +102,7 @@ def test_assignments(self):
with self.assertRaises(ValueError):
group.render_engine = [RenderEngine() for _ in range(len(group) - 1)]

# wavelengths
# wavelengths
wvl = 500
group.min_wavelength = wvl - 100
group.max_wavelength = wvl + 100
Expand Down Expand Up @@ -139,7 +139,7 @@ def test_assignments(self):
with self.assertRaises(ValueError):
group.spectral_bins = [1000] * (len(group) + 1)

# quiet
# quiet
quiet = [True] * len(group)
group.quiet = quiet
self.assertListEqual(group.quiet, quiet)
Expand All @@ -152,7 +152,7 @@ def test_assignments(self):
with self.assertRaises(ValueError):
group.quiet = [False] * (len(group) + 1)

# rays
# rays
probs = [0.2 + i*0.1 for i in range(len(group))]
max_depths = [5 + i for i in range(len(group))]
min_depths = [2 + i for i in range(len(group))]
Expand Down Expand Up @@ -196,7 +196,7 @@ def test_assignments(self):
group.ray_importance_sampling = [False] * (len(group) + 1)
with self.assertRaises(ValueError):
group.ray_important_path_weight = [0.7] * (len(group) + 1)

# samples
pixel_samples = [2000 + i*500 for i in range(len(group))]
per_task = [5000 + i*100 for i in range(len(group))]
Expand Down Expand Up @@ -352,7 +352,7 @@ class TargettedPixelGroupTestCase(PixelGroupTestCase):
_GROUP_CLASS = TargettedPixelGroup

def setUp(self):
self.observers = [TargettedPixel(targets=[Sphere()], pipelines=[PowerPipeline0D()]) for _ in range(self._NUM)]
self.observers = [TargetedPixel(targets=[Sphere()], pipelines=[PowerPipeline0D()]) for _ in range(self._NUM)]

def test_targets(self):
group = self._GROUP_CLASS(observers=self.observers)
Expand Down Expand Up @@ -381,8 +381,8 @@ def test_targets(self):

prob = 0.8
group.targetted_path_prob = prob
for group_targetted_path_prob in group.targetted_path_prob:
self.assertEqual(group_targetted_path_prob, prob)
for group_targeted_path_prob in group.targetted_path_prob:
self.assertEqual(group_targeted_path_prob, prob)

with self.assertRaises(ValueError):
group.targetted_path_prob = [0.7] * (len(group) + 1)
3 changes: 1 addition & 2 deletions cherab/tools/tests/test_voxels.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ def test_rectangle_area(self):
for rectangle in RECTANGULAR_VOXEL_COORDS:
coords = np.asarray(rectangle)
voxel = AxisymmetricVoxel(coords)
dx = coords[:, 0].ptp()
dy = coords[:, 1].ptp()
dx, dy = np.ptp(coords, axis=0)
expected_area = dx * dy
self.assertEqual(voxel.cross_sectional_area, expected_area)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[build-system]
requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython~=3.0", "raysect==0.8.1.*"]
requires = ["setuptools>=62.3", "numpy", "cython~=3.1", "raysect==0.9.1.*"]
build-backend="setuptools.build_meta"
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cython~=3.0
numpy>=1.14,<2.0
cython~=3.1
numpy>=2.0
scipy
matplotlib
raysect==0.8.1.*
raysect==0.9.1.*
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@
long_description=long_description,
long_description_content_type="text/markdown",
install_requires=[
"numpy>=1.14,<2.0",
"numpy>=2.0",
"scipy",
"matplotlib",
"raysect==0.8.1.*",
"raysect==0.9.1.*",
],
extras_require={
# Running ./dev/build_docs.sh runs setup.py, which requires cython.
"docs": ["cython~=3.0", "sphinx", "sphinx-rtd-theme", "sphinx-tabs"],
"docs": ["cython~=3.1", "sphinx", "sphinx-rtd-theme", "sphinx-tabs"],
},
packages=find_packages(include=["cherab*"]),
package_data={"": [
Expand Down