diff --git a/CHANGELOG.md b/CHANGELOG.md index 6390e90e..d55ef76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Release 1.6.0 (TBD) ------------------- API changes: +* Rename `TargettedPixelGroup` to `TargetedPixelGroup` for correct spelling. Still keep `TargettedPixelGroup` as an alias for backwards compatibility until the next major release. (#487) * Add emission model attribute access to line and lineshape . (#294) New: diff --git a/cherab/tools/observers/__init__.py b/cherab/tools/observers/__init__.py index d134ef63..99cf5ab7 100644 --- a/cherab/tools/observers/__init__.py +++ b/cherab/tools/observers/__init__.py @@ -21,4 +21,4 @@ from .calcam import load_calcam_calibration from .intersections import find_wall_intersection from .spectroscopy import SpectroscopicSightLine, SpectroscopicFibreOptic -from .group import PixelGroup, TargettedPixelGroup, SightLineGroup, FibreOpticGroup, SpectroscopicFibreOpticGroup, SpectroscopicSightLineGroup +from .group import PixelGroup, TargetedPixelGroup, TargettedPixelGroup, SightLineGroup, FibreOpticGroup, SpectroscopicFibreOpticGroup, SpectroscopicSightLineGroup diff --git a/cherab/tools/observers/group/__init__.py b/cherab/tools/observers/group/__init__.py index eca93585..bbcf8633 100644 --- a/cherab/tools/observers/group/__init__.py +++ b/cherab/tools/observers/group/__init__.py @@ -17,7 +17,8 @@ # under the Licence. from .fibreoptic import FibreOpticGroup -from .sightline import SightLineGroup -from .targettedpixel import TargettedPixelGroup from .pixel import PixelGroup +from .sightline import SightLineGroup from .spectroscopic import SpectroscopicFibreOpticGroup, SpectroscopicSightLineGroup +from .targetedpixel import TargetedPixelGroup +from .targettedpixel import TargettedPixelGroup diff --git a/cherab/tools/observers/group/targetedpixel.py b/cherab/tools/observers/group/targetedpixel.py new file mode 100644 index 00000000..9f4fb814 --- /dev/null +++ b/cherab/tools/observers/group/targetedpixel.py @@ -0,0 +1,123 @@ +# Copyright 2016-2021 Euratom +# Copyright 2016-2021 United Kingdom Atomic Energy Authority +# Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# +# Licensed under the EUPL, Version 1.1 or – as soon they will be approved by the +# European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://joinup.ec.europa.eu/software/page/eupl5 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. +# +# See the Licence for the specific language governing permissions and limitations +# under the Licence. + +from numpy import ndarray +from raysect.optical.observer import TargetedPixel + +from .base import Observer0DGroup + + +class TargetedPixelGroup(Observer0DGroup): + """ + A group of targeted pixels under a single scene-graph node. + + A scene-graph object regrouping a series of 'TargetedPixel' + observers as a scene-graph parent. Allows combined observation and display + control simultaneously. + + :ivar list x_width: Width of pixel along local x axis + :ivar list y_width: Width of pixel along local y axis + :ivar list targets: Targets for preferential sampling + :ivar list targeted_path_prob: Probability of ray being casted at the target + """ + + _OBSERVER_TYPE = TargetedPixel + + @property + def x_width(self): + return [pixel.x_width for pixel in self._observers] + + @x_width.setter + def x_width(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for pixel, v in zip(self._observers, value): + pixel.x_width = v + else: + raise ValueError( + "The length of 'x_width' ({}) mismatches the number of pixels ({}).".format(len(value), len(self._observers)) + ) + else: + for pixel in self._observers: + pixel.x_width = value + + @property + def y_width(self): + return [pixel.y_width for pixel in self._observers] + + @y_width.setter + def y_width(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for pixel, v in zip(self._observers, value): + pixel.y_width = v + else: + raise ValueError( + "The length of 'y_width' ({}) mismatches the number of pixels ({}).".format(len(value), len(self._observers)) + ) + else: + for pixel in self._observers: + pixel.y_width = value + + @property + 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 + list of lists containing targets specific for each pixel + in this case the number of lists must match number of pixels + + :rtype: list + """ + return [pixel.targets for pixel in self._observers] + + @targets.setter + def targets(self, value): + if all(isinstance(v, (list, tuple)) for v in value): + if len(value) == len(self._observers): + for pixel, v in zip(self._observers, value): + pixel.targets = v + else: + raise ValueError( + "The number of provided target lists' ({}) mismatches the number of pixels ({}).".format( + len(value), len(self._observers) + ) + ) + else: + # assuming a list of primitives, the pixel's setter will throw an error if not + for pixel in self._observers: + pixel.targets = value + + @property + def targeted_path_prob(self): + return [pixel.targeted_path_prob for pixel in self._observers] + + @targeted_path_prob.setter + def targeted_path_prob(self, value): + if isinstance(value, (list, tuple)): + if len(value) == len(self._observers): + for pixel, v in zip(self._observers, value): + pixel.targeted_path_prob = v + else: + raise ValueError( + "The length of 'value' ({}) mismatches the number of pixels ({}).".format(len(value), len(self._observers)) + ) + else: + for pixel in self._observers: + pixel.targeted_path_prob = value diff --git a/cherab/tools/observers/group/targettedpixel.py b/cherab/tools/observers/group/targettedpixel.py index f21694aa..5d1ac69f 100644 --- a/cherab/tools/observers/group/targettedpixel.py +++ b/cherab/tools/observers/group/targettedpixel.py @@ -16,17 +16,20 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from numpy import ndarray -from raysect.optical.observer import TargetedPixel +import warnings -from .base import Observer0DGroup +from .targetedpixel import TargetedPixelGroup as _TargetedPixelGroup -class TargettedPixelGroup(Observer0DGroup): +class TargettedPixelGroup(_TargetedPixelGroup): """ A group of targeted pixel under a single scene-graph node. - A scene-graph object regrouping a series of 'TargettedPixel' + .. deprecated:: + `TargettedPixelGroup` is deprecated and will be removed in version 2.0. + Use `TargetedPixelGroup` instead. + + A scene-graph object regrouping a series of `TargetedPixel` observers as a scene-graph parent. Allows combined observation and display control simultaneously. @@ -36,81 +39,19 @@ class TargettedPixelGroup(Observer0DGroup): :ivar list targetted_path_prob: Probability of ray being casted at the target """ - _OBSERVER_TYPE = TargetedPixel - - @property - def x_width(self): - return [pixel.x_width for pixel in self._observers] - - @x_width.setter - def x_width(self, value): - if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._observers): - for pixel, v in zip(self._observers, value): - pixel.x_width = v - else: - raise ValueError("The length of 'x_width' ({}) " - "mismatches the number of pixels ({}).".format(len(value), len(self._observers))) - else: - for pixel in self._observers: - pixel.x_width = value - - @property - def y_width(self): - return [pixel.y_width for pixel in self._observers] - - @y_width.setter - def y_width(self, value): - if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._observers): - for pixel, v in zip(self._observers, value): - pixel.y_width = v - else: - raise ValueError("The length of 'y_width' ({}) " - "mismatches the number of pixels ({}).".format(len(value), len(self._observers))) - else: - for pixel in self._observers: - pixel.y_width = value - - @property - 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 - list of lists containing targets specific for each pixel - in this case the number of lists must match number of pixels - - :rtype: list - """ - return [pixel.targets for pixel in self._observers] - - @targets.setter - def targets(self, value): - if all(isinstance(v, (list, tuple)) for v in value): - if len(value) == len(self._observers): - for pixel, v in zip(self._observers, value): - pixel.targets = v - else: - raise ValueError("The number of provided target lists' ({}) " - "mismatches the number of pixels ({}).".format(len(value), len(self._observers))) - else: - # assuming a list of primitives, the pixel's setter will throw an error if not - for pixel in self._observers: - pixel.targets = value + def __init__(self, *args, **kwargs): + warnings.warn( + "TargettedPixelGroup is deprecated and will be removed in version 2.0. " + + "Use TargetedPixelGroup instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) @property def targetted_path_prob(self): - return [pixel.targeted_path_prob for pixel in self._observers] + return self.targeted_path_prob @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.targeted_path_prob = v - else: - raise ValueError("The length of 'value' ({}) mismatches the number of pixels ({}).".format(len(value), len(self._observers))) - else: - for pixel in self._observers: - pixel.targeted_path_prob = value + self.targeted_path_prob = value diff --git a/cherab/tools/tests/test_observer_groups.py b/cherab/tools/tests/test_observer_groups.py index 0418eab9..a602531e 100644 --- a/cherab/tools/tests/test_observer_groups.py +++ b/cherab/tools/tests/test_observer_groups.py @@ -1,11 +1,12 @@ import unittest +import warnings from raysect.core.workflow import RenderEngine from raysect.optical.observer import Observer0D, SightLine, FibreOptic, Pixel, TargetedPixel, PowerPipeline0D, SpectralPowerPipeline0D from raysect.primitive import Sphere +from cherab.tools.observers.group import FibreOpticGroup, PixelGroup, SightLineGroup, TargetedPixelGroup, TargettedPixelGroup from cherab.tools.observers.group.base import Observer0DGroup -from cherab.tools.observers.group import SightLineGroup, FibreOpticGroup, PixelGroup, TargettedPixelGroup from cherab.tools.raytransfer import pipelines @@ -348,8 +349,8 @@ def test_widths(self): group.y_width = [1e-1] * (len(group) + 1) -class TargettedPixelGroupTestCase(PixelGroupTestCase): - _GROUP_CLASS = TargettedPixelGroup +class TargetedPixelGroupTestCase(PixelGroupTestCase): + _GROUP_CLASS = TargetedPixelGroup def setUp(self): self.observers = [TargetedPixel(targets=[Sphere()], pipelines=[PowerPipeline0D()]) for _ in range(self._NUM)] @@ -374,15 +375,33 @@ def test_targets(self): with self.assertRaises(ValueError): group.targets = targets - # targetted path prob + # targeted path prob prob = [0.9, 0.95, 1] - group.targetted_path_prob = prob - self.assertListEqual(group.targetted_path_prob, prob) + group.targeted_path_prob = prob + self.assertListEqual(group.targeted_path_prob, prob) prob = 0.8 - group.targetted_path_prob = prob - for group_targeted_path_prob in group.targetted_path_prob: + group.targeted_path_prob = prob + for group_targeted_path_prob in group.targeted_path_prob: self.assertEqual(group_targeted_path_prob, prob) with self.assertRaises(ValueError): - group.targetted_path_prob = [0.7] * (len(group) + 1) + group.targeted_path_prob = [0.7] * (len(group) + 1) + + +class TargettedPixelGroupTestCase(TargetedPixelGroupTestCase): + """Test case for deprecated TargettedPixelGroup class.""" + + _GROUP_CLASS = TargettedPixelGroup + + def test_deprecation_warning(self): + """Test that using TargettedPixelGroup raises a deprecation warning.""" + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + group = TargettedPixelGroup(observers=self.observers) + + # Check that a warning was issued + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[0].category, DeprecationWarning)) + self.assertIn("TargettedPixelGroup is deprecated", str(w[0].message)) + self.assertIn("Use TargetedPixelGroup instead", str(w[0].message)) diff --git a/docs/source/tools/observers.rst b/docs/source/tools/observers.rst index e93e2057..b3d5e612 100644 --- a/docs/source/tools/observers.rst +++ b/docs/source/tools/observers.rst @@ -127,6 +127,9 @@ in the group. .. autoclass:: cherab.tools.observers.group.PixelGroup :members: +.. autoclass:: cherab.tools.observers.group.TargetedPixelGroup + :members: + .. autoclass:: cherab.tools.observers.group.TargettedPixelGroup :members: