From d4b0b99830ef523c8626ce82aef7b2ab3c9bd65b Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 16 Jun 2021 20:00:22 +0300 Subject: [PATCH 01/59] Added first spectroscopic instruments. --- .../tools/observers/spectroscopy/__init__.py | 20 ++ .../observers/spectroscopy/instrument.py | 84 ++++++ .../observers/spectroscopy/polychromator.py | 150 ++++++++++ .../observers/spectroscopy/spectrometer.py | 273 ++++++++++++++++++ 4 files changed, 527 insertions(+) create mode 100644 cherab/tools/observers/spectroscopy/__init__.py create mode 100644 cherab/tools/observers/spectroscopy/instrument.py create mode 100644 cherab/tools/observers/spectroscopy/polychromator.py create mode 100644 cherab/tools/observers/spectroscopy/spectrometer.py diff --git a/cherab/tools/observers/spectroscopy/__init__.py b/cherab/tools/observers/spectroscopy/__init__.py new file mode 100644 index 00000000..1d8cb4cd --- /dev/null +++ b/cherab/tools/observers/spectroscopy/__init__.py @@ -0,0 +1,20 @@ + +# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# +# 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 .instrument import SpectroscopicInstrument +from .polychromator import PolychromatorFilter, Polychromator +from .spectrometer import Spectrometer, CzernyTurnerSpectrometer, SurveySpectrometer diff --git a/cherab/tools/observers/spectroscopy/instrument.py b/cherab/tools/observers/spectroscopy/instrument.py new file mode 100644 index 00000000..b3d6c65d --- /dev/null +++ b/cherab/tools/observers/spectroscopy/instrument.py @@ -0,0 +1,84 @@ + +# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# +# 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. + +class SpectroscopicInstrument: + """ + Base class for spectroscopic instruments (spectrometers, polychromators, etc.). + This is an abstract class. + + :param str name: Instrument name. + """ + + def __init__(self, name=''): + self.name = name + self._clear_spectral_settings() + + @property + def name(self): + """ Instrument name.""" + return self._name + + @name.setter + def name(self, value): + self._name = str(value) + self._pipeline_properties = None + + @property + def pipeline_properties(self): + """ + The list of properties (class, name, filter) of the pipelines used with + this instrument. + """ + if self._pipeline_properties is None: + self._update_pipeline_properties() + + return self._pipeline_properties + + @property + def min_wavelength(self): + """ Lower wavelength bound for spectral range.""" + if self._min_wavelength is None: + self._update_spectral_settings() + + return self._min_wavelength + + @property + def max_wavelength(self): + """ Upper wavelength bound for spectral range.""" + if self._max_wavelength is None: + self._update_spectral_settings() + + return self._max_wavelength + + @property + def spectral_bins(self): + """ The number of spectral samples over the wavelength range.""" + if self._spectral_bins is None: + self._update_spectral_settings() + + return self._spectral_bins + + def _clear_spectral_settings(self): + self._min_wavelength = None + self._max_wavelength = None + self._spectral_bins = None + + def _update_spectral_settings(self): + raise NotImplementedError("To be defined in subclass.") + + def _update_pipeline_properties(self): + raise NotImplementedError("To be defined in subclass.") diff --git a/cherab/tools/observers/spectroscopy/polychromator.py b/cherab/tools/observers/spectroscopy/polychromator.py new file mode 100644 index 00000000..7b2a2374 --- /dev/null +++ b/cherab/tools/observers/spectroscopy/polychromator.py @@ -0,0 +1,150 @@ + +# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# +# 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. + +import numpy as np +from raysect.optical import InterpolatedSF +from raysect.optical.observer import RadiancePipeline0D + +from .instrument import SpectroscopicInstrument + + +class PolychromatorFilter(InterpolatedSF): + """ + Defines a symmetrical trapezoidal polychromator filter as a Raysect's InterpolatedSF. + + :param float wavelength: Central wavelength of the filter in nm. + :param float window: Size of the filtering window in nm. Default is 3. + :param float flat_top: Size of the flat top part of the filter in nm. + Default is None (equal to window). + :param str name: Filter name (e.g. "H-alpha filter"). Default is ''. + + """ + + def __init__(self, wavelength, window=3., flat_top=None, name=''): + + if wavelength <= 0: + raise ValueError("Argument 'wavelength' must be positive.") + + if window <= 0: + raise ValueError("Argument 'window' must be positive.") + + flat_top = flat_top or window - 1.e-15 + + if flat_top <= 0: + raise ValueError("Argument 'flat_top' must be positive.") + if flat_top > window: + raise ValueError("Argument 'flat_top' must be less or equal than 'window'.") + if flat_top == window: + flat_top = window - 1.e-15 + + self._window = window + self._flat_top = flat_top + self._wavelength = wavelength + self._name = str(name) + + wavelengths = [wavelength - 0.5 * window, + wavelength - 0.5 * flat_top, + wavelength + 0.5 * flat_top, + wavelength + 0.5 * window] + samples = [0, 1, 1, 0] + super().__init__(wavelengths, samples, normalise=False) + + @property + def window(self): + """ Size of the filtering window in nm.""" + return self._window + + @property + def flat_top(self): + """ Size of the flat top part of the filter in nm.""" + return self._flat_top + + @property + def wavelength(self): + """ Central wavelength of the filter in nm.""" + return self._wavelength + + @property + def name(self): + """ Filter name.""" + return self._name + + +class Polychromator(SpectroscopicInstrument): + """ + A polychromator assembly with a set of different filters. + + :param list filters: List of the `PolychromatorFilter` instances. + :param int min_bins_per_window: Minimal number of spectral bins + per filtering window. Default is 10. + """ + + def __init__(self, filters, min_bins_per_window=10, name=''): + super().__init__(name) + self.min_bins_per_window = min_bins_per_window + self.filters = filters + + @property + def min_bins_per_window(self): + """ + Minimal number of spectral bins per filtering window. + """ + return self._min_bins_per_window + + @min_bins_per_window.setter + def min_bins_per_window(self, value): + + value = int(value) + if value <= 0: + raise ValueError("Attribute 'min_bins_per_window' must be positive.") + + self._min_bins_per_window = value + self._clear_spectral_settings() + + @property + def filters(self): + """ + List of the PolychromatorFilter instances. + """ + return self._filters + + @filters.setter + def filters(self, value): + for poly_filter in value: + if not isinstance(poly_filter, PolychromatorFilter): + raise TypeError('Property filters must contain only PolychromatorFilter instances.') + + self._filters = value + self._clear_spectral_settings() + self._pipeline_properties = None + + def _update_pipeline_properties(self): + self._pipeline_properties = [(RadiancePipeline0D, self._name + ': ' + poly_filter.name, poly_filter) for poly_filter in self._filters] + + def _update_spectral_settings(self): + + min_wavelength = np.inf + max_wavelength = 0 + step = np.inf + for poly_filter in self._filters: + step = min(step, poly_filter.window / self._min_bins_per_window) + min_wavelength = min(min_wavelength, poly_filter.wavelength - 0.5 * poly_filter.window) + max_wavelength = max(max_wavelength, poly_filter.wavelength + 0.5 * poly_filter.window) + + self._min_wavelength = min_wavelength + self._max_wavelength = max_wavelength + self._spectral_bins = int(np.ceil((max_wavelength - min_wavelength) / step)) diff --git a/cherab/tools/observers/spectroscopy/spectrometer.py b/cherab/tools/observers/spectroscopy/spectrometer.py new file mode 100644 index 00000000..74e098c4 --- /dev/null +++ b/cherab/tools/observers/spectroscopy/spectrometer.py @@ -0,0 +1,273 @@ + +# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# +# 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. + +import numpy as np +from raysect.optical.observer import SpectralRadiancePipeline0D + +from .instrument import SpectroscopicInstrument + + +class Spectrometer(SpectroscopicInstrument): + """ + Spectrometer base class. + This is an abstract class. + + :param int spectral_bins: The number of spectral samples over the wavelength range. + :param float reference_wavelength: Wavelength (in nm) corresponding to + the centre of reference bin. + :param int reference_bin: Reference bin index. Can be negative to specify the offset. + Default is None (spectral_bins // 2). + :param str name: Spectrometer name. + """ + + def __init__(self, spectral_bins, reference_wavelength, reference_bin=None, name=''): + super().__init__(name) + self.spectral_bins = spectral_bins + if reference_bin is None: + self.reference_bin = self._spectral_bins // 2 + else: + self.reference_bin = reference_bin + self.reference_wavelength = reference_wavelength + + @property + def spectral_bins(self): + """ + The number of spectral samples over the wavelength range. + """ + return self._spectral_bins + + @spectral_bins.setter + def spectral_bins(self, value): + + value = int(value) + if value <= 0: + raise ValueError("Attribute 'spectral_bins' must be > 0.") + + self._spectral_bins = value + self._clear_spectral_settings() + + @property + def reference_wavelength(self): + """ + Wavelength (in nm) corresponding to the centre of reference bin. + """ + return self._reference_wavelength + + @reference_wavelength.setter + def reference_wavelength(self, value): + + if value <= 0: + raise ValueError("Attribute 'reference_wavelength' must be > 0.") + + self._reference_wavelength = value + self._clear_spectral_settings() + + @property + def reference_bin(self): + """ + Reference bin index. + """ + return self._reference_bin + + @reference_bin.setter + def reference_bin(self, value): + + value = int(value) + + self._reference_bin = value + self._clear_spectral_settings() + + def _update_pipeline_properties(self): + self._pipeline_properties = [(SpectralRadiancePipeline0D, self._name, None)] + + def _clear_spectral_settings(self): + self._min_wavelength = None + self._max_wavelength = None + + +class SurveySpectrometer(Spectrometer): + """ + Survey spectrometer with a constant spectral resolution. + + Note: survey spectrometers usually have non-constant spectral resolution + in the supported wavelength range. However, Raysect does not support + the observers with variable spectral resolution. + + :param float resolution: Spectral resolution in nm (can be negative). + :param int spectral_bins: The number of spectral samples over the wavelength range. + :param float reference_wavelength: Wavelength (in nm) corresponding to + the centre of reference bin. + :param int reference_bin: Reference bin index. Can be negative to specify the offset. + Default is None (spectral_bins // 2). + :param str name: Spectrometer name. + """ + + def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bin=None, name=''): + super().__init__(spectral_bins, reference_wavelength, reference_bin, name) + self.resolution = resolution + + @property + def resolution(self): + """ + Spectrometer resolution. + """ + return self._resolution + + @resolution.setter + def resolution(self, value): + """ + Spectral resolution in nm (can be negative). + """ + if value == 0: + raise ValueError("Attribute 'resolution' must be non-zero.") + + self._resolution = value + self._clear_spectral_settings() + + def _update_spectral_settings(self): + + if self._resolution > 0: + self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * self._resolution + self._max_wavelength = self._min_wavelength + self._spectral_bins * self._resolution + else: + self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * self._resolution + self._max_wavelength = self._min_wavelength - self._spectral_bins * self._resolution + + +class CzernyTurnerSpectrometer(Spectrometer): + """ + Czerny-Turner high-resolution spectrometer. + + :param int diffraction_order: Diffraction order. + :param float grating: Diffraction grating in nm-1. + :param float focal_length: Focal length in nm. + :param float pixel_spacing: Pixel to pixel spacing on CCD in nm. + :param float diffraction_angle: Angle between incident and diffracted light in degrees. + :param int spectral_bins: The number of spectral samples over the wavelength range. + :param float reference_wavelength: Wavelength (in nm) corresponding to + the centre of reference bin. + :param int reference_bin: Reference bin index. Default is None (spectral_bins // 2). + :param str name: Spectrometer name. + """ + + def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, spectral_bins, + reference_wavelength, reference_bin=None, name=''): + super().__init__(spectral_bins, reference_wavelength, reference_bin, name) + self.diffraction_order = diffraction_order + self.grating = grating + self.focal_length = focal_length + self.pixel_spacing = pixel_spacing + self.diffraction_angle = diffraction_angle + + @property + def diffraction_order(self): + """ Diffraction order.""" + return self._diffraction_order + + @diffraction_order.setter + def diffraction_order(self, value): + + value = int(value) + if value <= 0: + raise ValueError("Attribute 'diffraction_order' must be positive.") + + self._diffraction_order = value + self._clear_spectral_settings() + + @property + def grating(self): + """ Diffraction grating in nm-1.""" + return self._grating + + @grating.setter + def grating(self, value): + + if value <= 0: + raise ValueError("Attribute 'grating' must be positive.") + + self._grating = value + self._clear_spectral_settings() + + @property + def focal_length(self): + """ Focal length in nm.""" + return self._focal_length + + @focal_length.setter + def focal_length(self, value): + + if value <= 0: + raise ValueError("Attribute 'focal_length' must be positive.") + + self._focal_length = value + self._clear_spectral_settings() + + @property + def pixel_spacing(self): + """ Pixel to pixel spacing on CCD in nm.""" + return self._pixel_spacing + + @pixel_spacing.setter + def pixel_spacing(self, value): + + if value == 0: + raise ValueError("Attribute 'pixel_spacing' must be non-zero.") + + self._pixel_spacing = value + self._clear_spectral_settings() + + @property + def diffraction_angle(self): + """ Angle between incident and diffracted light in degrees.""" + return np.rad2deg(self._diffraction_angle) + + @diffraction_angle.setter + def diffraction_angle(self, value): + + if value <= 0: + raise ValueError("Attribute 'diffraction_angle' must be positive.") + + self._diffraction_angle = np.deg2rad(value) + self._clear_spectral_settings() + + def _update_spectral_settings(self): + + resolution = self.resolution() + + if resolution > 0: + self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * resolution + self._max_wavelength = self._min_wavelength + self._spectral_bins * resolution + else: + self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * resolution + self._max_wavelength = self._min_wavelength - self._spectral_bins * resolution + + def resolution(self): + """ + Calculates spectral resolution in nm. + + :return: resolution + """ + grating = self._grating + m = self._diffraction_order + dxdp = self._pixel_spacing + angle = self._diffraction_angle + fl = self._focal_length + + p = 0.5 * m * grating * self._reference_wavelength + resolution = dxdp * (np.sqrt(np.cos(angle)**2 - p * p) - p * np.tan(angle)) / (m * fl * grating) + + return resolution From f63798976a964455f92fcd247451feb5a763ab9c Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 16 Jun 2021 21:33:18 +0300 Subject: [PATCH 02/59] Made 'resolution' a property of CzernyTurnerSpectrometer. --- .../observers/spectroscopy/spectrometer.py | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/cherab/tools/observers/spectroscopy/spectrometer.py b/cherab/tools/observers/spectroscopy/spectrometer.py index 74e098c4..fcbe31cf 100644 --- a/cherab/tools/observers/spectroscopy/spectrometer.py +++ b/cherab/tools/observers/spectroscopy/spectrometer.py @@ -244,22 +244,10 @@ def diffraction_angle(self, value): self._diffraction_angle = np.deg2rad(value) self._clear_spectral_settings() - def _update_spectral_settings(self): - - resolution = self.resolution() - - if resolution > 0: - self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * resolution - self._max_wavelength = self._min_wavelength + self._spectral_bins * resolution - else: - self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * resolution - self._max_wavelength = self._min_wavelength - self._spectral_bins * resolution - + @property def resolution(self): """ - Calculates spectral resolution in nm. - - :return: resolution + Spectral resolution in nm. """ grating = self._grating m = self._diffraction_order @@ -268,6 +256,17 @@ def resolution(self): fl = self._focal_length p = 0.5 * m * grating * self._reference_wavelength - resolution = dxdp * (np.sqrt(np.cos(angle)**2 - p * p) - p * np.tan(angle)) / (m * fl * grating) + _resolution = dxdp * (np.sqrt(np.cos(angle)**2 - p * p) - p * np.tan(angle)) / (m * fl * grating) + + return _resolution + + def _update_spectral_settings(self): + + resolution = self.resolution - return resolution + if resolution > 0: + self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * resolution + self._max_wavelength = self._min_wavelength + self._spectral_bins * resolution + else: + self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * resolution + self._max_wavelength = self._min_wavelength - self._spectral_bins * resolution From 439f5b286f03797eff1dea2693dca36d28e06c5a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 26 Jul 2021 18:10:21 +0300 Subject: [PATCH 03/59] Added Sphinx documentation and unit tests for spectroscopic instruments. --- .../tests/test_spectroscopic_instruments.py | 145 ++++++++++++++++++ docs/source/tools/observers.rst | 31 ++++ 2 files changed, 176 insertions(+) create mode 100644 cherab/tools/tests/test_spectroscopic_instruments.py diff --git a/cherab/tools/tests/test_spectroscopic_instruments.py b/cherab/tools/tests/test_spectroscopic_instruments.py new file mode 100644 index 00000000..d79b6445 --- /dev/null +++ b/cherab/tools/tests/test_spectroscopic_instruments.py @@ -0,0 +1,145 @@ +# 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. + +import unittest +import numpy as np + +from raysect.optical.observer.pipeline import RadiancePipeline0D, SpectralRadiancePipeline0D +from cherab.tools.observers.spectroscopy import PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer + + +class TestPolychromatorFilter(unittest.TestCase): + """ + Test for PolychromatorFilter class. + """ + + def test_spectrum(self): + wavelength = 500. + window = 6. + flat_top = 2. + poly_filter = PolychromatorFilter(wavelength, window, flat_top, 'test_filter') + wavelengths = np.linspace(496., 504., 9) + spectrum_true = np.array([0, 0, 0.5, 1., 1., 1., 0.5, 0, 0]) + spectrum_test = np.array([poly_filter(wvl) for wvl in wavelengths]) + self.assertTrue(np.all(spectrum_true == spectrum_test)) + + +class TestPolychromator(unittest.TestCase): + """ + Test cases for Polychromator class. + """ + + def setUp(self): + self.poly_filters_default = [PolychromatorFilter(400., 6., 2., 'filter 1'), + PolychromatorFilter(700., 8., 4., 'filter 2')] + self.min_bins_per_window_default = 10 + + def test_pipeline_properties(self): + polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default, 'test polychromator') + pipeline_properties_true = [(RadiancePipeline0D, 'test polychromator: filter 1', self.poly_filters_default[0]), + (RadiancePipeline0D, 'test polychromator: filter 2', self.poly_filters_default[1])] + self.assertSequenceEqual(pipeline_properties_true, polychromator.pipeline_properties) + + def test_spectral_properties(self): + polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default) + min_wavelength_true = 397. + max_wavelength_true = 704. + spectral_bins_true = 512 + self.assertTrue(polychromator.min_wavelength == min_wavelength_true and + polychromator.max_wavelength == max_wavelength_true and + polychromator.spectral_bins == spectral_bins_true) + + def test_filter_change(self): + """ Checks if the spectral properties are updated correctly when the filters are replaced.""" + polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default) + polychromator.min_bins_per_window = 20 + polychromator.filters = [PolychromatorFilter(500., 5., 2., 'filter 1'), + PolychromatorFilter(600., 7., 4., 'filter 2')] + min_wavelength_true = 497.5 + max_wavelength_true = 603.5 + spectral_bins_true = 424 + self.assertTrue(polychromator.min_wavelength == min_wavelength_true and + polychromator.max_wavelength == max_wavelength_true and + polychromator.spectral_bins == spectral_bins_true) + + +class TestSurveySpectrometer(unittest.TestCase): + """ + Test cases for SurveySpectrometer class. + """ + + def test_pipeline_properties(self): + resolution = 0.1 + reference_wavelength = 500 + reference_bin = 50 + spectral_bins = 200 + spectrometer = SurveySpectrometer(resolution, spectral_bins, reference_wavelength, reference_bin, name='test spectrometer') + pipeline_properties_true = [(SpectralRadiancePipeline0D, 'test spectrometer', None)] + self.assertSequenceEqual(pipeline_properties_true, spectrometer.pipeline_properties) + + def test_spectral_properties(self): + resolution = 0.1 + reference_wavelength = 500 + reference_bin = 50 + spectral_bins = 200 + spectrometer = SurveySpectrometer(resolution, spectral_bins, reference_wavelength, reference_bin, name='test spectrometer') + min_wavelength_true = 494.95 + max_wavelength_true = 514.95 + self.assertTrue(spectrometer.min_wavelength == min_wavelength_true and + spectrometer.max_wavelength == max_wavelength_true) + + +class TestCzernyTurnerSpectrometer(unittest.TestCase): + """ + Test cases for CzernyTurnerSpectrometer class. + """ + + def setUp(self): + self.diffraction_order = 1 + self.grating = 2.e-3 + self.focal_length = 1.e9 + self.pixel_spacing = 2.e4 + self.diffraction_angle = 10. + self.spectral_bins = 512 + self.reference_bin = 255 + + def test_resolution(self): + wavelengths = [350., 550., 750.] + resolutions_true = np.array([8.587997e-3, 7.199328e-3, 5.0599164e-3]) + spectrometer = CzernyTurnerSpectrometer(self.diffraction_order, self.grating, self.focal_length, self.pixel_spacing, + self.diffraction_angle, self.spectral_bins, 500., self.reference_bin, + name='test spectrometer') + resolutions = [] + for wvl in wavelengths: + spectrometer.reference_wavelength = wvl + resolutions.append(spectrometer.resolution) + self.assertTrue(np.all(np.abs(resolutions / resolutions_true - 1.) < 1.e-7)) + + def test_spectral_properties(self): + wavelength = 500. + min_wavelength_true = 498.0575 + max_wavelength_true = 501.9501 + spectrometer = CzernyTurnerSpectrometer(self.diffraction_order, self.grating, self.focal_length, self.pixel_spacing, + self.diffraction_angle, self.spectral_bins, wavelength, self.reference_bin, + name='test spectrometer') + self.assertTrue(abs(spectrometer.min_wavelength - min_wavelength_true) < 1.e-4 and + abs(spectrometer.max_wavelength - max_wavelength_true) < 1.e-4) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/source/tools/observers.rst b/docs/source/tools/observers.rst index 57dd5636..493bab2b 100644 --- a/docs/source/tools/observers.rst +++ b/docs/source/tools/observers.rst @@ -61,3 +61,34 @@ bolometer etendue :math:`G`, which is given by: .. autoclass:: cherab.tools.observers.bolometry.BolometerFoil :members: + + +.. _observers_spectroscopic_instruments: + +Spectroscopic instruments +------------------------- + +Spectroscopic instruments such as polychromators, survey and high-resolution spectrometers +simplify the setup of rendering pipelines and observers' spectral properties. The Cherab core +package provides base classes for spectroscopic instruments, so machine-specific packages +can build more advance instruments from them, such as instruments with spectral properties +based on the actual experimental setup for a given shot/pulse. + +.. autoclass:: cherab.tools.observers.spectroscopy.SpectroscopicInstrument + :members: + +.. autoclass:: cherab.tools.observers.spectroscopy.PolychromatorFilter + :members: + +.. autoclass:: cherab.tools.observers.spectroscopy.Polychromator + :members: + +.. autoclass:: cherab.tools.observers.spectroscopy.Spectrometer + :members: + +.. autoclass:: cherab.tools.observers.spectroscopy.SurveySpectrometer + :members: + +.. autoclass:: cherab.tools.observers.spectroscopy.CzernyTurnerSpectrometer + :members: + From 829b231dc2fc1e478d9b587d5ac3ad3bce754218 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 29 Jul 2021 23:35:29 +0300 Subject: [PATCH 04/59] Aligned the docs for spectroscopic instruments with the style used in Cherab. --- .../observers/spectroscopy/instrument.py | 20 +++++---- .../observers/spectroscopy/polychromator.py | 18 +++----- .../observers/spectroscopy/spectrometer.py | 45 ++++++------------- 3 files changed, 33 insertions(+), 50 deletions(-) diff --git a/cherab/tools/observers/spectroscopy/instrument.py b/cherab/tools/observers/spectroscopy/instrument.py index b3d6c65d..a2a59f60 100644 --- a/cherab/tools/observers/spectroscopy/instrument.py +++ b/cherab/tools/observers/spectroscopy/instrument.py @@ -21,6 +21,12 @@ class SpectroscopicInstrument: This is an abstract class. :param str name: Instrument name. + + :ivar list pipeline_properties: The list of properties (class, name, filter) of + the pipelines used with this instrument. + :ivar float min_wavelength: Lower wavelength bound for spectral range. + :ivar float max_wavelength: Upper wavelength bound for spectral range. + :ivar int spectral_bins: The number of spectral samples over the wavelength range. """ def __init__(self, name=''): @@ -29,7 +35,7 @@ def __init__(self, name=''): @property def name(self): - """ Instrument name.""" + # Instrument name. return self._name @name.setter @@ -39,10 +45,8 @@ def name(self, value): @property def pipeline_properties(self): - """ - The list of properties (class, name, filter) of the pipelines used with - this instrument. - """ + # The list of properties (class, name, filter) of the pipelines used with + # this instrument. if self._pipeline_properties is None: self._update_pipeline_properties() @@ -50,7 +54,7 @@ def pipeline_properties(self): @property def min_wavelength(self): - """ Lower wavelength bound for spectral range.""" + # Lower wavelength bound for spectral range. if self._min_wavelength is None: self._update_spectral_settings() @@ -58,7 +62,7 @@ def min_wavelength(self): @property def max_wavelength(self): - """ Upper wavelength bound for spectral range.""" + # Upper wavelength bound for spectral range. if self._max_wavelength is None: self._update_spectral_settings() @@ -66,7 +70,7 @@ def max_wavelength(self): @property def spectral_bins(self): - """ The number of spectral samples over the wavelength range.""" + # The number of spectral samples over the wavelength range. if self._spectral_bins is None: self._update_spectral_settings() diff --git a/cherab/tools/observers/spectroscopy/polychromator.py b/cherab/tools/observers/spectroscopy/polychromator.py index 7b2a2374..789a66f8 100644 --- a/cherab/tools/observers/spectroscopy/polychromator.py +++ b/cherab/tools/observers/spectroscopy/polychromator.py @@ -65,22 +65,22 @@ def __init__(self, wavelength, window=3., flat_top=None, name=''): @property def window(self): - """ Size of the filtering window in nm.""" + # Size of the filtering window in nm. return self._window @property def flat_top(self): - """ Size of the flat top part of the filter in nm.""" + # Size of the flat top part of the filter in nm. return self._flat_top @property def wavelength(self): - """ Central wavelength of the filter in nm.""" + # Central wavelength of the filter in nm. return self._wavelength @property def name(self): - """ Filter name.""" + # Filter name. return self._name @@ -91,6 +91,7 @@ class Polychromator(SpectroscopicInstrument): :param list filters: List of the `PolychromatorFilter` instances. :param int min_bins_per_window: Minimal number of spectral bins per filtering window. Default is 10. + :param str name: Polychromator name. """ def __init__(self, filters, min_bins_per_window=10, name=''): @@ -100,14 +101,11 @@ def __init__(self, filters, min_bins_per_window=10, name=''): @property def min_bins_per_window(self): - """ - Minimal number of spectral bins per filtering window. - """ + # Minimal number of spectral bins per filtering window. return self._min_bins_per_window @min_bins_per_window.setter def min_bins_per_window(self, value): - value = int(value) if value <= 0: raise ValueError("Attribute 'min_bins_per_window' must be positive.") @@ -117,9 +115,7 @@ def min_bins_per_window(self, value): @property def filters(self): - """ - List of the PolychromatorFilter instances. - """ + # List of the PolychromatorFilter instances. return self._filters @filters.setter diff --git a/cherab/tools/observers/spectroscopy/spectrometer.py b/cherab/tools/observers/spectroscopy/spectrometer.py index fcbe31cf..99bd5425 100644 --- a/cherab/tools/observers/spectroscopy/spectrometer.py +++ b/cherab/tools/observers/spectroscopy/spectrometer.py @@ -45,14 +45,11 @@ def __init__(self, spectral_bins, reference_wavelength, reference_bin=None, name @property def spectral_bins(self): - """ - The number of spectral samples over the wavelength range. - """ + # The number of spectral samples over the wavelength range. return self._spectral_bins @spectral_bins.setter def spectral_bins(self, value): - value = int(value) if value <= 0: raise ValueError("Attribute 'spectral_bins' must be > 0.") @@ -62,14 +59,11 @@ def spectral_bins(self, value): @property def reference_wavelength(self): - """ - Wavelength (in nm) corresponding to the centre of reference bin. - """ + # Wavelength (in nm) corresponding to the centre of reference bin. return self._reference_wavelength @reference_wavelength.setter def reference_wavelength(self, value): - if value <= 0: raise ValueError("Attribute 'reference_wavelength' must be > 0.") @@ -78,14 +72,11 @@ def reference_wavelength(self, value): @property def reference_bin(self): - """ - Reference bin index. - """ + # Reference bin index. return self._reference_bin @reference_bin.setter def reference_bin(self, value): - value = int(value) self._reference_bin = value @@ -114,6 +105,8 @@ class SurveySpectrometer(Spectrometer): :param int reference_bin: Reference bin index. Can be negative to specify the offset. Default is None (spectral_bins // 2). :param str name: Spectrometer name. + + :ivar float resolution: Spectral resolution in nm (can be negative). """ def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bin=None, name=''): @@ -122,16 +115,11 @@ def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bi @property def resolution(self): - """ - Spectrometer resolution. - """ + # Spectral resolution in nm (can be negative). return self._resolution @resolution.setter def resolution(self, value): - """ - Spectral resolution in nm (can be negative). - """ if value == 0: raise ValueError("Attribute 'resolution' must be non-zero.") @@ -162,6 +150,8 @@ class CzernyTurnerSpectrometer(Spectrometer): the centre of reference bin. :param int reference_bin: Reference bin index. Default is None (spectral_bins // 2). :param str name: Spectrometer name. + + :ivar float resolution: Spectral resolution in nm (can be negative). """ def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, spectral_bins, @@ -175,12 +165,11 @@ def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diff @property def diffraction_order(self): - """ Diffraction order.""" + # Diffraction order. return self._diffraction_order @diffraction_order.setter def diffraction_order(self, value): - value = int(value) if value <= 0: raise ValueError("Attribute 'diffraction_order' must be positive.") @@ -190,12 +179,11 @@ def diffraction_order(self, value): @property def grating(self): - """ Diffraction grating in nm-1.""" + # Diffraction grating in nm-1. return self._grating @grating.setter def grating(self, value): - if value <= 0: raise ValueError("Attribute 'grating' must be positive.") @@ -204,12 +192,11 @@ def grating(self, value): @property def focal_length(self): - """ Focal length in nm.""" + # Focal length in nm. return self._focal_length @focal_length.setter def focal_length(self, value): - if value <= 0: raise ValueError("Attribute 'focal_length' must be positive.") @@ -218,12 +205,11 @@ def focal_length(self, value): @property def pixel_spacing(self): - """ Pixel to pixel spacing on CCD in nm.""" + # Pixel to pixel spacing on CCD in nm. return self._pixel_spacing @pixel_spacing.setter def pixel_spacing(self, value): - if value == 0: raise ValueError("Attribute 'pixel_spacing' must be non-zero.") @@ -232,12 +218,11 @@ def pixel_spacing(self, value): @property def diffraction_angle(self): - """ Angle between incident and diffracted light in degrees.""" + # Angle between incident and diffracted light in degrees. return np.rad2deg(self._diffraction_angle) @diffraction_angle.setter def diffraction_angle(self, value): - if value <= 0: raise ValueError("Attribute 'diffraction_angle' must be positive.") @@ -246,9 +231,7 @@ def diffraction_angle(self, value): @property def resolution(self): - """ - Spectral resolution in nm. - """ + # Spectral resolution in nm (can be negative). grating = self._grating m = self._diffraction_order dxdp = self._pixel_spacing From b674e01b72daea43675c5fe15516c9150021cf6b Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 3 Aug 2021 20:33:38 +0300 Subject: [PATCH 05/59] Moved spectroscopic instruments from 'observers' to 'spectroscopy' submodule. Added code examples to docstrings. Added 'pipelines' property. --- .../{observers => }/spectroscopy/__init__.py | 4 +- .../spectroscopy/instrument.py | 17 ++++++- .../spectroscopy/polychromator.py | 21 ++++++++- .../spectroscopy/spectrometer.py | 45 ++++++++++++++++++- .../tests/test_spectroscopic_instruments.py | 2 +- docs/source/tools/observers.rst | 30 ------------- docs/source/tools/spectroscopy.rst | 38 ++++++++++++++++ docs/source/tools/tools.rst | 1 + 8 files changed, 122 insertions(+), 36 deletions(-) rename cherab/tools/{observers => }/spectroscopy/__init__.py (81%) rename cherab/tools/{observers => }/spectroscopy/instrument.py (80%) rename cherab/tools/{observers => }/spectroscopy/polychromator.py (83%) rename cherab/tools/{observers => }/spectroscopy/spectrometer.py (78%) create mode 100644 docs/source/tools/spectroscopy.rst diff --git a/cherab/tools/observers/spectroscopy/__init__.py b/cherab/tools/spectroscopy/__init__.py similarity index 81% rename from cherab/tools/observers/spectroscopy/__init__.py rename to cherab/tools/spectroscopy/__init__.py index 1d8cb4cd..207aaf87 100644 --- a/cherab/tools/observers/spectroscopy/__init__.py +++ b/cherab/tools/spectroscopy/__init__.py @@ -1,5 +1,7 @@ -# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# 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"); diff --git a/cherab/tools/observers/spectroscopy/instrument.py b/cherab/tools/spectroscopy/instrument.py similarity index 80% rename from cherab/tools/observers/spectroscopy/instrument.py rename to cherab/tools/spectroscopy/instrument.py index a2a59f60..fc0409b1 100644 --- a/cherab/tools/observers/spectroscopy/instrument.py +++ b/cherab/tools/spectroscopy/instrument.py @@ -1,5 +1,7 @@ -# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# 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"); @@ -24,6 +26,7 @@ class SpectroscopicInstrument: :ivar list pipeline_properties: The list of properties (class, name, filter) of the pipelines used with this instrument. + :ivar list pipelines: The list of pipelines. Each call returns a list with new instances. :ivar float min_wavelength: Lower wavelength bound for spectral range. :ivar float max_wavelength: Upper wavelength bound for spectral range. :ivar int spectral_bins: The number of spectral samples over the wavelength range. @@ -52,6 +55,18 @@ def pipeline_properties(self): return self._pipeline_properties + @property + def pipelines(self): + # The list of pipelines. Each call returns a list with new instances. + pl_list = [] + for (pl_class, pl_name, pl_filter) in self.pipeline_properties: + if pl_filter is None: + pl_list.append(pl_class(name=pl_name)) + else: + pl_list.append(pl_class(name=pl_name, filter=pl_filter)) + + return pl_list + @property def min_wavelength(self): # Lower wavelength bound for spectral range. diff --git a/cherab/tools/observers/spectroscopy/polychromator.py b/cherab/tools/spectroscopy/polychromator.py similarity index 83% rename from cherab/tools/observers/spectroscopy/polychromator.py rename to cherab/tools/spectroscopy/polychromator.py index 789a66f8..2d13a898 100644 --- a/cherab/tools/observers/spectroscopy/polychromator.py +++ b/cherab/tools/spectroscopy/polychromator.py @@ -1,5 +1,7 @@ -# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# 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"); @@ -31,7 +33,6 @@ class PolychromatorFilter(InterpolatedSF): :param float flat_top: Size of the flat top part of the filter in nm. Default is None (equal to window). :param str name: Filter name (e.g. "H-alpha filter"). Default is ''. - """ def __init__(self, wavelength, window=3., flat_top=None, name=''): @@ -92,6 +93,22 @@ class Polychromator(SpectroscopicInstrument): :param int min_bins_per_window: Minimal number of spectral bins per filtering window. Default is 10. :param str name: Polychromator name. + + .. code-block:: pycon + + >>> from raysect.optical import World + >>> from raysect.optical.observer import FibreOptic + >>> from cherab.tools.spectroscopy import Polychromator, PolychromatorFilter + >>> + >>> world = World() + >>> h_alpha_filter = PolychromatorFilter(656.1, name='H-alpha filter') + >>> ciii_465nm_filter = PolychromatorFilter(464.8, name='CIII 465 nm filter') + >>> polychromator = Polychromator([h_alpha_filter, ciii_465nm_filter], name='MyPolychromator') + >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) + >>> fibreoptic.min_wavelength = polychromator.min_wavelength + >>> fibreoptic.max_wavelength = polychromator.max_wavelength + >>> fibreoptic.spectral_bins = polychromator.spectral_bins + >>> fibreoptic.pipelines = polychromator.pipelines """ def __init__(self, filters, min_bins_per_window=10, name=''): diff --git a/cherab/tools/observers/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py similarity index 78% rename from cherab/tools/observers/spectroscopy/spectrometer.py rename to cherab/tools/spectroscopy/spectrometer.py index 99bd5425..e982918e 100644 --- a/cherab/tools/observers/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -1,5 +1,7 @@ -# Copyright 2014-2017 United Kingdom Atomic Energy Authority +# 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"); @@ -107,6 +109,32 @@ class SurveySpectrometer(Spectrometer): :param str name: Spectrometer name. :ivar float resolution: Spectral resolution in nm (can be negative). + + .. code-block:: pycon + + >>> from numpy import ceil + >>> from raysect.optical import World + >>> from raysect.optical.observer import FibreOptic + >>> from cherab.tools.spectroscopy import SurveySpectrometer, Polychromator, PolychromatorFilter + >>> + >>> world = World() + >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) + >>> # Here, the fibre optic is "connected" to both survey spectrometer and polychromator. + >>> + >>> # setting up the polychromator + >>> h_alpha_filter = PolychromatorFilter(656.1, name='H-alpha filter') + >>> ciii_465nm_filter = PolychromatorFilter(464.8, name='CIII 465 nm filter') + >>> polychromator = Polychromator([h_alpha_filter, ciii_465nm_filter], name='MyPolychromator') + >>> + >>> # setting up the survey spectrometer + >>> spectrometer = SurveySpectrometer(0.1, 1024, 500, name='MySpectrometer') + >>> + >>> fibreoptic.min_wavelength = min(spectrometer.min_wavelength, polychromator.min_wavelength) + >>> fibreoptic.max_wavelength = max(spectrometer.max_wavelength, polychromator.max_wavelength) + >>> bin_width = min((spectrometer.max_wavelength - spectrometer.min_wavelength) / spectrometer.spectral_bins, + >>> (polychromator.max_wavelength - polychromator.min_wavelength) / polychromator.spectral_bins) + >>> fibreoptic.spectral_bins = int(ceil((fibreoptic.max_wavelength - fibreoptic.min_wavelength) / bin_width)) + >>> fibreoptic.pipelines = spectrometer.pipelines + polychromator.pipelines """ def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bin=None, name=''): @@ -152,6 +180,21 @@ class CzernyTurnerSpectrometer(Spectrometer): :param str name: Spectrometer name. :ivar float resolution: Spectral resolution in nm (can be negative). + + .. code-block:: pycon + + >>> from raysect.optical import World + >>> from raysect.optical.observer import FibreOptic + >>> from cherab.tools.spectroscopy import CzernyTurnerSpectrometer + >>> + >>> world = World() + >>> hires_spectrometer = CzernyTurnerSpectrometer(1, 2.e-3, 1.e9, 2.e4, 10., 512, 600., + >>> name='MySpectrometer') + >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) + >>> fibreoptic.min_wavelength = hires_spectrometer.min_wavelength + >>> fibreoptic.max_wavelength = hires_spectrometer.max_wavelength + >>> fibreoptic.spectral_bins = hires_spectrometer.spectral_bins + >>> fibreoptic.pipelines = hires_spectrometer.pipelines """ def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, spectral_bins, diff --git a/cherab/tools/tests/test_spectroscopic_instruments.py b/cherab/tools/tests/test_spectroscopic_instruments.py index d79b6445..883dc8c6 100644 --- a/cherab/tools/tests/test_spectroscopic_instruments.py +++ b/cherab/tools/tests/test_spectroscopic_instruments.py @@ -20,7 +20,7 @@ import numpy as np from raysect.optical.observer.pipeline import RadiancePipeline0D, SpectralRadiancePipeline0D -from cherab.tools.observers.spectroscopy import PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer +from cherab.tools.spectroscopy import PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer class TestPolychromatorFilter(unittest.TestCase): diff --git a/docs/source/tools/observers.rst b/docs/source/tools/observers.rst index 493bab2b..3feda8d0 100644 --- a/docs/source/tools/observers.rst +++ b/docs/source/tools/observers.rst @@ -62,33 +62,3 @@ bolometer etendue :math:`G`, which is given by: .. autoclass:: cherab.tools.observers.bolometry.BolometerFoil :members: - -.. _observers_spectroscopic_instruments: - -Spectroscopic instruments -------------------------- - -Spectroscopic instruments such as polychromators, survey and high-resolution spectrometers -simplify the setup of rendering pipelines and observers' spectral properties. The Cherab core -package provides base classes for spectroscopic instruments, so machine-specific packages -can build more advance instruments from them, such as instruments with spectral properties -based on the actual experimental setup for a given shot/pulse. - -.. autoclass:: cherab.tools.observers.spectroscopy.SpectroscopicInstrument - :members: - -.. autoclass:: cherab.tools.observers.spectroscopy.PolychromatorFilter - :members: - -.. autoclass:: cherab.tools.observers.spectroscopy.Polychromator - :members: - -.. autoclass:: cherab.tools.observers.spectroscopy.Spectrometer - :members: - -.. autoclass:: cherab.tools.observers.spectroscopy.SurveySpectrometer - :members: - -.. autoclass:: cherab.tools.observers.spectroscopy.CzernyTurnerSpectrometer - :members: - diff --git a/docs/source/tools/spectroscopy.rst b/docs/source/tools/spectroscopy.rst new file mode 100644 index 00000000..406222c4 --- /dev/null +++ b/docs/source/tools/spectroscopy.rst @@ -0,0 +1,38 @@ + +Spectroscopy +============ + +The tools for plasma spectroscopy. + +.. _spectroscopy_instruments: + +Spectroscopic instruments +------------------------- + +Spectroscopic instruments such as polychromators, survey and high-resolution spectrometers +simplify the setup of properties of the observers and rendering pipelines. The instruments +are not connected to the scenegraph, so they cannot observe the world. However, the instruments +have properties, such as `min_wavelength`, `max_wavelength`, `spectral_bins`, +`pipeline_properties`, with which the observer can be configured. +The Cherab core package provides base classes for spectroscopic instruments, +so machine-specific packages can build more advance instruments from them, such as instruments +with spectral properties based on the actual experimental setup for a given shot/pulse. + +.. autoclass:: cherab.tools.spectroscopy.SpectroscopicInstrument + :members: + +.. autoclass:: cherab.tools.spectroscopy.PolychromatorFilter + :members: + +.. autoclass:: cherab.tools.spectroscopy.Polychromator + :members: + +.. autoclass:: cherab.tools.spectroscopy.Spectrometer + :members: + +.. autoclass:: cherab.tools.spectroscopy.SurveySpectrometer + :members: + +.. autoclass:: cherab.tools.spectroscopy.CzernyTurnerSpectrometer + :members: + diff --git a/docs/source/tools/tools.rst b/docs/source/tools/tools.rst index 3b356121..19f58e95 100644 --- a/docs/source/tools/tools.rst +++ b/docs/source/tools/tools.rst @@ -8,6 +8,7 @@ Tools materials primitives observers + spectroscopy tomography utility From 996a36ad913d95752cd09905194bea3c17405387 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 3 Aug 2021 21:24:33 +0300 Subject: [PATCH 06/59] Made Instrument.pipelines() a funstion instead of property to avoid confusion. --- cherab/tools/spectroscopy/instrument.py | 5 ++--- cherab/tools/spectroscopy/polychromator.py | 2 +- cherab/tools/spectroscopy/spectrometer.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cherab/tools/spectroscopy/instrument.py b/cherab/tools/spectroscopy/instrument.py index fc0409b1..f503c7f3 100644 --- a/cherab/tools/spectroscopy/instrument.py +++ b/cherab/tools/spectroscopy/instrument.py @@ -26,7 +26,6 @@ class SpectroscopicInstrument: :ivar list pipeline_properties: The list of properties (class, name, filter) of the pipelines used with this instrument. - :ivar list pipelines: The list of pipelines. Each call returns a list with new instances. :ivar float min_wavelength: Lower wavelength bound for spectral range. :ivar float max_wavelength: Upper wavelength bound for spectral range. :ivar int spectral_bins: The number of spectral samples over the wavelength range. @@ -55,9 +54,9 @@ def pipeline_properties(self): return self._pipeline_properties - @property def pipelines(self): - # The list of pipelines. Each call returns a list with new instances. + """ Returns a list of new pipelines according to `pipeline_properties`.""" + pl_list = [] for (pl_class, pl_name, pl_filter) in self.pipeline_properties: if pl_filter is None: diff --git a/cherab/tools/spectroscopy/polychromator.py b/cherab/tools/spectroscopy/polychromator.py index 2d13a898..27366a45 100644 --- a/cherab/tools/spectroscopy/polychromator.py +++ b/cherab/tools/spectroscopy/polychromator.py @@ -108,7 +108,7 @@ class Polychromator(SpectroscopicInstrument): >>> fibreoptic.min_wavelength = polychromator.min_wavelength >>> fibreoptic.max_wavelength = polychromator.max_wavelength >>> fibreoptic.spectral_bins = polychromator.spectral_bins - >>> fibreoptic.pipelines = polychromator.pipelines + >>> fibreoptic.pipelines = polychromator.pipelines() """ def __init__(self, filters, min_bins_per_window=10, name=''): diff --git a/cherab/tools/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py index e982918e..de67e36a 100644 --- a/cherab/tools/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -134,7 +134,7 @@ class SurveySpectrometer(Spectrometer): >>> bin_width = min((spectrometer.max_wavelength - spectrometer.min_wavelength) / spectrometer.spectral_bins, >>> (polychromator.max_wavelength - polychromator.min_wavelength) / polychromator.spectral_bins) >>> fibreoptic.spectral_bins = int(ceil((fibreoptic.max_wavelength - fibreoptic.min_wavelength) / bin_width)) - >>> fibreoptic.pipelines = spectrometer.pipelines + polychromator.pipelines + >>> fibreoptic.pipelines = spectrometer.pipelines() + polychromator.pipelines() """ def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bin=None, name=''): @@ -194,7 +194,7 @@ class CzernyTurnerSpectrometer(Spectrometer): >>> fibreoptic.min_wavelength = hires_spectrometer.min_wavelength >>> fibreoptic.max_wavelength = hires_spectrometer.max_wavelength >>> fibreoptic.spectral_bins = hires_spectrometer.spectral_bins - >>> fibreoptic.pipelines = hires_spectrometer.pipelines + >>> fibreoptic.pipelines = hires_spectrometer.pipelines() """ def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, spectral_bins, From 6b48415bcb3a0b18a9cbb5f738cffa2aa80762ee Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 11 Nov 2021 21:12:41 +0300 Subject: [PATCH 07/59] Renamed SpectroscopicInstrument.pipelines() to SpectroscopicInstrument.new_pipelines() to avoid confusion. --- cherab/tools/spectroscopy/instrument.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/tools/spectroscopy/instrument.py b/cherab/tools/spectroscopy/instrument.py index f503c7f3..e593cafe 100644 --- a/cherab/tools/spectroscopy/instrument.py +++ b/cherab/tools/spectroscopy/instrument.py @@ -54,7 +54,7 @@ def pipeline_properties(self): return self._pipeline_properties - def pipelines(self): + def new_pipelines(self): """ Returns a list of new pipelines according to `pipeline_properties`.""" pl_list = [] From 808126a194150261ac0ea2ea289e46fb7f7fa226 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Sat, 13 Nov 2021 18:32:08 +0300 Subject: [PATCH 08/59] Improved implementation of Polychromator and PolychromatorFilter. --- cherab/tools/spectroscopy/__init__.py | 2 +- cherab/tools/spectroscopy/polychromator.py | 126 +++++++++++++++------ 2 files changed, 91 insertions(+), 37 deletions(-) diff --git a/cherab/tools/spectroscopy/__init__.py b/cherab/tools/spectroscopy/__init__.py index 207aaf87..74bf4d16 100644 --- a/cherab/tools/spectroscopy/__init__.py +++ b/cherab/tools/spectroscopy/__init__.py @@ -18,5 +18,5 @@ # under the Licence. from .instrument import SpectroscopicInstrument -from .polychromator import PolychromatorFilter, Polychromator +from .polychromator import PolychromatorFilter, TrapezoidalFilter, Polychromator from .spectrometer import Spectrometer, CzernyTurnerSpectrometer, SurveySpectrometer diff --git a/cherab/tools/spectroscopy/polychromator.py b/cherab/tools/spectroscopy/polychromator.py index 27366a45..0a72c970 100644 --- a/cherab/tools/spectroscopy/polychromator.py +++ b/cherab/tools/spectroscopy/polychromator.py @@ -26,7 +26,78 @@ class PolychromatorFilter(InterpolatedSF): """ - Defines a symmetrical trapezoidal polychromator filter as a Raysect's InterpolatedSF. + Defines a polychromator filter as a Raysect's InterpolatedSF. + + :param object wavelengths: 1D array of wavelengths in nanometers. + :param object samples: 1D array of spectral samples. + :param bool normalise: True/false toggle for whether to normalise the + spectral function so its integral equals 1. + :param str name: Filter name (e.g. "H-alpha filter"). Default is ''. + + :ivar float min_wavelength: Lower wavelength bound of the filter's spectral range in nm. + :ivar float max_wavelength: Upper wavelength bound of the filter's spectral range in nm. + """ + + def __init__(self, wavelengths, samples, normalise=False, name=''): + + wavelengths = np.array(wavelengths, dtype=np.float64) + samples = np.array(samples, dtype=np.float64) + + if wavelengths.ndim != 1: + raise ValueError("Wavelength array must be 1D.") + + if samples.shape[0] != wavelengths.shape[0]: + raise ValueError("Wavelength and sample arrays must be the same length.") + + indices = np.argsort(wavelengths) + wavelengths = wavelengths[indices] + samples = samples[indices] + + self._min_wavelength = wavelengths[0] + self._max_wavelength = wavelengths[-1] + self._window = self._max_wavelength - self._min_wavelength + self._central_wavelength = 0.5 * (self._max_wavelength + self._min_wavelength) + + # setting the ends of the filter to zero, if they are not + if samples[0] != 0: + wavelengths = np.insert(wavelengths, 0, wavelengths[0] * (1. - 1.e-15)) + samples = np.insert(samples, 0, 0) + if samples[-1] != 0: + wavelengths = np.append(wavelengths, wavelengths[-1] * (1. + 1.e-15)) + samples = np.append(samples, 0) + + super().__init__(wavelengths, samples, normalise) + self._name = str(name) + + @property + def name(self): + # Filter name. + return self._name + + @property + def min_wavelength(self): + # Lower wavelength bound of the filter's spectral range in nm. + return self._min_wavelength + + @property + def max_wavelength(self): + # Upper wavelength bound of the filter's spectral range in nm. + return self._max_wavelength + + @property + def window(self): + # Size of the filtering window in nm. + return self._window + + @property + def central_wavelength(self): + # Central wavelength of the filter in nm. + return self._central_wavelength + + +class TrapezoidalFilter(PolychromatorFilter): + """ + Symmetrical trapezoidal polychromator filter. :param float wavelength: Central wavelength of the filter in nm. :param float window: Size of the filtering window in nm. Default is 3. @@ -35,55 +106,38 @@ class PolychromatorFilter(InterpolatedSF): :param str name: Filter name (e.g. "H-alpha filter"). Default is ''. """ - def __init__(self, wavelength, window=3., flat_top=None, name=''): + def __init__(self, central_wavelength, window=3., flat_top=None, name=''): - if wavelength <= 0: - raise ValueError("Argument 'wavelength' must be positive.") + if central_wavelength <= 0: + raise ValueError("Argument 'central_wavelength' must be positive.") if window <= 0: raise ValueError("Argument 'window' must be positive.") - flat_top = flat_top or window - 1.e-15 + flat_top = flat_top or window if flat_top <= 0: raise ValueError("Argument 'flat_top' must be positive.") if flat_top > window: raise ValueError("Argument 'flat_top' must be less or equal than 'window'.") - if flat_top == window: - flat_top = window - 1.e-15 - self._window = window self._flat_top = flat_top - self._wavelength = wavelength - self._name = str(name) - wavelengths = [wavelength - 0.5 * window, - wavelength - 0.5 * flat_top, - wavelength + 0.5 * flat_top, - wavelength + 0.5 * window] - samples = [0, 1, 1, 0] - super().__init__(wavelengths, samples, normalise=False) + if flat_top == window: + flat_top -= flat_top * 1.e-15 - @property - def window(self): - # Size of the filtering window in nm. - return self._window + wavelengths = [central_wavelength - 0.5 * window, + central_wavelength - 0.5 * flat_top, + central_wavelength + 0.5 * flat_top, + central_wavelength + 0.5 * window] + samples = [0, 1, 1, 0] + super().__init__(wavelengths, samples, normalise=False, name=name) @property def flat_top(self): # Size of the flat top part of the filter in nm. return self._flat_top - @property - def wavelength(self): - # Central wavelength of the filter in nm. - return self._wavelength - - @property - def name(self): - # Filter name. - return self._name - class Polychromator(SpectroscopicInstrument): """ @@ -98,17 +152,17 @@ class Polychromator(SpectroscopicInstrument): >>> from raysect.optical import World >>> from raysect.optical.observer import FibreOptic - >>> from cherab.tools.spectroscopy import Polychromator, PolychromatorFilter + >>> from cherab.tools.spectroscopy import Polychromator, TrapezoidalFilter >>> >>> world = World() - >>> h_alpha_filter = PolychromatorFilter(656.1, name='H-alpha filter') - >>> ciii_465nm_filter = PolychromatorFilter(464.8, name='CIII 465 nm filter') + >>> h_alpha_filter = TrapezoidalFilter(656.1, name='H-alpha filter') + >>> ciii_465nm_filter = TrapezoidalFilter(464.8, name='CIII 465 nm filter') >>> polychromator = Polychromator([h_alpha_filter, ciii_465nm_filter], name='MyPolychromator') >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) >>> fibreoptic.min_wavelength = polychromator.min_wavelength >>> fibreoptic.max_wavelength = polychromator.max_wavelength >>> fibreoptic.spectral_bins = polychromator.spectral_bins - >>> fibreoptic.pipelines = polychromator.pipelines() + >>> fibreoptic.pipelines = polychromator.new_pipelines() """ def __init__(self, filters, min_bins_per_window=10, name=''): @@ -155,8 +209,8 @@ def _update_spectral_settings(self): step = np.inf for poly_filter in self._filters: step = min(step, poly_filter.window / self._min_bins_per_window) - min_wavelength = min(min_wavelength, poly_filter.wavelength - 0.5 * poly_filter.window) - max_wavelength = max(max_wavelength, poly_filter.wavelength + 0.5 * poly_filter.window) + min_wavelength = min(min_wavelength, poly_filter.min_wavelength) + max_wavelength = max(max_wavelength, poly_filter.max_wavelength) self._min_wavelength = min_wavelength self._max_wavelength = max_wavelength From 5172888fa6eaa17186dbd72bfdbfce800c17ade3 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Sat, 13 Nov 2021 20:02:01 +0300 Subject: [PATCH 09/59] Updated the tests for the Polychromator and the filters. --- .../tests/test_spectroscopic_instruments.py | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/cherab/tools/tests/test_spectroscopic_instruments.py b/cherab/tools/tests/test_spectroscopic_instruments.py index 883dc8c6..ab33f170 100644 --- a/cherab/tools/tests/test_spectroscopic_instruments.py +++ b/cherab/tools/tests/test_spectroscopic_instruments.py @@ -20,7 +20,7 @@ import numpy as np from raysect.optical.observer.pipeline import RadiancePipeline0D, SpectralRadiancePipeline0D -from cherab.tools.spectroscopy import PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer +from cherab.tools.spectroscopy import TrapezoidalFilter, PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer class TestPolychromatorFilter(unittest.TestCase): @@ -28,11 +28,26 @@ class TestPolychromatorFilter(unittest.TestCase): Test for PolychromatorFilter class. """ + def test_spectrum(self): + wavelengths = [658, 654, 656] # unsorted + samples = [0.5, 0.5, 1] # non-zero at the ends + poly_filter = PolychromatorFilter(wavelengths, samples, name='test_filter') + wavelengths = np.linspace(653., 659., 7) + spectrum_true = np.array([0, 0.5, 0.75, 1., 0.75, 0.5, 0]) + spectrum_test = np.array([poly_filter(wvl) for wvl in wavelengths]) + self.assertTrue(np.all(spectrum_true == spectrum_test)) + + +class TestTrapezoidalFilter(unittest.TestCase): + """ + Test for TrapezoidalFilter class. + """ + def test_spectrum(self): wavelength = 500. window = 6. flat_top = 2. - poly_filter = PolychromatorFilter(wavelength, window, flat_top, 'test_filter') + poly_filter = TrapezoidalFilter(wavelength, window, flat_top, 'test_filter') wavelengths = np.linspace(496., 504., 9) spectrum_true = np.array([0, 0, 0.5, 1., 1., 1., 0.5, 0, 0]) spectrum_test = np.array([poly_filter(wvl) for wvl in wavelengths]) @@ -44,10 +59,9 @@ class TestPolychromator(unittest.TestCase): Test cases for Polychromator class. """ - def setUp(self): - self.poly_filters_default = [PolychromatorFilter(400., 6., 2., 'filter 1'), - PolychromatorFilter(700., 8., 4., 'filter 2')] - self.min_bins_per_window_default = 10 + poly_filters_default = (TrapezoidalFilter(400., 6., 2., 'filter 1'), + TrapezoidalFilter(700., 8., 4., 'filter 2')) + min_bins_per_window_default = 10 def test_pipeline_properties(self): polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default, 'test polychromator') @@ -68,8 +82,8 @@ def test_filter_change(self): """ Checks if the spectral properties are updated correctly when the filters are replaced.""" polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default) polychromator.min_bins_per_window = 20 - polychromator.filters = [PolychromatorFilter(500., 5., 2., 'filter 1'), - PolychromatorFilter(600., 7., 4., 'filter 2')] + polychromator.filters = [TrapezoidalFilter(500., 5., 2., 'filter 1'), + TrapezoidalFilter(600., 7., 4., 'filter 2')] min_wavelength_true = 497.5 max_wavelength_true = 603.5 spectral_bins_true = 424 @@ -109,14 +123,13 @@ class TestCzernyTurnerSpectrometer(unittest.TestCase): Test cases for CzernyTurnerSpectrometer class. """ - def setUp(self): - self.diffraction_order = 1 - self.grating = 2.e-3 - self.focal_length = 1.e9 - self.pixel_spacing = 2.e4 - self.diffraction_angle = 10. - self.spectral_bins = 512 - self.reference_bin = 255 + diffraction_order = 1 + grating = 2.e-3 + focal_length = 1.e9 + pixel_spacing = 2.e4 + diffraction_angle = 10. + spectral_bins = 512 + reference_bin = 255 def test_resolution(self): wavelengths = [350., 550., 750.] From 7dc613d0cf75e6c985e8c043c7efe21f734dee0f Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 18 Nov 2021 14:18:00 +0300 Subject: [PATCH 10/59] Reimplemented spectrometers in response to reviewer's comments. --- cherab/tools/spectroscopy/__init__.py | 2 +- cherab/tools/spectroscopy/spectrometer.py | 319 ++++++++++-------- .../tests/test_spectroscopic_instruments.py | 67 ++-- docs/source/tools/spectroscopy.rst | 12 +- 4 files changed, 221 insertions(+), 179 deletions(-) diff --git a/cherab/tools/spectroscopy/__init__.py b/cherab/tools/spectroscopy/__init__.py index 74bf4d16..23bbbe1f 100644 --- a/cherab/tools/spectroscopy/__init__.py +++ b/cherab/tools/spectroscopy/__init__.py @@ -19,4 +19,4 @@ from .instrument import SpectroscopicInstrument from .polychromator import PolychromatorFilter, TrapezoidalFilter, Polychromator -from .spectrometer import Spectrometer, CzernyTurnerSpectrometer, SurveySpectrometer +from .spectrometer import Spectrometer, CzernyTurnerSpectrometer diff --git a/cherab/tools/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py index de67e36a..c640179d 100644 --- a/cherab/tools/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -18,6 +18,7 @@ # under the Licence. import numpy as np +from raysect.optical import Spectrum from raysect.optical.observer import SpectralRadiancePipeline0D from .instrument import SpectroscopicInstrument @@ -25,161 +26,165 @@ class Spectrometer(SpectroscopicInstrument): """ - Spectrometer base class. - This is an abstract class. - - :param int spectral_bins: The number of spectral samples over the wavelength range. - :param float reference_wavelength: Wavelength (in nm) corresponding to - the centre of reference bin. - :param int reference_bin: Reference bin index. Can be negative to specify the offset. - Default is None (spectral_bins // 2). - :param str name: Spectrometer name. - """ - - def __init__(self, spectral_bins, reference_wavelength, reference_bin=None, name=''): - super().__init__(name) - self.spectral_bins = spectral_bins - if reference_bin is None: - self.reference_bin = self._spectral_bins // 2 - else: - self.reference_bin = reference_bin - self.reference_wavelength = reference_wavelength + Spectrometer that can accommodate multiple spectra. - @property - def spectral_bins(self): - # The number of spectral samples over the wavelength range. - return self._spectral_bins - - @spectral_bins.setter - def spectral_bins(self, value): - value = int(value) - if value <= 0: - raise ValueError("Attribute 'spectral_bins' must be > 0.") + Spectrometer is initialized with a sequence of calibration arrays (one array per accommodated + spectrum) containing the wavelengths of the pixel borders. Namely, the values + :math:`w_{k}^{i}` and :math:`w_{k}^{i+1}` define the spectral range of the pixel :math:`p_i` + of the `k`-th spectrum. After the spectrum is ray-traced, it can be recalibrated with + `spectrometer.calibrate(spectrum)`. - self._spectral_bins = value - self._clear_spectral_settings() - - @property - def reference_wavelength(self): - # Wavelength (in nm) corresponding to the centre of reference bin. - return self._reference_wavelength - - @reference_wavelength.setter - def reference_wavelength(self, value): - if value <= 0: - raise ValueError("Attribute 'reference_wavelength' must be > 0.") - - self._reference_wavelength = value - self._clear_spectral_settings() + Note that Raysect cannot raytrace the spectra with non-constant spectral resolution. + Thus, the actual number of spectral bins of raytraced spectrum is defined with + `min_bins_per_pixel` attribute. - @property - def reference_bin(self): - # Reference bin index. - return self._reference_bin - - @reference_bin.setter - def reference_bin(self, value): - value = int(value) - - self._reference_bin = value - self._clear_spectral_settings() - - def _update_pipeline_properties(self): - self._pipeline_properties = [(SpectralRadiancePipeline0D, self._name, None)] - - def _clear_spectral_settings(self): - self._min_wavelength = None - self._max_wavelength = None - - -class SurveySpectrometer(Spectrometer): - """ - Survey spectrometer with a constant spectral resolution. - - Note: survey spectrometers usually have non-constant spectral resolution - in the supported wavelength range. However, Raysect does not support - the observers with variable spectral resolution. - - :param float resolution: Spectral resolution in nm (can be negative). - :param int spectral_bins: The number of spectral samples over the wavelength range. - :param float reference_wavelength: Wavelength (in nm) corresponding to - the centre of reference bin. - :param int reference_bin: Reference bin index. Can be negative to specify the offset. - Default is None (spectral_bins // 2). + :param tuple wavelength_to_pixel: Wavelength-to-pixel calibration arrays. + :param int min_bins_per_pixel: Minimal number of spectral bins + per pixel. Default is 1. :param str name: Spectrometer name. - :ivar float resolution: Spectral resolution in nm (can be negative). + :ivar tuple wavelengths: Central wavelengths of the pixels. .. code-block:: pycon - >>> from numpy import ceil - >>> from raysect.optical import World + >>> from raysect.optical import World, Spectrum >>> from raysect.optical.observer import FibreOptic - >>> from cherab.tools.spectroscopy import SurveySpectrometer, Polychromator, PolychromatorFilter + >>> from cherab.tools.spectroscopy import Spectrometer + >>> from matplotlib import pyplot as plt + >>> + >>> wavelength_to_pixel = ([400., 400.5, 401.5, 402., 404.], + >>> [600., 600.5, 601.5, 602., 604., 607.]) + >>> spectrometer = Spectrometer(wavelength_to_pixel, min_bins_per_pixel=5, + >>> name='MySpectrometer') >>> >>> world = World() >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) - >>> # Here, the fibre optic is "connected" to both survey spectrometer and polychromator. + >>> fibreoptic.min_wavelength = spectrometer.min_wavelength + >>> fibreoptic.max_wavelength = spectrometer.max_wavelength + >>> fibreoptic.spectral_bins = spectrometer.spectral_bins + >>> fibreoptic.pipelines = spectrometer.new_pipelines() + >>> ... + >>> fibreoptic.observe() + >>> spectrum = Spectrum(fibreoptic.min_wavelength, fibreoptic.max_wavelength, fibreoptic.spectral_bins) + >>> spectrum.samples[:] = fibreoptic.pipelines[0].mean + >>> calibrated_spectra = spectrometer.calibrate(spectrum) + >>> wavelengths = spectrometer.wavelengths >>> - >>> # setting up the polychromator - >>> h_alpha_filter = PolychromatorFilter(656.1, name='H-alpha filter') - >>> ciii_465nm_filter = PolychromatorFilter(464.8, name='CIII 465 nm filter') - >>> polychromator = Polychromator([h_alpha_filter, ciii_465nm_filter], name='MyPolychromator') - >>> - >>> # setting up the survey spectrometer - >>> spectrometer = SurveySpectrometer(0.1, 1024, 500, name='MySpectrometer') - >>> - >>> fibreoptic.min_wavelength = min(spectrometer.min_wavelength, polychromator.min_wavelength) - >>> fibreoptic.max_wavelength = max(spectrometer.max_wavelength, polychromator.max_wavelength) - >>> bin_width = min((spectrometer.max_wavelength - spectrometer.min_wavelength) / spectrometer.spectral_bins, - >>> (polychromator.max_wavelength - polychromator.min_wavelength) / polychromator.spectral_bins) - >>> fibreoptic.spectral_bins = int(ceil((fibreoptic.max_wavelength - fibreoptic.min_wavelength) / bin_width)) - >>> fibreoptic.pipelines = spectrometer.pipelines() + polychromator.pipelines() + >>> plt.plot(wavelengths[0], calibrated_spectra[0]) + >>> plt.show() """ - def __init__(self, resolution, spectral_bins, reference_wavelength, reference_bin=None, name=''): - super().__init__(spectral_bins, reference_wavelength, reference_bin, name) - self.resolution = resolution + def __init__(self, wavelength_to_pixel, min_bins_per_pixel=1, name=''): + + self.min_bins_per_pixel = min_bins_per_pixel + self.wavelength_to_pixel = wavelength_to_pixel + super().__init__(name) + + @property + def wavelength_to_pixel(self): + # Wavelength-to-pixel calibration arrays. + return self._wavelength_to_pixel + + @wavelength_to_pixel.setter + def wavelength_to_pixel(self, value): + _wavelength_to_pixel = [] + _wavelengths = [] + for wl2pix in value: + wl2pix = np.array(wl2pix, dtype=float) + if wl2pix.ndim != 1: + raise ValueError('Attribute wavelength_to_pixel must only contain one-dimensional arrays.') + if wl2pix.size < 2: + raise ValueError('Attribute wavelength_to_pixel must only contain arrays of at least 2 elements.') + if np.any(np.diff(wl2pix) <= 0): + raise ValueError('Attribute wavelength_to_pixel must only contain monotonically increasing arrays.') + wl2pix.flags.writeable = False + _wavelength_to_pixel.append(wl2pix) + wl_center = 0.5 * (wl2pix[1:] + wl2pix[:-1]) + wl_center.flags.writeable = False + _wavelengths.append(wl_center) + self._wavelength_to_pixel = tuple(_wavelength_to_pixel) + self._wavelengths = tuple(_wavelengths) + self._clear_spectral_settings() + + @property + def wavelengths(self): + # Central wavelengths of the pixels. + return self._wavelengths @property - def resolution(self): - # Spectral resolution in nm (can be negative). - return self._resolution + def min_bins_per_pixel(self): + # Minimal number of spectral bins per pixel. + return self._min_bins_per_pixel - @resolution.setter - def resolution(self, value): - if value == 0: - raise ValueError("Attribute 'resolution' must be non-zero.") + @min_bins_per_pixel.setter + def min_bins_per_pixel(self, value): + value = int(value) + if value <= 0: + raise ValueError("Attribute 'min_bins_per_pixel' must be positive.") - self._resolution = value + self._min_bins_per_pixel = value self._clear_spectral_settings() - def _update_spectral_settings(self): + def _update_pipeline_properties(self): + self._pipeline_properties = [(SpectralRadiancePipeline0D, self._name, None)] - if self._resolution > 0: - self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * self._resolution - self._max_wavelength = self._min_wavelength + self._spectral_bins * self._resolution - else: - self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * self._resolution - self._max_wavelength = self._min_wavelength - self._spectral_bins * self._resolution + def _update_spectral_settings(self): + self._min_wavelength = min(wl2pix[0] for wl2pix in self._wavelength_to_pixel) + self._max_wavelength = max(wl2pix[-1] for wl2pix in self._wavelength_to_pixel) + step = min(np.diff(wl2pix).min() for wl2pix in self._wavelength_to_pixel) / self._min_bins_per_pixel + self._spectral_bins = int(np.ceil((self._max_wavelength - self._min_wavelength) / step)) + + def calibrate(self, spectrum): + """ + Calibrates the spectrum according to the `wavelength_to_pixel` arrays + by averaging it over the pixel widths. + + :param Spectrum spectrum: Spectrum to calibrate. + + :returns: A tuple of calibrated spectra as ndarrays. + """ + if not isinstance(spectrum, Spectrum): + raise TypeError('Argument spectrum must be a Spectrum instance.') + if spectrum.min_wavelength > self.min_wavelength or spectrum.max_wavelength < self.max_wavelength: + raise ValueError('Unable to calibrate the spectrum. ' + 'The spectrum has narrower range ({}, {}) than the spectrometer ({}, {}).'.format(spectrum.min_wavelength, + spectrum.max_wavelength, + self.min_wavelength, + self.max_wavelength)) + calibrated_spectra = [] + for wl2pix in self.wavelength_to_pixel: + calibrated_spectrum = np.zeros(wl2pix.size - 1) + for i in range(wl2pix.size - 1): + calibrated_spectrum[i] = spectrum.integrate(wl2pix[i], wl2pix[i + 1]) / (wl2pix[i + 1] - wl2pix[i]) + calibrated_spectra.append(calibrated_spectrum) + + return calibrated_spectra class CzernyTurnerSpectrometer(Spectrometer): """ - Czerny-Turner high-resolution spectrometer. + Czerny-Turner spectrometer. + + The Czerny-Turner spectrometer is initialized with the parameters of the diffraction scheme + and a sequence of accommodated spectra, each of which is determined by the lower wavelength + bound and the number of pixels. + + This spectrometer automatically fills the wavelength-to-pixel calibration arrays + according to the parameters of the diffraction scheme. :param int diffraction_order: Diffraction order. :param float grating: Diffraction grating in nm-1. :param float focal_length: Focal length in nm. :param float pixel_spacing: Pixel to pixel spacing on CCD in nm. :param float diffraction_angle: Angle between incident and diffracted light in degrees. - :param int spectral_bins: The number of spectral samples over the wavelength range. - :param float reference_wavelength: Wavelength (in nm) corresponding to - the centre of reference bin. - :param int reference_bin: Reference bin index. Default is None (spectral_bins // 2). + :param tuple accommodated_spectra: A sequence of (`min_wavelength`, `pixels`) pairs, specifying + the lower wavelength bound and the number of pixels + of accommodated spectra. + :param int min_bins_per_pixel: Minimal number of spectral bins + per pixel. Default is 1. :param str name: Spectrometer name. - :ivar float resolution: Spectral resolution in nm (can be negative). + :ivar tuple wavelength_to_pixel: Wavelength-to-pixel calibration arrays. .. code-block:: pycon @@ -188,23 +193,26 @@ class CzernyTurnerSpectrometer(Spectrometer): >>> from cherab.tools.spectroscopy import CzernyTurnerSpectrometer >>> >>> world = World() - >>> hires_spectrometer = CzernyTurnerSpectrometer(1, 2.e-3, 1.e9, 2.e4, 10., 512, 600., + >>> hires_spectrometer = CzernyTurnerSpectrometer(1, 2.e-3, 1.e9, 2.e4, 10., + >>> ((600., 512), (700., 128)), >>> name='MySpectrometer') >>> fibreoptic = FibreOptic(name="MyFibreOptic", parent=world) >>> fibreoptic.min_wavelength = hires_spectrometer.min_wavelength >>> fibreoptic.max_wavelength = hires_spectrometer.max_wavelength >>> fibreoptic.spectral_bins = hires_spectrometer.spectral_bins - >>> fibreoptic.pipelines = hires_spectrometer.pipelines() + >>> fibreoptic.pipelines = hires_spectrometer.new_pipelines() """ - def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, spectral_bins, - reference_wavelength, reference_bin=None, name=''): - super().__init__(spectral_bins, reference_wavelength, reference_bin, name) + def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, + accommodated_spectra, min_bins_per_pixel=1, name=''): self.diffraction_order = diffraction_order self.grating = grating self.focal_length = focal_length self.pixel_spacing = pixel_spacing self.diffraction_angle = diffraction_angle + self.accommodated_spectra = accommodated_spectra + self.min_bins_per_pixel = min_bins_per_pixel + self.name = name @property def diffraction_order(self): @@ -253,8 +261,8 @@ def pixel_spacing(self): @pixel_spacing.setter def pixel_spacing(self, value): - if value == 0: - raise ValueError("Attribute 'pixel_spacing' must be non-zero.") + if value <= 0: + raise ValueError("Attribute 'pixel_spacing' must be positive.") self._pixel_spacing = value self._clear_spectral_settings() @@ -273,26 +281,53 @@ def diffraction_angle(self, value): self._clear_spectral_settings() @property - def resolution(self): - # Spectral resolution in nm (can be negative). + def accommodated_spectra(self): + return self._accommodated_spectra + + @accommodated_spectra.setter + def accommodated_spectra(self, value): + _wavelength_to_pixel = [] + _wavelengths = [] + for min_wavelength, pixels in value: + if min_wavelength <= 0: + raise ValueError('The value of min_wavelength in accommodated_spectra must be positive.') + if pixels <= 0: + raise ValueError('The value of pixels in accommodated_spectra must be positive.') + pixels = int(pixels) + wl2pix = np.zeros(pixels + 1) + wl2pix[0] = min_wavelength + for i in range(1, pixels + 1): + wl2pix[i] = wl2pix[i - 1] + self.resolution(wl2pix[i - 1]) + wl2pix.flags.writeable = False + _wavelength_to_pixel.append(wl2pix) + wl_center = 0.5 * (wl2pix[1:] + wl2pix[:-1]) + wl_center.flags.writeable = False + _wavelengths.append(wl_center) + self._accommodated_spectra = value + self._wavelength_to_pixel = tuple(_wavelength_to_pixel) + self._wavelengths = tuple(_wavelengths) + self._clear_spectral_settings() + + @property + def wavelength_to_pixel(self): + # Wavelength-to-pixel calibration arrays. + return self._wavelength_to_pixel + + def resolution(self, wavelength): + """ + Calculates spectral resolution in nm for a given wavelength. + + :param wavelength: Wavelength in nm. + + :returns: Resolution in nm. + """ grating = self._grating m = self._diffraction_order dxdp = self._pixel_spacing angle = self._diffraction_angle fl = self._focal_length - p = 0.5 * m * grating * self._reference_wavelength + p = 0.5 * m * grating * wavelength _resolution = dxdp * (np.sqrt(np.cos(angle)**2 - p * p) - p * np.tan(angle)) / (m * fl * grating) return _resolution - - def _update_spectral_settings(self): - - resolution = self.resolution - - if resolution > 0: - self._min_wavelength = self._reference_wavelength - (self._reference_bin + 0.5) * resolution - self._max_wavelength = self._min_wavelength + self._spectral_bins * resolution - else: - self._min_wavelength = self._reference_wavelength + (self._spectral_bins - self._reference_bin - 0.5) * resolution - self._max_wavelength = self._min_wavelength - self._spectral_bins * resolution diff --git a/cherab/tools/tests/test_spectroscopic_instruments.py b/cherab/tools/tests/test_spectroscopic_instruments.py index ab33f170..a8d843d6 100644 --- a/cherab/tools/tests/test_spectroscopic_instruments.py +++ b/cherab/tools/tests/test_spectroscopic_instruments.py @@ -19,8 +19,9 @@ import unittest import numpy as np +from raysect.optical import Spectrum from raysect.optical.observer.pipeline import RadiancePipeline0D, SpectralRadiancePipeline0D -from cherab.tools.spectroscopy import TrapezoidalFilter, PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, SurveySpectrometer +from cherab.tools.spectroscopy import TrapezoidalFilter, PolychromatorFilter, Polychromator, CzernyTurnerSpectrometer, Spectrometer class TestPolychromatorFilter(unittest.TestCase): @@ -92,30 +93,35 @@ def test_filter_change(self): polychromator.spectral_bins == spectral_bins_true) -class TestSurveySpectrometer(unittest.TestCase): +class TestSpectrometer(unittest.TestCase): """ - Test cases for SurveySpectrometer class. + Test cases for Spectrometer class. """ def test_pipeline_properties(self): - resolution = 0.1 - reference_wavelength = 500 - reference_bin = 50 - spectral_bins = 200 - spectrometer = SurveySpectrometer(resolution, spectral_bins, reference_wavelength, reference_bin, name='test spectrometer') + wavelength_to_pixel = ([400., 400.5],) + spectrometer = Spectrometer(wavelength_to_pixel, name='test spectrometer') pipeline_properties_true = [(SpectralRadiancePipeline0D, 'test spectrometer', None)] self.assertSequenceEqual(pipeline_properties_true, spectrometer.pipeline_properties) def test_spectral_properties(self): - resolution = 0.1 - reference_wavelength = 500 - reference_bin = 50 - spectral_bins = 200 - spectrometer = SurveySpectrometer(resolution, spectral_bins, reference_wavelength, reference_bin, name='test spectrometer') - min_wavelength_true = 494.95 - max_wavelength_true = 514.95 + wavelength_to_pixel = ([400., 400.5, 401.5, 402., 404.], [600., 600.5, 601.5, 602., 604., 607.]) + spectrometer = Spectrometer(wavelength_to_pixel, min_bins_per_pixel=2, name='test spectrometer') + min_wavelength_true = 400. + max_wavelength_true = 607. + spectra_bins_true = 828 self.assertTrue(spectrometer.min_wavelength == min_wavelength_true and - spectrometer.max_wavelength == max_wavelength_true) + spectrometer.max_wavelength == max_wavelength_true and + spectrometer.spectral_bins == spectra_bins_true) + + def test_calibration(self): + wavelength_to_pixel = ([400., 400.5, 401.5, 402., 404.],) + spectrometer = Spectrometer(wavelength_to_pixel, name='test spectrometer') + spectrum = Spectrum(399, 405, 12) + s, ds = np.linspace(0, 6., 13, retstep=True) + spectrum.samples[:] = s[:-1] + 0.5 * ds + calibrated_spectra = spectrometer.calibrate(spectrum) + self.assertTrue(np.all(calibrated_spectra[0] == np.array([1.25, 2., 2.75, 4.]))) class TestCzernyTurnerSpectrometer(unittest.TestCase): @@ -128,30 +134,27 @@ class TestCzernyTurnerSpectrometer(unittest.TestCase): focal_length = 1.e9 pixel_spacing = 2.e4 diffraction_angle = 10. - spectral_bins = 512 - reference_bin = 255 + accommodated_spectra = ((400., 64), (500., 32)) + min_bins_per_pixel = 2 def test_resolution(self): - wavelengths = [350., 550., 750.] + wavelengths = np.array([350., 550., 750.]) resolutions_true = np.array([8.587997e-3, 7.199328e-3, 5.0599164e-3]) spectrometer = CzernyTurnerSpectrometer(self.diffraction_order, self.grating, self.focal_length, self.pixel_spacing, - self.diffraction_angle, self.spectral_bins, 500., self.reference_bin, - name='test spectrometer') - resolutions = [] - for wvl in wavelengths: - spectrometer.reference_wavelength = wvl - resolutions.append(spectrometer.resolution) + self.diffraction_angle, self.accommodated_spectra, name='test spectrometer') + resolutions = spectrometer.resolution(wavelengths) self.assertTrue(np.all(np.abs(resolutions / resolutions_true - 1.) < 1.e-7)) def test_spectral_properties(self): - wavelength = 500. - min_wavelength_true = 498.0575 - max_wavelength_true = 501.9501 + min_wavelength_true = 400 + max_wavelength_true = 500.24326 + spectra_bins_true = 26377 spectrometer = CzernyTurnerSpectrometer(self.diffraction_order, self.grating, self.focal_length, self.pixel_spacing, - self.diffraction_angle, self.spectral_bins, wavelength, self.reference_bin, - name='test spectrometer') - self.assertTrue(abs(spectrometer.min_wavelength - min_wavelength_true) < 1.e-4 and - abs(spectrometer.max_wavelength - max_wavelength_true) < 1.e-4) + self.diffraction_angle, self.accommodated_spectra, + min_bins_per_pixel=self.min_bins_per_pixel, name='test spectrometer') + self.assertTrue(spectrometer.min_wavelength == min_wavelength_true and + spectrometer.spectral_bins == spectra_bins_true and + abs(spectrometer.max_wavelength - max_wavelength_true) < 1.e-5) if __name__ == '__main__': diff --git a/docs/source/tools/spectroscopy.rst b/docs/source/tools/spectroscopy.rst index 406222c4..a116dd77 100644 --- a/docs/source/tools/spectroscopy.rst +++ b/docs/source/tools/spectroscopy.rst @@ -9,7 +9,7 @@ The tools for plasma spectroscopy. Spectroscopic instruments ------------------------- -Spectroscopic instruments such as polychromators, survey and high-resolution spectrometers +Spectroscopic instruments such as polychromators and spectrometers simplify the setup of properties of the observers and rendering pipelines. The instruments are not connected to the scenegraph, so they cannot observe the world. However, the instruments have properties, such as `min_wavelength`, `max_wavelength`, `spectral_bins`, @@ -24,15 +24,19 @@ with spectral properties based on the actual experimental setup for a given shot .. autoclass:: cherab.tools.spectroscopy.PolychromatorFilter :members: -.. autoclass:: cherab.tools.spectroscopy.Polychromator +.. autoclass:: cherab.tools.spectroscopy.TrapezoidalFilter + :show-inheritance: :members: -.. autoclass:: cherab.tools.spectroscopy.Spectrometer +.. autoclass:: cherab.tools.spectroscopy.Polychromator + :show-inheritance: :members: -.. autoclass:: cherab.tools.spectroscopy.SurveySpectrometer +.. autoclass:: cherab.tools.spectroscopy.Spectrometer + :show-inheritance: :members: .. autoclass:: cherab.tools.spectroscopy.CzernyTurnerSpectrometer + :show-inheritance: :members: From 6e7c75ed4a367fd328113b98588aca877d21a0f3 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 23 Nov 2021 20:43:59 +0300 Subject: [PATCH 11/59] Renamed SpectroscopicInstrument's new_pipelines() to create_pipeline(). --- cherab/tools/spectroscopy/instrument.py | 4 ++-- cherab/tools/spectroscopy/polychromator.py | 2 +- cherab/tools/spectroscopy/spectrometer.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cherab/tools/spectroscopy/instrument.py b/cherab/tools/spectroscopy/instrument.py index e593cafe..b2baf152 100644 --- a/cherab/tools/spectroscopy/instrument.py +++ b/cherab/tools/spectroscopy/instrument.py @@ -54,8 +54,8 @@ def pipeline_properties(self): return self._pipeline_properties - def new_pipelines(self): - """ Returns a list of new pipelines according to `pipeline_properties`.""" + def create_pipelines(self): + """ Returns a list of new pipelines created according to `pipeline_properties`.""" pl_list = [] for (pl_class, pl_name, pl_filter) in self.pipeline_properties: diff --git a/cherab/tools/spectroscopy/polychromator.py b/cherab/tools/spectroscopy/polychromator.py index 0a72c970..f81c6158 100644 --- a/cherab/tools/spectroscopy/polychromator.py +++ b/cherab/tools/spectroscopy/polychromator.py @@ -162,7 +162,7 @@ class Polychromator(SpectroscopicInstrument): >>> fibreoptic.min_wavelength = polychromator.min_wavelength >>> fibreoptic.max_wavelength = polychromator.max_wavelength >>> fibreoptic.spectral_bins = polychromator.spectral_bins - >>> fibreoptic.pipelines = polychromator.new_pipelines() + >>> fibreoptic.pipelines = polychromator.create_pipelines() """ def __init__(self, filters, min_bins_per_window=10, name=''): diff --git a/cherab/tools/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py index c640179d..436f301a 100644 --- a/cherab/tools/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -62,7 +62,7 @@ class Spectrometer(SpectroscopicInstrument): >>> fibreoptic.min_wavelength = spectrometer.min_wavelength >>> fibreoptic.max_wavelength = spectrometer.max_wavelength >>> fibreoptic.spectral_bins = spectrometer.spectral_bins - >>> fibreoptic.pipelines = spectrometer.new_pipelines() + >>> fibreoptic.pipelines = spectrometer.create_pipelines() >>> ... >>> fibreoptic.observe() >>> spectrum = Spectrum(fibreoptic.min_wavelength, fibreoptic.max_wavelength, fibreoptic.spectral_bins) @@ -200,7 +200,7 @@ class CzernyTurnerSpectrometer(Spectrometer): >>> fibreoptic.min_wavelength = hires_spectrometer.min_wavelength >>> fibreoptic.max_wavelength = hires_spectrometer.max_wavelength >>> fibreoptic.spectral_bins = hires_spectrometer.spectral_bins - >>> fibreoptic.pipelines = hires_spectrometer.new_pipelines() + >>> fibreoptic.pipelines = hires_spectrometer.create_pipelines() """ def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, From e11a6d510f9709eaa4104576e7b767a412a3c061 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 14 Dec 2021 01:17:23 +0300 Subject: [PATCH 12/59] In CzernyTurnerSpectrometer invoke wavelength_to_pixel update when changing the property which affects the resolution. --- cherab/tools/spectroscopy/spectrometer.py | 31 +++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cherab/tools/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py index 436f301a..5fbe3710 100644 --- a/cherab/tools/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -205,6 +205,7 @@ class CzernyTurnerSpectrometer(Spectrometer): def __init__(self, diffraction_order, grating, focal_length, pixel_spacing, diffraction_angle, accommodated_spectra, min_bins_per_pixel=1, name=''): + self._accommodated_spectra = None self.diffraction_order = diffraction_order self.grating = grating self.focal_length = focal_length @@ -226,7 +227,8 @@ def diffraction_order(self, value): raise ValueError("Attribute 'diffraction_order' must be positive.") self._diffraction_order = value - self._clear_spectral_settings() + # resolution has changed, recalculating wavelength_to_pixel + self._update_wavelength_to_pixel() @property def grating(self): @@ -239,7 +241,8 @@ def grating(self, value): raise ValueError("Attribute 'grating' must be positive.") self._grating = value - self._clear_spectral_settings() + # resolution has changed, recalculating wavelength_to_pixel + self._update_wavelength_to_pixel() @property def focal_length(self): @@ -252,7 +255,8 @@ def focal_length(self, value): raise ValueError("Attribute 'focal_length' must be positive.") self._focal_length = value - self._clear_spectral_settings() + # resolution has changed, recalculating wavelength_to_pixel + self._update_wavelength_to_pixel() @property def pixel_spacing(self): @@ -265,7 +269,8 @@ def pixel_spacing(self, value): raise ValueError("Attribute 'pixel_spacing' must be positive.") self._pixel_spacing = value - self._clear_spectral_settings() + # resolution has changed, recalculating wavelength_to_pixel + self._update_wavelength_to_pixel() @property def diffraction_angle(self): @@ -278,7 +283,8 @@ def diffraction_angle(self, value): raise ValueError("Attribute 'diffraction_angle' must be positive.") self._diffraction_angle = np.deg2rad(value) - self._clear_spectral_settings() + # resolution has changed, recalculating wavelength_to_pixel + self._update_wavelength_to_pixel() @property def accommodated_spectra(self): @@ -286,13 +292,22 @@ def accommodated_spectra(self): @accommodated_spectra.setter def accommodated_spectra(self, value): - _wavelength_to_pixel = [] - _wavelengths = [] for min_wavelength, pixels in value: if min_wavelength <= 0: raise ValueError('The value of min_wavelength in accommodated_spectra must be positive.') if pixels <= 0: raise ValueError('The value of pixels in accommodated_spectra must be positive.') + self._accommodated_spectra = value + self._update_wavelength_to_pixel() + + def _update_wavelength_to_pixel(self): + + if self._accommodated_spectra is None: + return + + _wavelength_to_pixel = [] + _wavelengths = [] + for min_wavelength, pixels in self._accommodated_spectra: pixels = int(pixels) wl2pix = np.zeros(pixels + 1) wl2pix[0] = min_wavelength @@ -303,9 +318,9 @@ def accommodated_spectra(self, value): wl_center = 0.5 * (wl2pix[1:] + wl2pix[:-1]) wl_center.flags.writeable = False _wavelengths.append(wl_center) - self._accommodated_spectra = value self._wavelength_to_pixel = tuple(_wavelength_to_pixel) self._wavelengths = tuple(_wavelengths) + self._clear_spectral_settings() @property From 95c89bcd88f89d586c779a25a9d624f61b7b8784 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 24 Jan 2022 21:59:49 +0300 Subject: [PATCH 13/59] Added more accurate Gaunt factor for Bremsstrahlung emission model. --- cherab/core/atomic/__init__.pxd | 1 + cherab/core/atomic/__init__.py | 1 + .../maxwellian_free_free_gaunt_factor.json | 946 ++++++++++++++++++ cherab/core/atomic/gaunt.pxd | 39 + cherab/core/atomic/gaunt.pyx | 152 +++ cherab/core/atomic/interface.pxd | 9 +- cherab/core/atomic/interface.pyx | 19 +- cherab/core/model/plasma/bremsstrahlung.pxd | 13 +- cherab/core/model/plasma/bremsstrahlung.pyx | 22 +- cherab/core/utility/constants.pxd | 7 +- cherab/core/utility/constants.pyx | 8 +- demos/emission_models/bremsstrahlung.py | 84 ++ docs/source/atomic/atomic_data.rst | 1 + docs/source/atomic/gaunt_factors.rst | 23 + 14 files changed, 1296 insertions(+), 29 deletions(-) create mode 100644 cherab/core/atomic/data/maxwellian_free_free_gaunt_factor.json create mode 100644 cherab/core/atomic/gaunt.pxd create mode 100644 cherab/core/atomic/gaunt.pyx create mode 100755 demos/emission_models/bremsstrahlung.py create mode 100644 docs/source/atomic/gaunt_factors.rst diff --git a/cherab/core/atomic/__init__.pxd b/cherab/core/atomic/__init__.pxd index 0aa3cb90..3b2aa5c6 100644 --- a/cherab/core/atomic/__init__.pxd +++ b/cherab/core/atomic/__init__.pxd @@ -22,4 +22,5 @@ from cherab.core.atomic.line cimport Line from cherab.core.atomic.interface cimport AtomicData from cherab.core.atomic.rates cimport * from cherab.core.atomic.zeeman cimport ZeemanStructure +from cherab.core.atomic.gaunt cimport * diff --git a/cherab/core/atomic/__init__.py b/cherab/core/atomic/__init__.py index 3bb11780..35a7f80e 100644 --- a/cherab/core/atomic/__init__.py +++ b/cherab/core/atomic/__init__.py @@ -22,3 +22,4 @@ from .interface import AtomicData from .rates import * from .zeeman import ZeemanStructure +from .gaunt import * diff --git a/cherab/core/atomic/data/maxwellian_free_free_gaunt_factor.json b/cherab/core/atomic/data/maxwellian_free_free_gaunt_factor.json new file mode 100644 index 00000000..124e0622 --- /dev/null +++ b/cherab/core/atomic/data/maxwellian_free_free_gaunt_factor.json @@ -0,0 +1,946 @@ +{ + "u": [ + 0.0001, + 0.001, + 0.01, + 0.1, + 1.0, + 10.0, + 100.0, + 1000.0, + 10000.0 + ], + "gamma2": [ + 1e-08, + 1.5848931924611143e-08, + 2.511886431509582e-08, + 3.981071705534969e-08, + 6.30957344480193e-08, + 1e-07, + 1.584893192461114e-07, + 2.5118864315095823e-07, + 3.981071705534969e-07, + 6.30957344480193e-07, + 1e-06, + 1.584893192461114e-06, + 2.5118864315095823e-06, + 3.981071705534969e-06, + 6.30957344480193e-06, + 1e-05, + 1.584893192461114e-05, + 2.5118864315095822e-05, + 3.9810717055349695e-05, + 6.309573444801929e-05, + 0.0001, + 0.00015848931924611142, + 0.00025118864315095795, + 0.00039810717055349735, + 0.000630957344480193, + 0.001, + 0.001584893192461114, + 0.0025118864315095794, + 0.003981071705534973, + 0.00630957344480193, + 0.01, + 0.015848931924611134, + 0.025118864315095794, + 0.039810717055349734, + 0.06309573444801933, + 0.1, + 0.15848931924611134, + 0.251188643150958, + 0.3981071705534972, + 0.6309573444801932, + 1.0, + 1.5848931924611136, + 2.51188643150958, + 3.9810717055349722, + 6.309573444801933, + 10.0, + 15.848931924611133, + 25.118864315095795, + 39.810717055349734, + 63.09573444801933, + 100.0, + 158.48931924611142, + 251.18864315095797, + 398.1071705534973, + 630.957344480193, + 1000.0, + 1584.893192461114, + 2511.88643150958, + 3981.0717055349733, + 6309.57344480193, + 10000.0, + 15848.93192461114, + 25118.864315095823, + 39810.71705534969, + 63095.7344480193, + 100000.0, + 158489.3192461114, + 251188.6431509582, + 398107.1705534969, + 630957.344480193, + 1000000.0, + 1584893.1924611141, + 2511886.4315095823, + 3981071.7055349695, + 6309573.44480193, + 10000000.0, + 15848931.924611142, + 25118864.315095823, + 39810717.05534969, + 63095734.448019296, + 100000000.0, + 158489319.2461111, + 251188643.1509582, + 398107170.5534969, + 630957344.4801943, + 1000000000.0, + 1584893192.4611108, + 2511886431.509582, + 3981071705.5349693, + 6309573444.801943, + 10000000000.0 + ], + "gaunt_factor": [ + [ + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.528, + 5.527, + 5.527, + 5.527, + 5.527, + 5.526, + 5.525, + 5.523, + 5.52, + 5.516, + 5.51, + 5.501, + 5.489, + 5.472, + 5.449, + 5.419, + 5.379, + 5.329, + 5.267, + 5.193, + 5.107, + 5.01, + 4.904, + 4.792, + 4.676, + 4.557, + 4.436, + 4.315, + 4.194, + 4.073, + 3.953, + 3.833, + 3.714, + 3.596, + 3.479, + 3.363, + 3.249, + 3.136, + 3.025, + 2.916, + 2.808, + 2.703, + 2.6, + 2.5, + 2.402, + 2.307, + 2.215, + 2.127, + 2.042, + 1.96, + 1.882, + 1.807, + 1.737, + 1.67, + 1.608, + 1.549, + 1.494, + 1.444, + 1.397, + 1.354, + 1.314, + 1.278, + 1.246, + 1.217, + 1.19, + 1.167, + 1.146, + 1.127, + 1.11, + 1.096, + 1.083, + 1.072 + ], + [ + 4.259, + 4.259, + 4.259, + 4.259, + 4.259, + 4.259, + 4.259, + 4.259, + 4.259, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.26, + 4.259, + 4.258, + 4.257, + 4.254, + 4.249, + 4.241, + 4.231, + 4.216, + 4.195, + 4.167, + 4.131, + 4.083, + 4.025, + 3.955, + 3.873, + 3.782, + 3.682, + 3.577, + 3.468, + 3.357, + 3.245, + 3.134, + 3.024, + 2.915, + 2.808, + 2.703, + 2.601, + 2.5, + 2.403, + 2.308, + 2.216, + 2.127, + 2.042, + 1.96, + 1.882, + 1.808, + 1.737, + 1.671, + 1.608, + 1.549, + 1.495, + 1.444, + 1.397, + 1.354, + 1.314, + 1.279, + 1.246, + 1.217, + 1.19, + 1.167, + 1.146, + 1.127, + 1.11, + 1.096, + 1.083, + 1.072, + 1.062, + 1.054, + 1.046, + 1.04, + 1.034, + 1.03, + 1.026, + 1.022, + 1.019, + 1.016 + ], + [ + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.002, + 3.003, + 3.003, + 3.003, + 3.004, + 3.004, + 3.005, + 3.006, + 3.007, + 3.008, + 3.01, + 3.011, + 3.013, + 3.015, + 3.016, + 3.018, + 3.018, + 3.016, + 3.012, + 3.004, + 2.991, + 2.971, + 2.944, + 2.907, + 2.86, + 2.803, + 2.735, + 2.658, + 2.574, + 2.486, + 2.396, + 2.306, + 2.216, + 2.129, + 2.045, + 1.963, + 1.885, + 1.811, + 1.74, + 1.673, + 1.61, + 1.552, + 1.497, + 1.446, + 1.399, + 1.355, + 1.316, + 1.28, + 1.247, + 1.218, + 1.191, + 1.167, + 1.146, + 1.128, + 1.111, + 1.096, + 1.084, + 1.072, + 1.063, + 1.054, + 1.047, + 1.04, + 1.035, + 1.03, + 1.026, + 1.022, + 1.019, + 1.016, + 1.014, + 1.012, + 1.01, + 1.009, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004 + ], + [ + 1.806, + 1.806, + 1.806, + 1.807, + 1.807, + 1.807, + 1.807, + 1.807, + 1.807, + 1.807, + 1.807, + 1.808, + 1.808, + 1.808, + 1.809, + 1.81, + 1.81, + 1.812, + 1.813, + 1.815, + 1.817, + 1.819, + 1.823, + 1.827, + 1.832, + 1.838, + 1.846, + 1.855, + 1.865, + 1.877, + 1.89, + 1.903, + 1.914, + 1.923, + 1.928, + 1.926, + 1.917, + 1.898, + 1.869, + 1.831, + 1.785, + 1.733, + 1.678, + 1.622, + 1.566, + 1.512, + 1.461, + 1.413, + 1.369, + 1.328, + 1.291, + 1.257, + 1.227, + 1.199, + 1.175, + 1.153, + 1.133, + 1.116, + 1.101, + 1.087, + 1.076, + 1.065, + 1.056, + 1.049, + 1.042, + 1.036, + 1.031, + 1.027, + 1.023, + 1.02, + 1.017, + 1.015, + 1.013, + 1.011, + 1.009, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001 + ], + [ + 0.8424, + 0.8425, + 0.8425, + 0.8426, + 0.8426, + 0.8427, + 0.8428, + 0.8429, + 0.8431, + 0.8433, + 0.8436, + 0.8439, + 0.8443, + 0.8449, + 0.8455, + 0.8464, + 0.8474, + 0.8487, + 0.8504, + 0.8525, + 0.8552, + 0.8586, + 0.8628, + 0.8682, + 0.875, + 0.8835, + 0.8944, + 0.9079, + 0.925, + 0.9461, + 0.9719, + 1.003, + 1.04, + 1.081, + 1.126, + 1.172, + 1.216, + 1.253, + 1.279, + 1.294, + 1.296, + 1.287, + 1.27, + 1.248, + 1.224, + 1.2, + 1.178, + 1.157, + 1.137, + 1.12, + 1.104, + 1.091, + 1.079, + 1.068, + 1.059, + 1.051, + 1.044, + 1.038, + 1.032, + 1.028, + 1.024, + 1.021, + 1.018, + 1.015, + 1.013, + 1.011, + 1.01, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002, + 1.002, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + [ + 0.3035, + 0.3035, + 0.3035, + 0.3035, + 0.3036, + 0.3036, + 0.3037, + 0.3038, + 0.3039, + 0.304, + 0.3042, + 0.3044, + 0.3046, + 0.305, + 0.3054, + 0.3059, + 0.3066, + 0.3074, + 0.3084, + 0.3098, + 0.3114, + 0.3136, + 0.3163, + 0.3197, + 0.324, + 0.3296, + 0.3367, + 0.3457, + 0.3573, + 0.3722, + 0.3913, + 0.4156, + 0.4464, + 0.485, + 0.5328, + 0.5905, + 0.6581, + 0.7343, + 0.8157, + 0.8975, + 0.9731, + 1.037, + 1.084, + 1.113, + 1.127, + 1.129, + 1.124, + 1.115, + 1.104, + 1.093, + 1.083, + 1.073, + 1.064, + 1.056, + 1.049, + 1.042, + 1.036, + 1.032, + 1.027, + 1.023, + 1.02, + 1.017, + 1.015, + 1.013, + 1.011, + 1.01, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002, + 1.002, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + [ + 0.09801, + 0.09802, + 0.09803, + 0.09804, + 0.09806, + 0.09808, + 0.0981, + 0.09814, + 0.09818, + 0.09823, + 0.09829, + 0.09838, + 0.09848, + 0.09861, + 0.09877, + 0.09898, + 0.09924, + 0.09957, + 0.09999, + 0.1005, + 0.1012, + 0.102, + 0.1031, + 0.1045, + 0.1062, + 0.1084, + 0.1113, + 0.115, + 0.1198, + 0.126, + 0.134, + 0.1445, + 0.1582, + 0.1759, + 0.199, + 0.2286, + 0.2662, + 0.3132, + 0.3706, + 0.4388, + 0.5173, + 0.6044, + 0.6967, + 0.7898, + 0.8782, + 0.9561, + 1.019, + 1.064, + 1.091, + 1.104, + 1.107, + 1.103, + 1.096, + 1.087, + 1.078, + 1.069, + 1.061, + 1.054, + 1.047, + 1.041, + 1.036, + 1.031, + 1.027, + 1.023, + 1.02, + 1.017, + 1.015, + 1.013, + 1.011, + 1.009, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002, + 1.002, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001, + 1.0, + 1.0 + ], + [ + 0.03107, + 0.03107, + 0.03107, + 0.03108, + 0.03108, + 0.03109, + 0.0311, + 0.03111, + 0.03112, + 0.03114, + 0.03116, + 0.03119, + 0.03122, + 0.03127, + 0.03132, + 0.03139, + 0.03148, + 0.03159, + 0.03173, + 0.03191, + 0.03214, + 0.03242, + 0.03279, + 0.03325, + 0.03384, + 0.0346, + 0.03558, + 0.03684, + 0.03847, + 0.0406, + 0.04337, + 0.04702, + 0.05181, + 0.05812, + 0.06644, + 0.07735, + 0.09158, + 0.11, + 0.1335, + 0.1631, + 0.1998, + 0.2445, + 0.2981, + 0.361, + 0.4334, + 0.5146, + 0.6031, + 0.696, + 0.7892, + 0.8773, + 0.9548, + 1.017, + 1.062, + 1.089, + 1.102, + 1.104, + 1.101, + 1.094, + 1.085, + 1.076, + 1.068, + 1.06, + 1.053, + 1.046, + 1.04, + 1.035, + 1.03, + 1.026, + 1.023, + 1.019, + 1.017, + 1.014, + 1.012, + 1.011, + 1.009, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002, + 1.001, + 1.001, + 1.001, + 1.001, + 1.001 + ], + [ + 0.009826, + 0.009827, + 0.009828, + 0.00983, + 0.009831, + 0.009834, + 0.009836, + 0.00984, + 0.009845, + 0.00985, + 0.009857, + 0.009866, + 0.009877, + 0.009892, + 0.009909, + 0.009932, + 0.00996, + 0.009996, + 0.01004, + 0.0101, + 0.01017, + 0.01026, + 0.01038, + 0.01053, + 0.01072, + 0.01097, + 0.01128, + 0.01169, + 0.01222, + 0.01291, + 0.01381, + 0.015, + 0.01656, + 0.01863, + 0.02137, + 0.02499, + 0.02975, + 0.03597, + 0.04404, + 0.05439, + 0.06753, + 0.08403, + 0.1046, + 0.1299, + 0.161, + 0.1987, + 0.244, + 0.2979, + 0.3609, + 0.4334, + 0.5146, + 0.6031, + 0.696, + 0.7891, + 0.8772, + 0.9547, + 1.017, + 1.061, + 1.089, + 1.101, + 1.104, + 1.1, + 1.093, + 1.085, + 1.076, + 1.068, + 1.06, + 1.053, + 1.046, + 1.04, + 1.035, + 1.03, + 1.026, + 1.023, + 1.019, + 1.017, + 1.014, + 1.012, + 1.011, + 1.009, + 1.008, + 1.007, + 1.006, + 1.005, + 1.004, + 1.004, + 1.003, + 1.003, + 1.002, + 1.002, + 1.002 + ] + ], + "reference": "M.A. de Avillez and D. Breitschwerdt, Temperature-averaged and total free-free Gaunt factors for κ and Maxwellian distributions of electrons, 2015, Astron. & Astrophys. 580, A124" +} diff --git a/cherab/core/atomic/gaunt.pxd b/cherab/core/atomic/gaunt.pxd new file mode 100644 index 00000000..42afa0da --- /dev/null +++ b/cherab/core/atomic/gaunt.pxd @@ -0,0 +1,39 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 cherab.core.math cimport Function2D + + +cdef class FreeFreeGauntFactor(): + + cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999 + + +cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): + + cdef: + readonly tuple u_range, gamma2_range + readonly dict raw_data + double _u_min, _u_max, _gamma2_min, _gamma2_max + Function2D _gaunt_factor + + +cdef class MaxwellianFreeFreeGauntFactor(InterpolatedFreeFreeGauntFactor): + + pass + diff --git a/cherab/core/atomic/gaunt.pyx b/cherab/core/atomic/gaunt.pyx new file mode 100644 index 00000000..1927281a --- /dev/null +++ b/cherab/core/atomic/gaunt.pyx @@ -0,0 +1,152 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 os import path +import numpy as np +import json + +from libc.math cimport log10, log, M_PI, sqrt +from raysect.core.math.function.float cimport Interpolator2DArray +from cherab.core.utility.constants cimport RYDBERG_CONSTANT_EV, SPEED_OF_LIGHT, ELEMENTARY_CHARGE, PLANCK_CONSTANT + +cimport cython + + +DEF EULER_GAMMA = 0.5772156649015329 + +cdef double PH_TO_EV_FACTOR = PLANCK_CONSTANT * SPEED_OF_LIGHT * 1e9 / ELEMENTARY_CHARGE + + +cdef class FreeFreeGauntFactor(): + """ + The base class for temperature-averaged free-free Gaunt factors. + """ + + cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999: + """ + Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. + + :param double zeff: Effective Z of the plasma. + :param double temperature: Electron temperature in eV. + :param double wavelength: Spectral wavelength. + + :return: free-free Gaunt factor + """ + raise NotImplementedError("The evaluate() virtual method must be implemented.") + + def __call__(self, double zeff, double temperature, double wavelength): + """ + Returns self.evaluate(zeff, temperature, wavelength). + """ + + return self.evaluate(zeff, temperature, wavelength) + + +cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): + r""" + The temperature-averaged free-free Gaunt factors interpolated in the space of parameters: + :math:`u = h{\nu}/kT` and :math:`{\gamma}^{2} = Z_{eff}^{2}Ry/kT`. + See T.R. Carson, 1988, Astron. & Astrophys., 189, + `319 `_ for details. + + The cubic interpolation in a semi-log space is used. + + The Born approximation is used outside the interpolation range. + + :param object u: A 1D array-like object of real values. + :param object gamma2: A 1D array-like object of real values. + :param object gaunt_factor: 2D array-like object of real values + storing the Gaunt factor values at u, gamma2. + + :ivar tuple u_range: The interpolation range of `u` parameter. + :ivar tuple gamma2_range: The interpolation range of :math:`\\gamma^2` parameter. + :ivar dict raw_data: Dictionary containing the raw data. + """ + + def __init__(self, object u, object gamma2, object gaunt_factor): + + u = np.array(u, dtype=np.float64) + u.flags.writeable = False + gamma2 = np.array(gamma2, dtype=np.float64) + gamma2.flags.writeable = False + gaunt_factor = np.array(gaunt_factor, dtype=np.float64) + gaunt_factor.flags.writeable = False + + self.raw_data = {'u': u, 'gamma2': gamma2, 'gaunt_factor': gaunt_factor} + + self._u_min = u.min() + self._u_max = u.max() + self._gamma2_min = gamma2.min() + self._gamma2_max = gamma2.max() + + self.u_range = (self._u_min, self._u_max) + self.gamma2_range = (self._gamma2_min, self._gamma2_max) + + self._gaunt_factor = Interpolator2DArray(np.log10(u), np.log10(gamma2), gaunt_factor, 'cubic', 'none', 0, 0) + + @cython.cdivision(True) + cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999: + """ + Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. + + :param double zeff: Effective Z of the plasma. + :param double temperature: Electron temperature in eV. + :param double wavelength: Spectral wavelength. + + :return: free-free Gaunt factor + """ + + cdef: + double u, gamma2 + + if zeff == 0: + + return 0 + + gamma2 = zeff * zeff * RYDBERG_CONSTANT_EV / temperature + u = PH_TO_EV_FACTOR / (temperature * wavelength) + + # classical limit + if u >= self._u_max or gamma2 >= self._gamma2_max: + + return 1 + + # Born approximation limit + if u < self._u_min or gamma2 < self._gamma2_min: + + return sqrt(3) / M_PI * (log(4 / u) - EULER_GAMMA) + + return self._gaunt_factor.evaluate(log10(u), log10(gamma2)) + + +cdef class MaxwellianFreeFreeGauntFactor(InterpolatedFreeFreeGauntFactor): + r""" + The Maxwellian-averaged free-free Gaunt factor interpolated over the data from Table A.1 in + M.A. de Avillez and D. Breitschwerdt, "Temperature-averaged and total free-free Gaunt factors + for κ and Maxwellian distributions of electrons", 2015, Astron. & Astrophys. 580, + `A124 `_. + + The Born approximation is used outside the interpolation range. + """ + + def __init__(self): + + with open(path.join(path.dirname(__file__), "data/maxwellian_free_free_gaunt_factor.json")) as f: + data = json.load(f) + + super().__init__(data['u'], data['gamma2'], data['gaunt_factor']) diff --git a/cherab/core/atomic/interface.pxd b/cherab/core/atomic/interface.pxd index 9ada928e..66d8faf8 100644 --- a/cherab/core/atomic/interface.pxd +++ b/cherab/core/atomic/interface.pxd @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -19,6 +19,7 @@ from cherab.core.atomic.elements cimport Element from cherab.core.atomic.line cimport Line from cherab.core.atomic.zeeman cimport ZeemanStructure +from cherab.core.atomic.gaunt cimport FreeFreeGauntFactor from cherab.core.atomic.rates cimport * @@ -56,3 +57,5 @@ cdef class AtomicData: cpdef ZeemanStructure zeeman_structure(self, Line line, object b_field=*) + cpdef FreeFreeGauntFactor free_free_gaunt_factor(self) + diff --git a/cherab/core/atomic/interface.pyx b/cherab/core/atomic/interface.pyx index e5219205..286ebfb4 100644 --- a/cherab/core/atomic/interface.pyx +++ b/cherab/core/atomic/interface.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -16,6 +16,8 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +from .gaunt import MaxwellianFreeFreeGauntFactor + cdef class AtomicData: """ @@ -92,3 +94,14 @@ cdef class AtomicData: cpdef ZeemanStructure zeeman_structure(self, Line line, object b_field=None): raise NotImplementedError("The zeeman_structure() virtual method is not implemented for this atomic data source.") + + cpdef FreeFreeGauntFactor free_free_gaunt_factor(self): + """ + Returns the Maxwellian-averaged free-free Gaunt factor interpolated over the data + from Table A.1 in M.A. de Avillez and D. Breitschwerdt, 2015, Astron. & Astrophys. 580, + `A124 `_. + + The Born approximation is used outside the interpolation range. + """ + + return MaxwellianFreeFreeGauntFactor() diff --git a/cherab/core/model/plasma/bremsstrahlung.pxd b/cherab/core/model/plasma/bremsstrahlung.pxd index 4da3422f..7005fcef 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pxd +++ b/cherab/core/model/plasma/bremsstrahlung.pxd @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -18,16 +18,13 @@ # cython: language_level=3 -from cherab.core.atomic cimport Line +from cherab.core.atomic cimport FreeFreeGauntFactor from cherab.core.plasma cimport PlasmaModel -from cherab.core.species cimport Species cdef class Bremsstrahlung(PlasmaModel): cdef: - Line _line - double _wavelength - Species _target_species + FreeFreeGauntFactor _gaunt_factor cdef double _bremsstrahlung(self, double wvl, double te, double ne, double zeff) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 473fb76b..250e125a 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -58,6 +58,12 @@ cdef class Bremsstrahlung(PlasmaModel): double lower_sample, upper_sample int i + # initialise Gaunt factor on first run + if self._gaunt_factor is None: + if self._atomic_data is None: + raise RuntimeError("The emission model is not connected to an atomic data source.") + self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() + ne = self._plasma.get_electron_distribution().density(point.x, point.y, point.z) if ne == 0: return spectrum @@ -74,7 +80,7 @@ cdef class Bremsstrahlung(PlasmaModel): lower_sample = self._bremsstrahlung(lower_wavelength, te, ne, z_effective) for i in range(spectrum.bins): - upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * i + upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) upper_sample = self._bremsstrahlung(upper_wavelength, te, ne, z_effective) spectrum.samples_mv[i] += 0.5 * (lower_sample + upper_sample) @@ -87,21 +93,21 @@ cdef class Bremsstrahlung(PlasmaModel): @cython.cdivision(True) cdef double _bremsstrahlung(self, double wvl, double te, double ne, double zeff): """ - :param wvl: in nm + :param wvl: in nm :param te: in eV :param ne: in m^-3 :param zeff: a.u. - :return: + :return: """ cdef double gaunt_factor, radiance, pre_factor # gaunt factor - gaunt_factor = max(1., 0.6183 * log(te) - 0.0821) + gaunt_factor = self._gaunt_factor(zeff, te, wvl) # bremsstrahlung equation W/m^3/str/nm pre_factor = 0.95e-19 * RECIP_4_PI * gaunt_factor * ne * ne * zeff / (sqrt(te) * wvl) - radiance = pre_factor * exp(- EXP_FACTOR / (te * wvl)) * PH_TO_J_FACTOR + radiance = pre_factor * exp(- EXP_FACTOR / (te * wvl)) * PH_TO_J_FACTOR # convert to W/m^3/str/nm return radiance / wvl diff --git a/cherab/core/utility/constants.pxd b/cherab/core/utility/constants.pxd index fd3fa568..9cce304d 100644 --- a/cherab/core/utility/constants.pxd +++ b/cherab/core/utility/constants.pxd @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -29,3 +29,4 @@ cdef: double PLANCK_CONSTANT double ELECTRON_CLASSICAL_RADIUS double ELECTRON_REST_MASS + double RYDBERG_CONSTANT_EV diff --git a/cherab/core/utility/constants.pyx b/cherab/core/utility/constants.pyx index 348e28c3..03e70617 100644 --- a/cherab/core/utility/constants.pyx +++ b/cherab/core/utility/constants.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -31,4 +31,4 @@ cdef: double PLANCK_CONSTANT = 6.62607015e-34 double ELECTRON_CLASSICAL_RADIUS = 2.8179403262e-15 double ELECTRON_REST_MASS = 9.1093837015e-31 - + double RYDBERG_CONSTANT_EV = 13.605693122994 diff --git a/demos/emission_models/bremsstrahlung.py b/demos/emission_models/bremsstrahlung.py new file mode 100755 index 00000000..b1da93f6 --- /dev/null +++ b/demos/emission_models/bremsstrahlung.py @@ -0,0 +1,84 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. + + +# External imports +import matplotlib.pyplot as plt +from scipy.constants import electron_mass, atomic_mass +from raysect.optical import World, Vector3D, Point3D, Ray +from raysect.primitive import Sphere +from raysect.optical.material.emitter.inhomogeneous import NumericalIntegrator + +# Cherab imports +from cherab.core import Species, Maxwellian, Plasma +from cherab.core.atomic.elements import deuterium, nitrogen +from cherab.core.model import Bremsstrahlung +from cherab.openadas import OpenADAS +from cherab.tools.plasmas import GaussianVolume + + +# tunables +ion_density = 1e20 +sigma = 1. + +# setup scenegraph +world = World() + +# create atomic data source +adas = OpenADAS(permit_extrapolation=True) + +# PLASMA ---------------------------------------------------------------------- +plasma = Plasma(parent=world) +plasma.atomic_data = adas +plasma.geometry = Sphere(sigma) +plasma.geometry_transform = None +plasma.integrator = NumericalIntegrator(step=0.01 * sigma) + +# define basic distributions +d_density = GaussianVolume(ion_density, sigma) +n_density = d_density * 0.01 +e_density = GaussianVolume(ion_density, sigma) +temperature = GaussianVolume(1000, sigma) +bulk_velocity = Vector3D(0, 0, 0) + +deuterium_mass = deuterium.atomic_weight * atomic_mass +d_distribution = Maxwellian(d_density, temperature, bulk_velocity, deuterium_mass) +nitrogen_mass = nitrogen.atomic_weight * atomic_mass +n_distribution = Maxwellian(n_density, temperature, bulk_velocity, nitrogen_mass) +e_distribution = Maxwellian(e_density, temperature, bulk_velocity, electron_mass) + +d1_species = Species(deuterium, 1, d_distribution) +n1_species = Species(nitrogen, 1, n_distribution) + +# define species +plasma.b_field = Vector3D(1.0, 1.0, 1.0) +plasma.electron_distribution = e_distribution +plasma.composition = [d1_species, n1_species] + +# add Bremsstrahlung to the plasma +plasma.models = [Bremsstrahlung()] + +# Ray-trace and plot the results +r = Ray(origin=Point3D(0, 0, -5), direction=Vector3D(0, 0, 1), + min_wavelength=380, max_wavelength=800, bins=256) +s = r.trace(world) +plt.plot(s.wavelengths, s.samples) +plt.xlabel('Wavelength (nm)') +plt.ylabel('Radiance (W/m^2/str/nm)') +plt.title('Observed Bremsstrahlung spectrum') +plt.show() diff --git a/docs/source/atomic/atomic_data.rst b/docs/source/atomic/atomic_data.rst index df3bc2db..650d89b4 100644 --- a/docs/source/atomic/atomic_data.rst +++ b/docs/source/atomic/atomic_data.rst @@ -6,3 +6,4 @@ Atomic Data elements_and_isotopes emission_lines rate_coefficients + gaunt_factors diff --git a/docs/source/atomic/gaunt_factors.rst b/docs/source/atomic/gaunt_factors.rst new file mode 100644 index 00000000..b7950ff9 --- /dev/null +++ b/docs/source/atomic/gaunt_factors.rst @@ -0,0 +1,23 @@ + +Gaunt factors +------------- + +This includes classes for temperature-averaged Gaunt factors used to calculate Bremsstrahlung (free-free Gaunt factor) +and radiative recombination continuum (bound-free Gaunt factor) emission. + + +Free-free Gaunt factors +^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.core.atomic.gaunt.FreeFreeGauntFactor + :members: + :special-members: __call__ + +.. autoclass:: cherab.core.atomic.gaunt.InterpolatedFreeFreeGauntFactor + :show-inheritance: + :members: + +.. autoclass:: cherab.core.atomic.gaunt.MaxwellianFreeFreeGauntFactor + :show-inheritance: + :members: + From 0246b9d6dc037c02f5e2a4b9eb8b4e8558292eef Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 27 Jan 2022 23:35:17 +0300 Subject: [PATCH 14/59] Improve FreeFreeGauntFactor.__call__() docstring and update CHANGELOG. --- CHANGELOG.md | 11 ++++++++++- cherab/core/atomic/gaunt.pyx | 8 +++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e211f506..30e9f43f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,18 @@ Project Changelog ================= -Release 1.3.0 (8 Dec 2021) +Release 1.4.0 (TBD) ------------------- +Bug fixes: +* Fix wavelength indexing in Bremsstrahlung emission model. (#352) + +New: +* Add new classes for free-free Gaunt factors and improve accuracy of the Gaunt factor used in Bremsstrahlung emission model. (#352) + +Release 1.3.0 (8 Dec 2021) +-------------------------- + API changes: * Use of Cherab's interpolators is now deprecated in favour of those upstream in Raysect. diff --git a/cherab/core/atomic/gaunt.pyx b/cherab/core/atomic/gaunt.pyx index 1927281a..911362a6 100644 --- a/cherab/core/atomic/gaunt.pyx +++ b/cherab/core/atomic/gaunt.pyx @@ -51,7 +51,13 @@ cdef class FreeFreeGauntFactor(): def __call__(self, double zeff, double temperature, double wavelength): """ - Returns self.evaluate(zeff, temperature, wavelength). + Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. + + :param double zeff: Effective Z of the plasma. + :param double temperature: Electron temperature in eV. + :param double wavelength: Spectral wavelength. + + :return: free-free Gaunt factor """ return self.evaluate(zeff, temperature, wavelength) From c627749bc726fa6cf42f701a5f22af96774bd2bb Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 31 Jan 2022 15:46:05 +0300 Subject: [PATCH 15/59] Made EFITEquilibrium.f_profile a public readonly attribute. --- cherab/tools/equilibrium/efit.pxd | 3 +-- cherab/tools/equilibrium/efit.pyx | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cherab/tools/equilibrium/efit.pxd b/cherab/tools/equilibrium/efit.pxd index c074d92d..5aff5b55 100644 --- a/cherab/tools/equilibrium/efit.pxd +++ b/cherab/tools/equilibrium/efit.pxd @@ -43,9 +43,8 @@ cdef class EFITEquilibrium: readonly double time readonly np.ndarray lcfs_polygon, limiter_polygon readonly np.ndarray psi_data, r_data, z_data - readonly Function1D q + readonly Function1D q, f_profile double _b_vacuum_magnitude, _b_vacuum_radius - Function1D _f_profile Function2D _dpsidr, _dpsidz diff --git a/cherab/tools/equilibrium/efit.pyx b/cherab/tools/equilibrium/efit.pyx index 955f6d39..6ea71bf4 100644 --- a/cherab/tools/equilibrium/efit.pyx +++ b/cherab/tools/equilibrium/efit.pyx @@ -119,7 +119,7 @@ cdef class EFITEquilibrium: self.z_range = z.min(), z.max() self._b_vacuum_magnitude = b_vacuum_magnitude self._b_vacuum_radius = b_vacuum_radius - self._f_profile = Interpolator1DArray(f_profile[0, :], f_profile[1, :], 'cubic', 'none', 0) + self.f_profile = Interpolator1DArray(f_profile[0, :], f_profile[1, :], 'cubic', 'none', 0) self.q = Interpolator1DArray(q_profile[0, :], q_profile[1, :], 'cubic', 'none', 0) # populate points @@ -130,7 +130,7 @@ cdef class EFITEquilibrium: # calculate b-field dpsi_dr, dpsi_dz = self._calculate_differentials(r, z, psi) - self.b_field = MagneticField(self.psi_normalised, dpsi_dr, dpsi_dz, self._f_profile, b_vacuum_radius, b_vacuum_magnitude, self.inside_lcfs) + self.b_field = MagneticField(self.psi_normalised, dpsi_dr, dpsi_dz, self.f_profile, b_vacuum_radius, b_vacuum_magnitude, self.inside_lcfs) # populate flux coordinate attributes self.toroidal_vector = ConstantVector2D(Vector3D(0, 1, 0)) From 560cc53652dce1b723917031a82f3e2b779d3e3a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 31 Jan 2022 16:09:21 +0300 Subject: [PATCH 16/59] Updated changelog. --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e211f506..684393f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,15 @@ Project Changelog ================= -Release 1.3.0 (8 Dec 2021) +Release 1.4.0 (TBD) ------------------- +New: +* Make f_profile (toroidal flux) a read-only attribute of EFITEquilibrium. (#355) + +Release 1.3.0 (8 Dec 2021) +-------------------------- + API changes: * Use of Cherab's interpolators is now deprecated in favour of those upstream in Raysect. From 729ef4f505b9a03f4c9d48f86065e30b80dc023d Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 31 Jan 2022 19:09:42 +0300 Subject: [PATCH 17/59] Updated EFITEquilibrium docstring with f_profile ivar description. --- cherab/tools/equilibrium/efit.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/cherab/tools/equilibrium/efit.pyx b/cherab/tools/equilibrium/efit.pyx index 6ea71bf4..8c9492e0 100644 --- a/cherab/tools/equilibrium/efit.pyx +++ b/cherab/tools/equilibrium/efit.pyx @@ -73,6 +73,7 @@ cdef class EFITEquilibrium: :ivar Function2D psi: The poloidal flux in the r-z plane, :math:`\psi(r,z)`. :ivar Function2D psi_normalised: The normalised poloidal flux in the r-z plane, :math:`\psi_n(r,z)`. + :ivar Function1D f_profile: The toroidal flux at the specified normalised poloidal flux, :math:`F(\psi_n)`. :ivar Function1D q: The safety factor :math:`q` at the specified normalised poloidal flux, :math:`q(\psi_n)`. :ivar VectorFunction2D b_field: A 2D function that returns the magnetic field vector at the specified point in the r-z plane, :math:`B(r, z)`. From 9bdd61bd37d0da34d333931d68faaf9bce517980 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 31 Jan 2022 19:59:26 +0300 Subject: [PATCH 18/59] Fix f_profile definition in EFITEquilibrium docstring and changelog. --- CHANGELOG.md | 2 +- cherab/tools/equilibrium/efit.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 684393f5..b6881a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Release 1.4.0 (TBD) ------------------- New: -* Make f_profile (toroidal flux) a read-only attribute of EFITEquilibrium. (#355) +* Make f_profile (current flux) a read-only attribute of EFITEquilibrium. (#355) Release 1.3.0 (8 Dec 2021) -------------------------- diff --git a/cherab/tools/equilibrium/efit.pyx b/cherab/tools/equilibrium/efit.pyx index 8c9492e0..cb23496a 100644 --- a/cherab/tools/equilibrium/efit.pyx +++ b/cherab/tools/equilibrium/efit.pyx @@ -73,7 +73,7 @@ cdef class EFITEquilibrium: :ivar Function2D psi: The poloidal flux in the r-z plane, :math:`\psi(r,z)`. :ivar Function2D psi_normalised: The normalised poloidal flux in the r-z plane, :math:`\psi_n(r,z)`. - :ivar Function1D f_profile: The toroidal flux at the specified normalised poloidal flux, :math:`F(\psi_n)`. + :ivar Function1D f_profile: The current flux at the specified normalised poloidal flux, :math:`F(\psi_n)`. :ivar Function1D q: The safety factor :math:`q` at the specified normalised poloidal flux, :math:`q(\psi_n)`. :ivar VectorFunction2D b_field: A 2D function that returns the magnetic field vector at the specified point in the r-z plane, :math:`B(r, z)`. From 349ae82aeb4bc6afa0fe676037e06efa69eeb887 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 17 Feb 2022 18:00:20 +0300 Subject: [PATCH 19/59] Use summation over charged species instead of Zeff in Bremsstrahlung model. --- cherab/core/atomic/gaunt.pxd | 2 +- cherab/core/atomic/gaunt.pyx | 20 ++--- cherab/core/model/plasma/bremsstrahlung.pxd | 9 +- cherab/core/model/plasma/bremsstrahlung.pyx | 98 ++++++++++++++++----- 4 files changed, 95 insertions(+), 34 deletions(-) diff --git a/cherab/core/atomic/gaunt.pxd b/cherab/core/atomic/gaunt.pxd index 42afa0da..827831b6 100644 --- a/cherab/core/atomic/gaunt.pxd +++ b/cherab/core/atomic/gaunt.pxd @@ -21,7 +21,7 @@ from cherab.core.math cimport Function2D cdef class FreeFreeGauntFactor(): - cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999 + cpdef double evaluate(self, double z, double temperature, double wavelength) except? -1e999 cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): diff --git a/cherab/core/atomic/gaunt.pyx b/cherab/core/atomic/gaunt.pyx index 911362a6..eb1dbe59 100644 --- a/cherab/core/atomic/gaunt.pyx +++ b/cherab/core/atomic/gaunt.pyx @@ -37,11 +37,11 @@ cdef class FreeFreeGauntFactor(): The base class for temperature-averaged free-free Gaunt factors. """ - cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999: + cpdef double evaluate(self, double z, double temperature, double wavelength) except? -1e999: """ Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. - :param double zeff: Effective Z of the plasma. + :param double z: Species charge or effective plasma charge. :param double temperature: Electron temperature in eV. :param double wavelength: Spectral wavelength. @@ -49,24 +49,24 @@ cdef class FreeFreeGauntFactor(): """ raise NotImplementedError("The evaluate() virtual method must be implemented.") - def __call__(self, double zeff, double temperature, double wavelength): + def __call__(self, double z, double temperature, double wavelength): """ Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. - :param double zeff: Effective Z of the plasma. + :param double z: Species charge or effective plasma charge. :param double temperature: Electron temperature in eV. :param double wavelength: Spectral wavelength. :return: free-free Gaunt factor """ - return self.evaluate(zeff, temperature, wavelength) + return self.evaluate(z, temperature, wavelength) cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): r""" The temperature-averaged free-free Gaunt factors interpolated in the space of parameters: - :math:`u = h{\nu}/kT` and :math:`{\gamma}^{2} = Z_{eff}^{2}Ry/kT`. + :math:`u = h{\nu}/kT` and :math:`{\gamma}^{2} = Z^{2}Ry/kT`. See T.R. Carson, 1988, Astron. & Astrophys., 189, `319 `_ for details. @@ -106,11 +106,11 @@ cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): self._gaunt_factor = Interpolator2DArray(np.log10(u), np.log10(gamma2), gaunt_factor, 'cubic', 'none', 0, 0) @cython.cdivision(True) - cpdef double evaluate(self, double zeff, double temperature, double wavelength) except? -1e999: + cpdef double evaluate(self, double z, double temperature, double wavelength) except? -1e999: """ Returns the temperature-averaged free-free Gaunt factor for the supplied parameters. - :param double zeff: Effective Z of the plasma. + :param double z: Species charge or effective plasma charge. :param double temperature: Electron temperature in eV. :param double wavelength: Spectral wavelength. @@ -120,11 +120,11 @@ cdef class InterpolatedFreeFreeGauntFactor(FreeFreeGauntFactor): cdef: double u, gamma2 - if zeff == 0: + if z == 0: return 0 - gamma2 = zeff * zeff * RYDBERG_CONSTANT_EV / temperature + gamma2 = z * z * RYDBERG_CONSTANT_EV / temperature u = PH_TO_EV_FACTOR / (temperature * wavelength) # classical limit diff --git a/cherab/core/model/plasma/bremsstrahlung.pxd b/cherab/core/model/plasma/bremsstrahlung.pxd index 7005fcef..f29cd6b0 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pxd +++ b/cherab/core/model/plasma/bremsstrahlung.pxd @@ -18,6 +18,7 @@ # cython: language_level=3 +from numpy cimport ndarray from cherab.core.atomic cimport FreeFreeGauntFactor from cherab.core.plasma cimport PlasmaModel @@ -26,5 +27,11 @@ cdef class Bremsstrahlung(PlasmaModel): cdef: FreeFreeGauntFactor _gaunt_factor + ndarray _species_charge, _species_density + double[::1] _species_density_mv, _species_charge_mv + + cdef double _bremsstrahlung(self, double wvl, double te, double ne) + + cdef int _populate_cache(self) except -1 + - cdef double _bremsstrahlung(self, double wvl, double te, double ne, double zeff) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 250e125a..1384d647 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -18,7 +18,10 @@ # cython: language_level=3 +import numpy as np from raysect.optical cimport Spectrum, Point3D, Vector3D +from cherab.core cimport Plasma, AtomicData +from cherab.core.species cimport Species from cherab.core.utility.constants cimport RECIP_4_PI, ELEMENTARY_CHARGE, SPEED_OF_LIGHT, PLANCK_CONSTANT from libc.math cimport sqrt, log, exp cimport cython @@ -38,11 +41,18 @@ cdef class Bremsstrahlung(PlasmaModel): et. al., 'ITER LIDAR performance analysis', Rev. Sci. Instrum. 79, 10E727 (2008), .. math:: - \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} g_{ff} n_e^2 Z_{eff} T_e^{1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, + \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} \\sum_{i} \\left(g_{ff}(Z_i, T_e, \\lambda) n_i Z_i^2\\right) n_e T_e^{1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, where the emission :math:`\\epsilon (\\lambda)` is in units of radiance (ph/s/sr/m^3/nm). """ + def __init__(self, Plasma plasma=None, AtomicData atomic_data=None): + + super().__init__(plasma, atomic_data) + + # ensure that cache is initialised + self._change() + def __repr__(self): return '' @@ -53,35 +63,38 @@ cdef class Bremsstrahlung(PlasmaModel): cpdef Spectrum emission(self, Point3D point, Vector3D direction, Spectrum spectrum): cdef: - double ne, te, z_effective + double ne, te double lower_wavelength, upper_wavelength double lower_sample, upper_sample + Species species int i - # initialise Gaunt factor on first run + # cache data on first run if self._gaunt_factor is None: - if self._atomic_data is None: - raise RuntimeError("The emission model is not connected to an atomic data source.") - self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() + self._populate_cache() ne = self._plasma.get_electron_distribution().density(point.x, point.y, point.z) - if ne == 0: + if ne <= 0: return spectrum te = self._plasma.get_electron_distribution().effective_temperature(point.x, point.y, point.z) - if te == 0: - return spectrum - z_effective = self._plasma.z_effective(point.x, point.y, point.z) - if z_effective == 0: + if te <= 0: return spectrum + # collect densities of charged species + i = 0 + for species in self._plasma.get_composition(): + if species.charge > 0: + self._species_density_mv[i] = species.distribution.density(point.x, point.y, point.z) + i += 1 + # numerically integrate using trapezium rule # todo: add sub-sampling to increase numerical accuracy lower_wavelength = spectrum.min_wavelength - lower_sample = self._bremsstrahlung(lower_wavelength, te, ne, z_effective) + lower_sample = self._bremsstrahlung(lower_wavelength, te, ne) for i in range(spectrum.bins): upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) - upper_sample = self._bremsstrahlung(upper_wavelength, te, ne, z_effective) + upper_sample = self._bremsstrahlung(upper_wavelength, te, ne) spectrum.samples_mv[i] += 0.5 * (lower_sample + upper_sample) @@ -91,23 +104,64 @@ cdef class Bremsstrahlung(PlasmaModel): return spectrum @cython.cdivision(True) - cdef double _bremsstrahlung(self, double wvl, double te, double ne, double zeff): + @cython.boundscheck(False) + @cython.wraparound(False) + cdef double _bremsstrahlung(self, double wvl, double te, double ne): """ - :param wvl: in nm - :param te: in eV - :param ne: in m^-3 - :param zeff: a.u. + :param double wvl: Wavelength in nm. + :param double te: Electron temperature in eV + :param double ne: Electron dencity in m^-3 :return: """ - cdef double gaunt_factor, radiance, pre_factor + cdef double ni_gff_z2, radiance, pre_factor, ni, z + cdef int i - # gaunt factor - gaunt_factor = self._gaunt_factor(zeff, te, wvl) + ni_gff_z2 = 0 + for i in range(self._species_charge_mv.shape[0]): + z = self._species_charge_mv[i] + ni = self._species_density_mv[i] + if ni > 0: + ni_gff_z2 += ni * self._gaunt_factor.evaluate(z, te, wvl) * z * z # bremsstrahlung equation W/m^3/str/nm - pre_factor = 0.95e-19 * RECIP_4_PI * gaunt_factor * ne * ne * zeff / (sqrt(te) * wvl) + pre_factor = 0.95e-19 * RECIP_4_PI * ni_gff_z2 * ne / (sqrt(te) * wvl) radiance = pre_factor * exp(- EXP_FACTOR / (te * wvl)) * PH_TO_J_FACTOR # convert to W/m^3/str/nm return radiance / wvl + + cdef int _populate_cache(self) except -1: + + cdef list species_charge + cdef Species species + + if self._plasma is None: + raise RuntimeError("The emission model is not connected to a plasma object.") + + if self._atomic_data is None: + raise RuntimeError("The emission model is not connected to an atomic data source.") + + # initialise Gaunt factor on first run + self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() + + species_charge = [] + for species in self._plasma.get_composition(): + if species.charge > 0: + species_charge.append(species.charge) + + # Gaunt factor takes Z as double to support Zeff, so caching Z as float64 + self._species_charge = np.array(species_charge, dtype=np.float64) + self._species_charge_mv = self._species_charge + + self._species_density = np.zeros_like(self._species_charge) + self._species_density_mv = self._species_density + + def _change(self): + + # clear cache to force regeneration on first use + self._gaunt_factor = None + self._species_charge = None + self._species_charge_mv = None + self._species_density = None + self._species_density_mv = None From a892e26ef1bba264a4d80407ea8a15a9ae8d9c46 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 22 Feb 2022 15:33:59 +0000 Subject: [PATCH 20/59] Enable testing of SART OpenCL inversion routines with POCL --- .github/workflows/ci.yml | 4 +++- cherab/tools/tests/test_sart_opencl.py | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19ec060b..9701d0b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,9 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib + run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib pyopencl[pocl] + - name: Work around PyOpenCL issue 537 + run: echo OCL_ICD_VENDORS=$(python -c 'import os, pyopencl; print(os.path.join(*pyopencl.__path__, ".libs"))') >> $GITHUB_ENV - name: Install Raysect from pypi run: pip install raysect - name: Build cherab diff --git a/cherab/tools/tests/test_sart_opencl.py b/cherab/tools/tests/test_sart_opencl.py index 794bf605..37be1cfd 100644 --- a/cherab/tools/tests/test_sart_opencl.py +++ b/cherab/tools/tests/test_sart_opencl.py @@ -16,10 +16,12 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +import gc import unittest import os import numpy as np from cherab.tools.inversions import SartOpencl +from cherab.tools.inversions.opencl import device_select try: import pyopencl as cl except ImportError: @@ -47,16 +49,24 @@ def setUp(self): # true emissivity in float32, shape: (11, 8) true_emissivity = np.load(os.path.join(os.path.dirname(__file__), 'data/true_emissivity.npy')) self.true_emissivity = true_emissivity.flatten() + # Any OpenCL device, including a CPU, will do for the tests. This enables + # POCL to be installed as an OpenCL driver for testing. + self.device = device_select(device_type=cl.device_type.ALL) + + def tearDown(self): + # Ensure the OpenCL device is properly released between tests. + self.device = None + gc.collect() def test_inversion(self): with SartOpencl(self.gm, block_size=256, copy_column_major=True, use_atomic=False, - steps_per_thread=64, block_size_row_maj=64) as inv_sart: + steps_per_thread=64, block_size_row_maj=64, device=self.device) as inv_sart: solution, residual = inv_sart(self.receiver) self.assertTrue(np.allclose(solution, self.true_emissivity, atol=1.e-2)) def test_inversion_atomic(self): with SartOpencl(self.gm, block_size=256, copy_column_major=True, use_atomic=True, - steps_per_thread=64, block_size_row_maj=64) as inv_sart: + steps_per_thread=64, block_size_row_maj=64, device=self.device) as inv_sart: solution, residual = inv_sart(self.receiver) self.assertTrue(np.allclose(solution, self.true_emissivity, atol=1.e-2)) @@ -65,6 +75,6 @@ def test_inversion_constrained(self): # The beta_laplace parameter is set to just 0.001 to reduce the impact of regularisation. This is a technical test only. laplacian_matrix = np.identity(self.gm.shape[1], dtype=np.float32) with SartOpencl(self.gm, laplacian_matrix=laplacian_matrix, block_size=256, copy_column_major=True, use_atomic=False, - steps_per_thread=64, block_size_row_maj=64) as inv_sart: + steps_per_thread=64, block_size_row_maj=64, device=self.device) as inv_sart: solution, residual = inv_sart(self.receiver, beta_laplace=0.001) self.assertTrue(np.allclose(solution / solution.max(), self.true_emissivity, atol=1.e-2)) From dee48c6da8f3cbc3b9111c1e58033c23f1557e3d Mon Sep 17 00:00:00 2001 From: Jakub Svoboda <61213232+skuba31@users.noreply.github.com> Date: Thu, 24 Feb 2022 09:58:43 +0100 Subject: [PATCH 21/59] Add group observers for Pixel and TargetedPixel (#332) * Add group observers for Pixel and TargetedPixel * Fix docstrings of pixel groups * Add import of pixel groups to init * Remove spectroscopic observers and groups The origin and direction properties are no longer desirabel. Spectroscopic observers are replaced by their former raysect base so their groups can directly inherit from Observer0DGroup * Keep spectroscopic observers and groups Spectroscopic observers and groups were added back for backward compatibility. Deprecation is now indicated in the docstrings. * Change getitem method to support slicing * Fix Group base class init Internal variable _observers was not properly initialized. This caused problems when creating group using parameter observers. * Improve docstrings and code layout * Relocate the deprecation indication * Change inapproriate uses of sight line equivalents * Fix targets setter * Update docs * Unify code layout * Fix getitem * Fix plotting functions * Fix plotting * Add observer type attribute * Squashed commit of the development branch * Update changelog * Add group unit tests, fix bugs * Add demo for observer group * Fix paths for generomak edge, tweak groups demo * Fix generomak plasma Co-authored-by: Jack Lovell --- CHANGELOG.md | 9 + cherab/generomak/plasma/plasma.py | 24 +- cherab/tools/observers/__init__.py | 2 +- cherab/tools/observers/group/__init__.py | 4 +- cherab/tools/observers/group/base.py | 510 +++++++----------- cherab/tools/observers/group/fibreoptic.py | 114 ++-- cherab/tools/observers/group/pixel.py | 70 +++ cherab/tools/observers/group/plotting.py | 170 ++++++ cherab/tools/observers/group/sightline.py | 104 ++-- cherab/tools/observers/group/spectroscopic.py | 420 +++++++++++++++ .../tools/observers/group/targettedpixel.py | 116 ++++ cherab/tools/observers/spectroscopy/base.py | 3 + .../observers/spectroscopy/fibreoptic.py | 3 + .../tools/observers/spectroscopy/sightline.py | 3 + cherab/tools/tests/test_observer_groups.py | 362 +++++++++++++ demos/observers/groups.py | 90 ++++ docs/source/tools/observers.rst | 35 +- 17 files changed, 1580 insertions(+), 459 deletions(-) create mode 100644 cherab/tools/observers/group/pixel.py create mode 100644 cherab/tools/observers/group/plotting.py create mode 100644 cherab/tools/observers/group/spectroscopic.py create mode 100644 cherab/tools/observers/group/targettedpixel.py create mode 100644 cherab/tools/tests/test_observer_groups.py create mode 100644 demos/observers/groups.py diff --git a/CHANGELOG.md b/CHANGELOG.md index b6881a6c..dbe42093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ Project Changelog Release 1.4.0 (TBD) ------------------- +API changes: +* Spectroscopic observers and their groups are deprecated and replaced by groups based on Raysect's 0D observers (#332) + New: * Make f_profile (current flux) a read-only attribute of EFITEquilibrium. (#355) +* Add group observer class for each of Raysect's 0D observers (#332) +* Add a demo for observer group handling and plotting + +Bug Fixes: +---------- +* Fixed generomak plasma edge data paths Release 1.3.0 (8 Dec 2021) -------------------------- diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index 53e5142a..8676918d 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -39,7 +39,7 @@ def load_edge_profiles(): """ Loads Generomak edge plasma profiles - Return a single dictionary with available edge and plasma species temperature and + Return a single dictionary with available edge and plasma species temperature and density profiles. The profiles are saved on a 2D triangular mesh. :return: dictionary with mesh, electron and plasma composition profiles @@ -51,7 +51,7 @@ def load_edge_profiles(): >>> >>> >>> data = load_edge_profiles() - >>> + >>> >>> # create electron temperature 2D mesh interpolator >>> te = Discrete2DMesh(data["mesh"]["vertex_coords"], data["mesh"]["triangles"], @@ -60,7 +60,7 @@ def load_edge_profiles(): >>> # create hydrogen 0+ density 2D mesh interpolator >>> n_h0 = Discrete2DMesh.instance(te, data["composition"]["hydrogen"][0]["temperature"]) """ - profiles_dir = os.path.join(os.path.dirname(__file__), "/data/plasma/edge") + profiles_dir = os.path.join(os.path.dirname(__file__), "data/edge") edge_data = RecursiveDict() path = os.path.join(profiles_dir, "mesh.json") @@ -101,7 +101,7 @@ def get_edge_interpolators(): te = Discrete2DMesh(profiles["mesh"]["vertex_coords"], profiles["mesh"]["triangles"], profiles["electron"]["temperature"], limit=False) - ne = Discrete2DMesh.instance(te, profiles["electron"]["temperature"], limit=False) + ne = Discrete2DMesh.instance(te, profiles["electron"]["density"], limit=False) mesh_interp["electron"]["temperature"] = te mesh_interp["electron"]["density"] = ne @@ -116,9 +116,9 @@ def get_edge_interpolators(): mesh_interp["composition"][elem_name][stage]["density"] = n mesh_interp["composition"][elem_name][stage]["element"] = stage_data["element"] - + return mesh_interp.freeze() - + def get_edge_distributions(): """ Provides Generomak edge Maxwellian distribution of plasma species @@ -145,7 +145,7 @@ def get_edge_distributions(): element = lookup_isotope(elem_name) except ValueError: element = lookup_element(elem_name) - + n3d = AxisymmetricMapper(stage_data["density"]) t3d = AxisymmetricMapper(stage_data["temperature"]) mass = element.atomic_weight * atomic_mass @@ -163,19 +163,19 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") :param name: name of the plasma node, defaults "Generomak edge plasma" :return: populated Plasma object """ - + # load Generomak equilibrium equilibrium = load_equilibrium() # create or check atomic_data if atomic_data is not None: if not isinstance(atomic_data, AtomicData): - raise ValueError("atomic_data has to be of type AtomicData") + raise ValueError("atomic_data has to be of type AtomicData") else: atomic_data = OpenADAS() # base plasma geometry on mesh vertices - profiles_dir = os.path.join(os.path.dirname(__file__), "data/plasma/edge") + profiles_dir = os.path.join(os.path.dirname(__file__), "data/edge") path = os.path.join(profiles_dir, "mesh.json") with open(path, "r") as fhl: mesh = json.load(fhl) @@ -192,8 +192,8 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") inner_column.transform = translate(0, 0, -padding) plasma_geometry = Subtract(outer_column, inner_column) - geometry_transform = translate(0, 0, -outer_column.height / 2) - + geometry_transform = translate(0, 0, z_range[0]) + # get distributions dists = get_edge_distributions() diff --git a/cherab/tools/observers/__init__.py b/cherab/tools/observers/__init__.py index e3b24bc2..d134ef63 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 SightLineGroup, FibreOpticGroup +from .group import PixelGroup, TargettedPixelGroup, SightLineGroup, FibreOpticGroup, SpectroscopicFibreOpticGroup, SpectroscopicSightLineGroup diff --git a/cherab/tools/observers/group/__init__.py b/cherab/tools/observers/group/__init__.py index 00ae7613..eca93585 100644 --- a/cherab/tools/observers/group/__init__.py +++ b/cherab/tools/observers/group/__init__.py @@ -1,4 +1,3 @@ - # Copyright 2016-2021 Euratom # Copyright 2016-2021 United Kingdom Atomic Energy Authority # Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas @@ -19,3 +18,6 @@ from .fibreoptic import FibreOpticGroup from .sightline import SightLineGroup +from .targettedpixel import TargettedPixelGroup +from .pixel import PixelGroup +from .spectroscopic import SpectroscopicFibreOpticGroup, SpectroscopicSightLineGroup diff --git a/cherab/tools/observers/group/base.py b/cherab/tools/observers/group/base.py index feac823c..3caf31ed 100644 --- a/cherab/tools/observers/group/base.py +++ b/cherab/tools/observers/group/base.py @@ -1,4 +1,3 @@ - # Copyright 2016-2021 Euratom # Copyright 2016-2021 United Kingdom Atomic Energy Authority # Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas @@ -18,33 +17,26 @@ # under the Licence. from numpy import ndarray -import matplotlib.pyplot as plt from raysect.core import Node from raysect.core.workflow import RenderEngine -from raysect.optical import Spectrum -from raysect.optical.observer import SpectralRadiancePipeline0D, SpectralPowerPipeline0D, RadiancePipeline0D +from raysect.optical.observer import Observer0D class Observer0DGroup(Node): """ - A base class for a group of 0D spectroscopic observers under a single scene-graph node. + A base class for handling groups of nonimaging observers as one Node. A scene-graph object regrouping a series of observers as a scene-graph parent. Allows combined observation and display control simultaneously. Note that for any property except `names` and `pipelines`, the same value can be shared between - all sight lines, or each sight line can be assigned with individual value. + all observers, or each observer can be assigned with individual value. - :ivar list names: A list of sight-line names. - :ivar list pipelines: A list of all pipelines connected to each sight-line in the group. - :ivar list/Point3D origin: The origin points for the sight lines. - :ivar list/Vector3D direction: The observation directions for the sight lines. - :ivar list/RenderEngine render_engine: Rendering engine used by the sight lines. + :ivar list names: A list of observer names. + :ivar list pipelines: A list of all pipelines connected to each observer in the group. + :ivar list/RenderEngine render_engine: Rendering engine used by the observers. Note that if the engine is shared, changing its - parameters for one sight line in a group will affect - all sight lines. - :ivar list/bool display_progress: Toggles the display of live render progress. - :ivar list/bool accumulate: Toggles whether to accumulate samples with subsequent - observations. + parameters for one observer in a group will affect + all observers. :ivar list/float min_wavelength: Lower wavelength bound for sampled spectral range. :ivar list/float max_wavelength: Upper wavelength bound for sampled spectral range. :ivar list/int spectral_bins: The number of spectral samples over the wavelength range. @@ -57,446 +49,330 @@ class Observer0DGroup(Node): :ivar list/int pixel_samples: The number of samples to take per pixel. :ivar list/int samples_per_task: Minimum number of samples to request per task. """ + _OBSERVER_TYPE = Observer0D - def __init__(self, parent=None, transform=None, name=None): + def __init__(self, parent=None, transform=None, name=None, observers=None): super().__init__(parent=parent, transform=transform, name=name) - - self._sight_lines = tuple() + self._observers = tuple() + if observers is not None: + for observer in observers: + self.add_observer(observer) def __getitem__(self, item): + try: + selected = self._observers[item] + except IndexError: + raise IndexError("observer number {} not available in this {} " + "with only {} observers.".format(item, self.__class__.__name__, len(self._observers))) + except TypeError: + if isinstance(item, str): + observers = [observer for observer in self._observers if observer.name == item] + if len(observers) == 1: + return observers[0] + + if len(observers) == 0: + raise ValueError("observer '{}' was not found in this {}.".format(item, self.__class__.__name__)) + + raise ValueError("Found {} observers with name {} in this {}.".format(len(observers), item, self.__class__.__name__)) + else: + raise TypeError("{} key must be of type int, slice or str.".format(self.__class__.__name__)) + return selected - if isinstance(item, int): - try: - return self._sight_lines[item] - except IndexError: - raise IndexError("Sight-line number {} not available in this {} " - "with only {} sight-lines.".format(item, self.__class__.__name__, len(self._sight_lines))) - elif isinstance(item, str): - sightlines = [sight_line for sight_line in self._sight_lines if sight_line.name == item] - if len(sightlines) == 1: - return sightlines[0] - - if len(sightlines) == 0: - raise ValueError("Sight-line '{}' was not found in this {}.".format(item, self.__class__.__name__)) - - raise ValueError("Found {} sight-lines with name {} in this {}.".format(len(sightlines), item, self.__class__.__name__)) - else: - raise TypeError("{} key must be of type int or str.".format(self.__class__.__name__)) + def __len__(self): + return len(self._observers) + + @property + def observers(self): + """ + A list of all observer object assigned to the group. + The group is set as a parent to any added observer. + + :rtype: tuple + """ + return self._observers + + @observers.setter + def observers(self, value): + if not isinstance(value, (list, tuple)): + raise TypeError("The observers attribute must be a list or tuple of {}.".format(self._OBSERVER_TYPE)) + if not all(isinstance(val, self._OBSERVER_TYPE) for val in value): + raise ValueError('All observers assigned to the group must be of type {}'.format(self._OBSERVER_TYPE)) + for observer in value: + observer.parent = self + self._observers = tuple(value) + + def add_observer(self, observer): + """Adds new observer to the group.""" + if not isinstance(observer, self._OBSERVER_TYPE): + raise ValueError("Can only add {} objects".format(self._OBSERVER_TYPE)) + observer.parent = self + self._observers = self._observers + (observer, ) @property def names(self): - # A list of sight-line names. - return [sight_line.name for sight_line in self._sight_lines] + # A list of observer names. + return [observer.name for observer in self._observers] @names.setter def names(self, value): if isinstance(value, (list, tuple)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.name = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.name = v else: raise ValueError("The length of 'names' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: raise TypeError("The names attribute must be a list or tuple.") @property - def origin(self): - # The origin points for the sight lines. - return [sight_line.origin for sight_line in self._sight_lines] - - @origin.setter - def origin(self, value): - if isinstance(value, (list, tuple)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.origin = v - else: - raise ValueError("The length of 'origin' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) - else: - for sight_line in self._sight_lines: - sight_line.origin = value - - @property - def direction(self): - # The observation directions for the sight lines. - return [sight_line.direction for sight_line in self._sight_lines] + def pipelines(self): + """ + A list of all pipelines connected to each observer in the group + + :param list pipelist: list of lists/tuples of already instantiated pipelines + :rtype: list + """ + return [observer.pipelines for observer in self._observers] - @direction.setter - def direction(self, value): - if isinstance(value, (list, tuple)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.direction = v - else: - raise ValueError("The length of 'direction' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + @pipelines.setter + def pipelines(self, pipelist): + if len(pipelist) == len(self._observers): + for observer, pipelines in zip(self._observers, pipelist): + observer.pipelines = pipelines else: - for sight_line in self._sight_lines: - sight_line.direction = value + raise ValueError('Length of pipelines list do not match number of observers in the group.') @property def render_engine(self): - # Rendering engine used by the sight lines. - return [sight_line.render_engine for sight_line in self._sight_lines] + """ + Rendering engine used by the observers. + :rtype: list + """ + return [observer.render_engine for observer in self._observers] @render_engine.setter def render_engine(self, value): if isinstance(value, (list, tuple)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): if isinstance(v, RenderEngine): - sight_line.render_engine = v + observer.render_engine = v else: raise TypeError("The list 'render_engine' must contain only RenderEngine instances.") else: raise ValueError("The length of 'render_engine' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: if not isinstance(value, RenderEngine): raise TypeError("The list 'render_engine' must contain only RenderEngine instances.") - for sight_line in self._sight_lines: - sight_line.render_engine = value - - @property - def display_progress(self): - # Toggles the display of live render progress. - return [sight_line.display_progress for sight_line in self._sight_lines] - - @display_progress.setter - def display_progress(self, value): - if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.display_progress = v - else: - raise ValueError("The length of 'display_progress' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) - else: - for sight_line in self._sight_lines: - sight_line.display_progress = value - - @property - def accumulate(self): - # Toggles whether to accumulate samples with subsequent calls to observe(). - return [sight_line.accumulate for sight_line in self._sight_lines] - - @accumulate.setter - def accumulate(self, value): - if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.accumulate = v - else: - raise ValueError("The length of 'accumulate' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) - else: - for sight_line in self._sight_lines: - sight_line.accumulate = value + for observer in self._observers: + observer.render_engine = value @property def min_wavelength(self): # Lower wavelength bound for sampled spectral range. - return [sight_line.min_wavelength for sight_line in self._sight_lines] + return [observer.min_wavelength for observer in self._observers] @min_wavelength.setter def min_wavelength(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.min_wavelength = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.min_wavelength = v else: raise ValueError("The length of 'min_wavelength' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.min_wavelength = value + for observer in self._observers: + observer.min_wavelength = value @property def max_wavelength(self): # Upper wavelength bound for sampled spectral range. - return [sight_line.max_wavelength for sight_line in self._sight_lines] + return [observer.max_wavelength for observer in self._observers] @max_wavelength.setter def max_wavelength(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.max_wavelength = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.max_wavelength = v else: raise ValueError("The length of 'max_wavelength' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.max_wavelength = value + for observer in self._observers: + observer.max_wavelength = value @property def spectral_bins(self): # The number of spectral samples over the wavelength range. - return [sight_line.spectral_bins for sight_line in self._sight_lines] + return [observer.spectral_bins for observer in self._observers] @spectral_bins.setter def spectral_bins(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.spectral_bins = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.spectral_bins = v else: raise ValueError("The length of 'spectral_bins' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.spectral_bins = value + for observer in self._observers: + observer.spectral_bins = value @property def ray_extinction_prob(self): # Probability of ray extinction after every material intersection. - return [sight_line.ray_extinction_prob for sight_line in self._sight_lines] + return [observer.ray_extinction_prob for observer in self._observers] @ray_extinction_prob.setter def ray_extinction_prob(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.ray_extinction_prob = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.ray_extinction_prob = v else: raise ValueError("The length of 'ray_extinction_prob' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.ray_extinction_prob = value + for observer in self._observers: + observer.ray_extinction_prob = value @property def ray_extinction_min_depth(self): # Minimum number of paths before russian roulette style ray extinction. - return [sight_line.ray_extinction_min_depth for sight_line in self._sight_lines] + return [observer.ray_extinction_min_depth for observer in self._observers] @ray_extinction_min_depth.setter def ray_extinction_min_depth(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.ray_extinction_min_depth = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.ray_extinction_min_depth = v else: raise ValueError("The length of 'ray_extinction_min_depth' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.ray_extinction_min_depth = value + for observer in self._observers: + observer.ray_extinction_min_depth = value @property def ray_max_depth(self): # Maximum number of Ray paths before terminating Ray. - return [sight_line.ray_max_depth for sight_line in self._sight_lines] + return [observer.ray_max_depth for observer in self._observers] @ray_max_depth.setter def ray_max_depth(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.ray_max_depth = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.ray_max_depth = v else: raise ValueError("The length of 'ray_max_depth' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.ray_max_depth = value + for observer in self._observers: + observer.ray_max_depth = value @property def ray_important_path_weight(self): # Relative weight of important path sampling. - return [sight_line.ray_important_path_weight for sight_line in self._sight_lines] + return [observer.ray_important_path_weight for observer in self._observers] @ray_important_path_weight.setter def ray_important_path_weight(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.ray_important_path_weight = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.ray_important_path_weight = v else: raise ValueError("The length of 'ray_important_path_weight' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.ray_important_path_weight = value + for observer in self._observers: + observer.ray_important_path_weight = value @property def pixel_samples(self): # The number of samples to take per pixel. - return [sight_line.pixel_samples for sight_line in self._sight_lines] + return [observer.pixel_samples for observer in self._observers] @pixel_samples.setter def pixel_samples(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.pixel_samples = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.pixel_samples = v else: raise ValueError("The length of 'pixel_samples' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.pixel_samples = value + for observer in self._observers: + observer.pixel_samples = value @property def samples_per_task(self): # Minimum number of samples to request per task. - return [sight_line.samples_per_task for sight_line in self._sight_lines] + return [observer.samples_per_task for observer in self._observers] @samples_per_task.setter def samples_per_task(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.samples_per_task = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.samples_per_task = v else: raise ValueError("The length of 'samples_per_task' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.samples_per_task = value - - @property - def pipelines(self): - # A list of all pipelines connected to each sight-line in the group. - return [sight_line.pipelines for sight_line in self._sight_lines] - - def connect_pipelines(self, properties=[(SpectralRadiancePipeline0D, None, None)]): - """ - Connects pipelines of given kinds and names to each sight-line in the group. - Connected pipelines are non-accumulating by default. - - :param list properties: 3-tuple list of pipeline properties in order (class, name, filter). - Default is [(SpectralRadiancePipeline0D, None, None)]. - The following pipeline classes are supported: - SpectralRadiacnePipeline0D, - SpectralPowerPipeline0D, - RadiacnePipeline0D, - PowerPipeline0D. - Filters are applied to the mono pipelines only, namely, - PowerPipeline0D or RadiacnePipeline0D. The values provided for spectral - pipelines will be ignored. The filter must be an instance of - SpectralFunction or None. - - """ - - for sight_line in self._sight_lines: - sight_line.connect_pipelines(properties) + for observer in self._observers: + observer.samples_per_task = value def observe(self): """ Starts the observation. """ - for sight_line in self._sight_lines: - sight_line.observe() - - def _get_same_pipelines(self, item): - pipelines = [] - sight_lines = [] - for sight_line in self._sight_lines: - try: - pipelines.append(sight_line.get_pipeline(item)) - except (ValueError, IndexError): - continue - else: - sight_lines.append(sight_line) - - if len(pipelines) == 0: - raise ValueError("Pipeline {} was not found for any sight-line in this {}.".format((item, self.__class__.__name__))) - - pipeline_types = set(type(pipeline) for pipeline in pipelines) - if len(pipeline_types) > 1: - raise ValueError("Pipelines {} have different types for different sight-lines.".format(item)) - - return pipelines, sight_lines - - def plot_total_signal(self, item=0, ax=None): - """ - Plots total (wavelength-integrated) signal for each sight line in the group. - - :param str/int item: The index or name of the pipeline. Default: 0. - :param Axes ax: Existing matplotlib axes. - - """ - - pipelines, sight_lines = self._get_same_pipelines(item) - - if ax is None: - _, ax = plt.subplots(constrained_layout=True) - - signal = [] - tick_labels = [] - for pipeline, sight_line in zip(pipelines, sight_lines): - if isinstance(pipeline, SpectralPowerPipeline0D): - spectrum = Spectrum(pipeline.min_wavelength, pipeline.max_wavelength, pipeline.bins) - spectrum.samples = pipeline.samples.mean - signal.append(spectrum.total()) - else: - signal.append(pipeline.value.mean) - - if sight_line.name and len(sight_line.name): - tick_labels.append(sight_line.name) - else: - tick_labels.append(self._sight_lines.index(sight_line)) - - if isinstance(pipeline, (SpectralRadiancePipeline0D, RadiancePipeline0D)): - ylabel = 'Radiance (W/m^2/str)' - else: # SpectralPowerPipeline0D or PowerPipeline0D - ylabel = 'Power (W)' - - ax.bar(list(range(len(signal))), signal, tick_label=tick_labels, label=item) - - if isinstance(item, int): - # check if pipelines share the same name - if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): - ax.set_title('{}: {}'.format(self.name, pipelines[0].name)) - else: - # pipelines have different names or name is not set - ax.set_title('{}: pipeline {}'.format(self.name, item)) - elif isinstance(item, str): - ax.set_title('{}: {}'.format(self.name, item)) - - ax.set_ylabel(ylabel) - ax.set_xlabel('Line of sight') - - return ax + for observer in self._observers: + observer.observe() - def plot_spectra(self, item=0, in_photons=False, ax=None): + def connect_pipelines(self, pipeline_classes, keywords_list=None, suppress_display_progress=True): """ - Plot the spectra observed by each line of sight in the group for a given pipeline. - - :param str/int item: The index or name of the pipeline. Default: 0. - :param bool in_photons: If True, plots the spectrum in photon/s/nm instead of W/nm. - Default is False. - :param Axes ax: Existing matplotlib axes. + Creates and connects a new set of given pipelines to each observer in the group. + + Pipeline classes are instantiated using parameters specified in appropriate dict from keywords list. + If keywords list is provided, it length must match the number of provided pipeline classes. + + :param list pipeline_classes: list of pipeline classes to be connected with observers + :param list keywords_list: list of dicts with keywords passed to init methods of pipeline classes + its length must match the number of pipeline classes + for default parameters place an empty dict to appropriate place in the list + :param bool suppress_display_progress: Toggles setting display_progress to False for each compatible pipeline (default=True) + + .. code-block:: pycon + + ... + >>> pipelines = [SpectralRadiancePipeline0D, RadiancePipeline0D] + >>> keywords = [{'name': 'MySpectralPipeline'}, {}] + >>> group.connect_pipelines(pipeline_classes=pipelines, keywords_list=keywords) + """ - - pipelines, sight_lines = self._get_same_pipelines(item) - - if ax is None: - _, ax = plt.subplots(constrained_layout=True) - - for sight_line in sight_lines: - sight_line.plot_spectrum(item=item, in_photons=in_photons, ax=ax, extras=False) - - if isinstance(pipelines[0], SpectralRadiancePipeline0D): - ylabel = 'Spectral radiance (photon/s/m^2/str/nm)' if in_photons else 'Spectral radiance (W/m^2/str/nm)' - else: # SpectralPowerPipeline0D - ylabel = 'Spectral power (photon/s/nm)' if in_photons else 'Spectral power (W/nm)' - - if isinstance(item, int): - # check if pipelines share the same name - if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): - ax.set_title('{}: {}'.format(self.name, pipelines[0].name)) - else: - # pipelines have different names or name is not set - ax.set_title('{}: pipeline {}'.format(self.name, item)) - elif isinstance(item, str): - ax.set_title('{}: {}'.format(self.name, item)) - - ax.set_xlabel('Wavelength (nm)') - ax.set_ylabel(ylabel) - ax.legend() - - return ax + if keywords_list is None: + keywords_list = [dict() for ppln in pipeline_classes] + if len(pipeline_classes) !=len (keywords_list): + raise ValueError('The number of given pipeline classes does not match the number of dicts in keyword list.\ + For each pipeline class there must be a parameter dict.') + for observer in self._observers: + pipelines = [] + for PipelineClass, kwargs in zip(pipeline_classes, keywords_list): + pipeline = PipelineClass(**kwargs) + if suppress_display_progress: + try: + pipeline.display_progress = False + except AttributeError: + pass + pipelines.append(pipeline) + observer.pipelines = pipelines + return diff --git a/cherab/tools/observers/group/fibreoptic.py b/cherab/tools/observers/group/fibreoptic.py index ab9e7678..571948f7 100644 --- a/cherab/tools/observers/group/fibreoptic.py +++ b/cherab/tools/observers/group/fibreoptic.py @@ -1,4 +1,3 @@ - # Copyright 2016-2021 Euratom # Copyright 2016-2021 United Kingdom Atomic Energy Authority # Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas @@ -18,7 +17,8 @@ # under the Licence. from numpy import ndarray -from cherab.tools.observers.spectroscopy import SpectroscopicFibreOptic +from raysect.optical.observer import FibreOptic + from .base import Observer0DGroup @@ -26,117 +26,91 @@ class FibreOpticGroup(Observer0DGroup): """ A group of fibre optics under a single scene-graph node. - A scene-graph object regrouping a series of 'SpectroscopicFibreOptic' + A scene-graph object regrouping a series of 'FibreOptic' observers as a scene-graph parent. Allows combined observation and display control simultaneously. - :ivar list sight_lines: A list of fibre optics (SpectroscopicFibreOptic instances) in this - group. :ivar list/float acceptance_angle: The angle in degrees between the z axis and the cone surface which defines the fibres solid angle sampling - area. The same value can be shared between all sight lines, - or each sight line can be assigned with individual value. + area. The same value can be shared between all observers, + or each observer can be assigned with individual value. :ivar list/float radius: The radius of the fibre tip in metres. This radius defines a circular area at the fibre tip which will be sampled over. The same value - can be shared between all sight lines, or each sight line can be + can be shared between all observers, or each observer can be assigned with individual value. .. code-block:: pycon >>> from math import cos, sin, pi - >>> from matplotlib import pyplot as plt + >>> + >>> import matplotlib.pyplot as plt + >>> from raysect.core import translate, rotate_basis, Point3D, Vector3D >>> from raysect.optical import World - >>> from raysect.optical.observer import SpectralPowerPipeline0D, PowerPipeline0D - >>> from raysect.core.math import Point3D, Vector3D - >>> from cherab.tools.observers import SpectroscopicFibreOptic, FibreOpticGroup + >>> from raysect.optical.observer import RadiancePipeline0D, SpectralRadiancePipeline0D, PowerPipeline0D, SpectralPowerPipeline0D, FibreOptic + >>> + >>> from cherab.tools.observers import FibreOpticGroup + >>> from cherab.tools.observers.group.plotting import plot_group_total, plot_group_spectra >>> >>> world = World() - ... - >>> group = FibreOpticGroup(parent=world) - >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, sin(pi/10)), name="Fibre 1")) - >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-1, 0, 0), name="Fibre 2")) - >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, -sin(pi/10)), name="Fibre 3")) - >>> group.connect_pipelines([(SpectralPowerPipeline0D, 'MySpectralPipeline', None), - (PowerPipeline0D, 'MyMonoPipeline', None)]) # add pipelines to all fibres in the group + >>> + >>> transform1 = translate(3., 0, 0) * rotate_basis(Vector3D(-cos(pi/10), 0, sin(pi/10)), Vector3D(0, 1, 0)) + >>> fibre1 = FibreOptic(transform=transform1, name="Fibre 1") + >>> transform2 = translate(3, 0 ,0) * rotate_basis(Vector3D(-1, 0, 0), Vector3D(0, 1, 0)) + >>> fibre2 = FibreOptic(transform=transform2, name="Fibre 2") + >>> transform3 = translate(3, 0, 0) * rotate_basis(Vector3D(-cos(pi/10), 0, -sin(pi/10)), Vector3D(0, 1, 0)) + >>> fibre3 = FibreOptic(transform=transform3, name="Fibre 3") + >>> + >>> group = FibreOpticGroup(name='MyFibreGroup', parent=world, observers=[fibre1, fibre2]) + >>> group.add_observer(fibre3) + >>> pipelines = [SpectralRadiancePipeline0D, RadiancePipeline0D] + >>> keywords = [{'name': 'MySpectralPipeline'}, {'name': 'MyMonoPipeline'}] + >>> group.connect_pipelines(pipelines, keywords) # add pipelines to all observers in the group >>> group.acceptance_angle = 2 # same value for all fibres in the group >>> group.radius = 2.e-3 >>> group.spectral_bins = 512 >>> group.pixel_samples = [2000, 1000, 2000] # individual value for each fibre in the group - >>> group.display_progress = False # control pipeline parameters through the group observer >>> group.observe() # combined observation - >>> group.plot_spectra(item='MySpectralPipeline', in_photons=True) # plot the spectra - >>> group.plot_total_signal(item='MyMonoPipeline') # plot the total signals + >>> + >>> plot_group_spectra(group, item='MySpectralPipeline', in_photons=True) # plot the spectra + >>> plot_group_total(group, item='MyMonoPipeline') # plot the total signals >>> plt.show() """ - - @property - def sight_lines(self): - return self._sight_lines - - @sight_lines.setter - def sight_lines(self, value): - - if not isinstance(value, (list, tuple)): - raise TypeError("The sight_lines attribute of FibreOpticGroup must be a list or tuple of SpectroscopicFibreOptics.") - - for sight_line in value: - if not isinstance(sight_line, SpectroscopicFibreOptic): - raise TypeError("The sight_lines attribute of FibreOpticGroup must be a list or tuple of " - "SpectroscopicFibreOptics. Value {} is not a SpectroscopicFibreOptic.".format(sight_line)) - - # Prevent external changes being made to this list - for sight_line in value: - sight_line.parent = self - - self._sight_lines = tuple(value) - - def add_sight_line(self, sight_line): - """ - Adds new fibre optic to the group. - - :param SpectroscopicFibreOptic sight_line: Fibre optic to add. - """ - - if not isinstance(sight_line, SpectroscopicFibreOptic): - raise TypeError("The sightline argument must be of type SpectroscopicFibreOptic.") - - sight_line.parent = self - self._sight_lines = self._sight_lines + (sight_line,) + _OBSERVER_TYPE = FibreOptic @property def acceptance_angle(self): # The angle in degrees between the z axis and the cone surface which defines the fibres # solid angle sampling area. - return [sight_line.acceptance_angle for sight_line in self._sight_lines] + return [observer.acceptance_angle for observer in self._observers] @acceptance_angle.setter def acceptance_angle(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.acceptance_angle = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.acceptance_angle = v else: raise ValueError("The length of 'acceptance_angle' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.acceptance_angle = value + for observer in self._observers: + observer.acceptance_angle = value @property def radius(self): # The radius of the fibre tip in metres. This radius defines a circular area at the fibre tip # which will be sampled over. - return [sight_line.radius for sight_line in self._sight_lines] + return [observer.radius for observer in self._observers] @radius.setter def radius(self, value): if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._sight_lines): - for sight_line, v in zip(self._sight_lines, value): - sight_line.radius = v + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.radius = v else: raise ValueError("The length of 'radius' ({}) " - "mismatches the number of sight-lines ({}).".format(len(value), len(self._sight_lines))) + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: - for sight_line in self._sight_lines: - sight_line.radius = value + for observer in self._observers: + observer.radius = value diff --git a/cherab/tools/observers/group/pixel.py b/cherab/tools/observers/group/pixel.py new file mode 100644 index 00000000..c8979d22 --- /dev/null +++ b/cherab/tools/observers/group/pixel.py @@ -0,0 +1,70 @@ +# 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 Pixel + +from .base import Observer0DGroup + + +class PixelGroup(Observer0DGroup): + """ + A group of pixels under a single scene-graph node. + + A scene-graph object regrouping a series of 'Pixel' + 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 + """ + _OBSERVER_TYPE = Pixel + + @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 diff --git a/cherab/tools/observers/group/plotting.py b/cherab/tools/observers/group/plotting.py new file mode 100644 index 00000000..097d52fa --- /dev/null +++ b/cherab/tools/observers/group/plotting.py @@ -0,0 +1,170 @@ +# 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. + +import warnings + +import matplotlib.pyplot as plt +from raysect.optical import Spectrum +from raysect.optical.observer import RadiancePipeline0D, PowerPipeline0D, SpectralRadiancePipeline0D, SpectralPowerPipeline0D + + +def select_pipelines(group, item): + """ + Selects pipelines of the same type based on index or name from the provided group. + + If name is used, error is raised when more than one pipeline is found in an observer. + An ValueError is also raised if found pipelines are not of the same type. + + :param Observer0DGroup group: Observer group from which to select pipelines. + :param str/int item: The index or name of the pipeline to be selected. + :return list pipelines: matching pipelines + :return list observers: observers with matching pipelines + """ + pipelines = [] + observers = [] + for observer in group.observers: + if isinstance(item, int): + try: + pipelines.append(observer.pipelines[item]) + observers.append(observer) + except IndexError: + warnings.warn('Invalid pipeline index {} for observer {} with {} pipelines'.format( + item, observer, len(observer.pipelines))) + elif isinstance(item, str): + matching_pipelines = [pipeline for pipeline in observer.pipelines if pipeline.name == item] + pipelines_num = len(matching_pipelines) + if pipelines_num == 0: + warnings.warn('Found no matching pipelines for name {} in observer {}'.format(item, observer)) + elif pipelines_num == 1: + pipelines.append(matching_pipelines[0]) + observers.append(observer) + else: + raise ValueError("Found {} matching pipelines for name {} in observer {}.".format( + pipelines_num, item, observer)) + + pipeline_types = set(type(pipeline) for pipeline in pipelines) + if len(pipeline_types) > 1: + raise ValueError("Pipelines {} have different types for different observers.".format(item)) + + return pipelines, observers + + +def plot_group_total(group, item=0, ax=None): + """ + Plots total (wavelength-integrated) signal for each observer in the group. + + :param Observer0DGroup group: Group class with observers hodling data to be plotted. + :param str/int item: The index or name of the pipeline. Default: 0. + :param Axes ax: Existing matplotlib axes. + :return Axes ax: Matplotlib axes with plotted spectra + """ + pipelines, observers = select_pipelines(group, item) + + if ax is None: + _, ax = plt.subplots(constrained_layout=True) + + signal = [] + tick_labels = [] + for pipeline, observer in zip(pipelines, observers): + if isinstance(pipeline, SpectralPowerPipeline0D): + spectrum = Spectrum(pipeline.min_wavelength, pipeline.max_wavelength, pipeline.bins) + spectrum.samples[:] = pipeline.samples.mean + signal.append(spectrum.total()) + else: + signal.append(pipeline.value.mean) + + if observer.name and len(observer.name): + tick_labels.append(observer.name) + else: + tick_labels.append(group.observers.index(observer)) + + if isinstance(pipeline, (SpectralRadiancePipeline0D, RadiancePipeline0D)): + ylabel = 'Radiance [W/m^2/str]' + else: # SpectralPowerPipeline0D or PowerPipeline0D + ylabel = 'Power [W]' + + ax.bar(list(range(len(signal))), signal, tick_label=tick_labels, label=item) + + if isinstance(item, int): + # check if pipelines share the same name + if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): + ax.set_title('{}: {}'.format(group.name, pipelines[0].name)) + else: + # pipelines have different names or name is not set + ax.set_title('{}: pipeline {}'.format(group.name, item)) + elif isinstance(item, str): + ax.set_title('{}: {}'.format(group.name, item)) + + ax.set_ylabel(ylabel) + ax.set_xlabel('Observer') + + return ax + + +def plot_group_spectra(group, item=0, in_photons=False, ax=None): + """ + Plot the spectra observed by each observer in the group for a given pipeline. + + :param Observer0DGroup group: Group class with observers hodling data to be plotted. + :param str/int item: The index or name of the pipeline. Default: 0. + :param bool in_photons: If True, plots the spectrum in photon/s/nm instead of W/nm. + Default is False. + :param Axes ax: Existing matplotlib axes. + :return Axes ax: Matplotlib axes with plotted spectra + """ + pipelines, observers = select_pipelines(group, item) + + if ax is None: + _, ax = plt.subplots(constrained_layout=True) + + for pipeline, observer in zip(pipelines, observers): + spectrum_observed = Spectrum(pipeline.min_wavelength, pipeline.max_wavelength, pipeline.bins) + spectrum_observed.samples[:] = pipeline.samples.mean + if in_photons: + # turn the samples into photon/s + spectrum = spectrum_observed.new_spectrum() + spectrum.samples[:] = spectrum_observed.to_photons() + else: + spectrum = spectrum_observed + + label = observer.name if observer.name and len(observer.name) else group.observers.index(observer) + + if spectrum.samples.size > 1: + ax.plot(spectrum.wavelengths, spectrum.samples, label=label) + else: + ax.plot(spectrum.wavelengths, spectrum.samples, marker='o', ls='none', label=label) + if isinstance(pipelines[0], SpectralRadiancePipeline0D): + ylabel = 'Spectral radiance [photon/s/m^2/str/nm]' if in_photons else 'Spectral radiance [W/m^2/str/nm]' + else: # SpectralPowerPipeline0D + ylabel = 'Spectral power [photon/s/nm]' if in_photons else 'Spectral power [W/nm]' + + if isinstance(item, int): + # check if pipelines share the same name + if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): + ax.set_title('{}: {}'.format(group.name, pipelines[0].name)) + else: + # pipelines have different names or name is not set + ax.set_title('{}: pipeline {}'.format(group.name, item)) + elif isinstance(item, str): + ax.set_title('{}: {}'.format(group.name, item)) + + ax.set_xlabel('Wavelength [nm]') + ax.set_ylabel(ylabel) + ax.legend() + + return ax \ No newline at end of file diff --git a/cherab/tools/observers/group/sightline.py b/cherab/tools/observers/group/sightline.py index 14e026cb..94f3cddc 100644 --- a/cherab/tools/observers/group/sightline.py +++ b/cherab/tools/observers/group/sightline.py @@ -1,4 +1,3 @@ - # Copyright 2016-2021 Euratom # Copyright 2016-2021 United Kingdom Atomic Energy Authority # Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas @@ -17,77 +16,74 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from cherab.tools.observers.spectroscopy import SpectroscopicSightLine +from numpy import ndarray +from raysect.optical.observer import SightLine + from .base import Observer0DGroup class SightLineGroup(Observer0DGroup): """ - A group of spectroscopic sight-lines under a single scene-graph node. + A group of SightLine under a single scene-graph node. - A scene-graph object regrouping a series of 'SpectroscopicSightLine' + A scene-graph object regrouping a series of 'SightLine' observers as a scene-graph parent. Allows combined observation and display control simultaneously. - :ivar list sight_lines: A list of lines of sight (SpectroscopicSightLine instances) - in this group. + :ivar list observers: A list of sight lines (SightLine instances) in this group. .. code-block:: pycon >>> from math import cos, sin, pi - >>> from matplotlib import pyplot as plt + >>> + >>> import matplotlib.pyplot as plt + >>> from raysect.core import translate, rotate_basis, Point3D, Vector3D >>> from raysect.optical import World - >>> from raysect.optical.observer import SpectralRadiancePipeline0D, RadiancePipeline0D - >>> from raysect.core.math import Point3D, Vector3D - >>> from cherab.tools.observers import SpectroscopicSightLine, SightLineGroup + >>> from raysect.optical.observer import RadiancePipeline0D, SpectralRadiancePipeline0D, PowerPipeline0D, SpectralPowerPipeline0D, SightLine + >>> + >>> from cherab.tools.observers import SightLineGroup + >>> from cherab.tools.observers.group.plotting import plot_group_total, plot_group_spectra >>> >>> world = World() - ... - >>> group = SightLineGroup(parent=world) - >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, sin(pi/10)), name="SightLine 1")) - >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-1, 0, 0), name="SightLine 2")) - >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, -sin(pi/10)), name="SightLine 3")) - >>> group.connect_pipelines([(SpectralRadiancePipeline0D, 'MySpectralPipeline', None), - (RadiancePipeline0D, 'MyMonoPipeline', None)]) # add pipelines to all sight lines in the group - >>> group.spectral_bins = 512 # same value for all sight lines in the group - >>> group.pixel_samples = [2000, 1000, 2000] # individual value for each sight line in the group - >>> group.display_progress = False # control pipeline parameters through the group observer + >>> + >>> transform1 = translate(3., 0, 0) * rotate_basis(Vector3D(-cos(pi/10), 0, sin(pi/10)), Vector3D(0, 1, 0)) + >>> sightline_1 = SightLine(transform=transform1, name="SightLine 1") + >>> transform2 = translate(3, 0 ,0) * rotate_basis(Vector3D(-1, 0, 0), Vector3D(0, 1, 0)) + >>> sightline_2 = SightLine(transform=transform2, name="SightLine 2") + >>> transform3 = translate(3, 0, 0) * rotate_basis(Vector3D(-cos(pi/10), 0, -sin(pi/10)), Vector3D(0, 1, 0)) + >>> sightline_3 = SightLine(transform=transform3, name="SightLine 3") + >>> + >>> group = SightLineGroup(name='MySightLineGroup', parent=world, observers=[sightline_1, sightline_2]) + >>> group.add_observer(sightline_3) + >>> pipelines = [SpectralRadiancePipeline0D, RadiancePipeline0D] + >>> keywords = [{'name': 'MySpectralPipeline'}, {'name': 'MyMonoPipeline'}] + >>> group.connect_pipelines(pipelines, keywords) # add pipelines to all observers in the group + >>> group.acceptance_angle = 2 # same value for all sightlines in the group + >>> group.radius = 2.e-3 + >>> group.spectral_bins = 512 + >>> group.pixel_samples = [2000, 1000, 2000] # individual value for each sightline in the group >>> group.observe() # combined observation - >>> group.plot_spectra(item='MySpectralPipeline', in_photons=True) # plot the spectra - >>> group.plot_total_signal(item='MyMonoPipeline') # plot the total signals + >>> + >>> plot_group_spectra(group, item='MySpectralPipeline', in_photons=True) # plot the spectra + >>> plot_group_total(group, item='MyMonoPipeline') # plot the total signals >>> plt.show() """ + _OBSERVER_TYPE = SightLine @property - def sight_lines(self): - return self._sight_lines - - @sight_lines.setter - def sight_lines(self, value): - - if not isinstance(value, (list, tuple)): - raise TypeError("The sight_lines attribute of LineOfSightGroup must be a list or tuple of SpectroscopicSightLines.") - - for sight_line in value: - if not isinstance(sight_line, SpectroscopicSightLine): - raise TypeError("The sight_lines attribute of LineOfSightGroup must be a list or tuple of " - "SpectroscopicSightLines. Value {} is not a SpectroscopicSightLine.".format(sight_line)) - - # Prevent external changes being made to this list - for sight_line in value: - sight_line.parent = self - - self._sight_lines = tuple(value) - - def add_sight_line(self, sight_line): - """ - Adds new line of sight to the group. - - :param SpectroscopicSightLine sight_line: Sight line to add. - """ - - if not isinstance(sight_line, SpectroscopicSightLine): - raise TypeError("The sight_line argument must be of type SpectroscopicSightLine.") - - sight_line.parent = self - self._sight_lines = self._sight_lines + (sight_line,) + def sensitivity(self): + # A list of observer sensitivities. + return [observer.sensitivity for observer in self._observers] + + @sensitivity.setter + def sensitivity(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.sensitivity = v + else: + raise ValueError("The length of 'sensitivity' ({}) ".format(len(value)) + + "mismatches the number of observers ({}).".format(len(self._observers))) + else: + for observer in self._observers: + observer.sensitivity = value diff --git a/cherab/tools/observers/group/spectroscopic.py b/cherab/tools/observers/group/spectroscopic.py new file mode 100644 index 00000000..0f20eff9 --- /dev/null +++ b/cherab/tools/observers/group/spectroscopic.py @@ -0,0 +1,420 @@ +# 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. + +import matplotlib.pyplot as plt +from numpy import ndarray +from raysect.optical import Spectrum +from raysect.optical.observer import SpectralRadiancePipeline0D, SpectralPowerPipeline0D, RadiancePipeline0D + +from .base import Observer0DGroup +from cherab.tools.observers.spectroscopy import SpectroscopicFibreOptic, SpectroscopicSightLine + + +class SpectroscopicObserver0DGroup(Observer0DGroup): + """ + .. deprecated:: 1.4.0 + Use SightLineGroup or FibreOpticGroup based on Raysect's observers instead. + + A base class for a group of 0D spectroscopic observers under a single scene-graph node. + + A scene-graph object regrouping a series of observers as a scene-graph parent. + Allows combined observation and display control simultaneously. + Note that for any property except `names` and `pipelines`, the same value can be shared between + all sight lines, or each sight line can be assigned with individual value. + + :ivar list/Point3D origin: The origin points for the sight lines. + :ivar list/Vector3D direction: The observation directions for the sight lines. + :ivar list/bool display_progress: Toggles the display of live render progress. + :ivar list/bool accumulate: Toggles whether to accumulate samples with subsequent + observations. + """ + + def __init__(self, parent=None, transform=None, name=None, observers=None): + super().__init__(parent=parent, transform=transform, name=name, observers=observers) + + @property + def sight_lines(self): + return self._observers + + @sight_lines.setter + def sight_lines(self, value): + self.observers = value + + def add_sight_line(self, sight_line): + """ + Adds new fibre optic to the group. + + :param SpectroscopicFibreOptic sight_line: Fibre optic to add. + """ + self.add_observer(sight_line) + + @property + def origin(self): + # The origin points for the sight lines. + return [sight_line.origin for sight_line in self._observers] + + @origin.setter + def origin(self, value): + if isinstance(value, (list, tuple)): + if len(value) == len(self._observers): + for sight_line, v in zip(self._observers, value): + sight_line.origin = v + else: + raise ValueError("The length of 'origin' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for sight_line in self._observers: + sight_line.origin = value + + @property + def direction(self): + # The observation directions for the sight lines. + return [sight_line.direction for sight_line in self._observers] + + @direction.setter + def direction(self, value): + if isinstance(value, (list, tuple)): + if len(value) == len(self._observers): + for sight_line, v in zip(self._observers, value): + sight_line.direction = v + else: + raise ValueError("The length of 'direction' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for sight_line in self._observers: + sight_line.direction = value + + + @property + def display_progress(self): + # Toggles the display of live render progress. + return [observer.display_progress for observer in self._observers] + + @display_progress.setter + def display_progress(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.display_progress = v + else: + raise ValueError("The length of 'display_progress' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.display_progress = value + + @property + def accumulate(self): + # Toggles whether to accumulate samples with subsequent calls to observe(). + return [observer.accumulate for observer in self._observers] + + @accumulate.setter + def accumulate(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.accumulate = v + else: + raise ValueError("The length of 'accumulate' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.accumulate = value + + + def connect_pipelines(self, properties=[(SpectralRadiancePipeline0D, None, None)]): + """ + Connects pipelines of given kinds and names to each sight-line in the group. + Connected pipelines are non-accumulating by default. + + :param list properties: 3-tuple list of pipeline properties in order (class, name, filter). + Default is [(SpectralRadiancePipeline0D, None, None)]. + The following pipeline classes are supported: + SpectralRadiacnePipeline0D, + SpectralPowerPipeline0D, + RadiacnePipeline0D, + PowerPipeline0D. + Filters are applied to the mono pipelines only, namely, + PowerPipeline0D or RadiacnePipeline0D. The values provided for spectral + pipelines will be ignored. The filter must be an instance of + SpectralFunction or None. + + """ + + for sight_line in self._observers: + sight_line.connect_pipelines(properties) + + def _get_same_pipelines(self, item): + pipelines = [] + sight_lines = [] + for sight_line in self._observers: + try: + pipelines.append(sight_line.get_pipeline(item)) + except (ValueError, IndexError): + continue + else: + sight_lines.append(sight_line) + + if len(pipelines) == 0: + raise ValueError("Pipeline {} was not found for any sight-line in this {}.".format((item, self.__class__.__name__))) + + pipeline_types = set(type(pipeline) for pipeline in pipelines) + if len(pipeline_types) > 1: + raise ValueError("Pipelines {} have different types for different sight-lines.".format(item)) + + return pipelines, sight_lines + + def plot_total_signal(self, item=0, ax=None): + """ + Plots total (wavelength-integrated) signal for each sight line in the group. + + :param str/int item: The index or name of the pipeline. Default: 0. + :param Axes ax: Existing matplotlib axes. + + """ + + pipelines, sight_lines = self._get_same_pipelines(item) + + if ax is None: + _, ax = plt.subplots(constrained_layout=True) + + signal = [] + tick_labels = [] + for pipeline, sight_line in zip(pipelines, sight_lines): + if isinstance(pipeline, SpectralPowerPipeline0D): + spectrum = Spectrum(pipeline.min_wavelength, pipeline.max_wavelength, pipeline.bins) + spectrum.samples = pipeline.samples.mean + signal.append(spectrum.total()) + else: + signal.append(pipeline.value.mean) + + if sight_line.name and len(sight_line.name): + tick_labels.append(sight_line.name) + else: + tick_labels.append(self._observers.index(sight_line)) + + if isinstance(pipeline, (SpectralRadiancePipeline0D, RadiancePipeline0D)): + ylabel = 'Radiance (W/m^2/str)' + else: # SpectralPowerPipeline0D or PowerPipeline0D + ylabel = 'Power (W)' + + ax.bar(list(range(len(signal))), signal, tick_label=tick_labels, label=item) + + if isinstance(item, int): + # check if pipelines share the same name + if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): + ax.set_title('{}: {}'.format(self.name, pipelines[0].name)) + else: + # pipelines have different names or name is not set + ax.set_title('{}: pipeline {}'.format(self.name, item)) + elif isinstance(item, str): + ax.set_title('{}: {}'.format(self.name, item)) + + ax.set_ylabel(ylabel) + ax.set_xlabel('Line of sight') + + return ax + + def plot_spectra(self, item=0, in_photons=False, ax=None): + """ + Plot the spectra observed by each line of sight in the group for a given pipeline. + + :param str/int item: The index or name of the pipeline. Default: 0. + :param bool in_photons: If True, plots the spectrum in photon/s/nm instead of W/nm. + Default is False. + :param Axes ax: Existing matplotlib axes. + """ + + pipelines, sight_lines = self._get_same_pipelines(item) + + if ax is None: + _, ax = plt.subplots(constrained_layout=True) + + for sight_line in sight_lines: + sight_line.plot_spectrum(item=item, in_photons=in_photons, ax=ax, extras=False) + + if isinstance(pipelines[0], SpectralRadiancePipeline0D): + ylabel = 'Spectral radiance (photon/s/m^2/str/nm)' if in_photons else 'Spectral radiance (W/m^2/str/nm)' + else: # SpectralPowerPipeline0D + ylabel = 'Spectral power (photon/s/nm)' if in_photons else 'Spectral power (W/nm)' + + if isinstance(item, int): + # check if pipelines share the same name + if len(set(pipeline.name for pipeline in pipelines)) == 1 and pipelines[0].name and len(pipelines[0].name): + ax.set_title('{}: {}'.format(self.name, pipelines[0].name)) + else: + # pipelines have different names or name is not set + ax.set_title('{}: pipeline {}'.format(self.name, item)) + elif isinstance(item, str): + ax.set_title('{}: {}'.format(self.name, item)) + + ax.set_xlabel('Wavelength (nm)') + ax.set_ylabel(ylabel) + ax.legend() + + return ax + + +class SpectroscopicFibreOpticGroup(SpectroscopicObserver0DGroup): + """ + .. deprecated:: 1.4.0 + Use FibreOpticGroup based on Raysect's FibreOptic observer instead. + + A group of fibre optics under a single scene-graph node. + + A scene-graph object regrouping a series of 'SpectroscopicFibreOptic' + observers as a scene-graph parent. Allows combined observation and display + control simultaneously. + + :ivar list sight_lines: A list of fibre optics (SpectroscopicFibreOptic instances) in this + group. + :ivar list/float acceptance_angle: The angle in degrees between the z axis and the cone + surface which defines the fibres solid angle sampling + area. The same value can be shared between all sight lines, + or each sight line can be assigned with individual value. + :ivar list/float radius: The radius of the fibre tip in metres. This radius defines a circular + area at the fibre tip which will be sampled over. The same value + can be shared between all sight lines, or each sight line can be + assigned with individual value. + + .. code-block:: pycon + + >>> from math import cos, sin, pi + >>> from matplotlib import pyplot as plt + >>> from raysect.optical import World + >>> from raysect.optical.observer import SpectralPowerPipeline0D, PowerPipeline0D + >>> from raysect.core.math import Point3D, Vector3D + >>> from cherab.tools.observers import SpectroscopicFibreOptic, FibreOpticGroup + >>> + >>> world = World() + ... + >>> group = FibreOpticGroup(parent=world) + >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, sin(pi/10)), name="Fibre 1")) + >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-1, 0, 0), name="Fibre 2")) + >>> group.add_sight_line(SpectroscopicFibreOptic(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, -sin(pi/10)), name="Fibre 3")) + >>> group.connect_pipelines([(SpectralPowerPipeline0D, 'MySpectralPipeline', None), + (PowerPipeline0D, 'MyMonoPipeline', None)]) # add pipelines to all fibres in the group + >>> group.acceptance_angle = 2 # same value for all fibres in the group + >>> group.radius = 2.e-3 + >>> group.spectral_bins = 512 + >>> group.pixel_samples = [2000, 1000, 2000] # individual value for each fibre in the group + >>> group.display_progress = False # control pipeline parameters through the group observer + >>> group.observe() # combined observation + >>> group.plot_spectra(item='MySpectralPipeline', in_photons=True) # plot the spectra + >>> group.plot_total_signal(item='MyMonoPipeline') # plot the total signals + >>> plt.show() + """ + _OBSERVER_TYPE = SpectroscopicFibreOptic + + @property + def acceptance_angle(self): + # The angle in degrees between the z axis and the cone surface which defines the fibres + # solid angle sampling area. + return [sight_line.acceptance_angle for sight_line in self._observers] + + @acceptance_angle.setter + def acceptance_angle(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for sight_line, v in zip(self._observers, value): + sight_line.acceptance_angle = v + else: + raise ValueError("The length of 'acceptance_angle' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for sight_line in self._observers: + sight_line.acceptance_angle = value + + @property + def radius(self): + # The radius of the fibre tip in metres. This radius defines a circular area at the fibre tip + # which will be sampled over. + return [sight_line.radius for sight_line in self._observers] + + @radius.setter + def radius(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for sight_line, v in zip(self._observers, value): + sight_line.radius = v + else: + raise ValueError("The length of 'radius' ({}) " + "mismatches the number of sight-lines ({}).".format(len(value), len(self._observers))) + else: + for sight_line in self._observers: + sight_line.radius = value + + +class SpectroscopicSightLineGroup(SpectroscopicObserver0DGroup): + """ + .. deprecated:: 1.4.0 + Use SightLineGroup based on Raysect's SightLine observer instead. + + A group of spectroscopic sight-lines under a single scene-graph node. + + A scene-graph object regrouping a series of 'SpectroscopicSightLine' + observers as a scene-graph parent. Allows combined observation and display + control simultaneously. + + :ivar list sight_lines: A list of lines of sight (SpectroscopicSightLine instances) + in this group. + + .. code-block:: pycon + + >>> from math import cos, sin, pi + >>> from matplotlib import pyplot as plt + >>> from raysect.optical import World + >>> from raysect.optical.observer import SpectralRadiancePipeline0D, RadiancePipeline0D + >>> from raysect.core.math import Point3D, Vector3D + >>> from cherab.tools.observers import SpectroscopicSightLine, SightLineGroup + >>> + >>> world = World() + ... + >>> group = SightLineGroup(parent=world) + >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, sin(pi/10)), name="SightLine 1")) + >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-1, 0, 0), name="SightLine 2")) + >>> group.add_sight_line(SpectroscopicSightLine(Point3D(3., 0, 0), Vector3D(-cos(pi/10), 0, -sin(pi/10)), name="SightLine 3")) + >>> group.connect_pipelines([(SpectralRadiancePipeline0D, 'MySpectralPipeline', None), + (RadiancePipeline0D, 'MyMonoPipeline', None)]) # add pipelines to all sight lines in the group + >>> group.spectral_bins = 512 # same value for all sight lines in the group + >>> group.pixel_samples = [2000, 1000, 2000] # individual value for each sight line in the group + >>> group.display_progress = False # control pipeline parameters through the group observer + >>> group.observe() # combined observation + >>> group.plot_spectra(item='MySpectralPipeline', in_photons=True) # plot the spectra + >>> group.plot_total_signal(item='MyMonoPipeline') # plot the total signals + >>> plt.show() + """ + _OBSERVER_TYPE = SpectroscopicSightLine + + @property + def sensitivity(self): + # A list of observer sensitivities. + return [observer.sensitivity for observer in self._observers] + + @sensitivity.setter + def names(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.sensitivity = v + else: + raise ValueError("The length of 'sensitivity' ({}) ".format(len(value)) + + "mismatches the number of observers ({}).".format(len(self._observers))) + else: + for observer in self._observers: + observer.sensitivity = value diff --git a/cherab/tools/observers/group/targettedpixel.py b/cherab/tools/observers/group/targettedpixel.py new file mode 100644 index 00000000..07d13e7f --- /dev/null +++ b/cherab/tools/observers/group/targettedpixel.py @@ -0,0 +1,116 @@ +# 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 TargettedPixel + +from .base import Observer0DGroup + + +class TargettedPixelGroup(Observer0DGroup): + """ + A group of targetted 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 + 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 targetted_path_prob: Probability of ray being casted at the target + """ + _OBSERVER_TYPE = TargettedPixel + + @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 targetted_path_prob(self): + return [pixel.targetted_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 + 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.targetted_path_prob = value diff --git a/cherab/tools/observers/spectroscopy/base.py b/cherab/tools/observers/spectroscopy/base.py index db552e2a..8186d002 100644 --- a/cherab/tools/observers/spectroscopy/base.py +++ b/cherab/tools/observers/spectroscopy/base.py @@ -25,6 +25,9 @@ class _SpectroscopicObserver0DBase: """ + .. deprecated:: 1.4.0 + Use Raysect's observer classes instead. + A base class for spectroscopic 0D observers. The observer allows to control some of the pipeline properties diff --git a/cherab/tools/observers/spectroscopy/fibreoptic.py b/cherab/tools/observers/spectroscopy/fibreoptic.py index ea697a73..59911ff9 100644 --- a/cherab/tools/observers/spectroscopy/fibreoptic.py +++ b/cherab/tools/observers/spectroscopy/fibreoptic.py @@ -27,6 +27,9 @@ class SpectroscopicFibreOptic(FibreOptic, _SpectroscopicObserver0DBase): """ + .. deprecated:: 1.4.0 + Use Raysect's FibreOptic observer instead. + An optic fibre spectroscopic observer with non-zero acceptance angle. Rays are sampled over a circular area at the fibre tip and a conical solid angle diff --git a/cherab/tools/observers/spectroscopy/sightline.py b/cherab/tools/observers/spectroscopy/sightline.py index a8393d31..6b52847b 100644 --- a/cherab/tools/observers/spectroscopy/sightline.py +++ b/cherab/tools/observers/spectroscopy/sightline.py @@ -27,6 +27,9 @@ class SpectroscopicSightLine(SightLine, _SpectroscopicObserver0DBase): """ + .. deprecated:: 1.4.0 + Use Raysect's SightLine observer instead. + A simple line of sight observer. Multiple `SpectroscopicSightLine` observers can be combined into `SightLineGroup`. diff --git a/cherab/tools/tests/test_observer_groups.py b/cherab/tools/tests/test_observer_groups.py new file mode 100644 index 00000000..5f127a99 --- /dev/null +++ b/cherab/tools/tests/test_observer_groups.py @@ -0,0 +1,362 @@ +import unittest + +from raysect.core.workflow import RenderEngine +from raysect.optical.observer import Observer0D, SightLine, FibreOptic, Pixel, TargettedPixel, PowerPipeline0D, SpectralPowerPipeline0D +from raysect.primitive import Sphere + +from cherab.tools.observers.group.base import Observer0DGroup +from cherab.tools.observers.group import SightLineGroup, FibreOpticGroup, PixelGroup, TargettedPixelGroup +from cherab.tools.raytransfer import pipelines + + +class Observer0DGroupTestCase(unittest.TestCase): + _GROUP_CLASS = Observer0DGroup + _NUM = 3 + + def setUp(self): + ObserverClass = self._GROUP_CLASS._OBSERVER_TYPE + self.observers = [ObserverClass(pipelines=[PowerPipeline0D()]) for _ in range(self._NUM)] + + def test_get_item(self): + """Tests all inputs for the __get_item__ method""" + group = self._GROUP_CLASS(observers=self.observers) + names = ['zero', 'one', 'two'] + group.names = names + + 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]) + + with self.assertRaises(IndexError): + group[len(group)] + + with self.assertRaises(TypeError): + group[1.2] + + with self.assertRaises(ValueError): + group['fail'] + + group.names = ['fail'] * len(group) + with self.assertRaises(ValueError): + group['fail'] + + def test_assignments(self): + """Test assignments of all supported attributes of Observer0DGroup""" + group = self._GROUP_CLASS() + group.observers = self.observers + + for grouped_observer, input_observer in zip(group.observers, self.observers): + self.assertIs(grouped_observer, input_observer, msg='Observers do not match') + + with self.assertRaises(ValueError): + group.observers = [Sphere()] + + with self.assertRaises(TypeError): + group.observers = Sphere() + + # names + names = ['zero', 'one', 'two'] + group.names = names + for grouped_observer, input_name in zip(group.observers, names): + self.assertEqual(grouped_observer.name, input_name, msg='Observer name do not match') + with self.assertRaises(ValueError): + group.names = ['fail'] + with self.assertRaises(TypeError): + group.names = 'fail' + + # pipelines + ppln_0 = PowerPipeline0D(name='pipeline zero, observer zero') + ppln_1 = PowerPipeline0D(name='pipeline one, observer one') + ppln_2 = PowerPipeline0D(name='pipeline two, observer two') + ppln_3 = PowerPipeline0D(name='pipeline three, observer two') + + pipelist = [[ppln_0], [ppln_1], [ppln_2, ppln_3]] + group.pipelines = pipelist + self.assertIs(group[0].pipelines[0], ppln_0, 'non matching pipeline') + self.assertIs(group[1].pipelines[0], ppln_1, 'non matching pipeline') + self.assertIs(group[2].pipelines[0], ppln_2, 'non matching pipeline') + self.assertIs(group[2].pipelines[1], ppln_3, 'non matching pipeline') + + with self.assertRaises(ValueError): + group.pipelines = [ppln_0] + + # render_engine + engine = RenderEngine() + group.render_engine = engine + for group_engine in group.render_engine: + self.assertIs(group_engine, engine) + + with self.assertRaises(TypeError): + group.render_engine = Sphere() + + engines = [RenderEngine() for _ in group.observers] + group.render_engine = engines + for group_engine, input_engine in zip(group.render_engine, engines): + self.assertIs(group_engine, input_engine) + + with self.assertRaises(TypeError): + group.render_engine = [RenderEngine() for _ in range(len(group) - 1)] + [Sphere()] + with self.assertRaises(ValueError): + group.render_engine = [RenderEngine() for _ in range(len(group) - 1)] + + # wavelengths + wvl = 500 + group.min_wavelength = wvl - 100 + group.max_wavelength = wvl + 100 + self.assertListEqual(group.min_wavelength, [wvl - 100] * len(group)) + self.assertListEqual(group.max_wavelength, [wvl + 100] * len(group)) + + min_wvls = [90 + 10*i for i in range(len(group))] + max_wvls = [100 + 10*i for i in range(len(group))] + group.min_wavelength = min_wvls + group.max_wavelength = max_wvls + self.assertListEqual(group.min_wavelength, min_wvls) + self.assertListEqual(group.max_wavelength, max_wvls) + + with self.assertRaises(ValueError): + group.max_wavelength = [100] * (len(group) - 1) + with self.assertRaises(ValueError): + group.min_wavelength = [90] * (len(group) - 1) + + # bins + bins = [200 + i*100 for i in range(len(group))] + group.spectral_bins = bins + self.assertListEqual(group.spectral_bins, bins) + + bins = 300 + group.spectral_bins = bins + for observer in group.observers: + self.assertEqual(observer.spectral_bins, bins) + + with self.assertRaises(ValueError): + group.spectral_bins = [1000] * (len(group) + 1) + + # rays + probs = [0.2 + i*0.1 for i in range(len(group))] + min_depths = [2 + i for i in range(len(group))] + max_depths = [5 + i for i in range(len(group))] + weights = [0.5 + i * 0.1 for i in range(len(group))] + group.ray_extinction_prob = probs + group.ray_extinction_min_depth = min_depths + group.ray_max_depth = max_depths + group.ray_important_path_weight = weights + self.assertListEqual(group.ray_extinction_prob, probs) + self.assertListEqual(group.ray_extinction_min_depth, min_depths) + self.assertListEqual(group.ray_max_depth, max_depths) + self.assertListEqual(group.ray_important_path_weight, weights) + + probs = 0.3 + min_depths = 3 + max_depths = 6 + weights = 0.7 + group.ray_extinction_prob = probs + group.ray_extinction_min_depth = min_depths + group.ray_max_depth = max_depths + group.ray_important_path_weight = weights + for observer in group.observers: + self.assertEqual(observer.ray_extinction_prob, probs) + self.assertEqual(observer.ray_extinction_min_depth, min_depths) + self.assertEqual(observer.ray_max_depth, max_depths) + self.assertEqual(observer.ray_important_path_weight, weights) + + with self.assertRaises(ValueError): + group.ray_extinction_prob = [0.5] * (len(group) + 1) + with self.assertRaises(ValueError): + group.ray_extinction_min_depth = [4] * (len(group) + 1) + with self.assertRaises(ValueError): + group.ray_max_depth = [8] * (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))] + group.pixel_samples = pixel_samples + group.samples_per_task = per_task + self.assertListEqual(group.pixel_samples, pixel_samples) + self.assertListEqual(group.samples_per_task, per_task) + + pixel_samples = 10000 + per_task = 30000 + group.pixel_samples = pixel_samples + group.samples_per_task = per_task + for observer in group.observers: + self.assertEqual(observer.pixel_samples, pixel_samples) + self.assertEqual(observer.samples_per_task, per_task) + + with self.assertRaises(ValueError): + group.pixel_samples = [5000] * (len(group) + 1) + with self.assertRaises(ValueError): + group.samples_per_task = [4000] * (len(group) + 1) + + def test_add_observer(self): + group = self._GROUP_CLASS() + for i in range(len(group)): + group.add_observer(observer=self.observers[i]) + self.assertIs(group.observers[i], self.observers[i], "Added observer is not the observer passed") + + def test_connect_pipelines(self): + group = self._GROUP_CLASS(observers=self.observers) + + ppln_classes = [PowerPipeline0D, SpectralPowerPipeline0D] + names = ['power', 'spectral'] + keywords = [ + dict(name=names[0]), + dict(name=names[1], display_progress=True), + ] + + with self.assertRaises(ValueError): + group.connect_pipelines(ppln_classes, keywords_list=[{}]) + + group.connect_pipelines(ppln_classes) + for pipelines in group.pipelines: + for i, pipeline in enumerate(pipelines): + self.assertIsInstance(pipeline, ppln_classes[i]) + self.assertIs(pipelines[1].display_progress, False) + + group.connect_pipelines(pipeline_classes=ppln_classes, keywords_list=keywords, suppress_display_progress=True) + for pipelines in group.pipelines: + for i, pipeline in enumerate(pipelines): + self.assertIsInstance(pipeline, ppln_classes[i]) + self.assertEqual(pipeline.name, names[i]) + self.assertIs(pipelines[1].display_progress, False) + + group.connect_pipelines(pipeline_classes=ppln_classes, keywords_list=keywords, suppress_display_progress=False) + for pipelines in group.pipelines: + for i, pipeline in enumerate(pipelines): + self.assertIsInstance(pipeline, ppln_classes[i]) + self.assertEqual(pipeline.name, names[i]) + self.assertIs(pipelines[1].display_progress, True) + + keywords2 = [ + dict(name=names[0]), + dict(name=names[1], display_progress=False), + ] + group.connect_pipelines(pipeline_classes=ppln_classes, keywords_list=keywords2, suppress_display_progress=False) + for pipelines in group.pipelines: + for i, pipeline in enumerate(pipelines): + self.assertIsInstance(pipeline, ppln_classes[i]) + self.assertEqual(pipeline.name, names[i]) + self.assertIs(pipelines[1].display_progress, False) + + +class SightLineGroupTestCase(Observer0DGroupTestCase): + _GROUP_CLASS = SightLineGroup + + def test_sensitivity(self): + sensitivities = [0.9, 0.8, 0.7] + + group = SightLineGroup(observers=self.observers) + group.sensitivity = sensitivities + self.assertListEqual(group.sensitivity, sensitivities) + + group.sensitivity = 1 + for sightline in group.observers: + self.assertEqual(sightline.sensitivity, 1) + + with self.assertRaises(ValueError): + group.sensitivity = [1] * (len(group) + 1) + + +class FibreOpticTestCase(Observer0DGroupTestCase): + _GROUP_CLASS = FibreOpticGroup + + def test_radius(self): + group = self._GROUP_CLASS(observers=self.observers) + + radius = [1e-2 for _ in group.observers] + group.radius = radius + self.assertListEqual(group.radius, radius) + + radius = 1e-3 + group.radius = radius + for group_radius in group.radius: + self.assertEqual(group_radius, radius) + + with self.assertRaises(ValueError): + group.radius = [1e-1 for _ in range(len(group) + 1)] + + def test_acceptance_angle(self): + group = self._GROUP_CLASS(observers=self.observers) + + acceptance_angle = [10] * len(group) + group.acceptance_angle = acceptance_angle + self.assertListEqual(group.acceptance_angle, acceptance_angle) + + acceptance_angle = 11 + group.acceptance_angle = acceptance_angle + for group_acceptance_angle in group.acceptance_angle: + self.assertEqual(group_acceptance_angle, acceptance_angle) + + with self.assertRaises(ValueError): + group.acceptance_angle = [12] * (len(group) + 1) + + +class PixelGroupTestCase(Observer0DGroupTestCase): + _GROUP_CLASS = PixelGroup + + def test_widths(self): + group = self._GROUP_CLASS(observers=self.observers) + + x_width = [1e-2 for _ in group.observers] + y_width = [1e-2 for _ in group.observers] + group.x_width = x_width + group.y_width = y_width + self.assertListEqual(group.x_width, x_width) + self.assertListEqual(group.y_width, y_width) + + x_width = 1e-3 + y_width = 1e-3 + group.x_width = x_width + group.y_width = y_width + for observer in group.observers: + self.assertEqual(observer.x_width, x_width) + self.assertEqual(observer.y_width, y_width) + + with self.assertRaises(ValueError): + group.x_width = [1e-1] * (len(group) + 1) + with self.assertRaises(ValueError): + group.y_width = [1e-1] * (len(group) + 1) + + +class TargettedPixelGroupTestCase(PixelGroupTestCase): + _GROUP_CLASS = TargettedPixelGroup + + def setUp(self): + self.observers = [TargettedPixel(targets=[Sphere()], pipelines=[PowerPipeline0D()]) for _ in range(self._NUM)] + + def test_targets(self): + group = self._GROUP_CLASS(observers=self.observers) + + targets = [Sphere(), Sphere()] + group.targets = targets + for observer in group: + self.assertEqual(len(targets), len(observer.targets)) + for observer_target, input_target in zip(observer.targets, targets): + self.assertIs(observer_target, input_target) + + targets = [[Sphere()] for _ in group.observers] + group.targets = targets + for observer, input_targets in zip(group.observers, targets): + for group_target, input_target in zip(observer.targets, input_targets): + self.assertIs(group_target, input_target) + + targets = [[Sphere()] for _ in range(len(group) + 1)] + with self.assertRaises(ValueError): + group.targets = targets + + # targetted path prob + prob = [0.9, 0.95, 1] + group.targetted_path_prob = prob + self.assertListEqual(group.targetted_path_prob, prob) + + 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) + + with self.assertRaises(ValueError): + group.targetted_path_prob = [0.7] * (len(group) + 1) diff --git a/demos/observers/groups.py b/demos/observers/groups.py new file mode 100644 index 00000000..5db127e1 --- /dev/null +++ b/demos/observers/groups.py @@ -0,0 +1,90 @@ +# Copyright 2014-2021 United Kingdom Atomic Energy Authority +# +# 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. + +""" +Simple group observer use demonstartion. Includes also example use of plotting routines. +""" + +import numpy as np +import matplotlib.pyplot as plt + +from raysect.core.math import Point3D, Vector3D, rotate_z, rotate_basis, translate +from raysect.optical import World +from raysect.optical.observer import SpectralRadiancePipeline0D, FibreOptic + +from cherab.core.model import ExcitationLine, RecombinationLine +from cherab.core.atomic import Line, hydrogen +from cherab.openadas import OpenADAS +from cherab.generomak.machine import load_first_wall +from cherab.tools.observers import FibreOpticGroup +from cherab.tools.observers.group.plotting import plot_group_spectra, plot_group_total +from cherab.generomak.plasma import get_edge_plasma + +############################################################################### +# Load the simulation and create a plasma object from it. +############################################################################### +plasma = get_edge_plasma() + +# Adding H-alpha excitation and recombination models +plasma.atomic_data = OpenADAS(permit_extrapolation=True) +h_alpha = Line(hydrogen, 0, (3, 2)) +plasma.models = [ExcitationLine(h_alpha), RecombinationLine(h_alpha)] + + +############################################################################### +# Observe the plasma with a group of optical fibres. +############################################################################### +world = World() +plasma.parent = world + +# Load the generomak first wall. +load_first_wall(world) + +# Create a group of optical fibres observing the divertor. +group = FibreOpticGroup(parent=world, name='Divertor Fibre Optic Array') +group.transform = rotate_z(22.5) +origin = Point3D(2.3, 0, 1.25) +angles = [-63.8, -66.5, -69.2, -71.9, -74.6] +direction_r = -np.cos(np.deg2rad(angles)) +direction_z = np.sin(np.deg2rad(angles)) +for i in range(len(angles)): + trans = translate(*origin) + rot = rotate_basis(Vector3D(direction_r[i], 0, direction_z[i]), Vector3D(0, 1, 0)) + fibre = FibreOptic(name='{}'.format(i + 1), transform=trans*rot) + group.add_observer(fibre) +group.connect_pipelines([SpectralRadiancePipeline0D], [{'name': 'SpectralRadiance'}]) + +# Set observer parameters for all observers in group +group.acceptance_angle = 1.4 +group.radius = 0.001 +group.pixel_samples = 5000 +group.min_wavelength = 655.5 +group.max_wavelength = 656.9 +group.spectral_bins = 256 + +# Observe. +print('Observing plasma...') +group.observe() + +############################################################################### +# Plot results using the plotting functions for groups +############################################################################### + +plt.ion() +plot_group_spectra(group, item='SpectralRadiance', in_photons=True) +plot_group_total(group, item='SpectralRadiance') +plt.ioff() +plt.show() diff --git a/docs/source/tools/observers.rst b/docs/source/tools/observers.rst index 5946cbac..e9a3a7e8 100644 --- a/docs/source/tools/observers.rst +++ b/docs/source/tools/observers.rst @@ -88,6 +88,9 @@ for the :class:`BolometerFoil`. Spectroscopic lines of sight ---------------------------- +.. deprecated:: 1.4.0 + Use Raysect's observer classes instead + Spectroscopic line of sight allows to control main parameters of the pipeline without accessing the pipeline directly. Multiple spectroscopic line of sight can be combined into a group. @@ -106,10 +109,11 @@ combined into a group. Group observers --------------- -Group observer is a collection of observers of the same type, for example an array of line of -sight. The parameters of individual observers in a group may differ. -Group observer allows combined observation, namely, calling the observe function for a group -leads to a sequential call of this function for each observer in the group. +Group observer is a collection of observers of the same type. All Observer0D classes +defined in Raysect are supoorted. The parameters of individual observers in a group +may differ. Group observer allows combined observation, namely, calling the observe +function for a group leads to a sequential call of this function for each observer +in the group. .. autoclass:: cherab.tools.observers.group.base.Observer0DGroup :members: @@ -119,3 +123,26 @@ leads to a sequential call of this function for each observer in the group. .. autoclass:: cherab.tools.observers.group.FibreOpticGroup :members: + +.. autoclass:: cherab.tools.observers.group.PixelGroup + :members: + +.. autoclass:: cherab.tools.observers.group.TargettedPixelGroup + :members: + +Spectroscopic Groups +^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 1.4.0 + Use groups based on Raysect's observer classes instead + +These groups take control of spectroscopic lines of sight observers. They support +direction and origin positioning and contain methods for plotting the power and +spectrum. Originally, these were called group observers and did not include the +Spectroscopic prefix in class name. + +.. autoclass:: cherab.tools.observers.SpectroscopicSightLine + :members + +.. autoclass:: cherab.tools.observers.SpectroscopicFibreOptic + :members From f4d316b733bf780e7c76cfb84f80c62936159cf4 Mon Sep 17 00:00:00 2001 From: Vlad Neverov Date: Mon, 7 Mar 2022 12:58:41 +0300 Subject: [PATCH 22/59] Improve SartOpencl solver and OpenCL utilities (#359) * Add verbose parameter to SartOpencl. Protect SartOpencl attributes. Update OpenCL utilities. Improve code style. * Update CHANGELOG.md. --- CHANGELOG.md | 11 +- .../tools/inversions/opencl/opencl_utils.py | 151 +++++++---- .../tools/inversions/opencl/sart_kernels.cl | 2 - .../inversions/opencl/sart_kernels_atomic.cl | 2 - cherab/tools/inversions/opencl/sart_opencl.py | 237 ++++++++++-------- 5 files changed, 230 insertions(+), 173 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbe42093..906c09e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,19 @@ Release 1.4.0 (TBD) ------------------- API changes: -* Spectroscopic observers and their groups are deprecated and replaced by groups based on Raysect's 0D observers (#332) +* Spectroscopic observers and their groups are deprecated and replaced by groups based on Raysect's 0D observers. (#332) New: * Make f_profile (current flux) a read-only attribute of EFITEquilibrium. (#355) -* Add group observer class for each of Raysect's 0D observers (#332) -* Add a demo for observer group handling and plotting +* Add group observer class for each of Raysect's 0D observers. (#332) +* Add a demo for observer group handling and plotting. +* Add verbose parameter to SartOpencl solver (default is False). (#358) Bug Fixes: ---------- -* Fixed generomak plasma edge data paths +* Fixed generomak plasma edge data paths. +* Fix and improve OpenCL utility functions. (#358) + Release 1.3.0 (8 Dec 2021) -------------------------- diff --git a/cherab/tools/inversions/opencl/opencl_utils.py b/cherab/tools/inversions/opencl/opencl_utils.py index ae8122ab..51747a93 100644 --- a/cherab/tools/inversions/opencl/opencl_utils.py +++ b/cherab/tools/inversions/opencl/opencl_utils.py @@ -17,10 +17,7 @@ # # See the Licence for the specific language governing permissions and limitations # under the Licence. -# -# The following code is created by Vladislav Neverov (NRC "Kurchatov Institute") for Cherab Spectroscopy Modelling Framework -from __future__ import print_function import warnings try: import pyopencl as cl @@ -32,7 +29,7 @@ def get_flops(device, verbose=False): """ - Returns the theoretical peak performance of specified OpenCL-compatible GPU or accelerator. + Returns the theoretical peak performance of specified OpenCL-compatible GPU or ACCELERATOR. Currently supports only Nvidia, AMD, Intel or Mali GPUs. :param pyopencl.Device device: OpenCL device. @@ -40,16 +37,23 @@ def get_flops(device, verbose=False): :return: Theoretical peak performance in GFLOPs. """ + if not _has_pyopencl: raise RuntimeError("The pyopencl module is required to run get_flops() function.") + + device_type = device.get_info(cl.device_info.TYPE) + if not (device_type & (cl.device_type.GPU | cl.device_type.ACCELERATOR)): + raise ValueError("Unsupported device type: {}.".format(cl.device_type.to_string(device_type))) + comp_units = device.get_info(cl.device_info.MAX_COMPUTE_UNITS) gpu_clock = device.get_info(cl.device_info.MAX_CLOCK_FREQUENCY) vendor = device.get_info(cl.device_info.VENDOR).lower() gflops = 0 + if "nvidia" in vendor: cc_maj = device.get_info(cl.device_info.COMPUTE_CAPABILITY_MAJOR_NV) cc_min = device.get_info(cl.device_info.COMPUTE_CAPABILITY_MINOR_NV) - alu_lanes = 128 + alu_lanes = 128 # default (as in 8.6) if cc_maj == 1: alu_lanes = 8 elif cc_maj == 2: @@ -62,81 +66,108 @@ def get_flops(device, verbose=False): alu_lanes = 64 if cc_min == 0 else 128 elif cc_maj == 7: alu_lanes = 64 + elif cc_maj == 8: + # For the devices with CUDA CC 8.6, the same ALUs can do both integer and float32 math. + # Theoreticaly this doubles the peak float32 performance as it double the number of ALU lanes with float32 support. + alu_lanes = 64 if cc_min == 0 else 128 gflops = comp_units * alu_lanes * 2 * gpu_clock / 1000. + elif "amd" in vendor or "advanced" in vendor: try: ww = device.get_info(cl.device_info.WAVEFRONT_WIDTH_AMD) except: ww = 64 gflops = comp_units * ww * 2 * gpu_clock / 1000. + elif "intel" in vendor: gflops = comp_units * 16 * gpu_clock / 1000. + elif "arm" in vendor: gflops = comp_units * 2 * 16 * gpu_clock / 1000. + else: - warnings.warn('Unsupported device vendor: %s. Unable to estimate theoretical peak performance.' % vendor) + warnings.warn('Unsupported device vendor: {}. Unable to estimate theoretical peak performance.'.format(vendor)) return 0 + if verbose: - print("Number of compute units: %d" % comp_units) - print("GPU maximum clock rate: %d MHz" % gpu_clock) - print("Estimated theoretical peak performance: %g GFLOPS" % gflops) + print("Number of compute units: {}".format(comp_units)) + print("GPU maximum clock rate: {} MHz".format(gpu_clock)) + print("Estimated theoretical peak performance: {} GFLOPS".format(gflops)) return gflops def get_best_gpu(platforms=None, device_type=None, verbose=False): """ - Finds the fastest (in terms of theoretical peak performance) GPU and/or accelerator available in specified OpenCL platforms + Finds the fastest (in terms of theoretical peak performance) GPU and/or accelerator + available in specified OpenCL platforms - :param list platforms: List of pyopencl.Platform instances. Default value: `platforms=None` (all available OpenCL platfroms). + :param list platforms: List of pyopencl.Platform instances. Default value: `platforms=None` + (all available OpenCL platfroms). :param pyopencl.device_type device_type: OpenCL device type (GPU, ACCELERATOR, or both). - Default value: `device_type=None` (GPU or accelerator). + Default value: `device_type=None` (GPU or accelerator). + If device_type is ALL or DEFAULT, all non-GPU/ACCELERATOR + devices will be skipped. :param bool verbose: Verbose output, defaults to `verbose=False`. - :return: The pyopencl.Device instance corresponding to the fastest GPU or accelerator available in the specified OpenCL platforms. + :return: The pyopencl.Device instance corresponding to the fastest GPU or accelerator available + in the specified OpenCL platforms. """ + if not _has_pyopencl: raise RuntimeError("The pyopencl module is required to run get_best_gpu() function.") + device_type = device_type or cl.device_type.GPU | cl.device_type.ACCELERATOR - if device_type == cl.device_type.DEFAULT: + if device_type == cl.device_type.DEFAULT or device_type == cl.device_type.ALL: device_type = cl.device_type.GPU | cl.device_type.ACCELERATOR - if not (cl.device_type.GPU | cl.device_type.ACCELERATOR) & device_type: - raise ValueError('This function works with GPU devices only') + + if not ((cl.device_type.GPU | cl.device_type.ACCELERATOR) & device_type): + raise ValueError('This function works with GPU devices only.') + + if verbose: + print("Selecting best GPU...") + if platforms is None: platforms = cl.get_platforms() - if verbose: - print("Selecting best GPU") device_best = None max_gflops = 0 for iplat, platform in enumerate(platforms): if verbose: - print("\nOpenCL platform %d: %s" % (iplat, platform.get_info(cl.platform_info.NAME))) + print("\nOpenCL platform {}: {}.".format(iplat, platform.get_info(cl.platform_info.NAME))) devices = platform.get_devices(device_type=device_type) for idev, device in enumerate(devices): if verbose: - print("\nDevice %d: %s %s" % (idev, device.get_info(cl.device_info.VENDOR), device.get_info(cl.device_info.NAME))) + print("\nDevice {}: {} {}.".format(idev, device.get_info(cl.device_info.VENDOR), + device.get_info(cl.device_info.NAME))) gflops = get_flops(device, verbose) if gflops > max_gflops: device_best = device + if device_best is None: - print("No supported GPUs found\n") + warnings.warn("No supported GPUs found.") return None - print("\nSelected OpenCL device: %s %s\n" % (device_best.get_info(cl.device_info.VENDOR), device_best.get_info(cl.device_info.NAME))) + + if verbose: + print("\nSelected OpenCL device: {} {}.\n".format(device_best.get_info(cl.device_info.VENDOR), + device_best.get_info(cl.device_info.NAME))) return device_best -def get_first_device(platforms=None, device_type=None): +def get_first_device(platforms=None, device_type=None, verbose=False): """ Returns the first OpenCL device of specified type available in specified OpenCL platforms :param list platforms: List of pyopencl.Platform instances. Default value: `platforms=None` (all available OpenCL platfroms). - :param pyopencl.device_type device_type: OpenCL device type (GPU, ACCELERATOR, or both). + :param pyopencl.device_type device_type: OpenCL device type (GPU, ACCELERATOR, CPU, ALL, etc.). Default value: `device_type=None` (GPU or accelerator). + :param bool verbose: Verbose output, defaults to `verbose=False`. :return: The pyopencl.Device instance corresponding to the first device available in the specified OpenCL platforms. """ + if not _has_pyopencl: raise RuntimeError("The pyopencl module is required to run get_first_device() function.") + device_type = device_type or cl.device_type.GPU | cl.device_type.ACCELERATOR if platforms is None: platforms = cl.get_platforms() @@ -144,56 +175,68 @@ def get_first_device(platforms=None, device_type=None): devices = platform.get_devices(device_type=device_type) if len(devices): device = devices[0] - print("Selected OpenCL device: %s %s\n" % (device.get_info(cl.device_info.VENDOR), device.get_info(cl.device_info.NAME))) + if verbose: + print("Selected OpenCL device: {} {}.\n".format(device.get_info(cl.device_info.VENDOR), + device.get_info(cl.device_info.NAME))) return device - print("\nThere are no devices of specified type\n") + + warnings.warn("Unable to find OpenCL devices of specified type.") return None def device_select(platfrom_id=None, device_id=None, device_type=None, verbose=False): """ - OpenCL device selector. Returns the most powerfull OpenCL device availabe if device_type is GPU or accelerator - or the first OpenCL device available if device_type is CPU. + OpenCL device selector. Returns the most powerfull OpenCL device availabe + if device_type is GPU or ACCELERATOR or the first OpenCL device available + if device_type is CPU, ALL or CUSTOM. :param int platfrom_id: OpenCL platform ID, defaults to `platfrom_id=None`. - :param int device_id: OpenCL device ID (in the selected OpenCL platform), defaults to `device_id=None`. + :param int device_id: OpenCL device ID (in the selected OpenCL platform), + defaults to `device_id=None`. :param pyopencl.device_type device_type: OpenCL device type (GPU, ACCELERATOR, etc.). - Default value: `device_type=None` (GPU or accelerator). + Default value: `device_type=None` (GPU | ACCELERATOR). :param bool verbose: Verbose output, defaults to `verbose=False`. :return: The pyopencl.Device instance corresponding to the selected OpenCL device. """ + if not _has_pyopencl: raise RuntimeError("The pyopencl module is required to run device_select() function.") + device_type = device_type or cl.device_type.GPU | cl.device_type.ACCELERATOR - platforms = cl.get_platforms() - n_platforms = len(platforms) if device_type == cl.device_type.DEFAULT: device_type = cl.device_type.GPU | cl.device_type.ACCELERATOR - non_gpu_device = not (cl.device_type.GPU | cl.device_type.ACCELERATOR) & device_type + + non_gpu_device = (device_type == cl.device_type.ALL) or not ((cl.device_type.GPU | cl.device_type.ACCELERATOR) & device_type) + + platforms = cl.get_platforms() + n_platforms = len(platforms) + + if platfrom_id is not None and platfrom_id >= n_platforms: + raise ValueError('The platform_id {} exceeds the number of OpenCL platforms ({}) found in the system.'.format(platfrom_id, n_platforms)) + if platfrom_id is None: if non_gpu_device: - return get_first_device(platforms, device_type) - return get_best_gpu(platforms, device_type, verbose) - if platfrom_id < n_platforms: - platform = platforms[platfrom_id] - devices = platform.get_devices(device_type=device_type) - n_devices = len(devices) - if device_id is None: - if non_gpu_device: - return get_first_device([platform], device_type) - return get_best_gpu([platform], device_type, verbose) - if device_id < n_devices: - device = devices[device_id] - print("Selected OpenCL device: %s %s" % (device.get_info(cl.device_info.VENDOR), device.get_info(cl.device_info.NAME))) - return device - warnings.warn('%s platform has %d devices of specified type\n' % (platform.get_info(cl.platform_info.NAME), n_platforms)) + return get_first_device(platforms, device_type, verbose) + # get_best_gpu() returns None for unsupported devices, if so return the first device + return get_best_gpu(platforms, device_type, verbose) or get_first_device(platforms, device_type, verbose) + + platform = platforms[platfrom_id] + devices = platform.get_devices(device_type=device_type) + n_devices = len(devices) + + if device_id is not None and device_id >= n_devices: + platform_name = platform.get_info(cl.platform_info.NAME) + raise ValueError('The device_id {} exceeds the number of devices ({}) of specified type in the {} platform.'.format(device_id, n_devices, platform_name)) + + if device_id is None: if non_gpu_device: - return get_first_device([platform], device_type) - return get_best_gpu([platform], device_type, verbose) - warnings.warn('System has only %d OpenCL platforms\n' % n_platforms) - if non_gpu_device: - return get_first_device(platforms, device_type) + return get_first_device([platform], device_type, verbose) + # get_best_gpu() returns None for unsupported devices, if so return the first device + return get_best_gpu([platform], device_type, verbose) or get_first_device([platform], device_type, verbose) - return get_best_gpu(platforms, device_type, verbose) + device = devices[device_id] + if verbose: + print("Selected OpenCL device: {} {}.".format(device.get_info(cl.device_info.VENDOR), device.get_info(cl.device_info.NAME))) + return device diff --git a/cherab/tools/inversions/opencl/sart_kernels.cl b/cherab/tools/inversions/opencl/sart_kernels.cl index c73b0e7d..63265b29 100644 --- a/cherab/tools/inversions/opencl/sart_kernels.cl +++ b/cherab/tools/inversions/opencl/sart_kernels.cl @@ -15,8 +15,6 @@ // // See the Licence for the specific language governing permissions and limitations // under the Licence. -// -// The following code is created by Vladislav Neverov (NRC "Kurchatov Institute") for Cherab Spectroscopy Modelling Framework #ifndef BLOCK_SIZE #define BLOCK_SIZE 256 diff --git a/cherab/tools/inversions/opencl/sart_kernels_atomic.cl b/cherab/tools/inversions/opencl/sart_kernels_atomic.cl index e9b6afc9..47307a15 100644 --- a/cherab/tools/inversions/opencl/sart_kernels_atomic.cl +++ b/cherab/tools/inversions/opencl/sart_kernels_atomic.cl @@ -15,8 +15,6 @@ // // See the Licence for the specific language governing permissions and limitations // under the Licence. -// -// The following code is created by Vladislav Neverov (NRC "Kurchatov Institute") for Cherab Spectroscopy Modelling Framework #ifndef BLOCK_SIZE #define BLOCK_SIZE 256 diff --git a/cherab/tools/inversions/opencl/sart_opencl.py b/cherab/tools/inversions/opencl/sart_opencl.py index afcf726d..4bd1d65f 100644 --- a/cherab/tools/inversions/opencl/sart_opencl.py +++ b/cherab/tools/inversions/opencl/sart_opencl.py @@ -17,10 +17,7 @@ # # See the Licence for the specific language governing permissions and limitations # under the Licence. -# -# The following code is created by Vladislav Neverov (NRC "Kurchatov Institute") for Cherab Spectroscopy Modelling Framework -from __future__ import print_function import os import numpy as np from timeit import default_timer as timer @@ -44,29 +41,37 @@ class SartOpencl: performed with single precision. :param np.ndarray geometry_matrix: The sensitivity matrix describing the coupling between - the detectors and the voxels. Must be an array with shape :math:`(N_d, N_s)`. + the detectors and the voxels. Must be an array + with shape :math:`(N_d, N_s)`. :param np.ndarray laplacian_matrix: The laplacian regularisation matrix of - shape :math:`(N_s, N_s)`. Default value: `laplacian_matrix=None`. + shape :math:`(N_s, N_s)`. + Default value: `laplacian_matrix=None`. :param pyopencl.Device device: OpenCL device which will be used for computations. - Default value: `device=None` (autoselect). + Default value: `device=None` (autoselect). :param int block_size: Number of GPU threads per block. Must be the power of 2. - For the best performance try from 256 to 1024 for Nvidia (use 1024 on high-end GPUs), - from 64 to 256 for AMD and from 16 to 64 for Intel GPUs. Default value: `block_size=256`. + For the best performance try from 256 to 1024 for Nvidia + (use 1024 on high-end GPUs), from 64 to 256 for AMD and from 16 to 64 + for Intel GPUs. Default value: `block_size=256`. :param bool copy_column_major: If True, the two copies of geometry matrix will be stored in - GPU memory. One in row-major order and the other one in column-major order. This - provides much better performance of the inversions but requires twice as much GPU memory. - Default value: `copy_column_major=True`. + GPU memory. One in row-major order and the other one in + column-major order. This provides much better performance + of the inversions but requires twice as much GPU memory. + Default value: `copy_column_major=True`. :param int block_size_row_maj: If `copy_column_major` is set to False, this parameter defines - the number of GPU threads per block in mat_vec_mult_row_maj() kernel used to calculate - y_hat. Must be lower than `block_size`. Default value: `block_size_row_maj=64` (optimal - value for Nvidia GPUs). + the number of GPU threads per block in mat_vec_mult_row_maj() + kernel used to calculate y_hat. Must be lower than + `block_size`. Default value: `block_size_row_maj=64` (optimal + value for Nvidia GPUs). :param bool use_atomic: If True, increases the number of thread blocks that can run in - parallel with the help of atomic operations (custom atomic add on floats). Set this - to False, if the atomic operations are running slow on your device (Nvidia GPUs before - Kepler, some AMD APUs, some Intel GPUs). Default value: `use_atomic=True`. + parallel with the help of atomic operations (custom atomic add on floats). + Set this to False, if the atomic operations are running slow on your device + (Nvidia GPUs before Kepler, some AMD APUs, some Intel GPUs). + Default value: `use_atomic=True`. :param int steps_per_thread: If `use_atomic` is set to True, this parameters defines the - maximum number of loop steps performed by the parallel threads in a single thread block. - Default value: `steps_per_thread=64` (optimal for Nvidia GPUs). + maximum number of loop steps performed by the parallel threads + in a single thread block. Default value: `steps_per_thread=64` + (optimal for Nvidia GPUs). + :param bool verbose: Verbose output, defaults to `verbose=False`. .. code-block:: pycon @@ -79,92 +84,99 @@ class SartOpencl: """ def __init__(self, geometry_matrix, laplacian_matrix=None, device=None, block_size=256, copy_column_major=True, block_size_row_maj=64, - use_atomic=True, steps_per_thread=64): + use_atomic=True, steps_per_thread=64, verbose=False): if not _has_pyopencl: raise RuntimeError("The pyopencl module is required to use the SartOpencl() inversion class.") + + self._verbose = verbose + if geometry_matrix.dtype != np.float32: # converting geometry_matrix to float32 if needed geometry_matrix = geometry_matrix.astype(np.float32) - self.m_detectors, self.n_sources = geometry_matrix.shape + + self._m_detectors, self._n_sources = geometry_matrix.shape cell_ray_densities = geometry_matrix.sum(0) ray_lengths = geometry_matrix.sum(1) - device = device or device_select() - self.use_atomic = use_atomic + device = device or device_select(verbose=verbose) + self._use_atomic = use_atomic steps_per_thread = min(block_size, steps_per_thread) steps_per_thread_row_maj = min(block_size_row_maj, steps_per_thread) # creating OpenCL context - self.cl_context = cl.Context([device]) + self._cl_context = cl.Context([device]) # reading and compiling OpenCL kernels kernels_filename = 'sart_kernels_atomic.cl' if use_atomic else 'sart_kernels.cl' kernel_source_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), kernels_filename) with open(kernel_source_file) as f_kernel: kernel_source = f_kernel.read() - compile_options = ['-DBLOCK_SIZE=%d' % block_size, '-DSTEPS_PER_THREAD=%d' % steps_per_thread, - '-DSTEPS_PER_THREAD_ROW_MAJ=%d' % steps_per_thread_row_maj, - '-DBLOCK_SIZE_ROW_MAJ=%d' % block_size_row_maj, '-cl-fast-relaxed-math'] - self.cl_prog = cl.Program(self.cl_context, kernel_source).build(options=compile_options) + compile_options = ['-DBLOCK_SIZE={}'.format(block_size), '-DSTEPS_PER_THREAD={}'.format(steps_per_thread), + '-DSTEPS_PER_THREAD_ROW_MAJ={}'.format(steps_per_thread_row_maj), + '-DBLOCK_SIZE_ROW_MAJ={}'.format(block_size_row_maj), '-cl-fast-relaxed-math'] + self._cl_prog = cl.Program(self._cl_context, kernel_source).build(options=compile_options) # creating buffers in device memory mf = cl.mem_flags - self.geometry_matrix_device = cl.Buffer(self.cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=geometry_matrix) + + self._geometry_matrix_device = cl.Buffer(self._cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=geometry_matrix) if copy_column_major: geometry_matric_col_maj = geometry_matrix.flatten(order='F') - self.geometry_matric_col_maj_device = cl.Buffer(self.cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=geometry_matric_col_maj) + self._geometry_matric_col_maj_device = cl.Buffer(self._cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=geometry_matric_col_maj) else: - self.geometry_matric_col_maj_device = None + self._geometry_matric_col_maj_device = None + if laplacian_matrix is not None: laplacian_matrix = laplacian_matrix.flatten(order='F').astype(np.float32) - self.laplacian_matrix_device = cl.Buffer(self.cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=laplacian_matrix) + self._laplacian_matrix_device = cl.Buffer(self._cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=laplacian_matrix) else: - self.laplacian_matrix_device = None - self.cell_ray_densities_device = cl.Buffer(self.cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=cell_ray_densities) - self.ray_lengths_device = cl.Buffer(self.cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=ray_lengths) - grad_penalty = np.zeros(self.n_sources, dtype=np.float32) - self.grad_penalty_device = cl.Buffer(self.cl_context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=grad_penalty) - self.solution_device = cl.Buffer(self.cl_context, mf.READ_WRITE, cell_ray_densities.nbytes) - self.detectors_device = cl.Buffer(self.cl_context, mf.READ_ONLY, ray_lengths.nbytes) - self.y_hat_device = cl.Buffer(self.cl_context, mf.READ_WRITE, ray_lengths.nbytes) + self._laplacian_matrix_device = None + + self._cell_ray_densities_device = cl.Buffer(self._cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=cell_ray_densities) + self._ray_lengths_device = cl.Buffer(self._cl_context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=ray_lengths) + grad_penalty = np.zeros(self._n_sources, dtype=np.float32) + self._grad_penalty_device = cl.Buffer(self._cl_context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=grad_penalty) + self._solution_device = cl.Buffer(self._cl_context, mf.READ_WRITE, cell_ray_densities.nbytes) + self._detectors_device = cl.Buffer(self._cl_context, mf.READ_ONLY, ray_lengths.nbytes) + self._y_hat_device = cl.Buffer(self._cl_context, mf.READ_WRITE, ray_lengths.nbytes) # calculating global and local work sizes - nrem = self.n_sources % block_size - gws_sources_x = self.n_sources + bool(nrem) * (block_size - nrem) - mrem = self.m_detectors % block_size - gws_detectors_x = self.m_detectors + bool(mrem) * (block_size - mrem) - mrem_rm = self.m_detectors % block_size_row_maj - gws_detectors_row_maj_x = self.m_detectors + bool(mrem_rm) * (block_size - mrem_rm) + nrem = self._n_sources % block_size + gws_sources_x = self._n_sources + bool(nrem) * (block_size - nrem) + mrem = self._m_detectors % block_size + gws_detectors_x = self._m_detectors + bool(mrem) * (block_size - mrem) + mrem_rm = self._m_detectors % block_size_row_maj + gws_detectors_row_maj_x = self._m_detectors + bool(mrem_rm) * (block_size - mrem_rm) if use_atomic: - gws_sources_row_maj_y = self.n_sources // steps_per_thread_row_maj + bool(self.n_sources % steps_per_thread_row_maj) - gws_sources_y = self.n_sources // steps_per_thread + bool(self.n_sources % steps_per_thread) - gws_detectors_y = self.m_detectors // steps_per_thread + bool(self.m_detectors % steps_per_thread) + gws_sources_row_maj_y = self._n_sources // steps_per_thread_row_maj + bool(self._n_sources % steps_per_thread_row_maj) + gws_sources_y = self._n_sources // steps_per_thread + bool(self._n_sources % steps_per_thread) + gws_detectors_y = self._m_detectors // steps_per_thread + bool(self._m_detectors % steps_per_thread) else: gws_sources_row_maj_y = gws_sources_y = gws_detectors_y = 1 - self.global_work_size = {} - self.local_work_size = {'default': (block_size, 1)} - self.global_work_size['trivial_sources'] = (gws_sources_x, 1) - self.global_work_size['trivial_detectors'] = (gws_detectors_x, 1) - self.global_work_size['iter'] = (gws_sources_x, gws_detectors_y) + self._global_work_size = {} + self._local_work_size = {'default': (block_size, 1)} + self._global_work_size['trivial_sources'] = (gws_sources_x, 1) + self._global_work_size['trivial_detectors'] = (gws_detectors_x, 1) + self._global_work_size['iter'] = (gws_sources_x, gws_detectors_y) if copy_column_major: - self.local_work_size['mult'] = self.local_work_size['default'] - self.global_work_size['mult'] = (gws_detectors_x, gws_sources_y) + self._local_work_size['mult'] = self._local_work_size['default'] + self._global_work_size['mult'] = (gws_detectors_x, gws_sources_y) else: - self.local_work_size['mult'] = (block_size_row_maj, 1) - self.global_work_size['mult'] = (gws_detectors_row_maj_x, gws_sources_row_maj_y) - self.global_work_size['grad'] = (gws_sources_x, gws_sources_y) + self._local_work_size['mult'] = (block_size_row_maj, 1) + self._global_work_size['mult'] = (gws_detectors_row_maj_x, gws_sources_row_maj_y) + self._global_work_size['grad'] = (gws_sources_x, gws_sources_y) def clean(self): """ Releases GPU buffers""" - self.geometry_matrix_device.release() - if self.geometry_matric_col_maj_device is not None: - self.geometry_matric_col_maj_device.release() - if self.laplacian_matrix_device is not None: - self.laplacian_matrix_device.release() - self.cell_ray_densities_device.release() - self.ray_lengths_device.release() - self.solution_device.release() - self.grad_penalty_device.release() - self.detectors_device.release() - self.y_hat_device.release() + self._geometry_matrix_device.release() + if self._geometry_matric_col_maj_device is not None: + self._geometry_matric_col_maj_device.release() + if self._laplacian_matrix_device is not None: + self._laplacian_matrix_device.release() + self._cell_ray_densities_device.release() + self._ray_lengths_device.release() + self._solution_device.release() + self._grad_penalty_device.release() + self._detectors_device.release() + self._y_hat_device.release() def __enter__(self): return self @@ -179,14 +191,14 @@ def update_laplacian_matrix(self, laplacian_matrix): :param np.ndarray laplacian_matrix: The laplacian regularisation matrix of shape :math:`(N_s, N_s)`. """ - if self.laplacian_matrix_device is not None: + if self._laplacian_matrix_device is not None: laplacian_matrix = laplacian_matrix.flatten(order='F').astype(np.float32) - queue = cl.CommandQueue(self.cl_context) - cl.enqueue_copy(queue, self.laplacian_matrix_device, laplacian_matrix) + queue = cl.CommandQueue(self._cl_context) + cl.enqueue_copy(queue, self._laplacian_matrix_device, laplacian_matrix) def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, relaxation=1.0, beta_laplace=0.01, conv_tol=1.e-4, time_limit=None): - """ + r""" Performs the inversion for a given measurement vector. :param np.ndarray measurement_vector: The measured power/radiance vector with @@ -211,12 +223,13 @@ def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, r shape :math:`(N_s)`, and the list of convergence values achieved after each iteration step. """ + time_start = timer() time_limit = time_limit or 1.e7 if initial_guess is None: - solution = np.zeros(self.n_sources, dtype=np.float32) + 1 / np.e + solution = np.zeros(self._n_sources, dtype=np.float32) + 1 / np.e elif isinstance(initial_guess, (float, int)): - solution = np.zeros(self.n_sources, dtype=np.float32) + initial_guess + solution = np.zeros(self._n_sources, dtype=np.float32) + initial_guess else: solution = initial_guess.astype(np.float32) # making a copy even if initial_guess is in float32 already measurement_max = measurement_vector.max() @@ -224,11 +237,11 @@ def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, r measurement_vector = (measurement_vector / measurement_max).astype(np.float32) measurement_squared = np.dot(measurement_vector, measurement_vector) y_hat_vector = np.empty_like(measurement_vector) # host y_hat - queue = cl.CommandQueue(self.cl_context) + queue = cl.CommandQueue(self._cl_context) # copying initial guess and measurement_vector to device - cl.enqueue_copy(queue, self.solution_device, solution) - cl.enqueue_copy(queue, self.detectors_device, measurement_vector) + cl.enqueue_copy(queue, self._solution_device, solution) + cl.enqueue_copy(queue, self._detectors_device, measurement_vector) # calculating y_hat on device self._calc_y_hat(queue) @@ -238,7 +251,6 @@ def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, r conv_tol = np.float32(conv_tol) success = False for k in range(max_iterations): - # print('Iteration: %d' % k) # making one iteration on device self._make_iteration(queue, relaxation, beta_laplace) @@ -246,7 +258,7 @@ def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, r self._calc_y_hat(queue) # copying y_hat to host - cl.enqueue_copy(queue, y_hat_vector, self.y_hat_device) + cl.enqueue_copy(queue, y_hat_vector, self._y_hat_device) # calculating convergence y_hat_squared = np.dot(y_hat_vector, y_hat_vector) @@ -256,48 +268,51 @@ def __call__(self, measurement_vector, initial_guess=None, max_iterations=250, r # checking conditions if k > 0 and np.abs(convergence[k] - convergence[k - 1]) < conv_tol: success = True - print('Convergence limit is reached in %.4f s with %d iterations' % (time_passed, k + 1)) + if self._verbose: + print('Convergence limit is reached in {:.4f} s with {} iterations.'.format(time_passed, k + 1)) break if time_passed > time_limit: - print('Time limit is exceeded') + # if no success, the user must be informed even if verbose is False + print('The time limit has been exceeded, but the convergence limit has not been reached.') break if (not success) and k == max_iterations - 1: - print('Maximum number of iterations is reached. Time passed: %.4f s' % time_passed) + # if no success, the user must be informed even if verbose is False + print('Maximum number of iterations is reached. Time passed: {:.4f} s.'.format(time_passed)) # copying solution to host - cl.enqueue_copy(queue, solution, self.solution_device) + cl.enqueue_copy(queue, solution, self._solution_device) return solution * measurement_max, convergence def _calc_y_hat(self, queue): - if self.use_atomic: - self.cl_prog.zero_all(queue, self.global_work_size['trivial_detectors'], self.local_work_size['default'], - self.y_hat_device, np.uint32(self.m_detectors)) - if self.geometry_matric_col_maj_device is None: - self.cl_prog.mat_vec_mult_row_major(queue, self.global_work_size['mult'], self.local_work_size['mult'], - self.geometry_matrix_device, self.solution_device, self.y_hat_device, - np.uint32(self.m_detectors), np.uint32(self.n_sources)) + if self._use_atomic: + self._cl_prog.zero_all(queue, self._global_work_size['trivial_detectors'], self._local_work_size['default'], + self._y_hat_device, np.uint32(self._m_detectors)) + if self._geometry_matric_col_maj_device is None: + self._cl_prog.mat_vec_mult_row_major(queue, self._global_work_size['mult'], self._local_work_size['mult'], + self._geometry_matrix_device, self._solution_device, self._y_hat_device, + np.uint32(self._m_detectors), np.uint32(self._n_sources)) else: - self.cl_prog.mat_vec_mult_col_major(queue, self.global_work_size['mult'], self.local_work_size['mult'], - self.geometry_matric_col_maj_device, self.solution_device, self.y_hat_device, - np.uint32(self.m_detectors), np.uint32(self.n_sources)) + self._cl_prog.mat_vec_mult_col_major(queue, self._global_work_size['mult'], self._local_work_size['mult'], + self._geometry_matric_col_maj_device, self._solution_device, self._y_hat_device, + np.uint32(self._m_detectors), np.uint32(self._n_sources)) def _make_iteration(self, queue, relaxation, beta_laplace): - if self.laplacian_matrix_device is not None: - if self.use_atomic: - self.cl_prog.zero_all(queue, self.global_work_size['trivial_sources'], self.local_work_size['default'], - self.grad_penalty_device, np.uint32(self.n_sources)) - self.cl_prog.mat_vec_mult_col_major(queue, self.global_work_size['grad'], self.local_work_size['default'], - self.laplacian_matrix_device, self.solution_device, self.grad_penalty_device, - np.uint32(self.n_sources), np.uint32(self.n_sources)) - self.cl_prog.vec_scalar_mult(queue, self.global_work_size['trivial_sources'], self.local_work_size['default'], - self.grad_penalty_device, np.float32(beta_laplace), np.uint32(self.n_sources)) + if self._laplacian_matrix_device is not None: + if self._use_atomic: + self._cl_prog.zero_all(queue, self._global_work_size['trivial_sources'], self._local_work_size['default'], + self._grad_penalty_device, np.uint32(self._n_sources)) + self._cl_prog.mat_vec_mult_col_major(queue, self._global_work_size['grad'], self._local_work_size['default'], + self._laplacian_matrix_device, self._solution_device, self._grad_penalty_device, + np.uint32(self._n_sources), np.uint32(self._n_sources)) + self._cl_prog.vec_scalar_mult(queue, self._global_work_size['trivial_sources'], self._local_work_size['default'], + self._grad_penalty_device, np.float32(beta_laplace), np.uint32(self._n_sources)) # grad_penalty is just an all-zero array if laplacian_matrix is not provided on initialisation - self.cl_prog.sart_iteration(queue, self.global_work_size['iter'], self.local_work_size['default'], - self.geometry_matrix_device, self.cell_ray_densities_device, self.ray_lengths_device, - self.y_hat_device, self.detectors_device, self.solution_device, self.grad_penalty_device, - np.float32(relaxation), np.uint32(self.n_sources), np.uint32(self.m_detectors)) - if self.use_atomic: - self.cl_prog.zero_negative(queue, self.global_work_size['trivial_sources'], self.local_work_size['default'], - self.solution_device, np.uint32(self.n_sources)) + self._cl_prog.sart_iteration(queue, self._global_work_size['iter'], self._local_work_size['default'], + self._geometry_matrix_device, self._cell_ray_densities_device, self._ray_lengths_device, + self._y_hat_device, self._detectors_device, self._solution_device, self._grad_penalty_device, + np.float32(relaxation), np.uint32(self._n_sources), np.uint32(self._m_detectors)) + if self._use_atomic: + self._cl_prog.zero_negative(queue, self._global_work_size['trivial_sources'], self._local_work_size['default'], + self._solution_device, np.uint32(self._n_sources)) From bbe65d56c682d01c25b116b64b7fe9afc400022e Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 8 Mar 2022 23:26:14 +0300 Subject: [PATCH 23/59] Add gaunt factor as optional parameter of the Bremsstrahlung emission model. --- cherab/core/model/plasma/bremsstrahlung.pxd | 1 + cherab/core/model/plasma/bremsstrahlung.pyx | 36 +++++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pxd b/cherab/core/model/plasma/bremsstrahlung.pxd index f29cd6b0..f8251b28 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pxd +++ b/cherab/core/model/plasma/bremsstrahlung.pxd @@ -27,6 +27,7 @@ cdef class Bremsstrahlung(PlasmaModel): cdef: FreeFreeGauntFactor _gaunt_factor + bint _user_provided_gaunt_factor ndarray _species_charge, _species_density double[::1] _species_density_mv, _species_charge_mv diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 1384d647..ba4b9cf6 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -21,6 +21,7 @@ import numpy as np from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core cimport Plasma, AtomicData +from cherab.core.atomic cimport FreeFreeGauntFactor from cherab.core.species cimport Species from cherab.core.utility.constants cimport RECIP_4_PI, ELEMENTARY_CHARGE, SPEED_OF_LIGHT, PLANCK_CONSTANT from libc.math cimport sqrt, log, exp @@ -44,15 +45,34 @@ cdef class Bremsstrahlung(PlasmaModel): \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} \\sum_{i} \\left(g_{ff}(Z_i, T_e, \\lambda) n_i Z_i^2\\right) n_e T_e^{1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, where the emission :math:`\\epsilon (\\lambda)` is in units of radiance (ph/s/sr/m^3/nm). + + :ivar Plasma plasma: The plasma to which this emission model is attached. Default is None. + :ivar AtomicData atomic_data: The atomic data provider for this model. Default is None. + :ivar FreeFreeGauntFactor gaunt_factor: Free-free Gaunt factor as a function of Z, Te and + wavelength. If not provided, + the `atomic_data` is used. """ - def __init__(self, Plasma plasma=None, AtomicData atomic_data=None): + def __init__(self, Plasma plasma=None, AtomicData atomic_data=None, FreeFreeGauntFactor gaunt_factor=None): super().__init__(plasma, atomic_data) + self.gaunt_factor = gaunt_factor + # ensure that cache is initialised self._change() + @property + def gaunt_factor(self): + + return self._gaunt_factor + + @gaunt_factor.setter + def gaunt_factor(self, value): + + self._gaunt_factor = value + self._user_provided_gaunt_factor = True if value else False + def __repr__(self): return '' @@ -70,7 +90,7 @@ cdef class Bremsstrahlung(PlasmaModel): int i # cache data on first run - if self._gaunt_factor is None: + if self._species_charge is None: self._populate_cache() ne = self._plasma.get_electron_distribution().density(point.x, point.y, point.z) @@ -139,11 +159,12 @@ cdef class Bremsstrahlung(PlasmaModel): if self._plasma is None: raise RuntimeError("The emission model is not connected to a plasma object.") - if self._atomic_data is None: - raise RuntimeError("The emission model is not connected to an atomic data source.") + if self._gaunt_factor is None: + if self._atomic_data is None: + raise RuntimeError("The emission model is not connected to an atomic data source.") - # initialise Gaunt factor on first run - self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() + # initialise Gaunt factor on first run using the atomic data + self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() species_charge = [] for species in self._plasma.get_composition(): @@ -160,7 +181,8 @@ cdef class Bremsstrahlung(PlasmaModel): def _change(self): # clear cache to force regeneration on first use - self._gaunt_factor = None + if not self._user_provided_gaunt_factor: + self._gaunt_factor = None self._species_charge = None self._species_charge_mv = None self._species_density = None From 00289d288f50fa0247ce8381b03359f9605ed5ef Mon Sep 17 00:00:00 2001 From: Matej Tomes Date: Wed, 23 Mar 2022 14:04:08 +0100 Subject: [PATCH 24/59] Generomak/core plasma (#360) * Add Generomak Core plasma a set of functions which provide core plasma for Generomak were added. The plasma population profiles follow either a double quadratic profil, which is used for temperatures and some densities (H1+, C6+) and exponentially decaying profiles for densities. The Profiles are mapped into 3D using the generomak equilibrium and assuming constant values along magnetic surfaces. * Add demo with Generomak core profiles plots. * Applying PR requested changes A lot of typo removed from docstrings Chaged plasma geometry z transfer to the correct value removed font changes from plots * Add imports of core plasma to __init__.py * Add get_core_profiles_arguments function Add function which returns a dictionary of core profile arguments. This shortens the list of parameters of the get_core_profles_decription function. Some more minor changes following the code review suggestions. * Fix PEP8 warnings * Change core profile chapes To achieve better blending with edge profiles, as suggested by Vlad Neverov. * Fix doctrings Adjudt default values of parameters and fix typos * Fill CHANGELOG --- CHANGELOG.md | 1 + cherab/generomak/plasma/__init__.py | 3 +- cherab/generomak/plasma/plasma.py | 389 ++++++++++++++++++- demos/generomak/plasma/plot_core_profiles.py | 65 ++++ 4 files changed, 452 insertions(+), 6 deletions(-) create mode 100644 demos/generomak/plasma/plot_core_profiles.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 906c09e1..9c5ca2d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ New: * Add group observer class for each of Raysect's 0D observers. (#332) * Add a demo for observer group handling and plotting. * Add verbose parameter to SartOpencl solver (default is False). (#358) +* Add Generomak core plasma profiles. (#360) Bug Fixes: ---------- diff --git a/cherab/generomak/plasma/__init__.py b/cherab/generomak/plasma/__init__.py index e7287963..8a28ab1a 100644 --- a/cherab/generomak/plasma/__init__.py +++ b/cherab/generomak/plasma/__init__.py @@ -1 +1,2 @@ -from .plasma import get_edge_distributions, get_edge_interpolators, get_edge_plasma \ No newline at end of file +from .plasma import get_edge_distributions, get_edge_interpolators, get_edge_plasma +from .plasma import get_core_distributions, get_core_plasma \ No newline at end of file diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index 8676918d..f06335b4 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -24,17 +24,20 @@ from raysect.core import Vector3D, translate from raysect.core.math.function.float.function2d.interpolate import Discrete2DMesh +from raysect.core.math.function.float import Arg1D, Exp1D, Constant1D from raysect.primitive import Cylinder, Subtract from cherab.core import AtomicData, Plasma, Maxwellian, Species from cherab.core.atomic.elements import hydrogen, carbon, lookup_isotope, lookup_element from cherab.core.utility import RecursiveDict from cherab.core.math.mappers import AxisymmetricMapper, VectorAxisymmetricMapper +from cherab.core.math.clamp import ClampInput1D from cherab.openadas import OpenADAS from cherab.generomak.equilibrium import load_equilibrium + def load_edge_profiles(): """ Loads Generomak edge plasma profiles @@ -86,6 +89,7 @@ def load_edge_profiles(): return edge_data.freeze() + def get_edge_interpolators(): """ Provides Generomak edge profiles 2d interpolator @@ -116,9 +120,9 @@ def get_edge_interpolators(): mesh_interp["composition"][elem_name][stage]["density"] = n mesh_interp["composition"][elem_name][stage]["element"] = stage_data["element"] - return mesh_interp.freeze() + def get_edge_distributions(): """ Provides Generomak edge Maxwellian distribution of plasma species @@ -154,6 +158,7 @@ def get_edge_distributions(): return dists.freeze() + def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma"): """ Provides Generomak Edge plasma. @@ -169,8 +174,8 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") # create or check atomic_data if atomic_data is not None: - if not isinstance(atomic_data, AtomicData): - raise ValueError("atomic_data has to be of type AtomicData") + if not isinstance(atomic_data, AtomicData): + raise ValueError("atomic_data has to be of type AtomicData") else: atomic_data = OpenADAS() @@ -185,7 +190,7 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") z_range = (vertex_coords[:, 1].min(), vertex_coords[:, 1].max()) plasma_height = z_range[1] - z_range[0] - padding = 1e-3 #enlarge for safety + padding = 1e-3 # enlarge for safety outer_column = Cylinder(radius=r_range[1], height=plasma_height) inner_column = Cylinder(radius=r_range[0], height=plasma_height + 2 * padding) @@ -199,7 +204,7 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") # create plasma composition list plasma_composition = [] - for elem_name, elem_data in dists["composition"].items(): + for elem_data in dists["composition"].values(): for stage, stage_data in elem_data.items(): species = Species(stage_data["element"], stage, stage_data["distribution"]) plasma_composition.append(species) @@ -215,3 +220,377 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") plasma.b_field = VectorAxisymmetricMapper(equilibrium.b_field) return plasma + + +def get_double_parabola(v_min, v_max, convexity, concavity, xmin=0, xmax=1): + """ + Returns a 1d double-quadratic Function1D + + The retuned Function1D is of the form + + .. math:: f(x) = ((v_{max} - v_{min}) * ((1 - ((1 - x_{norm}) ** convexity)) ** concavity) + v_min) + + where the :math: `x_norm` is calculated as + + .. math:: x_{norm} = (x - xmin) / (xmax - xmin). + + The returned function is decreasing and monotonous and its domain is [xmin, xmax]. + + :param v_min: The minimum value of the profile at xmax. + :param v_max: The maximum value of the profile at xmin. + :param convexity: Controls the convexity of the profile in the lower values part of the profile. + :param concavity: Controls the concavity of the profile in the higher values part of the profile. + :param xmin: The lower edge of the function domain. Defaults to 0. + :param xmax: The upper edge of the function domain Defaults to 1. + :return: Function1D + """ + + x = Arg1D() #the free parameter + + # funciton for the normalised free variable + x_norm = ClampInput1D((x - xmin) / (xmax - xmin), 0, 1) + + # profile function + return (v_max - v_min) * ((1 - ((1 - x_norm) ** convexity)) ** concavity) + v_min + + +def get_exponential_growth(initial_value, growth_rate, initial_position=1): + """ + returns exponentially growing Function1D + + The returned Function1D is of the form: + + ::math:: + v_0 \exp((x - x_0) * \lambda) + + where v_0 is the initial_value, x_0 is the initial_position and lambda is the growth_rate. + + :param initial_value: The value of the function at the initial position. + :param growth_rate: Growth constant of the profile. + :param initial_position: The initial position of the profile. Defaults to 1. + :return: Function1D + """ + + x = Arg1D() #the free parameter + return initial_value * Exp1D((x - initial_position) * growth_rate) + + +def get_maxwellian_distribution(equilibrium, f1d_density, f1d_temperature, f1d_vtor, f1d_vpol, f1d_vnorm, rest_mass): + """ Returns Maxwellian distribution for equilibrium mapped 1d profiles + + :param equilibrium: Instance of EFITEquilibrium + :param f1d_density: Function1D describing density profile. + :param f1d_temperature: Function1D describing temperature profile. + :param f1d_vtor: Function1D describing bulk toroidal rotation velocity profile. + :param f1d_vpol: Function1D describing bulk poloidal rotation velocity profile. + :param f1d_vnorm: Function1D describing bulk velocity normal to magnetic surfaces. + :rest_mass: Rest mass of the distribution species. + :return: Maxwellian distribution + """ + + + # map profiles to 3D + f3d_te = equilibrium.map3d(f1d_temperature) + f3d_ne = equilibrium.map3d(f1d_density) + f3d_v = equilibrium.map_vector3d(f1d_vtor, f1d_vpol, f1d_vnorm) + + # return Maxwellian distribution + return Maxwellian(f3d_ne, f3d_te, f3d_v, rest_mass) + + +def get_edge_profile_values(r, z, edge_interpolators=None): + """ + Evalueate edge plasma profiles at the position [r, z] + + :param r: Radial distance in cylindrical cordinates in m. + :param z: Elevation in cylindrical coordinates in m. + :param edge_interpolators: Dictionary with edge interpolators in the shape + returned by the get_edge_interpolators function. + :return: Dictionary of edge values at [R, Z] + """ + # load edge interpolators if not passed as argument + if edge_interpolators is None: + edge_interp = get_edge_interpolators() + else: + edge_interp = edge_interpolators + # create recursive dictionary to store profile values + lcfs_values = RecursiveDict() + + # add electron values + lcfs_values["electron"]["temperature"] = edge_interp["electron"]["temperature"](r, z) + lcfs_values["electron"]["density"] = edge_interp["electron"]["density"](r, z) + + # add species values + for spec, desc in edge_interp['composition'].items(): + for chrg, chrg_desc in desc.items(): + for prop, val in chrg_desc.items(): + if prop in ["temperature", "density"]: + lcfs_values["composition"][spec][chrg][prop] = val(r, z) + else: + lcfs_values["composition"][spec][chrg][prop] = val + + return lcfs_values.freeze() + + +def get_core_profiles_arguments(**kwargs): + """ + Returns dictionary with core profile arguments + + The function compares the passed keyword arguments with the list of core profile arguments (listed below). + If there is a match, the default value is overwritten by th passed value, the default value is kept + otherwise. + + List of core parameters, their meaning and default values + ne_core: (default 5e19) core electron density + ne_convexity: (default 1.09) (default ) convexity of the electron density profile + ne_concavity: (default 0.24) concavity of the electron density profile + te_core core: (default 3e3) electron temperature + te_convexity: (default 2.35) convexity of the electron temperature profile + te_concavity: (default 1.26) concavity of the electron temperature profile + nh_core: (default 5e19) density of H1+ + nh_convexity: (default 1.09) convexity of H1+ density profile + nh_concavity: (default 0.24) concavity of H1+ density profile + th_core: (default 2.8e3) H1+ temperature + th_convexity: (default 1) convexity of H1+ temperature profile + th_concavity: (default 0.82) concavity of H1+ temperature profile + th0_fraction: (default 0.8) H0 temperature factor + nh0_decay decay: (default 20) rate of H0 density profile + timp_core: (default 2.7e3) core impurity temperature + timp_convexity: (default 1) convexity of impurity temperature profile + timp_concavity: (default 0.82) concavity of impurity temperature profile + nimp_core: (default 5e17) impurity density + nimp_convexity: (default 1.09) convexity of impurity density profile + nimp_concavity: (default 0.24) concavity of impurity density profile + nimp_decay: (default 30) decay rate of impurity density profile (except bare nuclei) + vtor_core: (default 1e5) toroidal rotation velocity m/s + vtor_edge: (default 1e4) toroidal rotation velocity at the edge m/s + vtor_convexity: (default 2) convexity of the toroidal rotation profile + vtor_concavity: (default 4) concavity of the toroidal rotation profile + vpol_lcfs: (default 2e4) Bulk poloidal rotation velocity in m/s + vpol_decay: (default 0.08) Decay rate of poloidal rotation velocity + + :return: dictionary of profile arguments + """ + + core_args = {"ne_core": 5e19, "ne_convexity": 1.09, + "ne_concavity": 0.24, "te_core": 3e3, + "te_convexity": 2.35, "te_concavity": 1.26, + "nh_core": 5e19, "nh_convexity": 1.09, + "nh_concavity": 0.24, "th_core": 2.8e3, + "th_convexity": 1, "th_concavity": 0.82, + "th0_fraction": 0.8, "nh0_decay": 20, + "timp_core": 2.7e3, "timp_convexity": 1, + "timp_concavity": 0.82, "nimp_core": 5e17, + "nimp_convexity": 1.09, "nimp_concavity": 0.24, + "nimp_decay": 30, + "vtor_core": 1e5, "vtor_edge": 1e4, + "vtor_convexity": 2, "vtor_concavity": 4, + "vpol_lcfs": 2e4, "vpol_decay": 0.08} + + if not kwargs: + return core_args + + # change passed values of core args + for key, item in kwargs.items(): + core_args[key] = item + + return core_args + + +def get_core_profiles_description(lcfs_values=None, core_args=None): + """ + Returns dictionary of core profile functions and species descriptions + + :param lcfs_values: Dictionary of profile values at the separatrix on outer midplane. + The dictionary has to have the same format as the one returned by + the function get_edge_profile_values. The default value is the + dictionary returned by the call get_edge_profile_values for r, z + on last closed flux surface on outer midplane. + :param core_args: Dictionary with arguments describing the core profiles. The dictionary + has to have the same shape as the one returned by the funciton + get_core_profiles_description. The default value is the dictionary + returned by the get_core_profiles() call. + :return: dictionary of Function1D profiles + """ + if lcfs_values is None: + # get edge profiles and calculate profile values at midplane outer lcfs + equilibrium = load_equilibrium() + r = equilibrium.psin_to_r(1) + z = 0 + lcfs_values = get_edge_profile_values(r, z) + + if core_args is None: + core_args = get_core_profiles_arguments() + + # toroidal rotation profile + f1d_vtor = get_double_parabola(core_args["vtor_edge"], core_args["vtor_core"], + core_args["vtor_convexity"], core_args["vtor_concavity"]) + + # poloidal rotation profile + f1d_vpol = get_exponential_growth(core_args["vpol_lcfs"], core_args["vpol_decay"]) + + # velocity normal to magnetic surfaces + f1d_vnorm = Constant1D(0) + + # construct dictionary with 1D profile functions + profiles = RecursiveDict() + + # Setup electron profiles with double parabola shapes + profiles["electron"]["f1d_temperature"] = get_double_parabola(lcfs_values["electron"]["temperature"], + core_args["te_core"], core_args["te_convexity"], + core_args["te_concavity"], xmin=1, xmax=0) + profiles["electron"]["f1d_density"] = get_double_parabola(lcfs_values["electron"]["density"], + core_args["ne_core"], core_args["ne_convexity"], + core_args["ne_concavity"], xmin=1, xmax=0) + profiles["electron"]["f1d_vtor"] = Constant1D(0) + profiles["electron"]["f1d_vpol"] = Constant1D(0) + profiles["electron"]["f1d_vnorm"] = Constant1D(0) + + # Setup H1+ profiles with double parabola shapes + profiles["composition"]["hydrogen"][1]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["hydrogen"][1]["temperature"], + core_args["th_core"], core_args["th_convexity"], core_args["th_concavity"], + xmin=1, xmax=0) + profiles["composition"]["hydrogen"][1]["f1d_density"] = get_double_parabola(lcfs_values["composition"]["hydrogen"][1]["density"], + core_args["nh_core"], core_args["nh_convexity"], + core_args["nh_concavity"], xmin=1, xmax=0) + profiles["composition"]["hydrogen"][1]["f1d_vtor"] = f1d_vtor + profiles["composition"]["hydrogen"][1]["f1d_vpol"] = f1d_vpol + profiles["composition"]["hydrogen"][1]["f1d_vnorm"] = f1d_vnorm + + # setup H0+ profile shapes with temperature as a fraction of H1+ and density with decaying exponential + profiles["composition"]["hydrogen"][0]["f1d_temperature"] = core_args["th0_fraction"] * get_double_parabola(lcfs_values["composition"]["hydrogen"][0]["temperature"], + core_args["th_core"], core_args["th_convexity"], + core_args["th_concavity"], xmin=1, xmax=0) + profiles["composition"]["hydrogen"][0]["f1d_density"] = get_exponential_growth(lcfs_values["composition"]["hydrogen"][0]["density"], core_args["nh0_decay"]) + profiles["composition"]["hydrogen"][0]["f1d_vtor"] = f1d_vtor + profiles["composition"]["hydrogen"][0]["f1d_vpol"] = f1d_vpol + profiles["composition"]["hydrogen"][0]["f1d_vnorm"] = f1d_vnorm + + # setup C6+ profile shapes with double parabolas + profiles["composition"]["carbon"][6]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["carbon"][6]["temperature"], + core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], + xmin=1, xmax=0) + profiles["composition"]["carbon"][6]["f1d_density"] = get_double_parabola(lcfs_values["composition"]["carbon"][6]["density"], core_args["nimp_core"], + core_args["nimp_convexity"], core_args["nimp_concavity"], xmin=1, xmax=0) + profiles["composition"]["carbon"][6]["f1d_vtor"] = f1d_vtor + profiles["composition"]["carbon"][6]["f1d_vpol"] = f1d_vpol + profiles["composition"]["carbon"][6]["f1d_vnorm"] = f1d_vnorm + + # setup CX+ profile shapes with temperature as double parabolas and density with decaying exponentials + for chrg in range(6): + profiles["composition"]["carbon"][chrg]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["carbon"][chrg]["temperature"], + core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], + xmin=1, xmax=0) + profiles["composition"]["carbon"][chrg]["f1d_density"] = get_exponential_growth(lcfs_values["composition"]["carbon"][chrg]["density"], core_args["nimp_decay"]) + profiles["composition"]["carbon"][chrg]["f1d_vtor"] = f1d_vtor + profiles["composition"]["carbon"][chrg]["f1d_vpol"] = f1d_vpol + profiles["composition"]["carbon"][chrg]["f1d_vnorm"] = f1d_vnorm + + return profiles.freeze() + + +def get_core_distributions(profiles=None, equilibrium=None): + """ + Returns a dictionary of core plasma species Maxwellian distributions. + + :param profiles: Dictionary of core particle profiles. The dictionary has to have the same form + as the one returned by the function get_core_profiles_description. + The default value is the value returned by the call get_core_profiles_description(). + :param equilibrium: an instance of EFITEquilibrium. + :return: dictionary of core plasma species with Maxwellian distribution + """ + # get core profile data if not passed sa argument + if profiles is None: + profiles = get_core_profiles_description() + + # load plasma equilibrium if not passed as argument + if equilibrium is None: + equilibrium = load_equilibrium() + + # build a dictionary with Maxwellian distributions + species = RecursiveDict() + species["electron"] = get_maxwellian_distribution(equilibrium, rest_mass=electron_mass, + **profiles["electron"]) + for name, spec in profiles["composition"].items(): + spec_cherab = _get_cherab_element(name) + for chrg, desc in spec.items(): + rest_mass = atomic_mass * spec_cherab.atomic_weight + species["composition"][name][chrg] = get_maxwellian_distribution(equilibrium, rest_mass=rest_mass, **desc) + + return species.freeze() + + +def get_core_plasma(distributions=None, atomic_data=None, parent=None, name="Generomak core plasma"): + """ + Provides Generomak core plasma. + + :param distributions: A dictionary of plasma distributions. Has to have the same format as the + dictionary returned by get_core_distributions. The default value + is the value returned by the call get_core_distributions(). + :param atomic_data: Instance of AtomicData, default is OpenADAS() + :param parent: parent of the plasma node, defaults None + :param name: name of the plasma node, defaults "Generomak edge plasma" + :return: populated Plasma object + """ + + # load Generomak equilibrium + equilibrium = load_equilibrium() + + # create or check atomic_data + if atomic_data is not None: + if not isinstance(atomic_data, AtomicData): + raise ValueError("atomic_data has to be of type AtomicData") + else: + atomic_data = OpenADAS() + + # construct plasma primitive shape + padding = 1e-3 #enlarge for safety + plasma_height = equilibrium.z_range[1] - equilibrium.z_range[0] + outer_column = Cylinder(radius=equilibrium.r_range[1], height=plasma_height) + inner_column = Cylinder(radius=equilibrium.r_range[0], height=plasma_height + 2 * padding) + inner_column.transform = translate(0, 0, -padding) + plasma_geometry = Subtract(outer_column, inner_column) + + # coordinate transform of the plasma frame + geometry_transform = translate(0, 0, equilibrium.z_range[0]) + + # load core distributions if needed + if distributions is None: + dists = get_core_distributions() + else: + dists = distributions + + # create plasma composition list + plasma_composition = [] + for elem_name, elem_data in dists["composition"].items(): + for stage, stage_data in elem_data.items(): + elem = _get_cherab_element(elem_name) + species = Species(elem, stage, stage_data) + plasma_composition.append(species) + + # Populate plasma + plasma = Plasma(parent=parent) + plasma.name = name + plasma.geometry = plasma_geometry + plasma.atomic_data = atomic_data + plasma.electron_distribution = dists["electron"] + plasma.composition = plasma_composition + plasma.geometry_transform = geometry_transform + plasma.b_field = VectorAxisymmetricMapper(equilibrium.b_field) + + return plasma + + +def _get_cherab_element(name): + """Returns cherab element instance + + :param name: Name or label of the element Cherab has to know. + :return: Cherab element + """ + try: + return lookup_isotope(name) + except ValueError: + try: + return lookup_element(name) + except ValueError: + raise ValueError("Unknown element name '{}' by Cherab".format(name)) diff --git a/demos/generomak/plasma/plot_core_profiles.py b/demos/generomak/plasma/plot_core_profiles.py new file mode 100644 index 00000000..c12f0d5a --- /dev/null +++ b/demos/generomak/plasma/plot_core_profiles.py @@ -0,0 +1,65 @@ + +# 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. + +import numpy as np + +import matplotlib.pyplot as plt + +from cherab.core.math.samplers import sample1d_points +from cherab.generomak.plasma.plasma import get_core_profiles_description + +profiles = get_core_profiles_description() + +# setup temperature plot +_, ax_t = plt.subplots() +ax_t.set_title("Species Core Temperature Profiles") +ax_t.set_xlabel("Psin") +ax_t.set_ylabel("eV") + +# setup density plot +_, ax_n = plt.subplots() +ax_n.set_yscale("log") +ax_n.set_title("Species Core Density Profiles") +ax_n.set_xlabel("psin") +ax_n.set_ylabel("m^-3") + +psin = np.linspace(0, 1, 30) +# add hydrogen curves +for chrg, desc in profiles["composition"]["hydrogen"].items(): + vals = sample1d_points(desc["f1d_temperature"], psin) + ax_t.plot(psin, vals, label="H{:d}+".format(chrg)) + vals = sample1d_points(desc["f1d_density"], psin) + ax_n.plot(psin, vals, label="H{:d}+".format(chrg)) + +# add carbon curves +for chrg, desc in profiles["composition"]["carbon"].items(): + vals = sample1d_points(desc["f1d_temperature"], psin) + ax_t.plot(psin, vals, label="C{:d}+".format(chrg)) + vals = sample1d_points(desc["f1d_density"], psin) + ax_n.plot(psin, vals, label="C{:d}+".format(chrg)) + +# add electrons +vals = sample1d_points(profiles["electron"]["f1d_temperature"], psin) +ax_t.plot(psin, vals, label="electron", ls="dashed") +vals = sample1d_points(profiles["electron"]["f1d_density"], psin) +ax_n.plot(psin, vals, label="electron", ls="dashed") + +ax_t.legend() +ax_n.legend(ncol=2) +plt.show() From a8236300f40f345aee0923f2721a9310c119da67 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 23 Mar 2022 22:21:08 +0300 Subject: [PATCH 25/59] Replace pipeline_properties with pipeline_classes and pipeline_kwargs to match the new connect_pipelines() interface of the group observers. --- cherab/tools/spectroscopy/instrument.py | 52 ++++++++++++------- cherab/tools/spectroscopy/polychromator.py | 10 ++-- cherab/tools/spectroscopy/spectrometer.py | 7 ++- .../tests/test_spectroscopic_instruments.py | 23 +++++--- 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/cherab/tools/spectroscopy/instrument.py b/cherab/tools/spectroscopy/instrument.py index b2baf152..0e0d666c 100644 --- a/cherab/tools/spectroscopy/instrument.py +++ b/cherab/tools/spectroscopy/instrument.py @@ -24,14 +24,16 @@ class SpectroscopicInstrument: :param str name: Instrument name. - :ivar list pipeline_properties: The list of properties (class, name, filter) of - the pipelines used with this instrument. + :ivar list pipeline_classes: The list of pipeline classes used with this instrument. + :ivar list pipeline_kwargs: The list of dicts with keywords passed to init methods of + pipeline classes used with this instrument. :ivar float min_wavelength: Lower wavelength bound for spectral range. :ivar float max_wavelength: Upper wavelength bound for spectral range. :ivar int spectral_bins: The number of spectral samples over the wavelength range. """ def __init__(self, name=''): + self._pipeline_classes = None self.name = name self._clear_spectral_settings() @@ -43,28 +45,39 @@ def name(self): @name.setter def name(self, value): self._name = str(value) - self._pipeline_properties = None + self._pipeline_kwargs = None @property - def pipeline_properties(self): - # The list of properties (class, name, filter) of the pipelines used with - # this instrument. - if self._pipeline_properties is None: - self._update_pipeline_properties() + def pipeline_classes(self): + # The list of pipeline classes used with this instrument. + if self._pipeline_classes is None: + self._update_pipeline_classes() - return self._pipeline_properties + return self._pipeline_classes + + @property + def pipeline_kwargs(self): + # The list of dicts with keywords passed to init methods of + # pipeline classes used with this instrument. + if self._pipeline_kwargs is None: + self._update_pipeline_kwargs() + + return self._pipeline_kwargs def create_pipelines(self): - """ Returns a list of new pipelines created according to `pipeline_properties`.""" + """ Returns a list of new pipelines created according to `pipeline_classes` + and keyword arguments.""" + if self._pipeline_classes is None: + self._update_pipeline_classes() + if self._pipeline_kwargs is None: + self._update_pipeline_kwargs() - pl_list = [] - for (pl_class, pl_name, pl_filter) in self.pipeline_properties: - if pl_filter is None: - pl_list.append(pl_class(name=pl_name)) - else: - pl_list.append(pl_class(name=pl_name, filter=pl_filter)) + pipelines = [] + for PipelineClass, kwargs in zip(self._pipeline_classes, self._pipeline_kwargs): + pipeline = PipelineClass(**kwargs) + pipelines.append(pipeline) - return pl_list + return pipelines @property def min_wavelength(self): @@ -98,5 +111,8 @@ def _clear_spectral_settings(self): def _update_spectral_settings(self): raise NotImplementedError("To be defined in subclass.") - def _update_pipeline_properties(self): + def _update_pipeline_classes(self): + raise NotImplementedError("To be defined in subclass.") + + def _update_pipeline_kwargs(self): raise NotImplementedError("To be defined in subclass.") diff --git a/cherab/tools/spectroscopy/polychromator.py b/cherab/tools/spectroscopy/polychromator.py index f81c6158..56fb6e18 100644 --- a/cherab/tools/spectroscopy/polychromator.py +++ b/cherab/tools/spectroscopy/polychromator.py @@ -197,10 +197,14 @@ def filters(self, value): self._filters = value self._clear_spectral_settings() - self._pipeline_properties = None + self._pipeline_classes = None + self._pipeline_kwargs = None - def _update_pipeline_properties(self): - self._pipeline_properties = [(RadiancePipeline0D, self._name + ': ' + poly_filter.name, poly_filter) for poly_filter in self._filters] + def _update_pipeline_classes(self): + self._pipeline_classes = [RadiancePipeline0D for poly_filter in self._filters] + + def _update_pipeline_kwargs(self): + self._pipeline_kwargs = [{'name': self._name + ': ' + poly_filter.name, 'filter': poly_filter} for poly_filter in self._filters] def _update_spectral_settings(self): diff --git a/cherab/tools/spectroscopy/spectrometer.py b/cherab/tools/spectroscopy/spectrometer.py index 5fbe3710..585ac3f4 100644 --- a/cherab/tools/spectroscopy/spectrometer.py +++ b/cherab/tools/spectroscopy/spectrometer.py @@ -125,8 +125,11 @@ def min_bins_per_pixel(self, value): self._min_bins_per_pixel = value self._clear_spectral_settings() - def _update_pipeline_properties(self): - self._pipeline_properties = [(SpectralRadiancePipeline0D, self._name, None)] + def _update_pipeline_classes(self): + self._pipeline_classes = [SpectralRadiancePipeline0D] + + def _update_pipeline_kwargs(self): + self._pipeline_kwargs = [{'name': self._name}] def _update_spectral_settings(self): self._min_wavelength = min(wl2pix[0] for wl2pix in self._wavelength_to_pixel) diff --git a/cherab/tools/tests/test_spectroscopic_instruments.py b/cherab/tools/tests/test_spectroscopic_instruments.py index a8d843d6..211f7830 100644 --- a/cherab/tools/tests/test_spectroscopic_instruments.py +++ b/cherab/tools/tests/test_spectroscopic_instruments.py @@ -64,12 +64,17 @@ class TestPolychromator(unittest.TestCase): TrapezoidalFilter(700., 8., 4., 'filter 2')) min_bins_per_window_default = 10 - def test_pipeline_properties(self): + def test_pipeline_classes(self): polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default, 'test polychromator') - pipeline_properties_true = [(RadiancePipeline0D, 'test polychromator: filter 1', self.poly_filters_default[0]), - (RadiancePipeline0D, 'test polychromator: filter 2', self.poly_filters_default[1])] - self.assertSequenceEqual(pipeline_properties_true, polychromator.pipeline_properties) + pipeline_classes_true = [RadiancePipeline0D, RadiancePipeline0D] + self.assertSequenceEqual(pipeline_classes_true, polychromator.pipeline_classes) + def test_pipeline_kwargs(self): + polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default, 'test polychromator') + pipeline_kwargs_true = [{'name': 'test polychromator: filter 1', 'filter': self.poly_filters_default[0]}, + {'name': 'test polychromator: filter 2', 'filter': self.poly_filters_default[1]}] + self.assertSequenceEqual(pipeline_kwargs_true, polychromator.pipeline_kwargs) + def test_spectral_properties(self): polychromator = Polychromator(self.poly_filters_default, self.min_bins_per_window_default) min_wavelength_true = 397. @@ -98,11 +103,15 @@ class TestSpectrometer(unittest.TestCase): Test cases for Spectrometer class. """ - def test_pipeline_properties(self): + def test_pipeline_classes(self): + wavelength_to_pixel = ([400., 400.5],) + spectrometer = Spectrometer(wavelength_to_pixel, name='test spectrometer') + self.assertSequenceEqual([SpectralRadiancePipeline0D], spectrometer.pipeline_classes) + + def test_pipeline_kwargs(self): wavelength_to_pixel = ([400., 400.5],) spectrometer = Spectrometer(wavelength_to_pixel, name='test spectrometer') - pipeline_properties_true = [(SpectralRadiancePipeline0D, 'test spectrometer', None)] - self.assertSequenceEqual(pipeline_properties_true, spectrometer.pipeline_properties) + self.assertSequenceEqual([{'name': 'test spectrometer'}], spectrometer.pipeline_kwargs) def test_spectral_properties(self): wavelength_to_pixel = ([400., 400.5, 401.5, 402., 404.], [600., 600.5, 601.5, 602., 604., 607.]) From 3b7764444432f98883961803ddbfd3dfb27e7e5d Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 23 Mar 2022 23:51:03 +0300 Subject: [PATCH 26/59] Update CHANGELOG.md. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c5ca2d2..9c91c98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ New: * Add a demo for observer group handling and plotting. * Add verbose parameter to SartOpencl solver (default is False). (#358) * Add Generomak core plasma profiles. (#360) +* Add common spectroscopic instruments: Polychromator, SurveySpectrometer, CzernyTurnerSpectrometer. (#299) Bug Fixes: ---------- From e4ba124ebf3cf1857560481afacef53c587b5582 Mon Sep 17 00:00:00 2001 From: wave46 <59137482+wave46@users.noreply.github.com> Date: Mon, 4 Apr 2022 14:59:15 +0300 Subject: [PATCH 27/59] Feature/toroidal mesh from polygon (#365) * Add toroidal_mesh_from_polygon() and make axisymmetric_mesh_from_polygon() a wrapper * Added comments about deprecation to the 'axysimmetric_mesh' to the function and 'changelog.md', also updated '.gitignore' * Review updates to documentation and mesh-functions Removed deprecation warning Added check for toroidal_extent Some minor code improvements * added link to the toridal_mesh the documentation --- .gitignore | 1 + CHANGELOG.md | 1 + cherab/tools/primitives/__init__.py | 7 +- cherab/tools/primitives/axisymmetric_mesh.pxd | 9 +- cherab/tools/primitives/axisymmetric_mesh.pyx | 97 +++------- cherab/tools/primitives/toroidal_mesh.pxd | 22 +++ cherab/tools/primitives/toroidal_mesh.pyx | 169 ++++++++++++++++++ docs/source/tools/primitives.rst | 1 + 8 files changed, 228 insertions(+), 79 deletions(-) create mode 100644 cherab/tools/primitives/toroidal_mesh.pxd create mode 100644 cherab/tools/primitives/toroidal_mesh.pyx diff --git a/.gitignore b/.gitignore index b4bad474..7045bc61 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ build/ .nfs* .coverage htmlcov* +cherab.egg-info/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c5ca2d2..27b5ecac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ New: * Add a demo for observer group handling and plotting. * Add verbose parameter to SartOpencl solver (default is False). (#358) * Add Generomak core plasma profiles. (#360) +* Add toroidal_mesh_from_polygon for making mesh for not fully-360 degrees axisymmetric elements. (#365) Bug Fixes: ---------- diff --git a/cherab/tools/primitives/__init__.py b/cherab/tools/primitives/__init__.py index 8cf9e184..e4afae9d 100644 --- a/cherab/tools/primitives/__init__.py +++ b/cherab/tools/primitives/__init__.py @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -17,4 +17,5 @@ # under the Licence. from .annulus_mesh import generate_annulus_mesh_segments +from .toroidal_mesh import toroidal_mesh_from_polygon from .axisymmetric_mesh import axisymmetric_mesh_from_polygon diff --git a/cherab/tools/primitives/axisymmetric_mesh.pxd b/cherab/tools/primitives/axisymmetric_mesh.pxd index b2502d9a..8ab28503 100644 --- a/cherab/tools/primitives/axisymmetric_mesh.pxd +++ b/cherab/tools/primitives/axisymmetric_mesh.pxd @@ -1,7 +1,7 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -17,7 +17,6 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -cimport numpy as np from raysect.primitive.mesh cimport Mesh -cpdef Mesh axisymmetric_mesh_from_polygon(np.ndarray polygon, int num_toroidal_segments=*) +cpdef Mesh axisymmetric_mesh_from_polygon(object polygon, int num_toroidal_segments=*) diff --git a/cherab/tools/primitives/axisymmetric_mesh.pyx b/cherab/tools/primitives/axisymmetric_mesh.pyx index b1e55689..81533cb0 100644 --- a/cherab/tools/primitives/axisymmetric_mesh.pyx +++ b/cherab/tools/primitives/axisymmetric_mesh.pyx @@ -1,24 +1,38 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. -import numpy as np -cimport numpy as np -from libc.math cimport cos, sin from raysect.primitive.mesh.mesh cimport Mesh +from .toroidal_mesh import toroidal_mesh_from_polygon -cdef double DEG2RAD = 2 * np.pi / 360 - -cpdef Mesh axisymmetric_mesh_from_polygon(np.ndarray polygon, int num_toroidal_segments=500): +cpdef Mesh axisymmetric_mesh_from_polygon(object polygon, int num_toroidal_segments=500): """ Generates an Raysect Mesh primitive from the specified 2D polygon. - :param np.ndarray polygon: A numpy array with shape [N,2] specifying the wall outline polygon - in the R-Z plane. The polygon should not be closed, i.e. vertex i = 0 and i = N should not - be the same vertex, but neighbours. + :param object polygon: An object which can be converted to a numpy array with shape [N,2] + specifying the wall outline polygon in the R-Z plane. The polygon + should not be closed, i.e. vertex i = 0 and i = N should not be the + same vertex, but neighbours. :param int num_toroidal_segments: The number of repeating toroidal segments that will be used - to construct the mesh. + to construct the mesh. :return: A Raysect Mesh primitive constructed from the R-Z polygon using symmetry. - + .. code-block:: pycon >>> from cherab.tools.primitives import axisymmetric_mesh_from_polygon @@ -27,63 +41,4 @@ cpdef Mesh axisymmetric_mesh_from_polygon(np.ndarray polygon, int num_toroidal_s >>> mesh = axisymmetric_mesh_from_polygon(wall_polygon) """ - cdef: - int num_poloidal_vertices - int i, j, vid, v1_id, v2_id, v3_id, v4_id - float theta, r, x, y, z - np.ndarray vertices - list triangles - double[:,:] polygon_mv, vertices_mv - - num_poloidal_vertices = len(polygon) - theta = 360 / num_toroidal_segments - vertices = np.zeros((num_poloidal_vertices * num_toroidal_segments, 3)) - vertices_mv = vertices - polygon_mv = polygon - - for i in range(num_toroidal_segments): - for j in range(num_poloidal_vertices): - - r = polygon_mv[j, 0] - z = polygon_mv[j, 1] - x = r * cos(i * theta * DEG2RAD) - y = r * sin(i * theta * DEG2RAD) - - vid = i * num_poloidal_vertices + j - vertices_mv[vid, 0] = x - vertices_mv[vid, 1] = y - vertices_mv[vid, 2] = z - - # assemble mesh triangles - triangles = [] - for i in range(num_toroidal_segments): - for j in range(num_poloidal_vertices): - - if i == num_toroidal_segments - 1 and j == num_poloidal_vertices - 1: - v1_id = i * num_poloidal_vertices + j - v2_id = i * num_poloidal_vertices + 0 - v3_id = j - v4_id = 0 - - elif i == num_toroidal_segments - 1: - v1_id = i * num_poloidal_vertices + j - v2_id = i * num_poloidal_vertices + j + 1 - v3_id = j - v4_id = j + 1 - - elif j == num_poloidal_vertices - 1: - v1_id = i * num_poloidal_vertices + j - v2_id = i * num_poloidal_vertices - v3_id = i * num_poloidal_vertices + num_poloidal_vertices + j - v4_id = i * num_poloidal_vertices + num_poloidal_vertices - - else: - v1_id = i * num_poloidal_vertices + j - v2_id = i * num_poloidal_vertices + j + 1 - v3_id = i * num_poloidal_vertices + num_poloidal_vertices + j - v4_id = i * num_poloidal_vertices + num_poloidal_vertices + j + 1 - - triangles.append([v1_id, v2_id, v4_id]) - triangles.append([v4_id, v3_id, v1_id]) - - return Mesh(vertices=vertices, triangles=triangles, smoothing=False) + return toroidal_mesh_from_polygon(polygon, toroidal_extent=360, num_toroidal_segments=num_toroidal_segments) diff --git a/cherab/tools/primitives/toroidal_mesh.pxd b/cherab/tools/primitives/toroidal_mesh.pxd new file mode 100644 index 00000000..f7c90758 --- /dev/null +++ b/cherab/tools/primitives/toroidal_mesh.pxd @@ -0,0 +1,22 @@ + +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 raysect.primitive.mesh cimport Mesh + +cpdef Mesh toroidal_mesh_from_polygon(object polygon, double toroidal_extent, object polygon_triangles=*, int num_toroidal_segments=*) diff --git a/cherab/tools/primitives/toroidal_mesh.pyx b/cherab/tools/primitives/toroidal_mesh.pyx new file mode 100644 index 00000000..62cfa721 --- /dev/null +++ b/cherab/tools/primitives/toroidal_mesh.pyx @@ -0,0 +1,169 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. + +import numpy as np +cimport numpy as np +from libc.math cimport cos, sin +from raysect.primitive.mesh.mesh cimport Mesh +from raysect.core.math.polygon import triangulate2d +from raysect.core.math.cython cimport winding2d + + +cdef double DEG2RAD = 2 * np.pi / 360 + + +cpdef Mesh toroidal_mesh_from_polygon(object polygon, double toroidal_extent, object polygon_triangles=None, int num_toroidal_segments=500): + """ + Generates a watertight Raysect Mesh primitive from the specified 2D polygon in R-Z plane + by extending it in toroidal direction by a given angle and closing the + poloidal faces with triangulated polygons. + + :param object polygon: An object which can be converted to a numpy array with shape + [N,2] specifying the wall outline polygon in the R-Z plane. + The polygon should not be closed, i.e. vertex i = 0 and i = N + should not be the same vertex, but neighbours. + :param object polygon_triangles: An object which can be converted to a numpy array + with shape [M,3] specifying the triangulation + of a polygon (polygon_triangles = [[v1, v2, v3],...), + where v1, v2, v3 are the vertex array indices specifying + the triangle’s vertices. Should be with clockwise winding. + Defaults to None. + If not provided, the triangulation will be performed using + `triangulate2d(polygon)` from raysect.core.math.polygon. + :param float toroidal_extent: Angular extention of an element in toroidal direction (in degrees). + Note that in the case of toroidal_extent=360 produces + an axisymmetric mesh which has no end faces + :param int num_toroidal_segments: The number of repeating toroidal segments + per given `toroidal_extent` that will be used to construct + the mesh. Defaults to 500. + + :return: A watertight Raysect Mesh primitive constructed from the R-Z polygon. + + .. code-block:: pycon + + >>> from cherab.tools.primitives import toroidal_mesh_from_polygon + >>> + >>> # wall_polygon is your (N, 2) ndarray describing the polygon + >>> mesh = toroidal_mesh_from_polygon(wall_polygon, toroidal_extent = 50) + """ + + cdef: + int num_poloidal_vertices, num_toroidal_segments_loop + int i, j, vid, v1_id, v2_id, v3_id, v4_id + double theta, r, x, y, z + list triangles + double[:, :] polygon_mv, vertices_mv + + polygon = np.array(polygon, dtype=np.float64) + + if polygon.ndim != 2: + raise ValueError("The 'polygon' must be a two-dimensional array.") + + if polygon.shape[0] < 2: + raise ValueError("The 'polygon' must contain at least two vertices.") + + if polygon.shape[1] != 2: + raise ValueError("The 'polygon' must have [N, 2] shape.") + + if (toroidal_extent<=0) or (toroidal_extent>360): + raise ValueError("The 'toroidal_extent' should be in the range 0 < 'toroidal_extent' <= 360.") + + num_poloidal_vertices = polygon.shape[0] + theta = toroidal_extent / num_toroidal_segments # toroidal step + + vertices = np.zeros((num_poloidal_vertices * num_toroidal_segments, 3)) + vertices_mv = vertices + polygon_mv = polygon + + for i in range(num_toroidal_segments): + for j in range(num_poloidal_vertices): + + r = polygon_mv[j, 0] + z = polygon_mv[j, 1] + x = r * cos(i * theta * DEG2RAD) + y = r * sin(i * theta * DEG2RAD) + + vid = i * num_poloidal_vertices + j + vertices_mv[vid, 0] = x + vertices_mv[vid, 1] = y + vertices_mv[vid, 2] = z + + # assemble mesh triangles + triangles = [] + + if toroidal_extent != 360.: + if polygon_triangles is None: + # triangulating initial polygon in case of not full axisymmetric mesh + polygon_triangles = triangulate2d(polygon) + + else: + # user-defined triangulation + polygon_triangles = np.array(polygon_triangles, dtype=np.int32) + # check data sanity + _check_polygon_triangulation(polygon, polygon_triangles) + + triangles = triangles + polygon_triangles[:, [1, 0, 2]].tolist() + + num_toroidal_segments_loop = num_toroidal_segments if toroidal_extent == 360. else num_toroidal_segments - 1 + + for i in range(num_toroidal_segments_loop): + for j in range(num_poloidal_vertices): + + if i == num_toroidal_segments - 1 and j == num_poloidal_vertices - 1: + v1_id = i * num_poloidal_vertices + j + v2_id = i * num_poloidal_vertices + 0 + v3_id = j + v4_id = 0 + + elif i == num_toroidal_segments - 1: + v1_id = i * num_poloidal_vertices + j + v2_id = i * num_poloidal_vertices + j + 1 + v3_id = j + v4_id = j + 1 + + elif j == num_poloidal_vertices - 1: + v1_id = i * num_poloidal_vertices + j + v2_id = i * num_poloidal_vertices + v3_id = i * num_poloidal_vertices + num_poloidal_vertices + j + v4_id = i * num_poloidal_vertices + num_poloidal_vertices + + else: + v1_id = i * num_poloidal_vertices + j + v2_id = i * num_poloidal_vertices + j + 1 + v3_id = i * num_poloidal_vertices + num_poloidal_vertices + j + v4_id = i * num_poloidal_vertices + num_poloidal_vertices + j + 1 + + triangles.append([v1_id, v2_id, v4_id]) + triangles.append([v4_id, v3_id, v1_id]) + + if toroidal_extent != 360.: + triangles = triangles + ((num_toroidal_segments - 1) * num_poloidal_vertices + polygon_triangles).tolist() + + return Mesh(vertices=vertices, triangles=triangles, smoothing=False) + + +cdef _check_polygon_triangulation(np.ndarray polygon, np.ndarray polygon_triangles): + + + # check consistency + if not np.array_equal(np.unique(polygon_triangles), np.arange(polygon.shape[0], dtype=np.int32)): + raise ValueError("The data in 'polygon_triangles' does not match a given 'polygon'.") + # check winding + triangles = np.ascontiguousarray(polygon[polygon_triangles]) + if not all(winding2d(triangle) for triangle in triangles): + raise ValueError("All triangles in 'polygon_triangles' must be wound clockwise.") diff --git a/docs/source/tools/primitives.rst b/docs/source/tools/primitives.rst index 95026722..bf634b80 100644 --- a/docs/source/tools/primitives.rst +++ b/docs/source/tools/primitives.rst @@ -7,3 +7,4 @@ A few useful utilities for creating primitives particularly relevant for fusion. .. autofunction:: cherab.tools.primitives.axisymmetric_mesh.axisymmetric_mesh_from_polygon +.. autofunction:: cherab.tools.primitives.toroidal_mesh.toroidal_mesh_from_polygon \ No newline at end of file From 0f83e5d77cf4e868c28e697d87afbe26a537d3bd Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 4 Aug 2022 14:14:08 +0300 Subject: [PATCH 28/59] Add Generomak blended plasma profiles. --- CHANGELOG.md | 1 + cherab/generomak/plasma/__init__.py | 5 +- .../generomak/plasma/data/edge/carbon0.json | 3646 ++++++++--------- .../generomak/plasma/data/edge/hydrogen0.json | 454 +- cherab/generomak/plasma/plasma.py | 292 +- demos/generomak/plasma/plot_2d_plasma.py | 128 + demos/generomak/plasma/plot_2d_profiles.py | 144 + 7 files changed, 2504 insertions(+), 2166 deletions(-) create mode 100755 demos/generomak/plasma/plot_2d_plasma.py create mode 100755 demos/generomak/plasma/plot_2d_profiles.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b5ecac..1d628df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ New: * Add verbose parameter to SartOpencl solver (default is False). (#358) * Add Generomak core plasma profiles. (#360) * Add toroidal_mesh_from_polygon for making mesh for not fully-360 degrees axisymmetric elements. (#365) +* Add Generomak full plasma profiles obtained by blending the core and edge profiles. (#372) Bug Fixes: ---------- diff --git a/cherab/generomak/plasma/__init__.py b/cherab/generomak/plasma/__init__.py index 8a28ab1a..7cccf703 100644 --- a/cherab/generomak/plasma/__init__.py +++ b/cherab/generomak/plasma/__init__.py @@ -1,2 +1,3 @@ -from .plasma import get_edge_distributions, get_edge_interpolators, get_edge_plasma -from .plasma import get_core_distributions, get_core_plasma \ No newline at end of file +from .plasma import get_2d_distributions, get_edge_interpolators, get_edge_plasma +from .plasma import get_core_distributions, get_core_plasma +from .plasma import get_full_profiles, get_plasma diff --git a/cherab/generomak/plasma/data/edge/carbon0.json b/cherab/generomak/plasma/data/edge/carbon0.json index beee5028..cc58c95c 100644 --- a/cherab/generomak/plasma/data/edge/carbon0.json +++ b/cherab/generomak/plasma/data/edge/carbon0.json @@ -36,52 +36,52 @@ 0.8826349542181627, 0.8899903230020518, 0.8899903230020518, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7047757257456048, 0.7047757257456048, 0.7072344434153071, @@ -94,10 +94,10 @@ 0.771280502896162, 0.7378957943285048, 0.7378957943285048, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6668202352525383, 0.6668202352525383, 0.6939803492353266, @@ -114,56 +114,56 @@ 0.7550529537931084, 0.6698586018699858, 0.6698586018699858, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.8777104035584131, 0.8777104035584131, 0.8904284769390789, @@ -232,52 +232,52 @@ 0.8826349542181627, 0.8899903230020518, 0.8899903230020518, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7047757257456048, 0.7047757257456048, 0.7072344434153071, @@ -290,10 +290,10 @@ 0.771280502896162, 0.7378957943285048, 0.7378957943285048, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6668202352525383, 0.6668202352525383, 0.6939803492353266, @@ -310,56 +310,56 @@ 0.7550529537931084, 0.6698586018699858, 0.6698586018699858, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.8777104035584131, 0.8777104035584131, 0.8904284769390789, @@ -428,52 +428,52 @@ 0.9488695364408866, 0.9776952595352856, 0.9776952595352856, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7381857748401043, 0.7381857748401043, 0.7252051835877666, @@ -486,10 +486,10 @@ 0.6707223018957097, 0.6653150953392322, 0.6653150953392322, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6712374760547157, 0.6712374760547157, 0.6863147774504369, @@ -506,62 +506,62 @@ 0.7169300660266651, 0.7016922330175489, 0.7016922330175489, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 0.989668471223005, - 0.989668471223005, - 0.9084402862412485, - 0.9084402862412485, - 0.9888120737628983, - 0.9888120737628983, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 0.989668471223005, + 0.989668471223005, + 0.9084402862412485, + 0.9084402862412485, + 0.9888120737628983, + 0.9888120737628983, 0.9238193646007199, 0.9238193646007199, 0.9402302268252902, @@ -624,52 +624,52 @@ 1.0141940442254633, 1.0314660474570372, 1.0314660474570372, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7425897836430437, 0.7425897836430437, 0.7658060128718618, @@ -682,10 +682,10 @@ 0.671465665626478, 0.6819424130985049, 0.6819424130985049, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7291516897755482, 0.7291516897755482, 0.7241049927832115, @@ -702,56 +702,56 @@ 0.7451781374562225, 0.6630318889047037, 0.6630318889047037, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 1.041544898725567, 1.041544898725567, 0.9741726142287506, @@ -820,52 +820,52 @@ 1.0599891197764155, 1.0291050094043501, 1.0291050094043501, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7562683004400875, 0.7562683004400875, 0.7327700174161945, @@ -878,10 +878,10 @@ 0.6702462619986006, 0.7024151245985529, 0.7024151245985529, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.697839411880975, 0.697839411880975, 0.7256309169217358, @@ -898,56 +898,56 @@ 0.7184151082807516, 0.6821155525602305, 0.6821155525602305, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.9521070696129001, 0.9521070696129001, 0.875934444566366, @@ -1016,52 +1016,52 @@ 0.9521600600249424, 0.9985453326739754, 0.9985453326739754, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7107102149886928, 0.7107102149886928, 0.6925560368645347, @@ -1074,10 +1074,10 @@ 0.7163705771532305, 0.7085061508892283, 0.7085061508892283, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6568431829970128, 0.6568431829970128, 0.6666301188861303, @@ -1094,56 +1094,56 @@ 0.6694595197797648, 0.7170387931147423, 0.7170387931147423, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 20.447996996565863, 20.447996996565863, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.9327311785025072, 0.9327311785025072, 0.9597158436652123, @@ -1212,52 +1212,52 @@ 1.0298700935866976, 0.9528687209652565, 0.9528687209652565, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6387338189080094, 0.6387338189080094, 0.6234247702803535, @@ -1270,10 +1270,10 @@ 0.6924896272079825, 0.6741197425302109, 0.6741197425302109, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7490929991929967, 0.7490929991929967, 0.7573533243775917, @@ -1290,56 +1290,56 @@ 0.6863972902004013, 0.7098833398665082, 0.7098833398665082, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 29.736571479671202, 29.736571479671202, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 41.119296463288705, 41.119296463288705, 20.70607715528574, 20.70607715528574, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 20.447996996565863, 20.447996996565863, 11.907214594917132, 11.907214594917132, 11.053179545995052, 11.053179545995052, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.9374114989121731, 0.9374114989121731, 1.0162759619923405, @@ -1408,52 +1408,52 @@ 0.9172602251294597, 1.034558340712888, 1.034558340712888, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7698504483370217, 0.7698504483370217, 0.6974013203590386, @@ -1466,10 +1466,10 @@ 0.7918775452569733, 0.6937161461562047, 0.6937161461562047, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6507337442545676, 0.6507337442545676, 0.6520238017651679, @@ -1486,60 +1486,60 @@ 0.668291795847024, 0.715565672142988, 0.715565672142988, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 29.736571479671202, 29.736571479671202, 28.954414273401518, 28.954414273401518, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 41.119296463288705, 41.119296463288705, 20.70607715528574, 20.70607715528574, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 20.436121901400792, 20.436121901400792, 22.917604227150402, 22.917604227150402, 11.053179545995052, 11.053179545995052, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 1.0904314561361903, - 1.0904314561361903, - 1.085821664779066, - 1.085821664779066, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 1.0904314561361903, + 1.0904314561361903, + 1.085821664779066, + 1.085821664779066, 1.0145245945460468, 1.0145245945460468, 0.9934922693423827, @@ -1606,50 +1606,50 @@ 1.078138055032951, 0.6426753943036221, 0.6426753943036221, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7200699195816633, 0.7200699195816633, 0.755049895453662, @@ -1662,10 +1662,10 @@ 0.6385266008067373, 0.6417090838687141, 0.6417090838687141, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6539603547857009, 0.6539603547857009, 0.6483565406933778, @@ -1682,30 +1682,30 @@ 0.6835863641736272, 0.6922198691857843, 0.6922198691857843, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 29.736571479671202, 29.736571479671202, 28.954414273401518, 28.954414273401518, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 12.009592819963695, 12.009592819963695, 20.87818801631581, 20.87818801631581, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 20.462200798766613, 20.462200798766613, 23.184476175552565, @@ -1714,22 +1714,22 @@ 11.053179545995052, 40.216868497908706, 40.216868497908706, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 47.12362569631633, 47.12362569631633, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 1.271636695208476, 1.271636695208476, 1.077981642815545, @@ -1802,50 +1802,50 @@ 1.6613708772886773, 1.8250934621981263, 1.8250934621981263, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6754564865286883, 0.6754564865286883, 0.6830080259427875, @@ -1858,10 +1858,10 @@ 0.6935655385422379, 0.6392340134452367, 0.6392340134452367, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6822174139883256, 0.6822174139883256, 0.6678244940626191, @@ -1878,30 +1878,30 @@ 0.7375110677091551, 0.6868580384002779, 0.6868580384002779, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 29.736571479671202, 29.736571479671202, 28.954414273401518, 28.954414273401518, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 11.486304075009997, 11.486304075009997, 21.400253425490913, 21.400253425490913, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 20.46202541236162, 20.46202541236162, 28.1268806720096, @@ -1910,22 +1910,22 @@ 11.053179545995052, 40.216868497908706, 40.216868497908706, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 47.12362569631633, 47.12362569631633, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 4.073020328419045, 4.073020328419045, 1.5613878937645274, @@ -1990,58 +1990,58 @@ 1.9632484541651354, 2.1295862313767837, 2.1295862313767837, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6763005882159183, 0.6763005882159183, 0.7898026179802595, @@ -2054,10 +2054,10 @@ 0.6937591501537277, 0.7556495172304454, 0.7556495172304454, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.648692521126856, 0.648692521126856, 0.681328248605578, @@ -2074,18 +2074,18 @@ 0.6840589712407453, 0.7241738366283028, 0.7241738366283028, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 29.736571479671202, 29.736571479671202, 28.592746909327357, @@ -2110,38 +2110,38 @@ 10.298871329064708, 47.12362569631633, 47.12362569631633, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 4.25838478430837, - 4.25838478430837, - 2.2399058404942385, - 2.2399058404942385, - 1.6997660820960394, - 1.6997660820960394, - 1.4481581186260142, - 1.4481581186260142, - 1.6029574676720695, - 1.6029574676720695, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 4.25838478430837, + 4.25838478430837, + 2.2399058404942385, + 2.2399058404942385, + 1.6997660820960394, + 1.6997660820960394, + 1.4481581186260142, + 1.4481581186260142, + 1.6029574676720695, + 1.6029574676720695, 1.4764963798616977, 1.4764963798616977, 1.0559663423477441, @@ -2180,64 +2180,64 @@ 2.054170888626254, 3.687400861196182, 3.687400861196182, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7016610254721765, 0.7016610254721765, 0.7150149213822576, @@ -2250,10 +2250,10 @@ 0.7040376672975498, 0.7555713111217425, 0.7555713111217425, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6900798429694239, 0.6900798429694239, 0.7153411650615797, @@ -2270,18 +2270,18 @@ 0.6640386443184142, 0.6994095259037463, 0.6994095259037463, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 27.739809741851474, 27.739809741851474, 38.84926023705824, @@ -2306,38 +2306,38 @@ 10.298871329064708, 47.12362569631633, 47.12362569631633, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 12.78479636097352, 12.78479636097352, 3.6748473139947193, @@ -2368,72 +2368,72 @@ 3.527400649883651, 19.403170249953853, 19.403170249953853, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.691325710595777, 0.691325710595777, 0.6673680525040038, @@ -2446,10 +2446,10 @@ 0.7359544353459845, 0.6783759523982673, 0.6783759523982673, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6720141694439417, 0.6720141694439417, 0.6500762012735732, @@ -2466,18 +2466,18 @@ 0.6696363417318444, 0.6958262131290076, 0.6958262131290076, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 27.613459752902624, 27.613459752902624, 50.56896491975679, @@ -2502,42 +2502,42 @@ 9.900632466719646, 47.12362569631633, 47.12362569631633, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 12.102842213837954, 12.102842213837954, 1.7662817818874772, @@ -2564,72 +2564,72 @@ 4.423797944365727, 5.186650225483191, 5.186650225483191, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 6.087607566495069, 6.087607566495069, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6851429341217069, 0.6851429341217069, 0.6554378448138072, @@ -2642,10 +2642,10 @@ 0.7649226520925533, 0.6514028964424405, 0.6514028964424405, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.674137093925438, 0.674137093925438, 0.6775360949372078, @@ -2662,14 +2662,14 @@ 0.6772589719343018, 0.6916349149553257, 0.6916349149553257, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 10.416500681534718, 10.416500681534718, 36.59624148531928, @@ -2700,40 +2700,40 @@ 47.12362569631633, 7.919889561939525, 7.919889561939525, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 3.4987177325168757, 3.4987177325168757, 1.6479893314933964, @@ -2758,74 +2758,74 @@ 1.2275594077849972, 5.494688671136869, 5.494688671136869, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 6.087607566495069, 6.087607566495069, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7292143545266557, 0.7292143545266557, 0.6891375623444526, @@ -2838,10 +2838,10 @@ 0.7586520575858056, 0.6745911012955143, 0.6745911012955143, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6433718218861504, 0.6433718218861504, 0.6992843212317127, @@ -2858,14 +2858,14 @@ 0.6969783957041531, 0.7316343748400964, 0.7316343748400964, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 10.685891702999333, 10.685891702999333, 36.59624148531928, @@ -2896,36 +2896,36 @@ 8.577289612376159, 7.137119439478731, 7.137119439478731, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 9.916620092213877, 9.916620092213877, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 6.42574718762251, 6.42574718762251, 3.179734301380406, @@ -2956,72 +2956,72 @@ 2.9325974429358705, 23.358836476453074, 23.358836476453074, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 33.60428485689675, 33.60428485689675, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 6.087607566495069, 6.087607566495069, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 32.071443253865354, 32.071443253865354, 15.43404108838102, 15.43404108838102, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7621497992711334, 0.7621497992711334, 0.7179255867240416, @@ -3034,10 +3034,10 @@ 0.767890115166915, 0.7059415772256232, 0.7059415772256232, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.636204010449949, 0.636204010449949, 0.6790766242069662, @@ -3054,14 +3054,14 @@ 0.6941961806191215, 0.649713319935984, 0.649713319935984, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 12.239578698037612, 12.239578698037612, 19.242109356589207, @@ -3094,20 +3094,20 @@ 5.711142083726083, 6.620745038277721, 6.620745038277721, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 13.11318150205903, 13.11318150205903, 6.593562642107537, @@ -3156,50 +3156,50 @@ 33.669584773135576, 33.49419462323778, 33.49419462323778, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 8.171498524051001, 8.171498524051001, 38.45923270405153, 38.45923270405153, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 15.860288098546818, 15.860288098546818, 32.071443253865354, @@ -3208,16 +3208,16 @@ 15.43404108838102, 18.948368959923304, 18.948368959923304, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7029588848691198, 0.7029588848691198, 0.6739359300879681, @@ -3230,10 +3230,10 @@ 0.7166222347991128, 0.7328946603524116, 0.7328946603524116, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6357189827797727, 0.6357189827797727, 0.6683781783326145, @@ -3250,14 +3250,14 @@ 0.6741188063038498, 0.7088372005305378, 0.7088372005305378, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 12.229961156704775, 12.229961156704775, 18.50145319245743, @@ -3354,42 +3354,42 @@ 30.909596326069007, 27.39453570136138, 27.39453570136138, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 5.20877762345397, 5.20877762345397, 6.623242890209321, 6.623242890209321, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 4.480751652255091, 4.480751652255091, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 9.424871565066152, 9.424871565066152, 25.002127199915215, @@ -3404,16 +3404,16 @@ 15.43404108838102, 18.948368959923304, 18.948368959923304, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 15.397359739550415, 15.397359739550415, 15.397359739550415, 15.397359739550415, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6738766357517608, 0.6738766357517608, 0.6770712897564328, @@ -3426,10 +3426,10 @@ 0.7541213461486544, 0.6783959252273055, 0.6783959252273055, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6463107612640417, 0.6463107612640417, 0.666387511428406, @@ -3446,14 +3446,14 @@ 0.6821862064429534, 0.7122229695430697, 0.7122229695430697, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 23.867441447158193, 23.867441447158193, 18.074834812501706, @@ -3550,38 +3550,38 @@ 27.39453570136138, 35.695627926627225, 35.695627926627225, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 59.91215073481093, 59.91215073481093, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 5.20877762345397, 5.20877762345397, 6.623242890209321, 6.623242890209321, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 6.289243511711331, 6.289243511711331, 6.692733355640737, @@ -3604,12 +3604,12 @@ 24.511241873472486, 8.926741719040699, 8.926741719040699, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6638601995739754, 0.6638601995739754, 0.6726811995187293, @@ -3622,10 +3622,10 @@ 0.7778953790434571, 0.6578610482968759, 0.6578610482968759, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6443494918675741, 0.6443494918675741, 1.0633255808672593, @@ -3642,12 +3642,12 @@ 0.6765483137110837, 0.6952242195787759, 0.6952242195787759, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 5.307164528277598, 5.307164528277598, 20.442098770490496, @@ -3746,34 +3746,34 @@ 1.2827860276983667, 35.69119083782619, 35.69119083782619, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 59.91215073481093, 59.91215073481093, 59.91215073481093, 59.91215073481093, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 5.20877762345397, 5.20877762345397, 6.623242890209321, 6.623242890209321, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 0.6666763684683721, 0.6666763684683721, 0.7227419720315307, @@ -3802,10 +3802,10 @@ 6.311529444012602, 1.1100967036072755, 1.1100967036072755, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6891511064191441, 0.6891511064191441, 0.6774362932071071, @@ -3818,10 +3818,10 @@ 0.7280618598760566, 0.7217131216756966, 0.7217131216756966, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.702498199084334, 0.702498199084334, 0.8427326122145906, @@ -3838,12 +3838,12 @@ 0.6723606980277558, 0.6898971539988143, 0.6898971539988143, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 13.651777548017844, 13.651777548017844, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 7.851858361329716, 7.851858361329716, 18.578210646904243, @@ -3942,26 +3942,26 @@ 1.2281109075268166, 28.424399054118275, 28.424399054118275, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 47.648711995883474, 47.648711995883474, 52.974239043858134, 52.974239043858134, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 38.28753003771493, 38.28753003771493, 7.8610140309910435, 7.8610140309910435, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 0.6666763684683721, 0.6666763684683721, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 33.80948695073779, 33.80948695073779, 5.20877762345397, @@ -4014,10 +4014,10 @@ 0.7272484039983822, 0.6604385418842652, 0.6604385418842652, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6681542954021136, 0.6681542954021136, 0.8068853162416049, @@ -4154,8 +4154,8 @@ 0.6666763684683721, 0.6666763684683721, 0.6666763684683721, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 33.80948695073779, 33.80948695073779, 33.80948695073779, @@ -4210,10 +4210,10 @@ 0.743756196858879, 0.6876543925430885, 0.6876543925430885, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7303031233696048, 0.7303031233696048, 0.7414673730661835, @@ -4348,8 +4348,8 @@ 2.796235137205228, 0.6666763684683721, 0.6666763684683721, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, 33.80948695073779, 33.80948695073779, 21.166854066104175, @@ -4406,10 +4406,10 @@ 0.7093227899365309, 0.6636280778514962, 0.6636280778514962, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.726225420661078, 0.726225420661078, 0.703051009543059, @@ -4602,10 +4602,10 @@ 0.7129741351601812, 0.6611613086350877, 0.6611613086350877, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6949256881997445, 0.6949256881997445, 0.6979661769302773, @@ -4798,10 +4798,10 @@ 0.6918136717752184, 0.7215496565530365, 0.7215496565530365, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6959255779534731, 0.6959255779534731, 0.6880125303337811, @@ -4994,10 +4994,10 @@ 0.6835688879482187, 0.7268541902852392, 0.7268541902852392, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6848245547438186, 0.6848245547438186, 0.679257503139944, @@ -5190,10 +5190,10 @@ 0.683777479181487, 0.7169371189319192, 0.7169371189319192, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6898697537739775, 0.6898697537739775, 0.688960740392373, @@ -5386,10 +5386,10 @@ 0.6829814371141303, 0.714735239360631, 0.714735239360631, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6944113878520088, 0.6944113878520088, 0.6889494432609483, @@ -5582,10 +5582,10 @@ 0.6810513128479442, 0.7179132285360741, 0.7179132285360741, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7000713755260022, 0.7000713755260022, 0.6947092950801328, @@ -5778,10 +5778,10 @@ 0.6677305593510484, 0.7192147080082807, 0.7192147080082807, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6944829155460022, 0.6944829155460022, 0.6939876518009437, @@ -5974,10 +5974,10 @@ 0.6723199409734995, 0.7051740588547367, 0.7051740588547367, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6991042536949145, 0.6991042536949145, 0.6833483130175272, @@ -6170,10 +6170,10 @@ 0.6985494459532857, 0.684618210453817, 0.684618210453817, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.6994964077100628, 0.6994964077100628, 0.7044508551982791, @@ -6366,10 +6366,10 @@ 0.6881025953097255, 0.7203669529985169, 0.7203669529985169, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7151509862800808, 0.7151509862800808, 0.747833337831589, @@ -6562,10 +6562,10 @@ 0.6867911294229997, 0.7382354572523369, 0.7382354572523369, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7029822905281491, 0.7029822905281491, 0.7433765258618795, @@ -6758,10 +6758,10 @@ 0.705876852776521, 0.7732053842947257, 0.7732053842947257, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7010752598455384, 0.7010752598455384, 0.7448947105291513, @@ -6954,10 +6954,10 @@ 0.7567317324888662, 0.7563844549239631, 0.7563844549239631, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.7127471938902338, 0.7127471938902338, 0.7629452796026733, @@ -7150,10 +7150,10 @@ 0.7424246333129336, 0.8932101927033845, 0.8932101927033845, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.8063032955204114, 0.8063032955204114, 0.7377027444528318, @@ -7346,10 +7346,10 @@ 0.7424246333129336, 0.8932101927033845, 0.8932101927033845, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, + 152.00613642203447, 0.8063032955204114, 0.8063032955204114, 0.7377027444528318, @@ -14901,4 +14901,4 @@ 0.0, 0.0 ] -} \ No newline at end of file +} diff --git a/cherab/generomak/plasma/data/edge/hydrogen0.json b/cherab/generomak/plasma/data/edge/hydrogen0.json index 50252db1..b255b8b3 100644 --- a/cherab/generomak/plasma/data/edge/hydrogen0.json +++ b/cherab/generomak/plasma/data/edge/hydrogen0.json @@ -36,22 +36,22 @@ 1.972944700927401, 2.1469916156572784, 2.1469916156572784, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 1060.8616203299343, - 1060.8616203299343, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 237.02952717009856, 237.02952717009856, 510.5159897120308, @@ -94,10 +94,10 @@ 3.2178661145048255, 3.0058149630960105, 3.0058149630960105, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.228549122630633, 4.228549122630633, 4.632909532245744, @@ -114,8 +114,8 @@ 3.701953002018378, 3.344396295820652, 3.344396295820652, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 390.9182712497354, 390.9182712497354, 368.29180845487235, @@ -160,10 +160,10 @@ 327.66094502911096, 224.2842595343954, 224.2842595343954, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 2.28095818054478, 2.28095818054478, 2.2121432336404925, @@ -232,22 +232,22 @@ 1.972944700927401, 2.1469916156572784, 2.1469916156572784, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 1060.8616203299343, - 1060.8616203299343, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 237.02952717009856, 237.02952717009856, 510.5159897120308, @@ -290,10 +290,10 @@ 3.2178661145048255, 3.0058149630960105, 3.0058149630960105, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.228549122630633, 4.228549122630633, 4.632909532245744, @@ -310,8 +310,8 @@ 3.701953002018378, 3.344396295820652, 3.344396295820652, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 390.9182712497354, 390.9182712497354, 368.29180845487235, @@ -356,10 +356,10 @@ 327.66094502911096, 224.2842595343954, 224.2842595343954, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 2.28095818054478, 2.28095818054478, 2.2121432336404925, @@ -432,14 +432,14 @@ 773.3934409631391, 773.3934409631391, 773.3934409631391, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 1060.8616203299343, 1060.8616203299343, 172.10538098510307, @@ -486,10 +486,10 @@ 3.032072679696813, 3.370428818774048, 3.370428818774048, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 5.3896244750752, 5.3896244750752, 4.462110574008034, @@ -624,14 +624,14 @@ 2.147574884680287, 2.194014083967723, 2.194014083967723, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 773.3934409631391, 773.3934409631391, 773.3934409631391, 773.3934409631391, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 298.78233138681514, 298.78233138681514, 59.11285621707551, @@ -682,10 +682,10 @@ 3.1739117848101137, 3.592521996547854, 3.592521996547854, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.050162985962009, 4.050162985962009, 4.408568162903317, @@ -748,10 +748,10 @@ 223.31419795253365, 240.54622431848549, 240.54622431848549, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 2.3245336506386725, 2.3245336506386725, 2.224360425917933, @@ -820,10 +820,10 @@ 2.2302022287512653, 2.315271438417445, 2.315271438417445, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 69.51090013212614, 69.51090013212614, 492.924708325262, @@ -878,10 +878,10 @@ 2.9181949735012807, 3.2768293386558027, 3.2768293386558027, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.324206553120909, 4.324206553120909, 4.620829840413214, @@ -946,8 +946,8 @@ 156.99101750849775, 232.87885497898233, 232.87885497898233, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 2.2079833302574556, 2.2079833302574556, 2.2547071423586873, @@ -1016,10 +1016,10 @@ 2.1893738964614062, 2.04235408915594, 2.04235408915594, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 52.38173633232502, 52.38173633232502, 101.12902445436613, @@ -1074,10 +1074,10 @@ 3.4798216262090365, 3.1222993731414017, 3.1222993731414017, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.816413082204519, 4.816413082204519, 4.044458683573587, @@ -1142,8 +1142,8 @@ 92.01574712267337, 270.2239009185301, 270.2239009185301, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 2.2063231512587396, 2.2063231512587396, 2.1501370865704437, @@ -1212,8 +1212,8 @@ 2.3205649246785858, 2.1310041774083195, 2.1310041774083195, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, 100.91711897977912, 100.91711897977912, 44.17568980724506, @@ -1270,10 +1270,10 @@ 2.962358144089624, 2.6885652983502446, 2.6885652983502446, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.510954002590953, 4.510954002590953, 5.1300036622553815, @@ -1466,10 +1466,10 @@ 3.091068609355341, 3.595791860736873, 3.595791860736873, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.758281560358844, 3.758281560358844, 4.7689404762596235, @@ -1662,10 +1662,10 @@ 3.178992185951452, 3.0699698744951243, 3.0699698744951243, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.2251648103863184, 3.2251648103863184, 4.289231195965626, @@ -1858,10 +1858,10 @@ 2.525787178593943, 2.825822761312346, 2.825822761312346, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.9410852499050995, 3.9410852499050995, 3.3806876751642854, @@ -2054,10 +2054,10 @@ 3.019009700524693, 2.3753950839355458, 2.3753950839355458, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.197689853464683, 4.197689853464683, 3.755099077296867, @@ -2250,10 +2250,10 @@ 2.877357840683626, 3.041679111143497, 3.041679111143497, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.5308683699103307, 3.5308683699103307, 3.276232088652468, @@ -2446,10 +2446,10 @@ 2.7262409819915026, 4.0978295780089375, 4.0978295780089375, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.0636950981772966, 3.0636950981772966, 3.487733612772186, @@ -2642,10 +2642,10 @@ 2.8434261886757737, 2.888884534812159, 2.888884534812159, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 2.965325731994167, 2.965325731994167, 3.2970243030020376, @@ -2838,10 +2838,10 @@ 2.6858381957903403, 2.9947567566386066, 2.9947567566386066, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 2.707415654396568, 2.707415654396568, 3.4228154272283566, @@ -3034,10 +3034,10 @@ 3.1050752422844288, 2.5827768999906664, 2.5827768999906664, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.3679896994428393, 3.3679896994428393, 3.7827563898925267, @@ -3230,10 +3230,10 @@ 3.2214229008659983, 3.112208101269813, 3.112208101269813, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.079169463159205, 4.079169463159205, 4.373918362861358, @@ -3426,10 +3426,10 @@ 3.182294755648022, 3.540194869675025, 3.540194869675025, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.426191958807458, 3.426191958807458, 3.890835422082432, @@ -3622,10 +3622,10 @@ 2.9324045178903795, 3.031484542296727, 3.031484542296727, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.048504929076378, 4.048504929076378, 4.101542214851699, @@ -3818,10 +3818,10 @@ 3.1031142849634143, 3.186577866420189, 3.186577866420189, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 5.138463029164361, 5.138463029164361, 4.226479438221542, @@ -4014,10 +4014,10 @@ 2.9823071305632336, 3.301914899852422, 3.301914899852422, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.230204495667361, 4.230204495667361, 3.7793528325791326, @@ -4210,10 +4210,10 @@ 2.7946538508812133, 3.6766072947235355, 3.6766072947235355, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.347594361434184, 4.347594361434184, 3.2985799991388465, @@ -4406,10 +4406,10 @@ 2.9905828098601517, 3.2936387212347777, 3.2936387212347777, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.6020830522235667, 3.6020830522235667, 3.408852235202427, @@ -4602,10 +4602,10 @@ 2.990016392911645, 2.251912069764962, 2.251912069764962, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.33474979388571, 3.33474979388571, 3.552615972053928, @@ -4798,10 +4798,10 @@ 3.1068532609744706, 2.035272972280783, 2.035272972280783, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.346502929963464, 3.346502929963464, 3.4188598084373263, @@ -4994,10 +4994,10 @@ 3.050768558393544, 2.5736518761388956, 2.5736518761388956, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.719975297055793, 3.719975297055793, 3.6058544216667188, @@ -5190,10 +5190,10 @@ 2.9544671289969644, 2.6266115175413303, 2.6266115175413303, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.621674275397029, 3.621674275397029, 3.7044403682147324, @@ -5386,10 +5386,10 @@ 2.7672878919291497, 2.397112914080858, 2.397112914080858, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.811473760389393, 3.811473760389393, 3.8450036464581223, @@ -5582,10 +5582,10 @@ 2.630605459198078, 2.492375257046721, 2.492375257046721, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.876404241706099, 3.876404241706099, 3.662145218877284, @@ -5778,10 +5778,10 @@ 2.8051830270294658, 2.551368128365802, 2.551368128365802, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.877623582918886, 3.877623582918886, 4.0914143677369355, @@ -5974,10 +5974,10 @@ 2.6751353808596363, 2.908345372861055, 2.908345372861055, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.916432724608066, 3.916432724608066, 4.1287282310971465, @@ -6170,10 +6170,10 @@ 2.6903946222448782, 2.7719752652440692, 2.7719752652440692, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 3.539496944130319, 3.539496944130319, 4.011383928346568, @@ -6366,10 +6366,10 @@ 2.4393379088563094, 2.553484436847679, 2.553484436847679, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.608095663939136, 4.608095663939136, 4.553307572453338, @@ -6562,10 +6562,10 @@ 2.4487560963893076, 2.596682670133111, 2.596682670133111, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.667622184284084, 4.667622184284084, 4.634387022273851, @@ -6758,10 +6758,10 @@ 2.540889570856143, 3.099676149689748, 3.099676149689748, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.975413715838775, 4.975413715838775, 4.372279592238767, @@ -6954,10 +6954,10 @@ 2.8814299260339857, 2.778316014312814, 2.778316014312814, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 4.494170272614275, 4.494170272614275, 4.624706129623908, @@ -7150,10 +7150,10 @@ 3.1383402387080377, 3.087223090784384, 3.087223090784384, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 5.0891368198545335, 5.0891368198545335, 4.804279214073222, @@ -7346,10 +7346,10 @@ 3.1383402387080377, 3.087223090784384, 3.087223090784384, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, - 6241509074460.763, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, + 1060.8616203299343, 5.0891368198545335, 5.0891368198545335, 4.804279214073222, @@ -14901,4 +14901,4 @@ 0.0, 0.0 ] -} \ No newline at end of file +} diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index f06335b4..65fef528 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -1,7 +1,7 @@ -# Copyright 2016-2021 Euratom -# Copyright 2016-2021 United Kingdom Atomic Energy Authority -# Copyright 2016-2021 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -24,7 +24,8 @@ from raysect.core import Vector3D, translate from raysect.core.math.function.float.function2d.interpolate import Discrete2DMesh -from raysect.core.math.function.float import Arg1D, Exp1D, Constant1D +from raysect.core.math.function.float import Arg1D, Exp1D, Constant1D, Interpolator1DArray, Blend2D +from raysect.core.math.function.vector3d import Constant2D as ConstantVector2D, Blend2D as BlendVector2D from raysect.primitive import Cylinder, Subtract from cherab.core import AtomicData, Plasma, Maxwellian, Species @@ -106,64 +107,66 @@ def get_edge_interpolators(): profiles["mesh"]["triangles"], profiles["electron"]["temperature"], limit=False) ne = Discrete2DMesh.instance(te, profiles["electron"]["density"], limit=False) + ve = ConstantVector2D(Vector3D(0, 1.e-10, 0)) # avoid zero-length vectors for blending mesh_interp["electron"]["temperature"] = te mesh_interp["electron"]["density"] = ne + mesh_interp["electron"]["velocity"] = ve for elem_name, elem_data in profiles["composition"].items(): for stage, stage_data in elem_data.items(): t = Discrete2DMesh.instance(te, stage_data["temperature"], limit=False) n = Discrete2DMesh.instance(te, stage_data["density"], limit=False) + v = ConstantVector2D(Vector3D(0, 1.e-10, 0)) # avoid zero-length vectors for blending mesh_interp["composition"][elem_name][stage]["temperature"] = t mesh_interp["composition"][elem_name][stage]["density"] = n + mesh_interp["composition"][elem_name][stage]["velocity"] = v mesh_interp["composition"][elem_name][stage]["element"] = stage_data["element"] return mesh_interp.freeze() -def get_edge_distributions(): +def get_2d_distributions(profiles_2d=None): """ - Provides Generomak edge Maxwellian distribution of plasma species + Provides Generomak Maxwellian distribution of plasma species for 2d profiles + :param profiles_2d: Dictionary with 2D profile interpolators in the shape + returned by the get_edge_interpolators() or get_full_profiles() functions. + If not specified, will use the value returned by get_edge_interpolators(). :return: Dictionary holding instances of Maxwellian distributions for plasma species. """ - mesh_interp = get_edge_interpolators() - - zero_vector = Vector3D(0, 0, 0) + profiles_2d = profiles_2d or get_edge_interpolators() dists = RecursiveDict() - n3d = AxisymmetricMapper(mesh_interp["electron"]["density"]) - t3d = AxisymmetricMapper(mesh_interp["electron"]["temperature"]) + n3d = AxisymmetricMapper(profiles_2d["electron"]["density"]) + t3d = AxisymmetricMapper(profiles_2d["electron"]["temperature"]) + v3d = VectorAxisymmetricMapper(profiles_2d["electron"]["velocity"]) - dists["electron"] = Maxwellian(n3d, t3d, zero_vector, electron_mass) + dists["electron"] = Maxwellian(n3d, t3d, v3d, electron_mass) - for elem_name, elem_data in mesh_interp["composition"].items(): + for elem_name, elem_data in profiles_2d["composition"].items(): for stage, stage_data in elem_data.items(): - # get element or isotope - try: - element = lookup_isotope(elem_name) - except ValueError: - element = lookup_element(elem_name) + spec_cherab = _get_cherab_element(elem_name) n3d = AxisymmetricMapper(stage_data["density"]) t3d = AxisymmetricMapper(stage_data["temperature"]) - mass = element.atomic_weight * atomic_mass - dists["composition"][elem_name][stage]["distribution"] = Maxwellian(n3d, t3d, zero_vector, mass) - dists["composition"][elem_name][stage]["element"] = element + v3d = VectorAxisymmetricMapper(stage_data["velocity"]) + mass = spec_cherab.atomic_weight * atomic_mass + dists["composition"][elem_name][stage] = Maxwellian(n3d, t3d, v3d, mass) return dists.freeze() def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma"): """ - Provides Generomak Edge plasma. + Provides Generomak default edge plasma. - :param atomic_data: Instance of AtomicData, default isOpenADAS() + :param atomic_data: Instance of AtomicData, default is OpenADAS() :param parent: parent of the plasma node, defaults None :param name: name of the plasma node, defaults "Generomak edge plasma" :return: populated Plasma object @@ -172,12 +175,8 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") # load Generomak equilibrium equilibrium = load_equilibrium() - # create or check atomic_data - if atomic_data is not None: - if not isinstance(atomic_data, AtomicData): - raise ValueError("atomic_data has to be of type AtomicData") - else: - atomic_data = OpenADAS() + # get edge distributions + distributions = get_2d_distributions() # base plasma geometry on mesh vertices profiles_dir = os.path.join(os.path.dirname(__file__), "data/edge") @@ -188,38 +187,10 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") vertex_coords = np.asarray(mesh["vertex_coords"]) r_range = (vertex_coords[:, 0].min(), vertex_coords[:, 0].max()) z_range = (vertex_coords[:, 1].min(), vertex_coords[:, 1].max()) - plasma_height = z_range[1] - z_range[0] - - padding = 1e-3 # enlarge for safety - - outer_column = Cylinder(radius=r_range[1], height=plasma_height) - inner_column = Cylinder(radius=r_range[0], height=plasma_height + 2 * padding) - inner_column.transform = translate(0, 0, -padding) - - plasma_geometry = Subtract(outer_column, inner_column) - geometry_transform = translate(0, 0, z_range[0]) - - # get distributions - dists = get_edge_distributions() - # create plasma composition list - plasma_composition = [] - for elem_data in dists["composition"].values(): - for stage, stage_data in elem_data.items(): - species = Species(stage_data["element"], stage, stage_data["distribution"]) - plasma_composition.append(species) - - # Populate plasma - plasma = Plasma(parent=parent) - plasma.name = name - plasma.geometry = plasma_geometry - plasma.atomic_data = atomic_data - plasma.electron_distribution = dists["electron"] - plasma.composition = plasma_composition - plasma.geometry_transform = geometry_transform - plasma.b_field = VectorAxisymmetricMapper(equilibrium.b_field) - - return plasma + return get_plasma(equilibrium=equilibrium, distributions=distributions, + r_range=r_range, z_range=z_range, atomic_data=atomic_data, + parent=parent, name=name) def get_double_parabola(v_min, v_max, convexity, concavity, xmin=0, xmax=1): @@ -245,7 +216,7 @@ def get_double_parabola(v_min, v_max, convexity, concavity, xmin=0, xmax=1): :return: Function1D """ - x = Arg1D() #the free parameter + x = Arg1D() # the free parameter # funciton for the normalised free variable x_norm = ClampInput1D((x - xmin) / (xmax - xmin), 0, 1) @@ -271,7 +242,7 @@ def get_exponential_growth(initial_value, growth_rate, initial_position=1): :return: Function1D """ - x = Arg1D() #the free parameter + x = Arg1D() # the free parameter return initial_value * Exp1D((x - initial_position) * growth_rate) @@ -288,7 +259,6 @@ def get_maxwellian_distribution(equilibrium, f1d_density, f1d_temperature, f1d_v :return: Maxwellian distribution """ - # map profiles to 3D f3d_te = equilibrium.map3d(f1d_temperature) f3d_ne = equilibrium.map3d(f1d_density) @@ -308,28 +278,29 @@ def get_edge_profile_values(r, z, edge_interpolators=None): returned by the get_edge_interpolators function. :return: Dictionary of edge values at [R, Z] """ + # load edge interpolators if not passed as argument if edge_interpolators is None: edge_interp = get_edge_interpolators() else: edge_interp = edge_interpolators # create recursive dictionary to store profile values - lcfs_values = RecursiveDict() + values = RecursiveDict() # add electron values - lcfs_values["electron"]["temperature"] = edge_interp["electron"]["temperature"](r, z) - lcfs_values["electron"]["density"] = edge_interp["electron"]["density"](r, z) + values["electron"]["temperature"] = edge_interp["electron"]["temperature"](r, z) + values["electron"]["density"] = edge_interp["electron"]["density"](r, z) # add species values for spec, desc in edge_interp['composition'].items(): for chrg, chrg_desc in desc.items(): for prop, val in chrg_desc.items(): if prop in ["temperature", "density"]: - lcfs_values["composition"][spec][chrg][prop] = val(r, z) + values["composition"][spec][chrg][prop] = val(r, z) else: - lcfs_values["composition"][spec][chrg][prop] = val + values["composition"][spec][chrg][prop] = val - return lcfs_values.freeze() + return values.freeze() def get_core_profiles_arguments(**kwargs): @@ -371,26 +342,26 @@ def get_core_profiles_arguments(**kwargs): :return: dictionary of profile arguments """ - + core_args = {"ne_core": 5e19, "ne_convexity": 1.09, - "ne_concavity": 0.24, "te_core": 3e3, - "te_convexity": 2.35, "te_concavity": 1.26, - "nh_core": 5e19, "nh_convexity": 1.09, - "nh_concavity": 0.24, "th_core": 2.8e3, - "th_convexity": 1, "th_concavity": 0.82, - "th0_fraction": 0.8, "nh0_decay": 20, - "timp_core": 2.7e3, "timp_convexity": 1, - "timp_concavity": 0.82, "nimp_core": 5e17, - "nimp_convexity": 1.09, "nimp_concavity": 0.24, - "nimp_decay": 30, - "vtor_core": 1e5, "vtor_edge": 1e4, - "vtor_convexity": 2, "vtor_concavity": 4, - "vpol_lcfs": 2e4, "vpol_decay": 0.08} + "ne_concavity": 0.24, "te_core": 3e3, + "te_convexity": 2.35, "te_concavity": 1.26, + "nh_core": 5e19, "nh_convexity": 1.09, + "nh_concavity": 0.24, "th_core": 2.8e3, + "th_convexity": 1, "th_concavity": 0.82, + "th0_fraction": 0.8, "nh0_decay": 20, + "timp_core": 2.7e3, "timp_convexity": 1, + "timp_concavity": 0.82, "nimp_core": 5e17, + "nimp_convexity": 1.09, "nimp_concavity": 0.24, + "nimp_decay": 30, + "vtor_core": 1e5, "vtor_edge": 1e4, + "vtor_convexity": 2, "vtor_concavity": 4, + "vpol_lcfs": 2e4, "vpol_decay": 0.08} if not kwargs: return core_args - # change passed values of core args + # change passed values of core args for key, item in kwargs.items(): core_args[key] = item @@ -419,15 +390,23 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): z = 0 lcfs_values = get_edge_profile_values(r, z) + # manually correcting the LCFS densities for neutral species and low ionised carbon + # to better match the edge profiles + lcfs_values["composition"]["hydrogen"][0]["density"] = 1.e15 + lcfs_values["composition"]["carbon"][0]["density"] = 1.e5 + lcfs_values["composition"]["carbon"][1]["density"] = 1.5e5 + lcfs_values["composition"]["carbon"][2]["density"] = 1.e9 + lcfs_values["composition"]["carbon"][3]["density"] = 1.e13 + if core_args is None: core_args = get_core_profiles_arguments() # toroidal rotation profile f1d_vtor = get_double_parabola(core_args["vtor_edge"], core_args["vtor_core"], - core_args["vtor_convexity"], core_args["vtor_concavity"]) + core_args["vtor_convexity"], core_args["vtor_concavity"], xmin=1, xmax=0) # poloidal rotation profile - f1d_vpol = get_exponential_growth(core_args["vpol_lcfs"], core_args["vpol_decay"]) + f1d_vpol = get_exponential_growth(core_args["vpol_lcfs"], core_args["vpol_decay"]) # velocity normal to magnetic surfaces f1d_vnorm = Constant1D(0) @@ -442,7 +421,7 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): profiles["electron"]["f1d_density"] = get_double_parabola(lcfs_values["electron"]["density"], core_args["ne_core"], core_args["ne_convexity"], core_args["ne_concavity"], xmin=1, xmax=0) - profiles["electron"]["f1d_vtor"] = Constant1D(0) + profiles["electron"]["f1d_vtor"] = Constant1D(1.e-10) # avoid zero-length vectors for blending profiles["electron"]["f1d_vpol"] = Constant1D(0) profiles["electron"]["f1d_vnorm"] = Constant1D(0) @@ -461,7 +440,8 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): profiles["composition"]["hydrogen"][0]["f1d_temperature"] = core_args["th0_fraction"] * get_double_parabola(lcfs_values["composition"]["hydrogen"][0]["temperature"], core_args["th_core"], core_args["th_convexity"], core_args["th_concavity"], xmin=1, xmax=0) - profiles["composition"]["hydrogen"][0]["f1d_density"] = get_exponential_growth(lcfs_values["composition"]["hydrogen"][0]["density"], core_args["nh0_decay"]) + profiles["composition"]["hydrogen"][0]["f1d_density"] = get_exponential_growth( + lcfs_values["composition"]["hydrogen"][0]["density"], core_args["nh0_decay"]) profiles["composition"]["hydrogen"][0]["f1d_vtor"] = f1d_vtor profiles["composition"]["hydrogen"][0]["f1d_vpol"] = f1d_vpol profiles["composition"]["hydrogen"][0]["f1d_vnorm"] = f1d_vnorm @@ -471,7 +451,7 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], xmin=1, xmax=0) profiles["composition"]["carbon"][6]["f1d_density"] = get_double_parabola(lcfs_values["composition"]["carbon"][6]["density"], core_args["nimp_core"], - core_args["nimp_convexity"], core_args["nimp_concavity"], xmin=1, xmax=0) + core_args["nimp_convexity"], core_args["nimp_concavity"], xmin=1, xmax=0) profiles["composition"]["carbon"][6]["f1d_vtor"] = f1d_vtor profiles["composition"]["carbon"][6]["f1d_vpol"] = f1d_vpol profiles["composition"]["carbon"][6]["f1d_vnorm"] = f1d_vnorm @@ -481,7 +461,8 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): profiles["composition"]["carbon"][chrg]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["carbon"][chrg]["temperature"], core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], xmin=1, xmax=0) - profiles["composition"]["carbon"][chrg]["f1d_density"] = get_exponential_growth(lcfs_values["composition"]["carbon"][chrg]["density"], core_args["nimp_decay"]) + profiles["composition"]["carbon"][chrg]["f1d_density"] = get_exponential_growth( + lcfs_values["composition"]["carbon"][chrg]["density"], core_args["nimp_decay"]) profiles["composition"]["carbon"][chrg]["f1d_vtor"] = f1d_vtor profiles["composition"]["carbon"][chrg]["f1d_vpol"] = f1d_vpol profiles["composition"]["carbon"][chrg]["f1d_vnorm"] = f1d_vnorm @@ -493,8 +474,8 @@ def get_core_distributions(profiles=None, equilibrium=None): """ Returns a dictionary of core plasma species Maxwellian distributions. - :param profiles: Dictionary of core particle profiles. The dictionary has to have the same form - as the one returned by the function get_core_profiles_description. + :param profiles: Dictionary of core particle profiles. The dictionary has to have the same form + as the one returned by the function get_core_profiles_description. The default value is the value returned by the call get_core_profiles_description(). :param equilibrium: an instance of EFITEquilibrium. :return: dictionary of core plasma species with Maxwellian distribution @@ -510,23 +491,20 @@ def get_core_distributions(profiles=None, equilibrium=None): # build a dictionary with Maxwellian distributions species = RecursiveDict() species["electron"] = get_maxwellian_distribution(equilibrium, rest_mass=electron_mass, - **profiles["electron"]) + **profiles["electron"]) for name, spec in profiles["composition"].items(): spec_cherab = _get_cherab_element(name) for chrg, desc in spec.items(): rest_mass = atomic_mass * spec_cherab.atomic_weight - species["composition"][name][chrg] = get_maxwellian_distribution(equilibrium, rest_mass=rest_mass, **desc) + species["composition"][name][chrg] = get_maxwellian_distribution(equilibrium, rest_mass=rest_mass, **desc) return species.freeze() -def get_core_plasma(distributions=None, atomic_data=None, parent=None, name="Generomak core plasma"): +def get_core_plasma(atomic_data=None, parent=None, name="Generomak core plasma"): """ - Provides Generomak core plasma. + Provides Generomak default core plasma. - :param distributions: A dictionary of plasma distributions. Has to have the same format as the - dictionary returned by get_core_distributions. The default value - is the value returned by the call get_core_distributions(). :param atomic_data: Instance of AtomicData, default is OpenADAS() :param parent: parent of the plasma node, defaults None :param name: name of the plasma node, defaults "Generomak edge plasma" @@ -536,33 +514,119 @@ def get_core_plasma(distributions=None, atomic_data=None, parent=None, name="Gen # load Generomak equilibrium equilibrium = load_equilibrium() + # load core distributions + distributions = get_core_distributions(equilibrium=equilibrium) + + return get_plasma(equilibrium=equilibrium, distributions=distributions, + atomic_data=atomic_data, parent=parent, name=name) + + +def get_full_profiles(equilibrium=None, core_profiles=None, edge_profiles=None, mask=None): + """ + Blends core and edge profiles using the mask function as a modulator. + + :param equilibrium: an instance of EFITEquilibrium. The default value is the value returned by + load_equilibrium(). + :param core_profiles: Dictionary of core particle profiles. The dictionary has to have + the same form as the one returned by the function + get_core_profiles_description. + The default value is the value returned by the call + get_core_profiles_description(). + :param edge_profiles: Dictionary with edge interpolators in the shape + returned by the get_edge_interpolators function. + If not specified, will use the value returned by + get_edge_interpolators(). + :param Function2D mask: Scalar 2D function returning a value in the range [0, 1]. + If not specified, will use core profiles for psi_normal < 0.94, + the edge profiles for psi_normal > 1 and a weighted sum of core and + edge profiles for 0.94 < psi_normal < 1, with the edge profile weight + increasing from 0 to 1 linearly. + + :return: dictionary of blended plasma profiles with the sturcture identical to edge_profiles. + """ + + equilibrium = equilibrium or load_equilibrium() + + core_profiles = core_profiles or get_core_profiles_description() + + edge_profiles = edge_profiles or get_edge_interpolators() + + mask = mask or equilibrium.map2d(Interpolator1DArray([0, 0.94, 1.0, 1.1], [1, 1, 0, 0], 'linear', 'none', 0)) + + # blended core and edge profiles + blended_profiles = RecursiveDict() + + # map core profiles to 2D using the equilibrium + te_core = equilibrium.map2d(core_profiles["electron"]["f1d_temperature"]) + ne_core = equilibrium.map2d(core_profiles["electron"]["f1d_density"]) + ve_core = equilibrium.map_vector2d(core_profiles["electron"]["f1d_vtor"], + core_profiles["electron"]["f1d_vpol"], + core_profiles["electron"]["f1d_vnorm"]) + + blended_profiles["electron"]["temperature"] = Blend2D(edge_profiles["electron"]["temperature"], te_core, mask) + blended_profiles["electron"]["density"] = Blend2D(edge_profiles["electron"]["density"], ne_core, mask) + blended_profiles["electron"]["velocity"] = BlendVector2D(edge_profiles["electron"]["velocity"], ve_core, mask) + + for element, states in core_profiles["composition"].items(): + for charge, state in states.items(): + t_core = equilibrium.map2d(state["f1d_temperature"]) + n_core = equilibrium.map2d(state["f1d_density"]) + v_core = equilibrium.map_vector2d(state["f1d_vtor"], state["f1d_vpol"], state["f1d_vnorm"]) + + edge_state = edge_profiles["composition"][element][charge] + + blended_profiles["composition"][element][charge]["temperature"] = Blend2D(edge_state["temperature"], t_core, mask) + blended_profiles["composition"][element][charge]["density"] = Blend2D(edge_state["density"], n_core, mask) + blended_profiles["composition"][element][charge]["velocity"] = BlendVector2D(edge_state["velocity"], v_core, mask) + + return blended_profiles.freeze() + + +def get_plasma(equilibrium=None, distributions=None, r_range=None, z_range=None, atomic_data=None, parent=None, name="Generomak plasma"): + """ + Provides Generomak plasma. The full (core + edge) plasma is returned by default. + + :param equilibrium: an instance of EFITEquilibrium. The default value is the value returned by load_equilibrium(). + :param distributions: A dictionary of plasma distributions. Has to have the same format as the + dictionary returned by get_core_distributions or get_2d_distributions. + The default value is the value returned by the call: + get_2d_distributions(get_full_profiles(equilibrium)). + :param r_range: Plasma domain range (min, max) in R direction in meters. + :param z_range: Plasma domain range (min, max) in Z direction in meters. + :param atomic_data: Instance of AtomicData, default is OpenADAS() + :param parent: parent of the plasma node, defaults None + :param name: name of the plasma node, defaults "Generomak plasma" + :return: populated Plasma object + """ + + equilibrium = equilibrium or load_equilibrium() + + distributions = distributions or get_2d_distributions(get_full_profiles(equilibrium=equilibrium)) + + r_range = r_range or equilibrium.r_range + z_range = z_range or equilibrium.z_range + # create or check atomic_data if atomic_data is not None: if not isinstance(atomic_data, AtomicData): - raise ValueError("atomic_data has to be of type AtomicData") + raise ValueError("atomic_data has to be of type AtomicData") else: atomic_data = OpenADAS() - # construct plasma primitive shape - padding = 1e-3 #enlarge for safety - plasma_height = equilibrium.z_range[1] - equilibrium.z_range[0] - outer_column = Cylinder(radius=equilibrium.r_range[1], height=plasma_height) - inner_column = Cylinder(radius=equilibrium.r_range[0], height=plasma_height + 2 * padding) + # construct plasma primitive shape + padding = 1e-3 # enlarge for safety + plasma_height = z_range[1] - z_range[0] + outer_column = Cylinder(radius=r_range[1], height=plasma_height) + inner_column = Cylinder(radius=r_range[0], height=plasma_height + 2 * padding) inner_column.transform = translate(0, 0, -padding) plasma_geometry = Subtract(outer_column, inner_column) # coordinate transform of the plasma frame - geometry_transform = translate(0, 0, equilibrium.z_range[0]) - - # load core distributions if needed - if distributions is None: - dists = get_core_distributions() - else: - dists = distributions + geometry_transform = translate(0, 0, z_range[0]) # create plasma composition list plasma_composition = [] - for elem_name, elem_data in dists["composition"].items(): + for elem_name, elem_data in distributions["composition"].items(): for stage, stage_data in elem_data.items(): elem = _get_cherab_element(elem_name) species = Species(elem, stage, stage_data) @@ -573,7 +637,7 @@ def get_core_plasma(distributions=None, atomic_data=None, parent=None, name="Gen plasma.name = name plasma.geometry = plasma_geometry plasma.atomic_data = atomic_data - plasma.electron_distribution = dists["electron"] + plasma.electron_distribution = distributions["electron"] plasma.composition = plasma_composition plasma.geometry_transform = geometry_transform plasma.b_field = VectorAxisymmetricMapper(equilibrium.b_field) diff --git a/demos/generomak/plasma/plot_2d_plasma.py b/demos/generomak/plasma/plot_2d_plasma.py new file mode 100755 index 00000000..5fd0f729 --- /dev/null +++ b/demos/generomak/plasma/plot_2d_plasma.py @@ -0,0 +1,128 @@ + +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. + +import numpy as np +from matplotlib.colors import SymLogNorm +from matplotlib import pyplot as plt + +from cherab.core.math import sample3d +from cherab.core.atomic.elements import hydrogen, carbon + +from cherab.generomak.plasma.plasma import get_core_plasma, get_edge_plasma, get_plasma + +""" +This demo does the same plots as the plot_2d_profiles.py demo, but sampling the profiles from the Plasma objects. +""" + + +def plot_profiles(core_profile, edge_profile, full_profile, r_range, z_range, label): + + # Sample core profile on a regular grid + _, _, _, core_profile_samples = sample3d(core_profile, r_range, (0, 0, 1), z_range) + core_profile_samples = core_profile_samples.squeeze() + + # Sample edge profile on a regular grid + _, _, _, edge_profile_samples = sample3d(edge_profile, r_range, (0, 0, 1), z_range) + edge_profile_samples = edge_profile_samples.squeeze() + + # Sample blended profile on a regular grid + _, _, _, full_profile_samples = sample3d(full_profile, r_range, (0, 0, 1), z_range) + full_profile_samples = full_profile_samples.squeeze() + + fig = plt.figure(figsize=(9.5, 5.), tight_layout=True) + + vmax = full_profile_samples.max() + linthresh = 0.01 * min(core_profile_samples.max(), edge_profile_samples.max()) + color_norm = SymLogNorm(linthresh, vmin=0, vmax=vmax) + + core_profile_samples[core_profile_samples == 0] = np.nan + edge_profile_samples[edge_profile_samples == 0] = np.nan + full_profile_samples[full_profile_samples == 0] = np.nan + + ax_core = fig.add_subplot(131) + ax_core.imshow(core_profile_samples.T, extent=[r_range[0], r_range[1], z_range[0], z_range[1]], origin='lower', norm=color_norm, cmap='gnuplot') + ax_core.text(0.99, 0.99, 'Core', ha='right', va='top', transform=ax_core.transAxes) + ax_core.set_xlim(r_range[0], r_range[1]) + ax_core.set_ylim(z_range[0], z_range[1]) + ax_core.set_xlabel('R, m') + ax_core.set_ylabel('Z, m') + + ax_edge = fig.add_subplot(132, sharex=ax_core, sharey=ax_core) + img = ax_edge.imshow(edge_profile_samples.T, extent=[r_range[0], r_range[1], z_range[0], z_range[1]], origin='lower', norm=color_norm, cmap='gnuplot') + ax_edge.text(0.99, 0.99, 'Edge', ha='right', va='top', transform=ax_edge.transAxes) + ax_edge.set_xlabel('R, m') + + ax_blend = fig.add_subplot(133, sharex=ax_core, sharey=ax_core) + img = ax_blend.imshow(full_profile_samples.T, extent=[r_range[0], r_range[1], z_range[0], z_range[1]], origin='lower', norm=color_norm, cmap='gnuplot') + ax_blend.text(0.99, 0.99, 'Blended', ha='right', va='top', transform=ax_blend.transAxes) + ax_blend.set_xlabel('R, m') + + fig.colorbar(img, label=label) + + return fig + + +# get Generomak core plasma +core_plasma = get_core_plasma() + +# get Generomak edge plasma +edge_plasma = get_edge_plasma() + +# get Generomak full plasma (blended core and edge profiles) +full_plasma = get_plasma() + +# plasma domain +r_range = (0.78, 2.23, 290) +z_range = (-1.74, 1.49, 647) + +# Plotting plasma profiles +plot_profiles(core_plasma.electron_distribution.density, + edge_plasma.electron_distribution.density, + full_plasma.electron_distribution.density, + r_range, z_range, 'Electron density, m-3') + +plot_profiles(core_plasma.electron_distribution.effective_temperature, + edge_plasma.electron_distribution.effective_temperature, + full_plasma.electron_distribution.effective_temperature, + r_range, z_range, 'Electron temperature, eV') + +plot_profiles(core_plasma.composition.get(hydrogen, 1).distribution.effective_temperature, + edge_plasma.composition.get(hydrogen, 1).distribution.effective_temperature, + full_plasma.composition.get(hydrogen, 1).distribution.effective_temperature, + r_range, z_range, 'Ion temperature, eV') + +for element, charges in ((hydrogen, (0, 1)), (carbon, (0, 1, 2, 3, 4, 5, 6))): + for charge in charges: + state_str = ' {}+'.format(charge) if charge else ' 0' + plot_profiles(core_plasma.composition.get(element, charge).distribution.density, + edge_plasma.composition.get(element, charge).distribution.density, + full_plasma.composition.get(element, charge).distribution.density, + r_range, z_range, '{} density, m-3'.format(element.name + state_str)) + +plot_profiles(core_plasma.composition.get(hydrogen, 0).distribution.effective_temperature, + edge_plasma.composition.get(hydrogen, 0).distribution.effective_temperature, + full_plasma.composition.get(hydrogen, 0).distribution.effective_temperature, + r_range, z_range, 'neutral hydrogen effective temperature, eV') + +plot_profiles(core_plasma.composition.get(carbon, 0).distribution.effective_temperature, + edge_plasma.composition.get(carbon, 0).distribution.effective_temperature, + full_plasma.composition.get(carbon, 0).distribution.effective_temperature, + r_range, z_range, 'neutral carbon effective temperature, eV') + +plt.show() diff --git a/demos/generomak/plasma/plot_2d_profiles.py b/demos/generomak/plasma/plot_2d_profiles.py new file mode 100755 index 00000000..22a28c48 --- /dev/null +++ b/demos/generomak/plasma/plot_2d_profiles.py @@ -0,0 +1,144 @@ + +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. + +import numpy as np +from matplotlib.colors import SymLogNorm +from matplotlib.collections import PolyCollection +from matplotlib import pyplot as plt + +from cherab.core.math import sample2d +from cherab.core.utility import RecursiveDict + +from cherab.generomak.equilibrium import load_equilibrium +from cherab.generomak.plasma.plasma import get_core_profiles_description, load_edge_profiles, get_full_profiles + +""" +This demo plots core, edge and blended Generomak 2D plasma profiles. +""" + + +def plot_profiles(core_profile, edge_mesh, edge_data, full_profile, label): + + # get grid parameters + vertex_coords = np.asarray(edge_mesh["vertex_coords"]) + triangles = np.asarray(edge_mesh["triangles"]) + rl, ru = (vertex_coords[:, 0].min(), vertex_coords[:, 0].max()) + zl, zu = (vertex_coords[:, 1].min(), vertex_coords[:, 1].max()) + nr = 288 + nz = 647 + + edge_data = np.asarray(edge_data) + + # Sample core profile on a regular grid + _, _, core_profile_samples = sample2d(core_profile, (rl, ru, nr), (zl, zu, nz)) + + # Sample blended profile on a regular grid + _, _, profile_samples = sample2d(full_profile, (rl, ru, nr), (zl, zu, nz)) + + fig = plt.figure(figsize=(9.5, 5.), tight_layout=True) + + vmax = profile_samples.max() + linthresh = 0.01 * min(core_profile_samples.max(), edge_data.max()) + color_norm = SymLogNorm(linthresh, vmin=0, vmax=vmax) + + core_profile_samples[core_profile_samples == 0] = np.nan + profile_samples[profile_samples == 0] = np.nan + + ax_core = fig.add_subplot(131) + ax_core.imshow(core_profile_samples.T, extent=[rl, ru, zl, zu], origin='lower', norm=color_norm, cmap='gnuplot') + ax_core.text(0.99, 0.99, 'Core', ha='right', va='top', transform=ax_core.transAxes) + ax_core.set_xlim(rl, ru) + ax_core.set_ylim(zl, zu) + ax_core.set_xlabel('R, m') + ax_core.set_ylabel('Z, m') + + ax_edge = fig.add_subplot(132, sharex=ax_core, sharey=ax_core) + collection = PolyCollection(vertex_coords[triangles], norm=color_norm, cmap='gnuplot') + collection.set_array(edge_data) + ax_edge.add_collection(collection) + ax_edge.text(0.99, 0.99, 'Edge', ha='right', va='top', transform=ax_edge.transAxes) + ax_edge.set_aspect(1) + ax_edge.set_xlabel('R, m') + + ax_blend = fig.add_subplot(133, sharex=ax_core, sharey=ax_core) + img = ax_blend.imshow(profile_samples.T, extent=[rl, ru, zl, zu], origin='lower', norm=color_norm, cmap='gnuplot') + ax_blend.text(0.99, 0.99, 'Blended', ha='right', va='top', transform=ax_blend.transAxes) + ax_blend.set_xlabel('R, m') + + fig.colorbar(img, label=label) + + return fig + + +# load Generomak equilibrium +equilibrium = load_equilibrium() + +# load 1D core profiles, f(psi_norm) +core_profiles_1d = get_core_profiles_description() + +# load 2D edge profiles defined on a quadrilateral mesh +edge_data = load_edge_profiles() + +# load 2D plasma profiles covering both core and edge regions +# see the source code for the get_full_profiles() to learn how to blend core and edge profiles using a mask function +full_profiles = get_full_profiles(equilibrium, core_profiles_1d) + +# map core profiles to 2D using the equilibrium +core_profiles_2d = RecursiveDict() + +core_profiles_2d["electron"]["temperature"] = equilibrium.map2d(core_profiles_1d["electron"]["f1d_temperature"]) +core_profiles_2d["electron"]["density"] = equilibrium.map2d(core_profiles_1d["electron"]["f1d_density"]) + +for element, states in core_profiles_1d["composition"].items(): + for charge, state in states.items(): + core_profiles_2d["composition"][element][charge]["density"] = equilibrium.map2d(state["f1d_density"]) + core_profiles_2d["composition"][element][charge]["temperature"] = equilibrium.map2d(state["f1d_temperature"]) + +core_profiles_2d = core_profiles_2d.freeze() + +# Plotting plasma profiles +plot_profiles(core_profiles_2d["electron"]["density"], edge_data["mesh"], + edge_data["electron"]["density"], full_profiles["electron"]["density"], + 'Electron density, m-3') + +plot_profiles(core_profiles_2d["electron"]["temperature"], edge_data["mesh"], + edge_data["electron"]["temperature"], full_profiles["electron"]["temperature"], + 'Electron temperature, eV') + +plot_profiles(core_profiles_2d["composition"]["hydrogen"][1]["temperature"], edge_data["mesh"], + edge_data["composition"]["hydrogen"][1]["temperature"], + full_profiles["composition"]["hydrogen"][1]["temperature"], 'Ion temperature, eV') + +for element, states in core_profiles_2d["composition"].items(): + for charge, state in states.items(): + state_str = ' {}+'.format(charge) if charge else ' 0' + plot_profiles(state["density"], edge_data["mesh"], + edge_data["composition"][element][charge]["density"], + full_profiles["composition"][element][charge]["density"], + '{} density, m-3'.format(element + state_str)) + +plot_profiles(core_profiles_2d["composition"]["hydrogen"][0]["temperature"], edge_data["mesh"], + edge_data["composition"]["hydrogen"][0]["temperature"], + full_profiles["composition"]["hydrogen"][0]["temperature"], 'neutral hydrogen effective temperature, eV') + +plot_profiles(core_profiles_2d["composition"]["carbon"][0]["temperature"], edge_data["mesh"], + edge_data["composition"]["carbon"][0]["temperature"], + full_profiles["composition"]["carbon"][0]["temperature"], 'neutral carbon effective temperature, eV') + +plt.show() From e9a272f8f4e33aae728871021ebdfc0ead9ad1a6 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 4 Aug 2022 16:54:48 +0300 Subject: [PATCH 29/59] Correct Generomak plasma demo descriptions. --- demos/generomak/plasma/plot_2d_plasma.py | 8 ++++---- demos/generomak/plasma/plot_2d_profiles.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/demos/generomak/plasma/plot_2d_plasma.py b/demos/generomak/plasma/plot_2d_plasma.py index 5fd0f729..24750242 100755 --- a/demos/generomak/plasma/plot_2d_plasma.py +++ b/demos/generomak/plasma/plot_2d_plasma.py @@ -17,6 +17,10 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +""" +This demo does the same plots as the plot_2d_profiles.py demo, but samples the profiles from the Plasma objects. +""" + import numpy as np from matplotlib.colors import SymLogNorm from matplotlib import pyplot as plt @@ -26,10 +30,6 @@ from cherab.generomak.plasma.plasma import get_core_plasma, get_edge_plasma, get_plasma -""" -This demo does the same plots as the plot_2d_profiles.py demo, but sampling the profiles from the Plasma objects. -""" - def plot_profiles(core_profile, edge_profile, full_profile, r_range, z_range, label): diff --git a/demos/generomak/plasma/plot_2d_profiles.py b/demos/generomak/plasma/plot_2d_profiles.py index 22a28c48..34daa025 100755 --- a/demos/generomak/plasma/plot_2d_profiles.py +++ b/demos/generomak/plasma/plot_2d_profiles.py @@ -17,6 +17,10 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +""" +This demo plots core, edge and blended Generomak 2D plasma profiles. +""" + import numpy as np from matplotlib.colors import SymLogNorm from matplotlib.collections import PolyCollection @@ -28,10 +32,6 @@ from cherab.generomak.equilibrium import load_equilibrium from cherab.generomak.plasma.plasma import get_core_profiles_description, load_edge_profiles, get_full_profiles -""" -This demo plots core, edge and blended Generomak 2D plasma profiles. -""" - def plot_profiles(core_profile, edge_mesh, edge_data, full_profile, label): From 6ff197c259157b2c14baecf3b0282ebafaecc240 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Thu, 18 Aug 2022 09:55:29 +0200 Subject: [PATCH 30/59] Fix typo in Bremsstrahlung docstrings --- cherab/core/model/plasma/bremsstrahlung.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 473fb76b..9e2e2e8b 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -38,7 +38,7 @@ cdef class Bremsstrahlung(PlasmaModel): et. al., 'ITER LIDAR performance analysis', Rev. Sci. Instrum. 79, 10E727 (2008), .. math:: - \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} g_{ff} n_e^2 Z_{eff} T_e^{1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, + \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} g_{ff} n_e^2 Z_{eff} T_e^{-1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, where the emission :math:`\\epsilon (\\lambda)` is in units of radiance (ph/s/sr/m^3/nm). """ From 1f945c1faaa265b1d8494d1ba6f1915a21550f67 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 18 Aug 2022 18:30:24 +0300 Subject: [PATCH 31/59] Fix the docstring for the Bremsstrahlung model (#375). --- cherab/core/model/plasma/bremsstrahlung.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index ba4b9cf6..2ba7245b 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -42,7 +42,7 @@ cdef class Bremsstrahlung(PlasmaModel): et. al., 'ITER LIDAR performance analysis', Rev. Sci. Instrum. 79, 10E727 (2008), .. math:: - \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} \\sum_{i} \\left(g_{ff}(Z_i, T_e, \\lambda) n_i Z_i^2\\right) n_e T_e^{1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, + \\epsilon (\\lambda) = \\frac{0.95 \\times 10^{-19}}{\\lambda 4 \\pi} \\sum_{i} \\left(g_{ff}(Z_i, T_e, \\lambda) n_i Z_i^2\\right) n_e T_e^{-1/2} \\times \\exp{\\frac{-hc}{\\lambda T_e}}, where the emission :math:`\\epsilon (\\lambda)` is in units of radiance (ph/s/sr/m^3/nm). From b202dbb2ac2762c0f9259044e98dab8b5b1b7754 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 30 Aug 2022 20:35:12 +0300 Subject: [PATCH 32/59] Add Gaussian quadrature integrator and tests. --- cherab/core/math/integrators/__init__.pxd | 20 ++ cherab/core/math/integrators/__init__.py | 19 ++ .../core/math/integrators/integrators1d.pxd | 36 ++++ .../core/math/integrators/integrators1d.pyx | 199 ++++++++++++++++++ cherab/core/math/tests/test_integrators.py | 78 +++++++ 5 files changed, 352 insertions(+) create mode 100644 cherab/core/math/integrators/__init__.pxd create mode 100644 cherab/core/math/integrators/__init__.py create mode 100644 cherab/core/math/integrators/integrators1d.pxd create mode 100644 cherab/core/math/integrators/integrators1d.pyx create mode 100644 cherab/core/math/tests/test_integrators.py diff --git a/cherab/core/math/integrators/__init__.pxd b/cherab/core/math/integrators/__init__.pxd new file mode 100644 index 00000000..db06ef43 --- /dev/null +++ b/cherab/core/math/integrators/__init__.pxd @@ -0,0 +1,20 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 cherab.core.math.integrators.integrators1d cimport Integrator1D, GaussianQuadrature + diff --git a/cherab/core/math/integrators/__init__.py b/cherab/core/math/integrators/__init__.py new file mode 100644 index 00000000..86b7d58d --- /dev/null +++ b/cherab/core/math/integrators/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 .integrators1d import Integrator1D, GaussianQuadrature diff --git a/cherab/core/math/integrators/integrators1d.pxd b/cherab/core/math/integrators/integrators1d.pxd new file mode 100644 index 00000000..56ec342b --- /dev/null +++ b/cherab/core/math/integrators/integrators1d.pxd @@ -0,0 +1,36 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 cimport ndarray +from raysect.core.math.function.float cimport Function1D + + +cdef class Integrator1D: + + cpdef (double, double) integrate(self, Function1D func, double a, double b) + + +cdef class GaussianQuadrature(Integrator1D): + + cdef: + int _min_order, _max_order + double _rtol + ndarray _roots, _weights + double[:] _roots_mv, _weights_mv + + cdef _build_cash(self) diff --git a/cherab/core/math/integrators/integrators1d.pyx b/cherab/core/math/integrators/integrators1d.pyx new file mode 100644 index 00000000..177be590 --- /dev/null +++ b/cherab/core/math/integrators/integrators1d.pyx @@ -0,0 +1,199 @@ +# cython: language_level=3 + +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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. + +import numpy as np +from scipy.special import roots_legendre + +from libc.math cimport INFINITY +cimport cython + + +cdef class Integrator1D: + """ + Compute a definite integral of a one-dimensional function. + """ + + cpdef (double, double) integrate(self, Function1D func, double a, double b): + """ + Integrates a one-dimensional function over the given interval. + + :param Function1D func: A function to integrate. + :param double a: Lower limit of integration. + :param double b: Upper limit of integration. + + :returns: Two-element tuple containing the integral of func from a to b + and an estimate of the absolute error in the result. + """ + + raise NotImplementedError("The integrate() virtual method must be implemented.") + + +cdef class GaussianQuadrature(Integrator1D): + """ + Compute an integral of a one-dimensional function over a finite interval + using fixed-tolerance Gaussian quadrature. + (see Scipy `quadrature `). + + :param double relative_tolerance: Iteration stops when relative error between + last two iterates is less than this value. Default is 1.e-7. + :param int max_order: Maximum order on Gaussian quadrature. Default is 50. + :param int min_order: Minimum order on Gaussian quadrature. Default is 1. + + :ivar double relative_tolerance: Iteration stops when relative error between + last two iterates is less than this value. + :ivar int max_order: Maximum order on Gaussian quadrature. + :ivar int min_order: Minimum order on Gaussian quadrature. + """ + + def __init__(self, double relative_tolerance=1.e-7, int max_order=50, int min_order=1): + + if min_order < 1 or max_order < 1: + raise ValueError("Order of Gaussian quadrature must be >= 1.") + + if min_order >= max_order: + raise ValueError("Minimum order of Gaussian quadrature must be less than the maximum order.") + + self._min_order = min_order + self._max_order = max_order + self._build_cash() + + self.relative_tolerance = relative_tolerance + + @property + def min_order(self): + """ + Minimum order on Gaussian quadrature. + + :rtype: int + """ + return self._min_order + + @min_order.setter + def min_order(self, int value): + + if value < 1: + raise ValueError("Order of Gaussian quadrature must be >= 1.") + + if value > self._max_order: + raise ValueError("Minimum order of Gaussian quadrature must be less than or equal to the maximum order.") + + self._min_order = value + + self._build_cash() + + @property + def max_order(self): + """ + Maximum order on Gaussian quadrature. + + :rtype: float + """ + return self._max_order + + @max_order.setter + def max_order(self, int value): + + if value < 1: + raise ValueError("Order of Gaussian quadrature must be >= 1.") + + if value < self._min_order: + raise ValueError("Maximum order of Gaussian quadrature must be greater than or equal to the minimum order.") + + self._max_order = value + + self._build_cash() + + @property + def relative_tolerance(self): + """ + Iteration stops when relative error between last two iterates is less than this value. + + :rtype: double + """ + return self._rtol + + @relative_tolerance.setter + def relative_tolerance(self, double value): + + if value <= 0: + raise ValueError("Relative tolerance must be positive.") + + self._rtol = value + + cdef _build_cash(self): + """ + Caches the roots and weights of the Gauss-Legendre quadrature. + """ + + cdef: + int order, n, i + + n = (self._max_order + self._min_order) * (self._max_order - self._min_order + 1) // 2 + + self._roots = np.zeros(n, dtype=np.float64) + self._weights = np.zeros(n, dtype=np.float64) + + i = 0 + for order in range(self._min_order, self._max_order + 1): + self._roots[i:i + order], self._weights[i:i + order] = roots_legendre(order) + i += order + + self._roots_mv = self._roots + self._weights_mv = self._weights + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.cdivision(True) + @cython.initializedcheck(False) + cpdef (double, double) integrate(self, Function1D func, double a, double b): + """ + Integrates a one-dimensional function over a finite interval. + + :param Function1D func: A function to integrate. + :param double a: Lower limit of integration. + :param double b: Upper limit of integration. + + :returns: Two-element tuple containing Gaussian quadrature approximation to integral + and difference between last two estimates of the integral. + """ + + cdef: + int order, i, ibegin + double newval, oldval, error, x + + oldval = INFINITY + ibegin = 0 + + for order in range(self._min_order, self._max_order): + newval = 0 + for i in range(ibegin, ibegin + order): + x = a + 0.5 * (b - a) * (self._roots_mv[i] + 1.) + newval += self._weights_mv[i] * func.evaluate(x) + newval *= 0.5 * (b - a) + + error = abs(newval - oldval) + oldval = newval + + ibegin += order + + if error < self._rtol * abs(newval): + break + + return newval, error diff --git a/cherab/core/math/tests/test_integrators.py b/cherab/core/math/tests/test_integrators.py new file mode 100644 index 00000000..4e563cb6 --- /dev/null +++ b/cherab/core/math/tests/test_integrators.py @@ -0,0 +1,78 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 raysect.core.math.function.float import Exp1D, Arg1D +from cherab.core.math.integrators import GaussianQuadrature +from math import sqrt, pi +from scipy.special import erf +import unittest + + +class TestGaussianQuadrature(unittest.TestCase): + """Gaussian quadrature integrator tests.""" + + def test_properties(self): + """Test property assignment.""" + min_order = 3 + max_order = 30 + reltol = 1.e-6 + quadrature = GaussianQuadrature(relative_tolerance=reltol, max_order=max_order, min_order=min_order) + + self.assertEqual(quadrature.relative_tolerance, reltol) + self.assertEqual(quadrature.max_order, max_order) + self.assertEqual(quadrature.min_order, min_order) + + min_order = 0 + max_order = 2 # < min_order + reltol = -1 + + with self.assertRaises(ValueError): + quadrature.max_order = max_order + + with self.assertRaises(ValueError): + quadrature.min_order = min_order + + with self.assertRaises(ValueError): + quadrature.relative_tolerance = reltol + + min_order = 1 + max_order = 20 + reltol = 1.e-5 + + quadrature.relative_tolerance = reltol + quadrature.min_order = min_order + quadrature.max_order = max_order + + self.assertEqual(quadrature.relative_tolerance, reltol) + self.assertEqual(quadrature.min_order, min_order) + self.assertEqual(quadrature.max_order, max_order) + + def test_integrate(self): + """Test integration.""" + func = (2 / sqrt(pi)) * Exp1D(- Arg1D() * Arg1D()) + quadrature = GaussianQuadrature() + a = -0.5 + b = 3. + result, error = quadrature.integrate(func, a, b) + exact_integral = erf(b) - erf(a) + + self.assertAlmostEqual(result, exact_integral, places=7) + + +if __name__ == '__main__': + unittest.main() From a32a6a716ae4a8ce8b46103163c0c8b793cf9fa2 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 31 Aug 2022 19:11:34 +0300 Subject: [PATCH 33/59] Fix an error in GaussianQuadrature. Increase default relative tolerance value. --- cherab/core/math/integrators/integrators1d.pyx | 10 +++++----- cherab/core/math/tests/test_integrators.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cherab/core/math/integrators/integrators1d.pyx b/cherab/core/math/integrators/integrators1d.pyx index 177be590..7f9ce421 100644 --- a/cherab/core/math/integrators/integrators1d.pyx +++ b/cherab/core/math/integrators/integrators1d.pyx @@ -52,7 +52,7 @@ cdef class GaussianQuadrature(Integrator1D): (see Scipy `quadrature `). :param double relative_tolerance: Iteration stops when relative error between - last two iterates is less than this value. Default is 1.e-7. + last two iterates is less than this value. Default is 1.e-5. :param int max_order: Maximum order on Gaussian quadrature. Default is 50. :param int min_order: Minimum order on Gaussian quadrature. Default is 1. @@ -62,13 +62,13 @@ cdef class GaussianQuadrature(Integrator1D): :ivar int min_order: Minimum order on Gaussian quadrature. """ - def __init__(self, double relative_tolerance=1.e-7, int max_order=50, int min_order=1): + def __init__(self, double relative_tolerance=1.e-5, int max_order=50, int min_order=1): if min_order < 1 or max_order < 1: raise ValueError("Order of Gaussian quadrature must be >= 1.") - if min_order >= max_order: - raise ValueError("Minimum order of Gaussian quadrature must be less than the maximum order.") + if min_order > max_order: + raise ValueError("Minimum order of Gaussian quadrature must be less than or equal to the maximum order.") self._min_order = min_order self._max_order = max_order @@ -181,7 +181,7 @@ cdef class GaussianQuadrature(Integrator1D): oldval = INFINITY ibegin = 0 - for order in range(self._min_order, self._max_order): + for order in range(self._min_order, self._max_order + 1): newval = 0 for i in range(ibegin, ibegin + order): x = a + 0.5 * (b - a) * (self._roots_mv[i] + 1.) diff --git a/cherab/core/math/tests/test_integrators.py b/cherab/core/math/tests/test_integrators.py index 4e563cb6..a60a4eea 100644 --- a/cherab/core/math/tests/test_integrators.py +++ b/cherab/core/math/tests/test_integrators.py @@ -65,13 +65,13 @@ def test_properties(self): def test_integrate(self): """Test integration.""" func = (2 / sqrt(pi)) * Exp1D(- Arg1D() * Arg1D()) - quadrature = GaussianQuadrature() + quadrature = GaussianQuadrature(relative_tolerance=1.e-8) a = -0.5 b = 3. result, error = quadrature.integrate(func, a, b) exact_integral = erf(b) - erf(a) - self.assertAlmostEqual(result, exact_integral, places=7) + self.assertAlmostEqual(result, exact_integral, places=8) if __name__ == '__main__': From c4a604d5133ea302a33bd61253aa3af781029c7a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 31 Aug 2022 19:13:34 +0300 Subject: [PATCH 34/59] Add NumericallyIntegrableLineShapeModel class. Update StarkBroadenedLine to use numerical integration over the spectral bin. --- cherab/core/model/lineshape.pxd | 8 +++- cherab/core/model/lineshape.pyx | 65 ++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/cherab/core/model/lineshape.pxd b/cherab/core/model/lineshape.pxd index 711475b5..050d589e 100644 --- a/cherab/core/model/lineshape.pxd +++ b/cherab/core/model/lineshape.pxd @@ -24,6 +24,7 @@ cimport numpy as np from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core cimport Line, Species, Plasma, Beam from cherab.core.math cimport Function1D, Function2D +from cherab.core.math.integrators cimport Integrator1D from cherab.core.atomic.zeeman cimport ZeemanStructure @@ -45,6 +46,11 @@ cdef class LineShapeModel: cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum) +cdef class NumericallyIntegrableLineShapeModel(LineShapeModel): + + cdef Integrator1D integrator + + cdef class GaussianLine(LineShapeModel): pass @@ -57,7 +63,7 @@ cdef class MultipletLineShape(LineShapeModel): double[:,::1] _multiplet_mv -cdef class StarkBroadenedLine(LineShapeModel): +cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): cdef double _aij, _bij, _cij diff --git a/cherab/core/model/lineshape.pyx b/cherab/core/model/lineshape.pyx index 5e346175..c0bb9711 100644 --- a/cherab/core/model/lineshape.pyx +++ b/cherab/core/model/lineshape.pyx @@ -22,10 +22,12 @@ import numpy as np cimport numpy as np from libc.math cimport sqrt, erf, M_SQRT2, floor, ceil, fabs from raysect.optical.spectrum cimport new_spectrum +from raysect.core.math.function.float cimport Function1D from cherab.core cimport Plasma from cherab.core.atomic.elements import hydrogen, deuterium, tritium, helium, helium3, beryllium, boron, carbon, nitrogen, oxygen, neon from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d +from cherab.core.math.integrators cimport GaussianQuadrature from cherab.core.utility.constants cimport ATOMIC_MASS, ELEMENTARY_CHARGE, SPEED_OF_LIGHT cimport cython @@ -159,6 +161,24 @@ cdef class LineShapeModel: raise NotImplementedError('Child lineshape class must implement this method.') +cdef class NumericallyIntegrableLineShapeModel(LineShapeModel): + """ + Line shape model to be numerically integrated over the spectral bin. + + :param Line line: The emission line object for this line shape. + :param float wavelength: The rest wavelength for this emission line. + :param Species target_species: The target plasma species that is emitting. + :param Plasma plasma: The emitting plasma object. + :param Integrator1D integrator: Integrator1D instance to integrate the line shape + over the spectral bin. Default is `GaussianQuadrature()`. + """ + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, Integrator1D integrator=None): + + super().__init__(line, wavelength, target_species, plasma) + self.integrator = integrator or GaussianQuadrature() + + cdef class GaussianLine(LineShapeModel): """ Produces Gaussian line shape. @@ -289,7 +309,21 @@ cdef class MultipletLineShape(LineShapeModel): return spectrum -cdef class StarkBroadenedLine(LineShapeModel): +cdef class StarkFunction(Function1D): + + cdef double _a, _x0 + + def __init__(self, double wavelength, double lambda_1_2): + self._x0 = wavelength + self._a = (0.5 * lambda_1_2)**2.5 + + @cython.cdivision(True) + cdef double evaluate(self, double x) except? -1e999: + + return 1. / ((fabs(x - self._x0))**2.5 + self._a) + + +cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): """ Parametrised Stark broadened line shape based on the Model Microfield Method (MMM). Contains embedded atomic data in the form of fits to MMM. @@ -352,7 +386,8 @@ cdef class StarkBroadenedLine(LineShapeModel): Line(tritium, 0, (9, 3)): (5.588e-15, 0.7165, 0.033) } - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, dict stark_model_coefficients=None): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, Integrator1D integrator=None, + dict stark_model_coefficients=None): stark_model_coefficients = stark_model_coefficients or self.STARK_MODEL_COEFFICIENTS_DEFAULT @@ -371,7 +406,7 @@ cdef class StarkBroadenedLine(LineShapeModel): except IndexError: raise ValueError('Stark broadening coefficients for {} is not currently available.'.format(line)) - super().__init__(line, wavelength, target_species, plasma) + super().__init__(line, wavelength, target_species, plasma, integrator) def show_supported_transitions(self): """ Prints all supported transitions.""" @@ -384,11 +419,14 @@ cdef class StarkBroadenedLine(LineShapeModel): @cython.cdivision(True) cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - cdef double ne, te, lambda_1_2, lambda_5_2, wvl - cdef double cutoff_lower_wavelength, cutoff_upper_wavelength - cdef double lower_value, lower_wavelength, upper_value, upper_wavelength - cdef int start, end, i - cdef Spectrum raw_lineshape + cdef: + double ne, te, lambda_1_2, lambda_5_2, wvl + double cutoff_lower_wavelength, cutoff_upper_wavelength + double lower_wavelength, upper_wavelength + double bin_integral + int start, end, i + Spectrum raw_lineshape + StarkFunction stark_funcion ne = self.plasma.get_electron_distribution().density(point.x, point.y, point.z) if ne <= 0.0: @@ -400,6 +438,8 @@ cdef class StarkBroadenedLine(LineShapeModel): lambda_1_2 = self._cij * ne**self._aij / (te**self._bij) + stark_funcion = StarkFunction(self.wavelength, lambda_1_2) + # calculate and check end of limits cutoff_lower_wavelength = self.wavelength - LORENZIAN_CUTOFF_GAMMA * lambda_1_2 if spectrum.max_wavelength < cutoff_lower_wavelength: @@ -413,21 +453,18 @@ cdef class StarkBroadenedLine(LineShapeModel): start = max(0, floor((cutoff_lower_wavelength - spectrum.min_wavelength) / spectrum.delta_wavelength)) end = min(spectrum.bins, ceil((cutoff_upper_wavelength - spectrum.min_wavelength) / spectrum.delta_wavelength)) - # TODO - replace with cumulative integrals # add line to spectrum raw_lineshape = spectrum.new_spectrum() lower_wavelength = raw_lineshape.min_wavelength + start * raw_lineshape.delta_wavelength - lower_value = 1 / ((fabs(lower_wavelength - self.wavelength))**2.5 + (0.5 * lambda_1_2)**2.5) - for i in range(start, end): + for i in range(start, end): upper_wavelength = raw_lineshape.min_wavelength + raw_lineshape.delta_wavelength * (i + 1) - upper_value = 1 / ((fabs(upper_wavelength - self.wavelength))**2.5 + (0.5 * lambda_1_2)**2.5) - raw_lineshape.samples_mv[i] += 0.5 * (upper_value + lower_value) + bin_integral, _ = self.integrator.integrate(stark_funcion, lower_wavelength, upper_wavelength) + raw_lineshape.samples_mv[i] += bin_integral lower_wavelength = upper_wavelength - lower_value = upper_value # perform normalisation raw_lineshape.div_scalar(raw_lineshape.total()) From 6308e0fac89ec830697e25c000c6867ceba6edc1 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 31 Aug 2022 19:55:23 +0300 Subject: [PATCH 35/59] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b5ecac..1dfaac01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ New: * Add verbose parameter to SartOpencl solver (default is False). (#358) * Add Generomak core plasma profiles. (#360) * Add toroidal_mesh_from_polygon for making mesh for not fully-360 degrees axisymmetric elements. (#365) +* Add GaussianQuadrature integration method for Function1D. (#366) +* Add NumericallyIntegrableLineShapeModel for lineshapes that cannot be analytically integrated over a spectral bin. (#366) +* Add a numerical integration of StarkBroadenedLine over the spectral bin. (#366) Bug Fixes: ---------- From 526cc17342a6c7a3b910f138603c492e30adcc10 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Sun, 11 Sep 2022 03:05:02 +0300 Subject: [PATCH 36/59] Make Integrator1D a Function2D instance. --- .../core/math/integrators/integrators1d.pxd | 7 +-- .../core/math/integrators/integrators1d.pyx | 54 ++++++++++++------- cherab/core/math/tests/test_integrators.py | 14 +++-- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/cherab/core/math/integrators/integrators1d.pxd b/cherab/core/math/integrators/integrators1d.pxd index 56ec342b..8109e6d7 100644 --- a/cherab/core/math/integrators/integrators1d.pxd +++ b/cherab/core/math/integrators/integrators1d.pxd @@ -17,12 +17,13 @@ # under the Licence. from numpy cimport ndarray -from raysect.core.math.function.float cimport Function1D +from raysect.core.math.function.float cimport Function1D, Function2D -cdef class Integrator1D: +cdef class Integrator1D(Function2D): - cpdef (double, double) integrate(self, Function1D func, double a, double b) + cdef: + Function1D function cdef class GaussianQuadrature(Integrator1D): diff --git a/cherab/core/math/integrators/integrators1d.pyx b/cherab/core/math/integrators/integrators1d.pyx index 7f9ce421..1e69efd4 100644 --- a/cherab/core/math/integrators/integrators1d.pyx +++ b/cherab/core/math/integrators/integrators1d.pyx @@ -21,28 +21,35 @@ import numpy as np from scipy.special import roots_legendre +from raysect.core.math.function.float cimport autowrap_function1d + from libc.math cimport INFINITY cimport cython -cdef class Integrator1D: +cdef class Integrator1D(Function2D): """ Compute a definite integral of a one-dimensional function. + + :ivar Function1D integrand: A 1D function to integrate. """ - cpdef (double, double) integrate(self, Function1D func, double a, double b): + @property + def integrand(self): """ - Integrates a one-dimensional function over the given interval. - - :param Function1D func: A function to integrate. - :param double a: Lower limit of integration. - :param double b: Upper limit of integration. + A 1D function to integrate. - :returns: Two-element tuple containing the integral of func from a to b - and an estimate of the absolute error in the result. + :rtype: int """ + return self.function + + @integrand.setter + def integrand(self, object func): - raise NotImplementedError("The integrate() virtual method must be implemented.") + if func is None: + self.function = None + else: + self.function = autowrap_function1d(func) cdef class GaussianQuadrature(Integrator1D): @@ -51,18 +58,20 @@ cdef class GaussianQuadrature(Integrator1D): using fixed-tolerance Gaussian quadrature. (see Scipy `quadrature `). + :param object integrand: A 1D function to integrate. :param double relative_tolerance: Iteration stops when relative error between last two iterates is less than this value. Default is 1.e-5. :param int max_order: Maximum order on Gaussian quadrature. Default is 50. :param int min_order: Minimum order on Gaussian quadrature. Default is 1. + :ivar Function1D integrand: A 1D function to integrate. :ivar double relative_tolerance: Iteration stops when relative error between last two iterates is less than this value. :ivar int max_order: Maximum order on Gaussian quadrature. :ivar int min_order: Minimum order on Gaussian quadrature. """ - def __init__(self, double relative_tolerance=1.e-5, int max_order=50, int min_order=1): + def __init__(self, object integrand=None, double relative_tolerance=1.e-5, int max_order=50, int min_order=1): if min_order < 1 or max_order < 1: raise ValueError("Order of Gaussian quadrature must be >= 1.") @@ -74,6 +83,8 @@ cdef class GaussianQuadrature(Integrator1D): self._max_order = max_order self._build_cash() + self.integrand = integrand + self.relative_tolerance = relative_tolerance @property @@ -162,31 +173,34 @@ cdef class GaussianQuadrature(Integrator1D): @cython.wraparound(False) @cython.cdivision(True) @cython.initializedcheck(False) - cpdef (double, double) integrate(self, Function1D func, double a, double b): + cdef double evaluate(self, double a, double b) except? -1e999: """ Integrates a one-dimensional function over a finite interval. - :param Function1D func: A function to integrate. :param double a: Lower limit of integration. :param double b: Upper limit of integration. - :returns: Two-element tuple containing Gaussian quadrature approximation to integral - and difference between last two estimates of the integral. + :returns: Gaussian quadrature approximation to integral. """ cdef: int order, i, ibegin - double newval, oldval, error, x + double newval, oldval, error, x, c, d + + if self.function is None: + raise AttributeError("Integrand is not set.") oldval = INFINITY ibegin = 0 + c = 0.5 * (a + b) + d = 0.5 * (b - a) for order in range(self._min_order, self._max_order + 1): newval = 0 for i in range(ibegin, ibegin + order): - x = a + 0.5 * (b - a) * (self._roots_mv[i] + 1.) - newval += self._weights_mv[i] * func.evaluate(x) - newval *= 0.5 * (b - a) + x = c + d * self._roots_mv[i] + newval += self._weights_mv[i] * self.function.evaluate(x) + newval *= d error = abs(newval - oldval) oldval = newval @@ -196,4 +210,4 @@ cdef class GaussianQuadrature(Integrator1D): if error < self._rtol * abs(newval): break - return newval, error + return newval diff --git a/cherab/core/math/tests/test_integrators.py b/cherab/core/math/tests/test_integrators.py index a60a4eea..a62ffafd 100644 --- a/cherab/core/math/tests/test_integrators.py +++ b/cherab/core/math/tests/test_integrators.py @@ -31,11 +31,12 @@ def test_properties(self): min_order = 3 max_order = 30 reltol = 1.e-6 - quadrature = GaussianQuadrature(relative_tolerance=reltol, max_order=max_order, min_order=min_order) + quadrature = GaussianQuadrature(integrand=Arg1D, relative_tolerance=reltol, max_order=max_order, min_order=min_order) self.assertEqual(quadrature.relative_tolerance, reltol) self.assertEqual(quadrature.max_order, max_order) self.assertEqual(quadrature.min_order, min_order) + self.assertEqual(quadrature.integrand, Arg1D) min_order = 0 max_order = 2 # < min_order @@ -57,21 +58,26 @@ def test_properties(self): quadrature.relative_tolerance = reltol quadrature.min_order = min_order quadrature.max_order = max_order + quadrature.integrand = Exp1D self.assertEqual(quadrature.relative_tolerance, reltol) self.assertEqual(quadrature.min_order, min_order) self.assertEqual(quadrature.max_order, max_order) + self.assertEqual(quadrature.integrand, Exp1D) def test_integrate(self): """Test integration.""" - func = (2 / sqrt(pi)) * Exp1D(- Arg1D() * Arg1D()) quadrature = GaussianQuadrature(relative_tolerance=1.e-8) a = -0.5 b = 3. - result, error = quadrature.integrate(func, a, b) + + with self.assertRaises(AttributeError): # integrand is not set + quadrature(a, b) + + quadrature.integrand = (2 / sqrt(pi)) * Exp1D(- Arg1D() * Arg1D()) exact_integral = erf(b) - erf(a) - self.assertAlmostEqual(result, exact_integral, places=8) + self.assertAlmostEqual(quadrature(a, b), exact_integral, places=8) if __name__ == '__main__': From 86d908b4fe4cb7f523921992925c1e019914cedc Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Sun, 11 Sep 2022 03:08:35 +0300 Subject: [PATCH 37/59] Remove NumericallyIntegratedLineShapeModel. Add integrator to LineShapeModel. --- cherab/core/model/lineshape.pxd | 8 ++----- cherab/core/model/lineshape.pyx | 37 +++++++++++---------------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/cherab/core/model/lineshape.pxd b/cherab/core/model/lineshape.pxd index 050d589e..2591efe3 100644 --- a/cherab/core/model/lineshape.pxd +++ b/cherab/core/model/lineshape.pxd @@ -42,15 +42,11 @@ cdef class LineShapeModel: double wavelength Species target_species Plasma plasma + Integrator1D integrator cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum) -cdef class NumericallyIntegrableLineShapeModel(LineShapeModel): - - cdef Integrator1D integrator - - cdef class GaussianLine(LineShapeModel): pass @@ -63,7 +59,7 @@ cdef class MultipletLineShape(LineShapeModel): double[:,::1] _multiplet_mv -cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): +cdef class StarkBroadenedLine(LineShapeModel): cdef double _aij, _bij, _cij diff --git a/cherab/core/model/lineshape.pyx b/cherab/core/model/lineshape.pyx index c0bb9711..96c36495 100644 --- a/cherab/core/model/lineshape.pyx +++ b/cherab/core/model/lineshape.pyx @@ -148,37 +148,22 @@ cdef class LineShapeModel: :param float wavelength: The rest wavelength for this emission line. :param Species target_species: The target plasma species that is emitting. :param Plasma plasma: The emitting plasma object. + :param Integrator1D integrator: Integrator1D instance to integrate the line shape + over the spectral bin. Default is None. """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, Integrator1D integrator=None): self.line = line self.wavelength = wavelength self.target_species = target_species self.plasma = plasma + self.integrator = integrator cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): raise NotImplementedError('Child lineshape class must implement this method.') -cdef class NumericallyIntegrableLineShapeModel(LineShapeModel): - """ - Line shape model to be numerically integrated over the spectral bin. - - :param Line line: The emission line object for this line shape. - :param float wavelength: The rest wavelength for this emission line. - :param Species target_species: The target plasma species that is emitting. - :param Plasma plasma: The emitting plasma object. - :param Integrator1D integrator: Integrator1D instance to integrate the line shape - over the spectral bin. Default is `GaussianQuadrature()`. - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, Integrator1D integrator=None): - - super().__init__(line, wavelength, target_species, plasma) - self.integrator = integrator or GaussianQuadrature() - - cdef class GaussianLine(LineShapeModel): """ Produces Gaussian line shape. @@ -323,7 +308,7 @@ cdef class StarkFunction(Function1D): return 1. / ((fabs(x - self._x0))**2.5 + self._a) -cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): +cdef class StarkBroadenedLine(LineShapeModel): """ Parametrised Stark broadened line shape based on the Model Microfield Method (MMM). Contains embedded atomic data in the form of fits to MMM. @@ -342,6 +327,9 @@ cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): :param dict stark_model_coefficients: Alternative model coefficients in the form {line_ij: (c_ij, a_ij, b_ij), ...}. If None, the default model parameters will be used. + :param Integrator1D integrator: Integrator1D instance to integrate the line shape + over the spectral bin. Default is `GaussianQuadrature()`. + """ STARK_MODEL_COEFFICIENTS_DEFAULT = { @@ -386,8 +374,8 @@ cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): Line(tritium, 0, (9, 3)): (5.588e-15, 0.7165, 0.033) } - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, Integrator1D integrator=None, - dict stark_model_coefficients=None): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, + dict stark_model_coefficients=None, integrator=GaussianQuadrature()): stark_model_coefficients = stark_model_coefficients or self.STARK_MODEL_COEFFICIENTS_DEFAULT @@ -426,7 +414,6 @@ cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): double bin_integral int start, end, i Spectrum raw_lineshape - StarkFunction stark_funcion ne = self.plasma.get_electron_distribution().density(point.x, point.y, point.z) if ne <= 0.0: @@ -438,7 +425,7 @@ cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): lambda_1_2 = self._cij * ne**self._aij / (te**self._bij) - stark_funcion = StarkFunction(self.wavelength, lambda_1_2) + self.integrator.function = StarkFunction(self.wavelength, lambda_1_2) # calculate and check end of limits cutoff_lower_wavelength = self.wavelength - LORENZIAN_CUTOFF_GAMMA * lambda_1_2 @@ -461,7 +448,7 @@ cdef class StarkBroadenedLine(NumericallyIntegrableLineShapeModel): for i in range(start, end): upper_wavelength = raw_lineshape.min_wavelength + raw_lineshape.delta_wavelength * (i + 1) - bin_integral, _ = self.integrator.integrate(stark_funcion, lower_wavelength, upper_wavelength) + bin_integral = self.integrator.evaluate(lower_wavelength, upper_wavelength) raw_lineshape.samples_mv[i] += bin_integral lower_wavelength = upper_wavelength From 7f1e732690b11b508ac0eae00236a4ed54851a05 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 13 Oct 2022 15:40:53 +0100 Subject: [PATCH 38/59] Fix CI by pinning raysect install version in script (#381) Specify raysect 0.7.1 to install. Also need to pin pyopencl to 2022.1 to work around OpenCL issue. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9701d0b7..56bc565f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,11 +26,11 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib pyopencl[pocl] + run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib pyopencl[pocl]==2022.1 - name: Work around PyOpenCL issue 537 run: echo OCL_ICD_VENDORS=$(python -c 'import os, pyopencl; print(os.path.join(*pyopencl.__path__, ".libs"))') >> $GITHUB_ENV - name: Install Raysect from pypi - run: pip install raysect + run: pip install raysect==0.7.1 - name: Build cherab run: dev/build.sh - name: Run tests From 52e274cdf4ef743d655ddaf0e5d30eea30ec369b Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Thu, 18 Aug 2022 09:51:22 +0200 Subject: [PATCH 39/59] Add quiet property to Observer0DGroup --- cherab/tools/observers/group/base.py | 17 +++++++++++++++++ cherab/tools/tests/test_observer_groups.py | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cherab/tools/observers/group/base.py b/cherab/tools/observers/group/base.py index 3caf31ed..aee80f7d 100644 --- a/cherab/tools/observers/group/base.py +++ b/cherab/tools/observers/group/base.py @@ -331,6 +331,23 @@ def samples_per_task(self, value): for observer in self._observers: observer.samples_per_task = value + @property + def quiet(self): + return [observer.quiet for observer in self._observers] + + @quiet.setter + def quiet(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.quiet = v + else: + raise ValueError("The length of 'quiet' ({}) " + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.quiet = value + def observe(self): """ Starts the observation. diff --git a/cherab/tools/tests/test_observer_groups.py b/cherab/tools/tests/test_observer_groups.py index 5f127a99..d2f73905 100644 --- a/cherab/tools/tests/test_observer_groups.py +++ b/cherab/tools/tests/test_observer_groups.py @@ -133,7 +133,20 @@ def test_assignments(self): with self.assertRaises(ValueError): group.spectral_bins = [1000] * (len(group) + 1) - + + # quiet + quiet = [True] * len(group) + group.quiet = quiet + self.assertListEqual(group.quiet, quiet) + + quiet = False + group.quiet = quiet + for observer in group.observers: + self.assertEqual(observer.quiet, quiet) + + with self.assertRaises(ValueError): + group.quiet = [False] * (len(group) + 1) + # rays probs = [0.2 + i*0.1 for i in range(len(group))] min_depths = [2 + i for i in range(len(group))] From 3ce7a344b1ff526959666f004f787b2f5e3486b8 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Fri, 19 Aug 2022 11:36:33 +0200 Subject: [PATCH 40/59] Reorder and add missing attributes --- cherab/tools/observers/group/base.py | 175 +++++++++++++++++---------- 1 file changed, 108 insertions(+), 67 deletions(-) diff --git a/cherab/tools/observers/group/base.py b/cherab/tools/observers/group/base.py index aee80f7d..50d65788 100644 --- a/cherab/tools/observers/group/base.py +++ b/cherab/tools/observers/group/base.py @@ -32,22 +32,22 @@ class Observer0DGroup(Node): all observers, or each observer can be assigned with individual value. :ivar list names: A list of observer names. - :ivar list pipelines: A list of all pipelines connected to each observer in the group. :ivar list/RenderEngine render_engine: Rendering engine used by the observers. Note that if the engine is shared, changing its parameters for one observer in a group will affect all observers. - :ivar list/float min_wavelength: Lower wavelength bound for sampled spectral range. + :ivar list/int spectral_bins: The number of smaller sub-spectrum rays the full spectrum will be divided into. + :ivar list/int spectral_rays: The number of smaller sub-spectrum rays the full spectrum will be divided into. :ivar list/float max_wavelength: Upper wavelength bound for sampled spectral range. - :ivar list/int spectral_bins: The number of spectral samples over the wavelength range. - :ivar list/float ray_extinction_prob: Probability of ray extinction after every material - intersection. - :ivar list/float ray_extinction_min_depth: Minimum number of paths before russian roulette - style ray extinction. + :ivar list/float min_wavelength: Lower wavelength bound for sampled spectral range. + :ivar list/float ray_extinction_prob: Probability of ray extinction after every material intersection. :ivar list/int ray_max_depth: Maximum number of Ray paths before terminating Ray. + :ivar list/float ray_extinction_min_depth: Minimum number of paths before russian roulette style ray extinction. + :ivar list/bool ray_importance_sampling: Toggle importance sampling behaviour (default=True). :ivar list/float ray_important_path_weight: Relative weight of important path sampling. :ivar list/int pixel_samples: The number of samples to take per pixel. :ivar list/int samples_per_task: Minimum number of samples to request per task. + :ivar list pipelines: A list of all pipelines connected to each observer in the group. """ _OBSERVER_TYPE = Observer0D @@ -110,7 +110,9 @@ def add_observer(self, observer): @property def names(self): - # A list of observer names. + """ + A list of observer names. + """ return [observer.name for observer in self._observers] @names.setter @@ -125,24 +127,14 @@ def names(self, value): else: raise TypeError("The names attribute must be a list or tuple.") - @property - def pipelines(self): + def observe(self): """ - A list of all pipelines connected to each observer in the group - - :param list pipelist: list of lists/tuples of already instantiated pipelines - :rtype: list + Starts the observation. """ - return [observer.pipelines for observer in self._observers] - - @pipelines.setter - def pipelines(self, pipelist): - if len(pipelist) == len(self._observers): - for observer, pipelines in zip(self._observers, pipelist): - observer.pipelines = pipelines - else: - raise ValueError('Length of pipelines list do not match number of observers in the group.') + for observer in self._observers: + observer.observe() + # _ObserverBase attributes and properties @property def render_engine(self): """ @@ -170,22 +162,40 @@ def render_engine(self, value): observer.render_engine = value @property - def min_wavelength(self): - # Lower wavelength bound for sampled spectral range. - return [observer.min_wavelength for observer in self._observers] + def spectral_bins(self): + # The number of spectral samples over the wavelength range. + return [observer.spectral_bins for observer in self._observers] - @min_wavelength.setter - def min_wavelength(self, value): + @spectral_bins.setter + def spectral_bins(self, value): if isinstance(value, (list, tuple, ndarray)): if len(value) == len(self._observers): for observer, v in zip(self._observers, value): - observer.min_wavelength = v + observer.spectral_bins = v else: - raise ValueError("The length of 'min_wavelength' ({}) " + raise ValueError("The length of 'spectral_bins' ({}) " "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: for observer in self._observers: - observer.min_wavelength = value + observer.spectral_bins = value + + @property + def spectral_rays(self): + # The number of spectral samples over the wavelength range. + return [observer.spectral_rays for observer in self._observers] + + @spectral_rays.setter + def spectral_rays(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.spectral_rays = v + else: + raise ValueError("The length of 'spectral_rays' ({}) " + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.spectral_rays = value @property def max_wavelength(self): @@ -206,22 +216,22 @@ def max_wavelength(self, value): observer.max_wavelength = value @property - def spectral_bins(self): - # The number of spectral samples over the wavelength range. - return [observer.spectral_bins for observer in self._observers] + def min_wavelength(self): + # Lower wavelength bound for sampled spectral range. + return [observer.min_wavelength for observer in self._observers] - @spectral_bins.setter - def spectral_bins(self, value): + @min_wavelength.setter + def min_wavelength(self, value): if isinstance(value, (list, tuple, ndarray)): if len(value) == len(self._observers): for observer, v in zip(self._observers, value): - observer.spectral_bins = v + observer.min_wavelength = v else: - raise ValueError("The length of 'spectral_bins' ({}) " + raise ValueError("The length of 'min_wavelength' ({}) " "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: for observer in self._observers: - observer.spectral_bins = value + observer.min_wavelength = value @property def ray_extinction_prob(self): @@ -241,6 +251,24 @@ def ray_extinction_prob(self, value): for observer in self._observers: observer.ray_extinction_prob = value + @property + def ray_max_depth(self): + # Maximum number of Ray paths before terminating Ray. + return [observer.ray_max_depth for observer in self._observers] + + @ray_max_depth.setter + def ray_max_depth(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.ray_max_depth = v + else: + raise ValueError("The length of 'ray_max_depth' ({}) " + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.ray_max_depth = value + @property def ray_extinction_min_depth(self): # Minimum number of paths before russian roulette style ray extinction. @@ -260,22 +288,22 @@ def ray_extinction_min_depth(self, value): observer.ray_extinction_min_depth = value @property - def ray_max_depth(self): - # Maximum number of Ray paths before terminating Ray. - return [observer.ray_max_depth for observer in self._observers] + def ray_importance_sampling(self): + # Relative weight of important path sampling. + return [observer.ray_importance_sampling for observer in self._observers] - @ray_max_depth.setter - def ray_max_depth(self, value): + @ray_importance_sampling.setter + def ray_importance_sampling(self, value): if isinstance(value, (list, tuple, ndarray)): if len(value) == len(self._observers): for observer, v in zip(self._observers, value): - observer.ray_max_depth = v + observer.ray_importance_sampling = v else: - raise ValueError("The length of 'ray_max_depth' ({}) " + raise ValueError("The length of 'ray_importance_sampling' ({}) " "mismatches the number of observers ({}).".format(len(value), len(self._observers))) else: for observer in self._observers: - observer.ray_max_depth = value + observer.ray_importance_sampling = value @property def ray_important_path_weight(self): @@ -295,6 +323,25 @@ def ray_important_path_weight(self, value): for observer in self._observers: observer.ray_important_path_weight = value + @property + def quiet(self): + return [observer.quiet for observer in self._observers] + + @quiet.setter + def quiet(self, value): + if isinstance(value, (list, tuple, ndarray)): + if len(value) == len(self._observers): + for observer, v in zip(self._observers, value): + observer.quiet = v + else: + raise ValueError("The length of 'quiet' ({}) " + "mismatches the number of observers ({}).".format(len(value), len(self._observers))) + else: + for observer in self._observers: + observer.quiet = value + + + # Observer0D attributes and properties @property def pixel_samples(self): # The number of samples to take per pixel. @@ -332,28 +379,22 @@ def samples_per_task(self, value): observer.samples_per_task = value @property - def quiet(self): - return [observer.quiet for observer in self._observers] - - @quiet.setter - def quiet(self, value): - if isinstance(value, (list, tuple, ndarray)): - if len(value) == len(self._observers): - for observer, v in zip(self._observers, value): - observer.quiet = v - else: - raise ValueError("The length of 'quiet' ({}) " - "mismatches the number of observers ({}).".format(len(value), len(self._observers))) - else: - for observer in self._observers: - observer.quiet = value - - def observe(self): + def pipelines(self): """ - Starts the observation. + A list of all pipelines connected to each observer in the group + + :param list pipelist: list of lists/tuples of already instantiated pipelines + :rtype: list """ - for observer in self._observers: - observer.observe() + return [observer.pipelines for observer in self._observers] + + @pipelines.setter + def pipelines(self, pipelist): + if len(pipelist) == len(self._observers): + for observer, pipelines in zip(self._observers, pipelist): + observer.pipelines = pipelines + else: + raise ValueError('Length of pipelines list do not match number of observers in the group.') def connect_pipelines(self, pipeline_classes, keywords_list=None, suppress_display_progress=True): """ From 30d37c890e02f97430d3b02f5e816734a2a22511 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Fri, 19 Aug 2022 14:06:00 +0200 Subject: [PATCH 41/59] Update tests --- cherab/tools/tests/test_observer_groups.py | 29 ++++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/cherab/tools/tests/test_observer_groups.py b/cherab/tools/tests/test_observer_groups.py index d2f73905..1f5b7cb0 100644 --- a/cherab/tools/tests/test_observer_groups.py +++ b/cherab/tools/tests/test_observer_groups.py @@ -121,15 +121,20 @@ def test_assignments(self): with self.assertRaises(ValueError): group.min_wavelength = [90] * (len(group) - 1) - # bins + # spectral bins = [200 + i*100 for i in range(len(group))] + rays = [2] * len(group) group.spectral_bins = bins + group.spectral_rays = rays self.assertListEqual(group.spectral_bins, bins) bins = 300 + rays = 1 group.spectral_bins = bins + group.spectral_rays = rays for observer in group.observers: self.assertEqual(observer.spectral_bins, bins) + self.assertEqual(observer.spectral_rays, rays) with self.assertRaises(ValueError): group.spectral_bins = [1000] * (len(group) + 1) @@ -149,38 +154,46 @@ def test_assignments(self): # rays probs = [0.2 + i*0.1 for i in range(len(group))] - min_depths = [2 + i 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))] + sampling = [False] * len(group) weights = [0.5 + i * 0.1 for i in range(len(group))] group.ray_extinction_prob = probs - group.ray_extinction_min_depth = min_depths group.ray_max_depth = max_depths + group.ray_extinction_min_depth = min_depths + group.ray_importance_sampling = sampling group.ray_important_path_weight = weights self.assertListEqual(group.ray_extinction_prob, probs) - self.assertListEqual(group.ray_extinction_min_depth, min_depths) self.assertListEqual(group.ray_max_depth, max_depths) + self.assertListEqual(group.ray_extinction_min_depth, min_depths) + self.assertListEqual(group.ray_importance_sampling, sampling) self.assertListEqual(group.ray_important_path_weight, weights) probs = 0.3 - min_depths = 3 max_depths = 6 + min_depths = 3 + sampling = True weights = 0.7 group.ray_extinction_prob = probs - group.ray_extinction_min_depth = min_depths group.ray_max_depth = max_depths + group.ray_extinction_min_depth = min_depths + group.ray_importance_sampling = sampling group.ray_important_path_weight = weights for observer in group.observers: self.assertEqual(observer.ray_extinction_prob, probs) - self.assertEqual(observer.ray_extinction_min_depth, min_depths) self.assertEqual(observer.ray_max_depth, max_depths) + self.assertEqual(observer.ray_extinction_min_depth, min_depths) + self.assertEqual(observer.ray_importance_sampling, sampling) self.assertEqual(observer.ray_important_path_weight, weights) with self.assertRaises(ValueError): group.ray_extinction_prob = [0.5] * (len(group) + 1) + with self.assertRaises(ValueError): + group.ray_max_depth = [8] * (len(group) + 1) with self.assertRaises(ValueError): group.ray_extinction_min_depth = [4] * (len(group) + 1) with self.assertRaises(ValueError): - group.ray_max_depth = [8] * (len(group) + 1) + group.ray_importance_sampling = [False] * (len(group) + 1) with self.assertRaises(ValueError): group.ray_important_path_weight = [0.7] * (len(group) + 1) From 175dda66dfde37bdfbab13678ff357848a481b2b Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Fri, 26 Aug 2022 13:46:11 +0200 Subject: [PATCH 42/59] Fix docstrings --- cherab/tools/observers/group/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/tools/observers/group/base.py b/cherab/tools/observers/group/base.py index 50d65788..7fc6c54d 100644 --- a/cherab/tools/observers/group/base.py +++ b/cherab/tools/observers/group/base.py @@ -36,7 +36,7 @@ class Observer0DGroup(Node): Note that if the engine is shared, changing its parameters for one observer in a group will affect all observers. - :ivar list/int spectral_bins: The number of smaller sub-spectrum rays the full spectrum will be divided into. + :ivar list/int spectral_bins: The number of spectral samples over the wavelength range. :ivar list/int spectral_rays: The number of smaller sub-spectrum rays the full spectrum will be divided into. :ivar list/float max_wavelength: Upper wavelength bound for sampled spectral range. :ivar list/float min_wavelength: Lower wavelength bound for sampled spectral range. From 368965a9644ad57206deda2572e938c6c3f12213 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 27 Jan 2022 16:34:56 +0000 Subject: [PATCH 43/59] Explicitly specify build-backend Work around https://github.com/pypa/pip/issues/6264 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 8c92c2a1..9cfc8c15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,3 @@ [build-system] requires = ["setuptools", "wheel", "numpy>=1.14", "cython>=0.28", "raysect==0.7.1"] +build-backend="setuptools.build_meta" From 0b2a21ab79b0b540a9ee882df3f08d6b11007cc2 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 27 Jan 2022 17:19:53 +0000 Subject: [PATCH 44/59] Update installation instructions to use modern best practices - Prefer pip install to the (deprecated) manual calling of setup.py. - Suggest the use of virtual environments as an alternative to a user install. - Fix a bit of whitespace mess in the documentation. --- README.md | 70 ++++++++++++---------- docs/source/installation_and_structure.rst | 36 +++++++---- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index b3141d05..963b0834 100644 --- a/README.md +++ b/README.md @@ -12,56 +12,66 @@ for guidance on using the code. Installation ------------ -Cherab is a large code framework consisting of a core package and feature -packages. Users will generally install the core package and the specific -feature packages they need for their work. For example, users working on the -JET tokamak will require the ``cherab-core`` package, and the ``cherab-jet`` +Cherab is a large code framework consisting of a core package and feature +packages. Users will generally install the core package and the specific +feature packages they need for their work. For example, users working on the +JET tokamak will require the `cherab-core` package, and the `cherab-jet` package. -Unless developing new code for a cherab package, most users should clone the -master branch. When developing new features for cherab, the development branch +Unless developing new code for a cherab package, most users should clone the +master branch. When developing new features for cherab, the development branch should be used as the base. -All cherab packages are standard python packages and basic installation is +All cherab packages are standard python packages and basic installation is achieved with: ``` -python setup.py install +pip install cherab ``` -This will compile the Cherab cython extensions and install the package. If you -don't have administrator access to install the package, add the ``--user`` flag +This will compile the Cherab cython extensions and install the package. If you +don't have administrator access to install the package, add the `--user` flag to the above line to install the package under your own user account. +Alternatively, consider creating a [virtual environment](https://docs.python.org/3/tutorial/venv.html) +and installing `cherab` in the environment. -When developing cherab it is usually preferred that the packages be installed -in "develop" mode: +When developing cherab it is usually preferred that the packages be installed +in "editable" mode. Clone this repository and change directory to the root of +the repository, then run: ``` -python setup.py develop +pip install -e . ``` -This will cause the original installation folder to be added to the -site-package path. Modifications to the code will therefore be visible to -python next time the code is imported. The ``--user`` flag should be used if -you do not have administrative permission for your python installation. +This will cause the original installation folder to be added to the site-package +path. Modifications to the code will therefore be visible to python next time +the code is imported. A virtual environment or the ``--user`` flag should be +used if you do not have administrative permission for your python installation. +If you make any changes to Cython files you will need to run `./dev/build.sh` to +rebuild the relevant files. -As all the Cherab packages are dependent on the ``cherab-core`` package, this -package must be installed first. Note that other packages may have their own -inter-dependencies, see the specific package documentation for more -information. +As all the Cherab packages are dependent on the ``cherab-core`` package, this +package must be installed first. Note that other packages may have their own +inter-dependencies, see the specific package documentation for more information. + +Cherab is organised as a namespace package, where each of the submodules is +installed in the same location as the core package. Any submodules using Cython +with a build-time dependency on Cherab need to use a Cython version newer than +3.0a5, due to a [bug](https://github.com/cython/cython/issues/2918) in how +earlier versions of Cython handle namespaces. Governance ---------- -The management of the project is divided into Scientific and Technical Project -Management. The Scientific management happens through the normal community +The management of the project is divided into Scientific and Technical Project +Management. The Scientific management happens through the normal community routes such as JET and MST1 task force meetings, ITPA meetings, etc. -The Technical Management Committee (TMC) is a smaller subset of the community, -being responsible for ensuring the integrity and high code quality of Cherab is -maintained. These TMC members would have in-depth knowledge of the code base -through a demonstrated history of contributing to the project. The TMC would -primarily be responsible for accepting / rejecting merge requests on the basis +The Technical Management Committee (TMC) is a smaller subset of the community, +being responsible for ensuring the integrity and high code quality of Cherab is +maintained. These TMC members would have in-depth knowledge of the code base +through a demonstrated history of contributing to the project. The TMC would +primarily be responsible for accepting / rejecting merge requests on the basis of code / physics algorithm quality standards. @@ -78,6 +88,6 @@ TMC Members Citing The Code --------------- -* Dr Carine Giroud, Dr Alex Meakins, Dr Matthew Carr, Dr Alfonso Baciero, & -Mr Corentin Bertrand. (2018, March 23). Cherab Spectroscopy Modelling Framework +* Dr Carine Giroud, Dr Alex Meakins, Dr Matthew Carr, Dr Alfonso Baciero, & +Mr Corentin Bertrand. (2018, March 23). Cherab Spectroscopy Modelling Framework (Version v0.1.0). Zenodo. http://doi.org/10.5281/zenodo.1206142 diff --git a/docs/source/installation_and_structure.rst b/docs/source/installation_and_structure.rst index 9d544184..3af9d3ad 100644 --- a/docs/source/installation_and_structure.rst +++ b/docs/source/installation_and_structure.rst @@ -39,7 +39,7 @@ that Cherab can calculate. This package is strictly managed by the Cherab develo require some type of atomic data for their calculations. The base types of reaction rates and photon emissivity coefficients are defined in the Core API Module, `cherab `_. A default atomic data source module based on - the `OpenADAS project `_, is included in the package. In future +the `OpenADAS project `_, is included in the package. In future other atomic data sources, such as the ALADDIN database for example, could be made available through additional packages. @@ -78,7 +78,9 @@ The easiest way to install Cherab with OpenADAS is using `pip `_ to avoid the risk of +conflicting versions of packages in your Python environment. Installing from source @@ -94,17 +96,29 @@ If all the required dependencies are present (cython, numpy, scipy, matplotlib a start the Cherab compilation and installation process. If you don't have administrator access to install the package, add the ``--user`` flag to the above line to install the package under your own user account. -When developing cherab it is usually preferred that the packages be installed in "develop" mode:: +As all the Cherab packages are dependent on the core ``cherab`` package, this package must be installed first. +Note that other packages may have their own inter-dependencies, see the specific package documentation for +more information. + +Installing for development +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When developing cherab it is usually preferred that the packages be installed in "develop" mode. +Clone the project from the development repository, locate the folder containing setup.py and run:: + + pip install -e . + +The alternative command if pip is not available is:: python setup.py develop -This will cause the original installation folder to be added to the site-package path. Modifications to -the code will therefore be visible to python next time the code is imported. The ``--user`` flag should be -used if you do not have administrative permission for your python installation. +Either command will cause the original installation folder to be added to the +site-package path. Modifications to the code will therefore be visible to python next +time the code is imported. If you are modifying Cython source files then run +``./dev/build.sh`` to re-build those files in order for the changes to be visible. A +virtual environment, or the ``--user`` flag, should be used if you do not have +administrative permission for your python installation. -As all the Cherab packages are dependent on the core ``cherab`` package, this package must be installed first. -Note that other packages may have their own inter-dependencies, see the specific package documentation for -more information. When developing new features for Cherab, the development branch should be used as the base. @@ -128,9 +142,9 @@ Testing ~~~~~~~ A selection of test scripts can be run with the `nose` testing framework. These are routinely -run on the development version. Running ``nosetests`` at the terminal in the source directory +run on the development version. Running ``./dev/test.sh`` at the terminal in the source directory should run all of these tests to completion without errors or failures. -Many of the demos used throughout the Raysect documentation are distributed with the source code in +Many of the demos used throughout the documentation are distributed with the source code in the ``demo`` folder. From 183e8030a1ffa7bfb012776a2462d36110347da0 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 28 Jan 2022 15:49:02 +0000 Subject: [PATCH 45/59] Remove Cython as a runtime dependency Cython is now only a build-time dependency, as specified by PEP518. It is not required for running Cherab, so has been removed from install_requires. A quick check showed there was one module, radiation_function.pyx, which imported Cython at runtime. That was unnecessary, so it's been changed to cimport Cython at build time instead. As an added bonus, a small performance improvement (using a Cython accessor function rather than a Python property) has been made, and a pxd file to enable the module to be cimported has been added. --- cherab/tools/emitters/radiation_function.pxd | 25 ++++++ cherab/tools/emitters/radiation_function.pyx | 14 ++-- setup.py | 86 +++++++------------- 3 files changed, 59 insertions(+), 66 deletions(-) create mode 100644 cherab/tools/emitters/radiation_function.pxd diff --git a/cherab/tools/emitters/radiation_function.pxd b/cherab/tools/emitters/radiation_function.pxd new file mode 100644 index 00000000..8c9ac3b0 --- /dev/null +++ b/cherab/tools/emitters/radiation_function.pxd @@ -0,0 +1,25 @@ +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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 raysect.optical.material.emitter cimport InhomogeneousVolumeEmitter +from cherab.core.math.function cimport Function3D + + +cdef class RadiationFunction(InhomogeneousVolumeEmitter): + cdef: + readonly Function3D radiation_function diff --git a/cherab/tools/emitters/radiation_function.pyx b/cherab/tools/emitters/radiation_function.pyx index c9cd414b..a9b0800d 100644 --- a/cherab/tools/emitters/radiation_function.pyx +++ b/cherab/tools/emitters/radiation_function.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2022 Euratom +# Copyright 2016-2022 United Kingdom Atomic Energy Authority +# Copyright 2016-2022 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"); @@ -20,7 +20,7 @@ from raysect.optical cimport Point3D, Vector3D, Spectrum, World, Ray, Primitive, from raysect.optical.material.emitter cimport InhomogeneousVolumeEmitter, NumericalIntegrator from cherab.core.math.function cimport Function3D, autowrap_function3d from libc.math cimport M_PI -import cython +cimport cython cdef class RadiationFunction(InhomogeneousVolumeEmitter): @@ -51,10 +51,6 @@ cdef class RadiationFunction(InhomogeneousVolumeEmitter): >>> def rad_function_3d(x, y, z): return 0 >>> radiation_emitter = RadiationFunction(rad_function_3d) """ - - cdef: - readonly Function3D radiation_function - def __init__(self, radiation_function, step=0.1): super().__init__(NumericalIntegrator(step=step)) @@ -68,7 +64,7 @@ cdef class RadiationFunction(InhomogeneousVolumeEmitter): AffineMatrix3D world_to_local, AffineMatrix3D local_to_world): cdef int index - cdef double wvl_range = ray.max_wavelength - ray.min_wavelength + cdef double wvl_range = ray.get_max_wavelength() - ray.get_min_wavelength() cdef double emission emission = self.radiation_function.evaluate(point.x, point.y, point.z) / (4 * M_PI * wvl_range) diff --git a/setup.py b/setup.py index db4bb706..d7810f52 100644 --- a/setup.py +++ b/setup.py @@ -4,19 +4,15 @@ import os import os.path as path import multiprocessing +from Cython.Build import cythonize multiprocessing.set_start_method('fork') -use_cython = True force = False profile = False line_profile = False install_rates = False -if "--skip-cython" in sys.argv: - use_cython = False - del sys.argv[sys.argv.index("--skip-cython")] - if "--force" in sys.argv: force = True del sys.argv[sys.argv.index("--force")] @@ -43,57 +39,34 @@ compilation_args.append("-DCYTHON_TRACE=1") compilation_args.append("-DCYTHON_TRACE_NOGIL=1") cython_directives["linetrace"] = True - -if use_cython: - - from Cython.Build import cythonize - - # build .pyx extension list - extensions = [] - for package in source_paths: - for root, dirs, files in os.walk(path.join(setup_path, package)): - for file in files: - if path.splitext(file)[1] == ".pyx": - pyx_file = path.relpath(path.join(root, file), setup_path) - module = path.splitext(pyx_file)[0].replace("/", ".") - extensions.append( - Extension( - module, - [pyx_file], - include_dirs=compilation_includes, - extra_compile_args=compilation_args, - ), - ) - - if profile: - cython_directives["profile"] = True - - # generate .c files from .pyx - extensions = cythonize( - extensions, - nthreads=multiprocessing.cpu_count(), - force=force, - compiler_directives=cython_directives, - ) - -else: - - # build .c extension list - extensions = [] - for package in source_paths: - for root, dirs, files in os.walk(path.join(setup_path, package)): - for file in files: - if path.splitext(file)[1] == ".c": - c_file = path.relpath(path.join(root, file), setup_path) - module = path.splitext(c_file)[0].replace("/", ".") - extensions.append( - Extension( - module, - [c_file], - include_dirs=compilation_includes, - extra_compile_args=compilation_args, - ), - ) +if profile: + cython_directives["profile"] = True + + +extensions = [] +for package in source_paths: + for root, dirs, files in os.walk(path.join(setup_path, package)): + for file in files: + if path.splitext(file)[1] == ".pyx": + pyx_file = path.relpath(path.join(root, file), setup_path) + module = path.splitext(pyx_file)[0].replace("/", ".") + extensions.append( + Extension( + module, + [pyx_file], + include_dirs=compilation_includes, + extra_compile_args=compilation_args, + ), + ) + + +# generate .c files from .pyx +extensions = cythonize( + extensions, + nthreads=multiprocessing.cpu_count(), + force=force, + compiler_directives=cython_directives, +) # parse the package version number with open(path.join(path.dirname(__file__), "cherab/core/VERSION")) as version_file: @@ -131,7 +104,6 @@ "scipy", "matplotlib", "raysect==0.7.1", - "cython>=0.28", ], packages=find_packages(), include_package_data=True, From b1b1b7fdeb0a217b94f605593873f49b93f007f3 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 22 Feb 2022 15:43:07 +0000 Subject: [PATCH 46/59] Use oldest-supported-numpy as build dependency Automatically get the oldest supported numpy version for each Python version being built, for maximum compatibility of the built wheels. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9cfc8c15..f896f8f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "wheel", "numpy>=1.14", "cython>=0.28", "raysect==0.7.1"] +requires = ["setuptools", "wheel", "oldest-supported-numpy", "cython>=0.28", "raysect==0.7.1"] build-backend="setuptools.build_meta" From 6a915f9edc1c584b3c89856efabbea0da400cec3 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 17 Oct 2022 18:59:44 +0300 Subject: [PATCH 47/59] Remove Function2D inheritance for Integrator1D. Forbid setting the integrand to None. --- .../core/math/integrators/integrators1d.pxd | 6 ++- .../core/math/integrators/integrators1d.pyx | 42 ++++++++++++------- cherab/core/math/tests/test_integrators.py | 4 -- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/cherab/core/math/integrators/integrators1d.pxd b/cherab/core/math/integrators/integrators1d.pxd index 8109e6d7..c451285f 100644 --- a/cherab/core/math/integrators/integrators1d.pxd +++ b/cherab/core/math/integrators/integrators1d.pxd @@ -20,11 +20,13 @@ from numpy cimport ndarray from raysect.core.math.function.float cimport Function1D, Function2D -cdef class Integrator1D(Function2D): +cdef class Integrator1D: cdef: Function1D function + cdef double evaluate(self, double a, double b) except? -1e999 + cdef class GaussianQuadrature(Integrator1D): @@ -34,4 +36,4 @@ cdef class GaussianQuadrature(Integrator1D): ndarray _roots, _weights double[:] _roots_mv, _weights_mv - cdef _build_cash(self) + cdef _build_cache(self) diff --git a/cherab/core/math/integrators/integrators1d.pyx b/cherab/core/math/integrators/integrators1d.pyx index 1e69efd4..54ec4059 100644 --- a/cherab/core/math/integrators/integrators1d.pyx +++ b/cherab/core/math/integrators/integrators1d.pyx @@ -21,13 +21,13 @@ import numpy as np from scipy.special import roots_legendre -from raysect.core.math.function.float cimport autowrap_function1d +from raysect.core.math.function.float cimport autowrap_function1d, Constant1D from libc.math cimport INFINITY cimport cython -cdef class Integrator1D(Function2D): +cdef class Integrator1D: """ Compute a definite integral of a one-dimensional function. @@ -44,12 +44,25 @@ cdef class Integrator1D(Function2D): return self.function @integrand.setter - def integrand(self, object func): + def integrand(self, object func not None): - if func is None: - self.function = None - else: - self.function = autowrap_function1d(func) + self.function = autowrap_function1d(func) + + cdef double evaluate(self, double a, double b) except? -1e999: + + raise NotImplementedError("The evaluate() method has not been implemented.") + + def __call__(self, double a, double b): + """ + Integrates a one-dimensional function over a finite interval. + + :param double a: Lower limit of integration. + :param double b: Upper limit of integration. + + :returns: Definite integral of a one-dimensional function. + """ + + return self.evaluate(a, b) cdef class GaussianQuadrature(Integrator1D): @@ -58,7 +71,7 @@ cdef class GaussianQuadrature(Integrator1D): using fixed-tolerance Gaussian quadrature. (see Scipy `quadrature `). - :param object integrand: A 1D function to integrate. + :param object integrand: A 1D function to integrate. Default is Constant1D(0). :param double relative_tolerance: Iteration stops when relative error between last two iterates is less than this value. Default is 1.e-5. :param int max_order: Maximum order on Gaussian quadrature. Default is 50. @@ -71,7 +84,7 @@ cdef class GaussianQuadrature(Integrator1D): :ivar int min_order: Minimum order on Gaussian quadrature. """ - def __init__(self, object integrand=None, double relative_tolerance=1.e-5, int max_order=50, int min_order=1): + def __init__(self, object integrand=Constant1D(0), double relative_tolerance=1.e-5, int max_order=50, int min_order=1): if min_order < 1 or max_order < 1: raise ValueError("Order of Gaussian quadrature must be >= 1.") @@ -81,7 +94,7 @@ cdef class GaussianQuadrature(Integrator1D): self._min_order = min_order self._max_order = max_order - self._build_cash() + self._build_cache() self.integrand = integrand @@ -107,7 +120,7 @@ cdef class GaussianQuadrature(Integrator1D): self._min_order = value - self._build_cash() + self._build_cache() @property def max_order(self): @@ -129,7 +142,7 @@ cdef class GaussianQuadrature(Integrator1D): self._max_order = value - self._build_cash() + self._build_cache() @property def relative_tolerance(self): @@ -148,7 +161,7 @@ cdef class GaussianQuadrature(Integrator1D): self._rtol = value - cdef _build_cash(self): + cdef _build_cache(self): """ Caches the roots and weights of the Gauss-Legendre quadrature. """ @@ -187,9 +200,6 @@ cdef class GaussianQuadrature(Integrator1D): int order, i, ibegin double newval, oldval, error, x, c, d - if self.function is None: - raise AttributeError("Integrand is not set.") - oldval = INFINITY ibegin = 0 c = 0.5 * (a + b) diff --git a/cherab/core/math/tests/test_integrators.py b/cherab/core/math/tests/test_integrators.py index a62ffafd..fa967165 100644 --- a/cherab/core/math/tests/test_integrators.py +++ b/cherab/core/math/tests/test_integrators.py @@ -70,10 +70,6 @@ def test_integrate(self): quadrature = GaussianQuadrature(relative_tolerance=1.e-8) a = -0.5 b = 3. - - with self.assertRaises(AttributeError): # integrand is not set - quadrature(a, b) - quadrature.integrand = (2 / sqrt(pi)) * Exp1D(- Arg1D() * Arg1D()) exact_integral = erf(b) - erf(a) From e62f0adde3eae62cdf949a2db8013f352c5167ea Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 17 Oct 2022 21:52:05 +0300 Subject: [PATCH 48/59] Add initialisation checks to StarkFunction and make it return a normalised line shape. --- cherab/core/model/lineshape.pyx | 41 +++++++++++++++++----------- cherab/core/tests/test_lineshapes.py | 32 +++++++++++++--------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/cherab/core/model/lineshape.pyx b/cherab/core/model/lineshape.pyx index 96c36495..69278560 100644 --- a/cherab/core/model/lineshape.pyx +++ b/cherab/core/model/lineshape.pyx @@ -19,6 +19,8 @@ # under the Licence. import numpy as np +from scipy.special import hyp2f1 + cimport numpy as np from libc.math cimport sqrt, erf, M_SQRT2, floor, ceil, fabs from raysect.optical.spectrum cimport new_spectrum @@ -80,8 +82,6 @@ cpdef double thermal_broadening(double wavelength, double temperature, double at # the number of standard deviations outside the rest wavelength the line is considered to add negligible value (including a margin for safety) DEF GAUSSIAN_CUTOFF_SIGMA = 10.0 -DEF LORENZIAN_CUTOFF_GAMMA = 50.0 - @cython.cdivision(True) @cython.initializedcheck(False) @@ -294,18 +294,36 @@ cdef class MultipletLineShape(LineShapeModel): return spectrum +DEF LORENZIAN_CUTOFF_GAMMA = 50.0 + + cdef class StarkFunction(Function1D): + """ + Normalised Stark function for the StarkBroadenedLine line shape. + """ - cdef double _a, _x0 + cdef double _a, _x0, _norm + + STARK_NORM_COEFFICIENT = 4 * LORENZIAN_CUTOFF_GAMMA * hyp2f1(0.4, 1, 1.4, -(2 * LORENZIAN_CUTOFF_GAMMA)**2.5) def __init__(self, double wavelength, double lambda_1_2): + + if wavelength <= 0: + raise ValueError("Argument 'wavelength' must be positive.") + + if lambda_1_2 <= 0: + raise ValueError("Argument 'lambda_1_2' must be positive.") + self._x0 = wavelength self._a = (0.5 * lambda_1_2)**2.5 + # normalise, so the integral over x is equal to 1 in the limits + # (_x0 - LORENZIAN_CUTOFF_GAMMA * lambda_1_2, _x0 + LORENZIAN_CUTOFF_GAMMA * lambda_1_2) + self._norm = (0.5 * lambda_1_2)**1.5 / self.STARK_NORM_COEFFICIENT @cython.cdivision(True) cdef double evaluate(self, double x) except? -1e999: - return 1. / ((fabs(x - self._x0))**2.5 + self._a) + return self._norm / ((fabs(x - self._x0))**2.5 + self._a) cdef class StarkBroadenedLine(LineShapeModel): @@ -441,25 +459,16 @@ cdef class StarkBroadenedLine(LineShapeModel): end = min(spectrum.bins, ceil((cutoff_upper_wavelength - spectrum.min_wavelength) / spectrum.delta_wavelength)) # add line to spectrum - raw_lineshape = spectrum.new_spectrum() - - lower_wavelength = raw_lineshape.min_wavelength + start * raw_lineshape.delta_wavelength + lower_wavelength = spectrum.min_wavelength + start * spectrum.delta_wavelength for i in range(start, end): - upper_wavelength = raw_lineshape.min_wavelength + raw_lineshape.delta_wavelength * (i + 1) + upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) bin_integral = self.integrator.evaluate(lower_wavelength, upper_wavelength) - raw_lineshape.samples_mv[i] += bin_integral + spectrum.samples_mv[i] += radiance * bin_integral / spectrum.delta_wavelength lower_wavelength = upper_wavelength - # perform normalisation - raw_lineshape.div_scalar(raw_lineshape.total()) - - for i in range(start, end): - # Radiance ??? - spectrum.samples_mv[i] += radiance * raw_lineshape.samples_mv[i] - return spectrum diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index c29f1f2e..da4dc41d 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -18,17 +18,17 @@ import unittest -import os import numpy as np -from scipy.special import erf +from scipy.special import erf, hyp2f1 +from scipy.integrate import quadrature from raysect.core import Point3D, Vector3D from raysect.core.math.function.float import Arg1D, Constant1D from raysect.optical import Spectrum from cherab.core import Line +from cherab.core.math.integrators import GaussianQuadrature from cherab.core.atomic import deuterium, nitrogen, ZeemanStructure -from cherab.openadas import OpenADAS from cherab.tools.plasmas.slab import build_constant_slab_plasma from cherab.core.model import GaussianLine, MultipletLineShape, StarkBroadenedLine, ZeemanTriplet, ParametrisedZeemanTriplet, ZeemanMultiplet @@ -116,7 +116,7 @@ def test_multiplet_line_shape(self): for i in range(bins): self.assertAlmostEqual(multi_gaussian[i], spectrum.samples[i], delta=1e-10, - msg='GaussianLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + msg='MultipletLineShape.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_zeeman_triplet(self): # setting up a line shape model @@ -167,7 +167,7 @@ def test_zeeman_triplet(self): for pol in ('no', 'pi', 'sigma'): for i in range(bins): self.assertAlmostEqual(tri_gaussian[pol][i], spectrum[pol].samples[i], delta=1e-10, - msg='GaussianLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + msg='ZeemanTriplet.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_parametrised_zeeman_triplet(self): # setting up a line shape model @@ -219,7 +219,7 @@ def test_parametrised_zeeman_triplet(self): for pol in ('no', 'pi', 'sigma'): for i in range(bins): self.assertAlmostEqual(tri_gaussian[pol][i], spectrum[pol].samples[i], delta=1e-10, - msg='GaussianLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + msg='ParametrisedZeemanTriplet.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_zeeman_multiplet(self): # setting up a line shape model @@ -277,14 +277,15 @@ def test_zeeman_multiplet(self): for pol in ('no', 'pi', 'sigma'): for i in range(bins): self.assertAlmostEqual(tri_gaussian[pol][i], spectrum[pol].samples[i], delta=1e-10, - msg='GaussianLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + msg='ZeemanMultiplet.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_stark_broadened_line(self): # setting up a line shape model line = Line(deuterium, 0, (6, 2)) # D-delta line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma) + integrator = GaussianQuadrature(relative_tolerance=1.e-5) + stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma, integrator=integrator) # spectrum parameters min_wavelength = wavelength - 0.2 @@ -304,14 +305,19 @@ def test_stark_broadened_line(self): te = self.plasma.electron_distribution.effective_temperature(point.x, point.y, point.z) lambda_1_2 = cij * ne**aij / (te**bij) + lorenzian_cutoff_gamma = 50 + stark_norm_coeff = 4 * lorenzian_cutoff_gamma * hyp2f1(0.4, 1, 1.4, -(2 * lorenzian_cutoff_gamma)**2.5) + norm = (0.5 * lambda_1_2)**1.5 / stark_norm_coeff + wavelengths, delta = np.linspace(min_wavelength, max_wavelength, bins + 1, retstep=True) - stark_lineshape = 1 / ((np.abs(wavelengths - wavelength))**2.5 + (0.5 * lambda_1_2)**2.5) - stark_lineshape = 0.5 * (stark_lineshape[1:] + stark_lineshape[:-1]) - stark_lineshape /= stark_lineshape.sum() * delta + + def stark_lineshape(x): + return norm / ((np.abs(x - wavelength))**2.5 + (0.5 * lambda_1_2)**2.5) for i in range(bins): - self.assertAlmostEqual(stark_lineshape[i], spectrum.samples[i], delta=1e-10, - msg='GaussianLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + stark_bin = quadrature(stark_lineshape, wavelengths[i], wavelengths[i + 1], rtol=integrator.relative_tolerance)[0] / delta + self.assertAlmostEqual(stark_bin, spectrum.samples[i], delta=1e-9, + msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) if __name__ == '__main__': From 308208ceccfa8af6cfbab4c8aa308234696a6137 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 18 Oct 2022 23:14:03 +0300 Subject: [PATCH 49/59] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dfaac01..edc760b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ New: * Add Generomak core plasma profiles. (#360) * Add toroidal_mesh_from_polygon for making mesh for not fully-360 degrees axisymmetric elements. (#365) * Add GaussianQuadrature integration method for Function1D. (#366) -* Add NumericallyIntegrableLineShapeModel for lineshapes that cannot be analytically integrated over a spectral bin. (#366) +* Add integrator attribute to LineShapeModel to use with lineshapes that cannot be analytically integrated over a spectral bin. (#366) * Add a numerical integration of StarkBroadenedLine over the spectral bin. (#366) Bug Fixes: From e8b05ca679e625bc18d334a7e139f81c5e95a76b Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Mon, 7 Nov 2022 12:30:52 +0100 Subject: [PATCH 50/59] Fix Bremsstrahlung trapezium indexing --- CHANGELOG.md | 1 + cherab/core/model/plasma/bremsstrahlung.pyx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b5ecac..dbe69a21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ New: Bug Fixes: ---------- +* Fixed Bremsstrahlung trapezium evaluation (#384). * Fixed generomak plasma edge data paths. * Fix and improve OpenCL utility functions. (#358) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 9e2e2e8b..18f4e19d 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -74,7 +74,7 @@ cdef class Bremsstrahlung(PlasmaModel): lower_sample = self._bremsstrahlung(lower_wavelength, te, ne, z_effective) for i in range(spectrum.bins): - upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * i + upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) upper_sample = self._bremsstrahlung(upper_wavelength, te, ne, z_effective) spectrum.samples_mv[i] += 0.5 * (lower_sample + upper_sample) From d9fcd2c934804395e335bd6c7af3cd5557a2f334 Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 3 Dec 2022 00:18:15 +0300 Subject: [PATCH 51/59] Use ionisation balance for core profiles to fulfill plasma neutrality. Tweak core profile parameters. --- cherab/generomak/plasma/plasma.py | 117 ++++++++++++------------------ 1 file changed, 47 insertions(+), 70 deletions(-) diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index 65fef528..ab7ef95d 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -34,6 +34,8 @@ from cherab.core.math.mappers import AxisymmetricMapper, VectorAxisymmetricMapper from cherab.core.math.clamp import ClampInput1D +from cherab.tools.plasmas.ionisation_balance import interpolators1d_from_elementdensity, interpolators1d_match_plasma_neutrality + from cherab.openadas import OpenADAS from cherab.generomak.equilibrium import load_equilibrium @@ -318,21 +320,16 @@ def get_core_profiles_arguments(**kwargs): te_core core: (default 3e3) electron temperature te_convexity: (default 2.35) convexity of the electron temperature profile te_concavity: (default 1.26) concavity of the electron temperature profile - nh_core: (default 5e19) density of H1+ - nh_convexity: (default 1.09) convexity of H1+ density profile - nh_concavity: (default 0.24) concavity of H1+ density profile th_core: (default 2.8e3) H1+ temperature - th_convexity: (default 1) convexity of H1+ temperature profile - th_concavity: (default 0.82) concavity of H1+ temperature profile + th_convexity: (default 2) convexity of H1+ temperature profile + th_concavity: (default 1.26) concavity of H1+ temperature profile th0_fraction: (default 0.8) H0 temperature factor - nh0_decay decay: (default 20) rate of H0 density profile - timp_core: (default 2.7e3) core impurity temperature - timp_convexity: (default 1) convexity of impurity temperature profile - timp_concavity: (default 0.82) concavity of impurity temperature profile + timp_core: (default 2.8e3) core impurity temperature + timp_convexity: (default 2) convexity of impurity temperature profile + timp_concavity: (default 1.26) concavity of impurity temperature profile nimp_core: (default 5e17) impurity density nimp_convexity: (default 1.09) convexity of impurity density profile nimp_concavity: (default 0.24) concavity of impurity density profile - nimp_decay: (default 30) decay rate of impurity density profile (except bare nuclei) vtor_core: (default 1e5) toroidal rotation velocity m/s vtor_edge: (default 1e4) toroidal rotation velocity at the edge m/s vtor_convexity: (default 2) convexity of the toroidal rotation profile @@ -343,19 +340,13 @@ def get_core_profiles_arguments(**kwargs): :return: dictionary of profile arguments """ - core_args = {"ne_core": 5e19, "ne_convexity": 1.09, - "ne_concavity": 0.24, "te_core": 3e3, - "te_convexity": 2.35, "te_concavity": 1.26, - "nh_core": 5e19, "nh_convexity": 1.09, - "nh_concavity": 0.24, "th_core": 2.8e3, - "th_convexity": 1, "th_concavity": 0.82, - "th0_fraction": 0.8, "nh0_decay": 20, - "timp_core": 2.7e3, "timp_convexity": 1, - "timp_concavity": 0.82, "nimp_core": 5e17, - "nimp_convexity": 1.09, "nimp_concavity": 0.24, - "nimp_decay": 30, - "vtor_core": 1e5, "vtor_edge": 1e4, - "vtor_convexity": 2, "vtor_concavity": 4, + core_args = {"ne_core": 5e19, "ne_convexity": 1.09, "ne_concavity": 0.24, + "te_core": 3e3, "te_convexity": 2.35, "te_concavity": 1.26, + "th_core": 2.8e3, "th_convexity": 2, "th_concavity": 1.26, + "th0_fraction": 0.8, + "timp_core": 2.8e3, "timp_convexity": 2, "timp_concavity": 1.26, + "nimp_core": 5e17, "nimp_convexity": 1.09, "nimp_concavity": 0.24, + "vtor_core": 1e5, "vtor_edge": 1e4, "vtor_convexity": 2, "vtor_concavity": 4, "vpol_lcfs": 2e4, "vpol_decay": 0.08} if not kwargs: @@ -390,13 +381,8 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): z = 0 lcfs_values = get_edge_profile_values(r, z) - # manually correcting the LCFS densities for neutral species and low ionised carbon - # to better match the edge profiles - lcfs_values["composition"]["hydrogen"][0]["density"] = 1.e15 - lcfs_values["composition"]["carbon"][0]["density"] = 1.e5 - lcfs_values["composition"]["carbon"][1]["density"] = 1.5e5 - lcfs_values["composition"]["carbon"][2]["density"] = 1.e9 - lcfs_values["composition"]["carbon"][3]["density"] = 1.e13 + # total carbon impurity density at lcfs + nimp_lcfs = sum([value["density"] for _, value in lcfs_values["composition"]["carbon"].items()]) if core_args is None: core_args = get_core_profiles_arguments() @@ -425,47 +411,38 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): profiles["electron"]["f1d_vpol"] = Constant1D(0) profiles["electron"]["f1d_vnorm"] = Constant1D(0) - # Setup H1+ profiles with double parabola shapes - profiles["composition"]["hydrogen"][1]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["hydrogen"][1]["temperature"], - core_args["th_core"], core_args["th_convexity"], core_args["th_concavity"], - xmin=1, xmax=0) - profiles["composition"]["hydrogen"][1]["f1d_density"] = get_double_parabola(lcfs_values["composition"]["hydrogen"][1]["density"], - core_args["nh_core"], core_args["nh_convexity"], - core_args["nh_concavity"], xmin=1, xmax=0) - profiles["composition"]["hydrogen"][1]["f1d_vtor"] = f1d_vtor - profiles["composition"]["hydrogen"][1]["f1d_vpol"] = f1d_vpol - profiles["composition"]["hydrogen"][1]["f1d_vnorm"] = f1d_vnorm - - # setup H0+ profile shapes with temperature as a fraction of H1+ and density with decaying exponential - profiles["composition"]["hydrogen"][0]["f1d_temperature"] = core_args["th0_fraction"] * get_double_parabola(lcfs_values["composition"]["hydrogen"][0]["temperature"], - core_args["th_core"], core_args["th_convexity"], - core_args["th_concavity"], xmin=1, xmax=0) - profiles["composition"]["hydrogen"][0]["f1d_density"] = get_exponential_growth( - lcfs_values["composition"]["hydrogen"][0]["density"], core_args["nh0_decay"]) - profiles["composition"]["hydrogen"][0]["f1d_vtor"] = f1d_vtor - profiles["composition"]["hydrogen"][0]["f1d_vpol"] = f1d_vpol - profiles["composition"]["hydrogen"][0]["f1d_vnorm"] = f1d_vnorm - - # setup C6+ profile shapes with double parabolas - profiles["composition"]["carbon"][6]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["carbon"][6]["temperature"], - core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], - xmin=1, xmax=0) - profiles["composition"]["carbon"][6]["f1d_density"] = get_double_parabola(lcfs_values["composition"]["carbon"][6]["density"], core_args["nimp_core"], - core_args["nimp_convexity"], core_args["nimp_concavity"], xmin=1, xmax=0) - profiles["composition"]["carbon"][6]["f1d_vtor"] = f1d_vtor - profiles["composition"]["carbon"][6]["f1d_vpol"] = f1d_vpol - profiles["composition"]["carbon"][6]["f1d_vnorm"] = f1d_vnorm - - # setup CX+ profile shapes with temperature as double parabolas and density with decaying exponentials - for chrg in range(6): - profiles["composition"]["carbon"][chrg]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"]["carbon"][chrg]["temperature"], - core_args["timp_core"], core_args["timp_convexity"], core_args["timp_concavity"], + # total carbon density + carbon_total_density = get_double_parabola(nimp_lcfs, core_args["nimp_core"], + core_args["nimp_convexity"], core_args["nimp_concavity"], xmin=1, xmax=0) + + # solve ionisation balance + openadas = OpenADAS(permit_extrapolation=True) + psin_1d = np.linspace(0, 1, 256) + density_profiles = {} + density_profiles["carbon"] = interpolators1d_from_elementdensity(openadas, carbon, psin_1d, carbon_total_density, + profiles["electron"]["f1d_density"], + profiles["electron"]["f1d_temperature"]) + + density_profiles["hydrogen"] = interpolators1d_match_plasma_neutrality(openadas, hydrogen, psin_1d, [density_profiles["carbon"]], + profiles["electron"]["f1d_density"], + profiles["electron"]["f1d_temperature"]) + + # Setup ion profiles + for element, prefix in ((hydrogen, "h"), (carbon, "imp")): + name = element.name + for chrg in range(element.atomic_number + 1): + profiles["composition"][name][chrg]["f1d_temperature"] = get_double_parabola(lcfs_values["composition"][name][chrg]["temperature"], + core_args["t{}_core".format(prefix)], + core_args["t{}_convexity".format(prefix)], + core_args["t{}_concavity".format(prefix)], xmin=1, xmax=0) - profiles["composition"]["carbon"][chrg]["f1d_density"] = get_exponential_growth( - lcfs_values["composition"]["carbon"][chrg]["density"], core_args["nimp_decay"]) - profiles["composition"]["carbon"][chrg]["f1d_vtor"] = f1d_vtor - profiles["composition"]["carbon"][chrg]["f1d_vpol"] = f1d_vpol - profiles["composition"]["carbon"][chrg]["f1d_vnorm"] = f1d_vnorm + profiles["composition"][name][chrg]["f1d_density"] = density_profiles[name][chrg] + profiles["composition"][name][chrg]["f1d_vtor"] = f1d_vtor + profiles["composition"][name][chrg]["f1d_vpol"] = f1d_vpol + profiles["composition"][name][chrg]["f1d_vnorm"] = f1d_vnorm + + # multiply H0 temperature by th0_fraction + profiles["composition"]["hydrogen"][0]["f1d_temperature"] *= core_args["th0_fraction"] return profiles.freeze() From 65b545b01e83dd9f6a8411c477c1dd86b8534f91 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 22 Dec 2022 17:17:09 +0300 Subject: [PATCH 52/59] Added pre-calculated core profiles and the functions to load them. Switched to use pre-calculated core profiles by default. --- .../generomak/plasma/data/core/carbon0.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon1.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon2.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon3.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon4.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon5.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/carbon6.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/electrons.json | 1292 ++++++++++++++++ .../generomak/plasma/data/core/hydrogen0.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/hydrogen1.json | 1294 +++++++++++++++++ .../generomak/plasma/data/core/psi_norm.json | 260 ++++ cherab/generomak/plasma/plasma.py | 99 +- demos/generomak/plasma/plot_2d_plasma.py | 2 +- demos/generomak/plasma/plot_2d_profiles.py | 4 +- demos/generomak/plasma/plot_core_profiles.py | 7 +- 15 files changed, 13294 insertions(+), 16 deletions(-) create mode 100644 cherab/generomak/plasma/data/core/carbon0.json create mode 100644 cherab/generomak/plasma/data/core/carbon1.json create mode 100644 cherab/generomak/plasma/data/core/carbon2.json create mode 100644 cherab/generomak/plasma/data/core/carbon3.json create mode 100644 cherab/generomak/plasma/data/core/carbon4.json create mode 100644 cherab/generomak/plasma/data/core/carbon5.json create mode 100644 cherab/generomak/plasma/data/core/carbon6.json create mode 100644 cherab/generomak/plasma/data/core/electrons.json create mode 100644 cherab/generomak/plasma/data/core/hydrogen0.json create mode 100644 cherab/generomak/plasma/data/core/hydrogen1.json create mode 100644 cherab/generomak/plasma/data/core/psi_norm.json diff --git a/cherab/generomak/plasma/data/core/carbon0.json b/cherab/generomak/plasma/data/core/carbon0.json new file mode 100644 index 00000000..d8763cf4 --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon0.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.5297458338687, + 2782.7582301214393, + 2762.5950477966007, + 2735.8851983639343, + 2703.411010968356, + 2665.8945476185836, + 2624.0003243064125, + 2578.338228519893, + 2529.466541110544, + 2477.8949930363638, + 2424.08780488189, + 2368.4666705053005, + 2311.4136566031007, + 2253.2739981035534, + 2194.3587756195216, + 2134.9474661044947, + 2075.290361665309, + 2015.6108544256936, + 1956.107587588782, + 1896.9564745570865, + 1838.3125892477842, + 1780.3119316786645, + 1723.0730735666177, + 1666.6986891326694, + 1611.2769765905086, + 1556.8829759454748, + 1503.5797887772371, + 1451.4197056453777, + 1400.4452466615378, + 1350.6901206300427, + 1302.1801079829825, + 1254.9338725355856, + 1208.9637068710047, + 1164.2762159367799, + 1120.8729432030323, + 1078.7509434988951, + 1037.903306411775, + 998.3196339061595, + 959.986475596583, + 922.8877248943963, + 887.004979041061, + 852.3178658425425, + 818.8043397302939, + 786.4409495946626, + 755.2030806662577, + 725.0651725599275, + 696.0009154442936, + 667.9834261571025, + 640.9854059526704, + 614.9792814421413, + 589.9373301697594, + 565.8317921585761, + 542.6349686565285, + 520.3193092183391, + 498.8574881697798, + 478.22247141812255, + 458.3875744958107, + 439.3265126530432, + 421.0134437488174, + 403.4230046287076, + 386.5303416208766, + 370.3111357293156, + 354.74162305475744, + 339.79861092883743, + 325.4594902056625, + 311.70224411672444, + 298.5054540598485, + 285.84830266038824, + 273.71057441296176, + 262.0726541844933, + 250.91552383399323, + 240.22075718122792, + 229.97051353503508, + 220.14752997240066, + 210.73511254139078, + 201.7171265444924, + 193.07798604378416, + 184.80264271546582, + 176.8765741685898, + 169.28577183121658, + 162.01672849659778, + 155.05642561229888, + 148.39232038631826, + 142.01233277619647, + 135.90483241974619, + 130.05862555934604, + 124.46294200564746, + 119.10742218101434, + 113.98210427798426, + 109.07741156349235, + 104.38413985546642, + 99.89344519467129, + 95.59683173130277, + 91.48613984279793, + 87.55353449656745, + 83.79149386891429, + 80.19279822915814, + 76.7505190960298, + 73.4580086716009, + 70.30888955644176, + 67.29704474828924, + 64.41660792525342, + 61.66195401348148, + 59.027690038231896, + 56.508646256439604, + 54.09986756812256, + 51.79660520330721, + 49.59430868060964, + 47.488618033112196, + 45.4753562967661, + 43.55052225621492, + 41.710283442635344, + 39.95096937796647, + 38.26906505970934, + 36.66120468032837, + 35.12416557519362, + 33.65486239291572, + 32.25034148188933, + 30.907775486848166, + 29.624458149229337, + 28.39779930519129, + 27.225320075155384, + 26.104648238820975, + 25.033513789664312, + 24.009744663032187, + 23.03126263202616, + 22.09607936549286, + 21.20229264253159, + 20.348082718071975, + 19.531708834175102, + 18.751505871858317, + 18.005881138361524, + 17.29331128491523, + 16.612339350196027, + 15.961571924800158, + 15.339676432189938, + 14.745378521713246, + 14.17745956942282, + 13.634754282558198, + 13.116148403687774, + 12.62057651063149, + 12.147019908417766, + 11.694504609655857, + 11.262099399819382, + 10.848913984069997, + 10.454097212357015, + 10.076835379651857, + 9.716350598285864, + 9.37189923947122, + 9.042770441189758, + 8.728284679739293, + 8.427792402331782, + 8.140672718228085, + 7.866332145997564, + 7.604203414576368, + 7.353744315892651, + 7.114436606910324, + 6.88578495902669, + 6.66731595284398, + 6.458577116405904, + 6.259136005074808, + 6.068579321290205, + 5.886512072521773, + 5.712556765802551, + 5.546352637286773, + 5.387554915341459, + 5.235834115747535, + 5.09087536763343, + 4.952377768832103, + 4.820053769398764, + 4.693628582080498, + 4.572839618581702, + 4.457435950513924, + 4.347177793965752, + 4.241836016675181, + 4.1411916668281155, + 4.045035522543728, + 3.953167661156071, + 3.8653970474291826, + 3.7815411398866017, + 3.701425514464914, + 3.624883504742671, + 3.5517558580176334, + 3.4818904065449097, + 3.4151417532737645, + 3.3513709714494597, + 3.2904453174754256, + 3.23223795645469, + 3.176627699856822, + 3.1234987547774122, + 3.0727404842841777, + 3.0242471783623297, + 2.9779178349937, + 2.933655950925356, + 2.8913693217009238, + 2.850969850548094, + 2.8123733657334666, + 2.7754994460091185, + 2.7402712537980722, + 2.706615375774928, + 2.6744616705174318, + 2.6437431229153345, + 2.6143957050402027, + 2.586358243189803, + 2.5595722908346312, + 2.5339820072055117, + 2.509534041273594, + 2.4861774208836924, + 2.4638634468137517, + 2.442545591541203, + 2.4221794025087373, + 2.4027224096911914, + 2.384134037270308, + 2.366375519239019, + 2.3494098187571737, + 2.3332015510957254, + 2.3177169100087984, + 2.302923597380779, + 2.288790756005567, + 2.2752889053561534, + 2.2623898802136084, + 2.250066772028685, + 2.23829387289304, + 2.2270466220056035, + 2.21630155452372, + 2.2060362526908253, + 2.196229299141507, + 2.1868602322873785, + 2.1779095036891287, + 2.1693584373282744, + 2.1611891906943277, + 2.1533847176048013, + 2.145928732682899, + 2.1388056774176825, + 2.1320006877379507, + 2.125499563030551, + 2.1192887365398065, + 2.113355247087553, + 2.1076867120526006, + 2.1022713015558105, + 2.0970977137961215, + 2.092155151486069, + 2.0874332993384708, + 2.082922302556797, + 2.0786127462849016, + 2.0744956359735283, + 2.070562378622614, + 2.0668047648594987, + 2.0632149518174363, + 2.059785446777764, + 2.0565090915410997, + 2.05337904749653, + 2.050388781356637, + 2.0475320515291053, + 1.9864209304153415 + ], + "density": [ + 0.04048744608721366, + 0.040991967564575016, + 0.041600133540233744, + 0.04226815318206501, + 0.04300976853695856, + 0.04382292413886765, + 0.044710990818178135, + 0.04566084382265294, + 0.04668132671587653, + 0.04778384269676666, + 0.04894347040181093, + 0.050217658359466025, + 0.05155120707621356, + 0.05298459045905281, + 0.05451328150461624, + 0.05613579339020678, + 0.05786886344594796, + 0.05971555622426197, + 0.06168940973780461, + 0.06379299848100341, + 0.06604924274633829, + 0.06845688699486069, + 0.07104012824009528, + 0.07381877203820006, + 0.07680888578177371, + 0.07999015982750216, + 0.0834079665219863, + 0.08705720329088246, + 0.09096350307186839, + 0.09519183101684933, + 0.09977767252368747, + 0.10473300929035899, + 0.1101772697322425, + 0.11612224522135849, + 0.12263233516381865, + 0.12965535501683825, + 0.13730843623793318, + 0.1456718556947092, + 0.15482815214674314, + 0.1649193448672138, + 0.17609578865161815, + 0.18843317970688814, + 0.20225092445258935, + 0.21769147208669645, + 0.235057725643753, + 0.25450284419119984, + 0.27607481059071376, + 0.3001968017285936, + 0.32721833903790154, + 0.35788158051591024, + 0.3927201787927346, + 0.3419825951457436, + 4.4007276393775686e-05, + 2.118242147089871e-06, + 9.990808414127548e-06, + 2.1760842922898868e-05, + 1.0701015086025121e-05, + 2.0686100893291997e-05, + 1.4102252243972512e-05, + 1.6341843536751136e-05, + 5.920055478903454e-05, + 3.1202822019714874e-05, + 7.006577902969221e-06, + 3.0485999375746203e-110, + 1.351046152877299e-05, + 3.723317857953726e-05, + 1.4621524348837632e-05, + 8.234384469567877e-05, + 4.724949485563587e-07, + 1.3234130610459686e-05, + 1.1250023529740069e-06, + 3.40650350246754e-05, + 8.61113675417637e-05, + 3.4544273659392032e-06, + 1.115441842931324e-05, + 6.750427753043206e-05, + 0.000101851956735232, + 4.1417751607125216e-07, + 1.733113479346441e-05, + 0.00014160026465313052, + 0.00017297702950056632, + 0.00026253696716014923, + 0.0003314571871220419, + 0.0006750813370322345, + 0.0011644314857210615, + 0.0019017827396563996, + 0.0032704725357429448, + 0.006078302645905549, + 0.010855190117619993, + 0.01953392347223397, + 1.8680620540029122e-05, + 7.144970850284283e-05, + 5.542912364767046e-06, + 7.435691322796886e-05, + 0.00016143391343427039, + 0.00020306133691749687, + 0.00028544558695254034, + 0.0008743656306073129, + 0.0024050292905490136, + 0.005775071597051694, + 0.0005348662947240205, + 0.0006539565495507168, + 0.0010281871631532197, + 0.0013522734276574968, + 0.0018752787770719434, + 0.0022794719905617836, + 0.0032833530457091267, + 0.001990519405536644, + 0.005452626266015973, + 0.00677493232228747, + 0.008864117251803522, + 0.011092803666997013, + 0.013852370395545835, + 0.0172470165417693, + 0.020371187929570215, + 0.025505315179375047, + 0.031074048340423287, + 0.037995782307585234, + 0.0465310460856086, + 0.05631424807984447, + 0.06745275939537863, + 0.08017572144032707, + 0.09451365752132886, + 0.11074564103118154, + 0.12885395814665107, + 0.1495571109436204, + 0.17305975617364092, + 0.19900124477774955, + 0.22795492765149622, + 0.26016815091843437, + 0.29558301290421074, + 0.335098447301964, + 0.3787529583978551, + 0.42680395334454346, + 0.47905611444170587, + 0.5362824136346813, + 0.5985500572439016, + 0.6662418422094794, + 0.7392810015960789, + 0.8192667858555257, + 0.9041791387661262, + 0.9967330727340975, + 1.0961554182560838, + 1.202232925311796, + 1.3149305765995665, + 1.435581443569537, + 1.5641755524988206, + 1.6995539260866523, + 1.8438087576258209, + 1.9952089389535912, + 2.1548842155881163, + 2.3225752489437097, + 2.497782026083675, + 2.6799685020901842, + 2.8702102138142576, + 3.067718277784516, + 3.272652118255717, + 3.4841842881068805, + 3.70264957750554, + 3.9265053166806303, + 4.157418486084073, + 4.391689392465598, + 4.632972611931735, + 4.8774029454280745, + 5.12689907629093, + 5.380601479886791, + 5.635293049863927, + 5.894617217838846, + 6.15540931208811, + 6.41854607357553, + 6.681319307917702, + 6.9455701730729045, + 7.20987457968002, + 7.475425480246554, + 7.738667122716035, + 8.00131956130091, + 8.26244637126789, + 8.522348348950743, + 8.779348920756686, + 9.032914882475161, + 9.284651592551567, + 9.532433301321392, + 9.777230760031252, + 10.0174789157339, + 10.254100706292657, + 10.487234177305957, + 10.71545840617981, + 10.939496800352414, + 11.158697017879582, + 11.37314606319091, + 11.58237151830138, + 11.786714430857282, + 11.98677813625825, + 12.1808353928344, + 12.369922844931047, + 12.55375159835506, + 12.733137363800198, + 12.905822998243242, + 13.074456274693999, + 13.237236622767275, + 13.396079440071818, + 13.55086631379442, + 13.69892325592837, + 13.84289554141327, + 13.981971275333507, + 14.117407405854768, + 14.246818740400403, + 14.37203389084361, + 14.493587620099008, + 14.609735497673544, + 14.723242173478098, + 14.831297504428605, + 14.935912532356166, + 15.037379352591227, + 15.133755168376675, + 15.226465373590704, + 15.31670960797513, + 15.402724885287265, + 15.486363892362997, + 15.566439939250113, + 15.642186731562068, + 15.717136638192025, + 15.787527775619653, + 15.85536956371587, + 15.919950135878985, + 15.982642593928864, + 16.043113366621782, + 16.09989366562893, + 16.15516174675919, + 16.207812578733854, + 16.259269128589985, + 16.308251288450823, + 16.354604739329357, + 16.39882478129081, + 16.441254777863826, + 16.482492572267855, + 16.52136186970244, + 16.559074281078065, + 16.594586374436723, + 16.62951333749602, + 16.662596876693172, + 16.693809896725444, + 16.723989679209343, + 16.75356267510578, + 16.779924132332624, + 16.807121709533483, + 16.831205414235242, + 16.856245388846574, + 16.878685435533402, + 16.901672493576328, + 16.92253181927624, + 16.94316073854124, + 16.9612022642688, + 16.979916247897123, + 16.997137492131074, + 17.325004601219867 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 0 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon1.json b/cherab/generomak/plasma/data/core/carbon1.json new file mode 100644 index 00000000..b2d5c389 --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon1.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 0.48421585057396443, + 0.4902687240676583, + 0.49744711278943604, + 0.5051954006896824, + 0.5136620119549894, + 0.5228114951520789, + 0.5326752171258001, + 0.5430889205581807, + 0.5541504144444251, + 0.5659869467934964, + 0.5782948447729008, + 0.5917437089789345, + 0.6056712485911827, + 0.620542945043669, + 0.6362922713807064, + 0.6528879784388633, + 0.670509736386118, + 0.6891758543862906, + 0.7090235821831914, + 0.7300618223316414, + 0.7525298929070332, + 0.7763877953370656, + 0.8018826318051548, + 0.8292062565837163, + 0.8585038018336258, + 0.889508177208562, + 0.9226832625401263, + 0.9579291559694979, + 0.9954837795225133, + 1.0360065994185788, + 1.0798223982450068, + 1.1269947674435963, + 1.1787338800321392, + 1.2350760742733788, + 1.2966027474529238, + 1.3626536962098275, + 1.4343556208152823, + 1.5124235977441491, + 1.597576238405414, + 1.691121912675909, + 1.7944189321292177, + 1.9080331127816117, + 2.0349641993946186, + 2.176361663537031, + 2.3349380858639535, + 2.5118452848013555, + 2.707159678115966, + 2.9246174853883784, + 3.1671420854781687, + 3.4413506012389776, + 3.751694445122891, + 3.2477798067270536, + 0.006362863907386773, + 0.007372973669191416, + 0.009216593289128903, + 0.011548156028595503, + 0.014242714795831715, + 0.01786146488055299, + 0.022262162510133958, + 0.027939765134883766, + 0.03551634957985896, + 0.04443958387088995, + 0.055958579944039195, + 0.07088116012661974, + 0.09018659935564127, + 0.11496387559603737, + 0.14633834787935424, + 0.1875802388118898, + 0.23936517945868985, + 0.30754162306911564, + 0.3958068353549918, + 0.5112101892350656, + 0.6620966618289457, + 0.858660078735121, + 1.1181241421419907, + 1.46073435693994, + 1.911282430745415, + 2.5029504245674246, + 3.2846111449205577, + 4.318608003844665, + 5.687666141161567, + 7.506630045311503, + 9.92851137619441, + 13.163696880564427, + 17.493477080108423, + 23.303174917833566, + 31.091974173382223, + 41.45633739453835, + 55.267703371244075, + 73.72690488904475, + 98.20863422520927, + 131.2870853869013, + 175.83683138019174, + 235.99204766064628, + 316.38571854740593, + 421.7539328588253, + 559.4357994437013, + 738.9164751802173, + 972.3391869076578, + 1275.153777920394, + 1666.7711728749346, + 2171.6699589645095, + 2819.988751814856, + 3648.7031646271603, + 4702.668366138428, + 6035.779233963542, + 7686.47757537614, + 9663.067856260159, + 12007.31337433541, + 14768.024487039194, + 18001.483822967057, + 21772.849505706734, + 26157.550424254878, + 31242.376433647383, + 37126.23247711799, + 43920.55849586252, + 51749.31820504792, + 60748.85541615466, + 71036.10519259654, + 82505.25321944867, + 95172.6035262625, + 109100.32854050657, + 124351.58313892053, + 140991.89397504224, + 159090.80831541793, + 178723.66872728983, + 199973.32015148445, + 222931.63069035482, + 247704.87774357302, + 274402.6314683522, + 303149.57485778275, + 334081.81511881127, + 367346.4298088521, + 403095.30458799424, + 441271.5467435238, + 481872.1263701006, + 525005.3743482265, + 570775.3503247264, + 619280.1323497095, + 670610.2296256337, + 724847.0330594878, + 782061.4947899283, + 842312.7805629332, + 905647.1583655913, + 972096.9884098681, + 1041679.8789890977, + 1114397.9704522684, + 1190237.4178496636, + 1269168.0928057458, + 1351143.3680309465, + 1436100.2152481882, + 1523959.3923222842, + 1614625.8637893011, + 1707989.3899476596, + 1803925.2961875116, + 1902295.3488111356, + 2002948.8264528697, + 2105723.6679099784, + 2210447.758156687, + 2316940.2461408074, + 2425012.996869253, + 2534471.9531783885, + 2645118.713026125, + 2756751.8441781844, + 2869168.440277809, + 2982165.4309753384, + 3095540.9198091724, + 3209095.535627261, + 3322633.485232032, + 3435963.746723633, + 3548904.406828813, + 3661274.607561598, + 3772901.237685582, + 3883621.082668308, + 3993279.51143659, + 4101731.027144971, + 4208839.542291893, + 4314478.668059076, + 4418531.841795906, + 4520892.427679443, + 4621463.716299819, + 4720158.78415636, + 4816900.409340878, + 4911620.80796254, + 5004261.422094195, + 5094772.583239676, + 5183113.162616801, + 5269250.234491738, + 5353158.645119106, + 5434820.618175021, + 5514225.314109624, + 5591368.407651634, + 5666251.643182443, + 5738882.3707925305, + 5809273.161495088, + 5877441.340120579, + 5943408.591682677, + 6007200.515329759, + 6068846.311127089, + 6128378.312740362, + 6185831.708189282, + 6241244.1418844825, + 6294655.385905712, + 6346107.111966662, + 6395642.518902035, + 6443306.118805427, + 6489143.4314059075, + 6533200.829506674, + 6575525.270986898, + 6616164.092233446, + 6655164.89427008, + 6692575.279163696, + 6728442.694407392, + 6762814.329286974, + 6795737.335340715, + 6827258.228432123, + 6857422.965645077, + 6886276.833068685, + 6913864.435669005, + 6940229.566958312, + 6965415.187144678, + 6989463.413916613, + 7012415.368759321, + 7034311.272954711, + 7055190.338080873, + 7075090.793272931, + 7094049.833290375, + 7112103.614543278, + 7129287.29681578, + 7145634.973864277, + 7161179.732926623, + 7175953.608746651, + 7189987.629596161, + 7203311.823403726, + 7215955.222910218, + 7227945.884573181, + 7239310.885176149, + 7250076.380752132, + 7260267.580029734, + 7269908.81296078, + 7279023.497225329, + 7287634.200202345, + 7295762.6574449, + 7303429.782592609, + 7310655.664301383, + 7317459.684297547, + 7323860.396159016, + 7329875.705663031, + 7335522.739642086, + 7340818.009363841, + 7345777.309661405, + 7350415.843108907, + 7354748.154845227, + 7358788.250898356, + 7362549.51046329, + 7180737.62151424 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 1 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon2.json b/cherab/generomak/plasma/data/core/carbon2.json new file mode 100644 index 00000000..c7f6d89a --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon2.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 51.7269475194356, + 51.746952820624216, + 52.12388596214026, + 52.940163304633074, + 54.24294247848471, + 56.07509778838055, + 58.48625229633952, + 61.5368349714206, + 65.3030344598767, + 69.8785578386221, + 75.37529754084595, + 81.93078413627545, + 89.70164187223259, + 98.87428659903192, + 109.65942092421292, + 122.2933234051085, + 137.035367139739, + 154.16006793143808, + 173.94684809895617, + 196.66154529962796, + 222.53238297052988, + 251.7134046195229, + 284.2420121677726, + 319.983444861121, + 358.5598123137554, + 398.9683223412675, + 440.76433555850025, + 484.2490374594113, + 530.1044415099693, + 579.4915040302302, + 634.1713927433501, + 696.6820796978036, + 770.6142544227597, + 860.9672258522206, + 970.0620403589148, + 1099.2314423795724, + 1252.1185630618302, + 1432.9887166910512, + 1646.7948934705498, + 1899.2398766520157, + 2196.8166107464817, + 2546.8186480837194, + 2957.3244606006606, + 3437.1830720402863, + 3995.651317855098, + 4645.819288446255, + 5408.159661965817, + 6304.394233890673, + 7360.988631169922, + 8610.408235852314, + 10092.612861174073, + 11853.166711872651, + 13944.655327903243, + 16470.555060218794, + 19507.094824911295, + 23149.859879749787, + 27528.59209934883, + 32805.44146433598, + 39181.431974834195, + 46906.5301364389, + 56292.49024857901, + 67729.29859868562, + 81704.69552439438, + 98758.342767045, + 119563.79546648111, + 145008.98972599223, + 176209.22723183295, + 214569.82314126665, + 261867.1097143878, + 320353.5889126574, + 392894.6330259606, + 483146.86838287814, + 595791.405155585, + 736839.602081546, + 914034.8394921427, + 1137314.4676054649, + 1417795.5431438887, + 1770046.1195692741, + 2213501.0254945313, + 2773182.8952475805, + 3481388.8206957136, + 4379900.14495483, + 5522876.267226655, + 6980639.120267879, + 8844609.797864616, + 11234098.954064578, + 14292249.250883874, + 18171268.57400767, + 23102344.070654884, + 29391183.874651648, + 37439200.11924598, + 47773938.07168198, + 61088401.78943633, + 78290898.38971062, + 100252654.95122445, + 127678619.24781525, + 161848156.43389606, + 204348916.72694722, + 257122494.6832895, + 322522738.08793396, + 403373666.46606535, + 503023672.34567523, + 625393056.5647471, + 775013474.5069699, + 957060449.3713105, + 1177383070.8421464, + 1437802639.9565027, + 1734246225.369133, + 2068654420.0998182, + 2443569577.2558446, + 2862029131.1002626, + 3327664301.685319, + 3844746919.3145304, + 4418179733.538788, + 5053433114.860162, + 5756438161.127043, + 6533452755.5666065, + 7390922881.304381, + 8331837423.650323, + 9334354678.472628, + 10392166684.043266, + 11504279057.327274, + 12669790141.968946, + 13888035317.62539, + 15158729343.112686, + 16482091274.303825, + 17858939920.92599, + 19290752420.740105, + 20779851078.546913, + 22328898303.12378, + 23941318873.556976, + 25621068937.484055, + 27372541721.972637, + 29200132814.888065, + 31095452170.414516, + 33054347498.016705, + 35079589851.19578, + 37173581351.618835, + 39338307423.93913, + 41575301929.46964, + 43885622896.80891, + 46269837046.91366, + 48728011738.553474, + 51259713312.86814, + 53864011077.12315, + 56539486357.443695, + 59284246173.27229, + 62095941159.639656, + 64971787396.43695, + 67908591808.401474, + 70902780790.1204, + 73950431683.65508, + 77047306712.3454, + 80188888946.12115, + 83370419851.9303, + 86586937966.57417, + 89833318223.03227, + 93104311461.33716, + 96394583667.15906, + 99698754499.39764, + 103011434697.43271, + 106327261990.32256, + 109640935175.03737, + 112947246068.6951, + 116241109095.60986, + 119517588310.54092, + 122771921715.4813, + 125999542772.99667, + 129196099063.11314, + 132357468080.74841, + 135479838062.1428, + 138559536495.15198, + 141593161723.4288, + 144577614224.11197, + 147510062820.63623, + 150387945085.9323, + 153208965707.6481, + 155971093006.19897, + 158672553796.7852, + 161311826790.89548, + 163887634728.62924, + 166398935429.4681, + 168844911944.07657, + 171224961977.36823, + 173538686747.2005, + 175785879428.2105, + 177966513320.9635, + 180080729873.50775, + 182128826668.6315, + 184111245479.87717, + 186028560485.09735, + 187881466715.82117, + 189670768808.52518, + 191397370113.5591, + 193062262208.28482, + 194666514849.56927, + 196211266395.00525, + 197697714711.99057, + 199127108590.06726, + 200500739660.8907, + 201819934831.14236, + 203086049222.54807, + 204300459614.14108, + 205464558376.57272, + 206579747883.16934, + 207647435385.08353, + 208669028330.83698, + 209645930113.7524, + 210579536224.9345, + 211471230792.85892, + 212322383488.85797, + 213134346775.05908, + 213908449029.64093, + 214645996841.59247, + 215348286616.73877, + 216016580696.22003, + 216652114874.8826, + 217256097283.5322, + 217829707477.1932, + 218374095710.14105, + 218890382383.57236, + 219379657649.66318, + 219842981155.79706, + 220281381919.172, + 220695858314.53354, + 221087378166.2643, + 221456878931.67258, + 221805267966.18643, + 222133422860.92596, + 222442191842.04816, + 222732394226.20602, + 223004820922.12497, + 223260234973.31854, + 223499372134.43323, + 223722941475.72876, + 223931626010.58966, + 224126083341.021, + 224306946317.5971, + 224474823708.58456, + 224630300876.42337, + 224773940456.65158, + 224906283038.34857, + 225027847842.1965, + 225139133394.53152, + 225240618195.1795, + 225332761378.8069, + 225416003364.91315, + 225490766500.28265, + 225557455686.99042, + 225616459000.8862, + 225668148294.455, + 225712879788.01535, + 225750994644.923, + 225782819533.8887, + 225808667174.40283, + 214533479339.8047 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 2 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon3.json b/cherab/generomak/plasma/data/core/carbon3.json new file mode 100644 index 00000000..d561f286 --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon3.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 1696833.2929320585, + 1695585.5346261703, + 1703034.1483737943, + 1721256.0083039757, + 1751174.826856844, + 1793462.9777274628, + 1848789.2681748066, + 1917932.0502929946, + 2001843.1487110062, + 2101691.8507644087, + 2218901.8049322264, + 2355187.4339219616, + 2512593.5720948935, + 2693541.143056974, + 2900880.7853058777, + 3137956.567619743, + 3408681.8389850566, + 3717629.590246343, + 4070140.147236769, + 4472449.503360834, + 4931842.339843194, + 5456834.574082767, + 6057391.4069654485, + 6745188.043835769, + 7533894.08882723, + 8438040.68619504, + 9474927.617135428, + 10666144.076443244, + 12037269.181392074, + 13618691.263185747, + 15446615.360481534, + 17564303.70763869, + 20023607.824385516, + 22886777.35580498, + 26222719.857324515, + 30112198.083528586, + 34654282.723995, + 39966924.52730038, + 46190732.40796578, + 53493550.959011845, + 62076004.239125356, + 72178217.79676005, + 84087979.04679102, + 98153684.12489405, + 114790268.27579233, + 134480496.8088131, + 157796732.52584448, + 185454477.7662331, + 218321063.70239368, + 257449759.17177022, + 304122403.0477343, + 359902414.63618606, + 426700895.5275371, + 506859421.1661952, + 603150803.3245813, + 718665327.1439896, + 857491337.6972063, + 1024713797.2305741, + 1226629970.51637, + 1471064146.446006, + 1767770494.5111785, + 2128950440.3712964, + 2569871178.013945, + 3107483535.2911277, + 3762666980.8892303, + 4562658718.166039, + 5541407710.59275, + 6741327176.803194, + 8215528044.0905075, + 10030670665.17434, + 12270612334.574423, + 15041080240.00464, + 18475666848.57919, + 22743531642.40487, + 28059304890.69837, + 34694130667.19102, + 42949504729.17792, + 53215803474.32276, + 66005843073.27365, + 81970232639.91074, + 101935947429.07399, + 126955857114.0498, + 158372211099.3084, + 197897781357.9156, + 247719125699.63535, + 310636487052.79944, + 389922612130.1703, + 488927829361.61676, + 612743572971.3516, + 767966754305.5444, + 963061978933.3938, + 1208880863704.0518, + 1519300911417.147, + 1911986213718.501, + 2402412088320.5693, + 3001219151011.2773, + 3730120534876.667, + 4615158784849.36, + 5686907738665.593, + 6980763900484.745, + 8537026859965.436, + 10400699753349.402, + 12620966467453.045, + 15250350343992.717, + 18343622207917.098, + 21956583495935.363, + 26064693092269.617, + 30544136400340.87, + 35377753295293.17, + 40555945070609.02, + 46074045244408.83, + 51933029405493.94, + 58139222942210.87, + 64703130552471.7, + 71637626192466.19, + 78955815493426.89, + 86668917902746.72, + 94784516303177.33, + 103263931645205.48, + 111787224185099.47, + 120246770335232.78, + 128605747496359.89, + 136833759942592.23, + 144907844298562.9, + 152813152835414.7, + 160543211765841.1, + 168099722187906.75, + 175491935454603.38, + 182735876734993.4, + 189852619551918.88, + 196867432542592.47, + 203808284935431.44, + 210704581917170.66, + 217583961405604.62, + 224394192250872.2, + 231116082117766.88, + 237773792771364.6, + 244387675489041.34, + 250974441603543.8, + 257547405498000.12, + 264116767501773.88, + 270689913774420.62, + 277271717683311.97, + 283864832683560.44, + 290469970659869.75, + 297086162435741.6, + 303710998999869.2, + 310340853188191.1, + 316971082286105.75, + 323596212423999.56, + 330210105837718.44, + 336806112130098.3, + 343377204653890.2, + 349916103076979.94, + 356415383111422.3, + 362867574303987.75, + 369265246706052.7, + 375601087170428.94, + 381867965963014.5, + 388058994328742.4, + 394167573613298.7, + 400187436512475.75, + 406112680998405.06, + 411937797455005.75, + 417657689540276.8, + 423267689282113.25, + 428763566902546.94, + 434141535854502.4, + 439398253542820.25, + 444530818187850.8, + 449536811414876.2, + 454414150553507.06, + 459161179124996.75, + 463776666728802.44, + 468259765082445.1, + 472609990374218.7, + 476827204446105.7, + 480911595099409.4, + 484863655789798.3, + 488684164954596.6, + 492374165189189.6, + 495934942466422.6, + 499368005568998.6, + 502675065882530.7, + 505858017676230.6, + 508918918977704.56, + 511859973130526.25, + 514683511105241.2, + 517391974619713.7, + 519987900109775.94, + 522473903578741.44, + 524852666342586.44, + 527126921677870.1, + 529299442370347.3, + 531373029154628.2, + 533350500028981.56, + 535234680423608.1, + 537028394196253.75, + 538734455425684.3, + 540355660970247.06, + 541894783756889.0, + 543354566764333.56, + 544737717663353.94, + 546046904076213.9, + 547284749417582.06, + 548453829279530.9, + 549556668323423.5, + 550595737642856.25, + 551573452562394.7, + 552492170838372.0, + 553354191229003.4, + 554161752403094.4, + 554917020632621.5, + 555622101348608.25, + 556279072705655.25, + 556889927902278.8, + 557456598671095.75, + 557980955650210.25, + 558464808917578.9, + 558909908669404.6, + 559317946024909.9, + 559690553941324.56, + 560029308223890.9, + 560335728617201.25, + 560611279965183.6, + 560857373428226.7, + 561075367746842.94, + 561266570542317.8, + 561432239645791.94, + 561573584447694.25, + 561691767260778.06, + 561787904690278.2, + 561863069005538.4, + 561918289508186.7, + 561954553892423.7, + 561972809593324.8, + 561973965120028.06, + 561958891370560.56, + 561928422925964.1, + 561883359321281.1, + 561824466291756.2, + 561752476992557.2, + 561668093190769.7, + 561571986428726.75, + 561464799157707.44, + 561347145841483.9, + 561219614029419.6, + 561082765398626.2, + 560937136765227.25, + 560783241064650.5, + 560621568301310.75, + 560452586467449.8, + 560276742431891.2, + 560094462798939.7, + 559906154737711.9, + 519569650787351.1 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 3 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon4.json b/cherab/generomak/plasma/data/core/carbon4.json new file mode 100644 index 00000000..47ad77e9 --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon4.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 46350405566.4054, + 46284237625.31838, + 46377376549.137474, + 46670924446.71948, + 47180482713.78791, + 47915138259.74194, + 48882300889.21657, + 50089810565.15369, + 51547022732.33509, + 53265438570.25678, + 55259122012.65607, + 57545021624.12109, + 60143260320.84436, + 63077429051.469345, + 66374906777.00613, + 70067221925.64133, + 74190466880.22955, + 78785775449.38242, + 83899872865.75154, + 89585708209.65178, + 95903180036.86243, + 102919967269.99362, + 110712479034.47878, + 119366939061.52307, + 128980381012.53004, + 139650356255.3698, + 151493758818.3758, + 164655600080.99313, + 179302730877.29105, + 195627523788.2097, + 213852271426.5203, + 234234433172.45392, + 257072908865.32074, + 282714170388.7059, + 311476125733.1768, + 343728268938.59607, + 379948809932.3586, + 420690633544.8943, + 466594150949.36475, + 518402716039.77686, + 576981020112.0531, + 643337087885.5581, + 718648637928.0991, + 804309566656.8345, + 901936517731.6887, + 1013174699432.6936, + 1139774935542.7197, + 1284159542010.1714, + 1449192365214.294, + 1638263013716.838, + 1855394300765.4873, + 2105373695410.4524, + 2393914584397.565, + 2727854650178.9463, + 3114672632419.614, + 3561244443333.942, + 4078057118720.018, + 4678093148887.286, + 5377140773975.816, + 6194475953440.215, + 7153727019705.466, + 8283975395893.132, + 9620926371866.709, + 11198091896859.557, + 13056165093226.484, + 15251094943219.5, + 17851237199403.375, + 20940359891494.133, + 24621431200712.773, + 29021405474548.914, + 34297285209370.332, + 40643817115878.99, + 48303284204591.195, + 57577989916903.66, + 68846203133507.68, + 82575863200086.94, + 99191653487398.58, + 119279439872784.33, + 143654027332428.78, + 173346839323390.3, + 209668709470650.44, + 254291921482299.34, + 309357550616115.0, + 377616004551139.75, + 462610980484455.0, + 568935169243997.1, + 701805790886232.4, + 865973256198488.8, + 1069469897938848.2, + 1322843184955839.5, + 1639752367136081.5, + 2037895759219286.5, + 2540187857311035.5, + 3176220966949160.5, + 3974318084294743.5, + 4957628931592609.0, + 6166621547030046.0, + 7649980316205009.0, + 9465032414640132.0, + 1.167815021201937e+16, + 1.4364618692277636e+16, + 1.760776889672942e+16, + 2.149721890418564e+16, + 2.612615968793039e+16, + 3.158776631002165e+16, + 3.7970969353840856e+16, + 4.52687863954724e+16, + 5.335944463086585e+16, + 6.2229908295669816e+16, + 7.186462354298768e+16, + 8.224162698271402e+16, + 9.333233640328437e+16, + 1.051001076547748e+17, + 1.1749765655473509e+17, + 1.3046371799766349e+17, + 1.4391954758695259e+17, + 1.5776602862745238e+17, + 1.7188219580417078e+17, + 1.860924287861691e+17, + 2.0000578021076806e+17, + 2.1344414086257683e+17, + 2.2629718005560486e+17, + 2.384692421863735e+17, + 2.498824535514498e+17, + 2.604788577222335e+17, + 2.7022136553746403e+17, + 2.7909348028404227e+17, + 2.8709791375615872e+17, + 2.942534901122749e+17, + 3.005948389926677e+17, + 3.061667080728738e+17, + 3.1102163520002963e+17, + 3.1521700349012704e+17, + 3.188116802063171e+17, + 3.2183961044985395e+17, + 3.2435828220173325e+17, + 3.264375086754881e+17, + 3.2813887595373184e+17, + 3.295162754327499e+17, + 3.306165604397081e+17, + 3.3148025418647725e+17, + 3.321422598062225e+17, + 3.326325410056661e+17, + 3.32976754800993e+17, + 3.3319682689075654e+17, + 3.333114663703342e+17, + 3.333366204791668e+17, + 3.332858725073262e+17, + 3.331707873420125e+17, + 3.3300120975678624e+17, + 3.3278552069020934e+17, + 3.325308566056332e+17, + 3.322432966946062e+17, + 3.319280222669887e+17, + 3.315894522167183e+17, + 3.31231357998432e+17, + 3.308569611179469e+17, + 3.304690157406602e+17, + 3.300698786616609e+17, + 3.296615685611911e+17, + 3.2924581618800666e+17, + 3.288241068686598e+17, + 3.2839771652957235e+17, + 3.2796774223744435e+17, + 3.275351281085987e+17, + 3.271006873058874e+17, + 3.2666512072974803e+17, + 3.2622903291511296e+17, + 3.2579294556561664e+17, + 3.2535730908884525e+17, + 3.249224982684324e+17, + 3.244888569206653e+17, + 3.2405668223940326e+17, + 3.236262244858278e+17, + 3.2319769953504006e+17, + 3.227712934116456e+17, + 3.22347166191095e+17, + 3.219254553603287e+17, + 3.2150627871695424e+17, + 3.210897368740363e+17, + 3.2067591542741376e+17, + 3.202648868338333e+17, + 3.198567120409589e+17, + 3.19451441904167e+17, + 3.190491184198753e+17, + 3.186497758007563e+17, + 3.1825344141450534e+17, + 3.178601366046735e+17, + 3.174698774094042e+17, + 3.1708267519167014e+17, + 3.166985371926715e+17, + 3.1631746701840864e+17, + 3.1593946506808256e+17, + 3.1556452891173523e+17, + 3.151926536235753e+17, + 3.1482383207651834e+17, + 3.1445805520277523e+17, + 3.140953122246476e+17, + 3.1373559085913184e+17, + 3.1337887749952186e+17, + 3.130251573767114e+17, + 3.126744147026179e+17, + 3.123266327978007e+17, + 3.119817942051087e+17, + 3.1163988079097114e+17, + 3.113008738357073e+17, + 3.109647541141207e+17, + 3.106315019674411e+17, + 3.103010973675901e+17, + 3.099735199746016e+17, + 3.096487491879422e+17, + 3.0932676419239776e+17, + 3.0900753972786e+17, + 3.0869105054967526e+17, + 3.083772840437199e+17, + 3.080662189016621e+17, + 3.0775783376675834e+17, + 3.074521072573213e+17, + 3.071490179876445e+17, + 3.068485445867482e+17, + 3.0655066571506963e+17, + 3.0625536007936736e+17, + 3.0596260644602336e+17, + 3.056723836528408e+17, + 3.0538467061958035e+17, + 3.050994463572906e+17, + 3.048166899765865e+17, + 3.0453638069496154e+17, + 3.042584978432657e+17, + 3.039830208713851e+17, + 3.0370992935322266e+17, + 3.0343920299108154e+17, + 3.0317082161944416e+17, + 3.029047652082736e+17, + 3.0264101386582874e+17, + 3.0237954784109037e+17, + 3.021203475258075e+17, + 3.018633934561758e+17, + 3.0160866631429037e+17, + 3.013561469292451e+17, + 3.011058162780258e+17, + 3.0085765548619405e+17, + 3.0061164582834656e+17, + 3.003677687284272e+17, + 3.0012600575984256e+17, + 2.9988633864545626e+17, + 2.996487492574651e+17, + 2.994132196171548e+17, + 2.991797318945194e+17, + 2.989482684078314e+17, + 2.98718811623132e+17, + 2.9849134415361574e+17, + 2.982658487589408e+17, + 2.980423083445655e+17, + 2.9782070596092915e+17, + 2.7247030199525245e+17 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 4 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon5.json b/cherab/generomak/plasma/data/core/carbon5.json new file mode 100644 index 00000000..b62ee60a --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon5.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 161024132134966.47, + 160653503219502.1, + 160507242359572.56, + 160667836607887.75, + 161159174288638.38, + 161989547325367.2, + 163160868851305.97, + 164672538431461.94, + 166523370342109.3, + 168712636114736.53, + 171240653439960.9, + 174109128869520.62, + 177321363766464.66, + 180882384938404.34, + 184799035962934.97, + 189080051019599.56, + 193736124830517.1, + 198779987453727.56, + 204226489773322.7, + 210092703809260.47, + 216398040979350.88, + 223164390920621.16, + 230416283256009.7, + 238181074678749.84, + 246488677361616.25, + 255347696083510.9, + 264777740351847.78, + 274822297666057.56, + 285530597548920.0, + 296958209680815.56, + 309167755802187.0, + 322229722682802.0, + 336223391977405.75, + 351237037130031.44, + 367316468989557.44, + 384512645316222.44, + 402912558128665.9, + 422611765562034.25, + 443715236374775.9, + 466338364535512.5, + 490608107464424.5, + 516664263896114.6, + 544660909569018.25, + 574771346195028.4, + 607182200712998.2, + 641993775762799.1, + 679285757098039.9, + 719329437626251.1, + 762435817884073.2, + 808959351131308.6, + 859304387830781.4, + 913932717589034.6, + 973372418549912.6, + 1038228266498749.4, + 1108965758582219.4, + 1185530665811968.8, + 1268623485069471.2, + 1359148843563016.0, + 1458160473424394.8, + 1566887347887052.8, + 1686765177753372.5, + 1819474480244125.5, + 1966944277299036.2, + 2129644809721581.0, + 2308435409989728.5, + 2505507727432574.0, + 2723405054905318.5, + 2965083298551671.5, + 3233983555918746.0, + 3534118624816150.5, + 3870176221435777.5, + 4247642221714308.5, + 4672947861350066.0, + 5153645539587730.0, + 5698618664034045.0, + 6317959266999076.0, + 7015040256490839.0, + 7797878657778269.0, + 8680238046734384.0, + 9678419565046922.0, + 1.0811736328238992e+16, + 1.2103072275208854e+16, + 1.35795351531977e+16, + 1.5273210653384718e+16, + 1.7222019001933152e+16, + 1.9470878411506988e+16, + 2.206058988419503e+16, + 2.5010246550634016e+16, + 2.8374503295553108e+16, + 3.221862128793027e+16, + 3.6616618204665976e+16, + 4.1651074122496936e+16, + 4.74120543092295e+16, + 5.399462249641901e+16, + 6.143726358298171e+16, + 6.9713794415034904e+16, + 7.88602209992365e+16, + 8.889182371626213e+16, + 9.979072697762856e+16, + 1.1149427025010378e+17, + 1.2388317531927544e+17, + 1.3677125579435957e+17, + 1.4989901351691597e+17, + 1.6293374324877622e+17, + 1.7547842758551754e+17, + 1.8709055180135395e+17, + 1.9727096976715142e+17, + 2.055969980363844e+17, + 2.1184190259838147e+17, + 2.1585866802009814e+17, + 2.175780909076306e+17, + 2.1700505602378323e+17, + 2.142110900199348e+17, + 2.0932504552875306e+17, + 2.02523454416483e+17, + 1.940214986495018e+17, + 1.8406488653204474e+17, + 1.7292237180532278e+17, + 1.609070270228471e+17, + 1.485192170274923e+17, + 1.3607450007252114e+17, + 1.2380272018238762e+17, + 1.1189501743619742e+17, + 1.0050487014189278e+17, + 8.974958274849342e+16, + 7.971243093411478e+16, + 7.04455417952653e+16, + 6.197344649530697e+16, + 5.42978817655598e+16, + 4.7399830330996056e+16, + 4.124606771225955e+16, + 3.579255597379613e+16, + 3.098812762090215e+16, + 2.677841934624266e+16, + 2.313267459153055e+16, + 1.9995837148790244e+16, + 1.7299828713764886e+16, + 1.498424019897001e+16, + 1.2995976233140328e+16, + 1.1288719115750174e+16, + 9822303083643866.0, + 8562060401319240.0, + 7478179071658524.0, + 6545096357996279.0, + 5740941409601873.0, + 5047032901085019.0, + 4447432774883569.5, + 3928554174161130.5, + 3478819914604156.5, + 3088366959685935.5, + 2748792041830096.0, + 2452933605216769.0, + 2194685492053311.5, + 1968838156523490.2, + 1770943605623496.8, + 1597200692861872.5, + 1444357804385388.5, + 1309630363135666.2, + 1190630927952283.8, + 1085309978444810.6, + 991905753181653.0, + 908901750211121.1, + 834990707915531.6, + 769044064015345.5, + 710086044389402.1, + 657271664609963.1, + 609868038549173.2, + 567238482949368.1, + 528828986803599.75, + 494156681915552.7, + 462801819568105.44, + 434394305887636.3, + 408610314143343.06, + 385166603830043.2, + 363814226223222.2, + 344334005110715.2, + 326532652735273.4, + 310239427130823.75, + 295303251427072.06, + 281590227809429.2, + 268981489039979.28, + 257371339064654.56, + 246665641500495.62, + 236780420950391.28, + 227640647282167.97, + 219179177413892.6, + 211335832859303.44, + 204056594461261.25, + 197292898404320.6, + 191001019886732.9, + 185141532760857.62, + 179678835102545.62, + 174580732073686.62, + 169818068643664.22, + 165364405752635.5, + 161195734386968.4, + 157290222775333.8, + 153627992565845.03, + 150190920391755.9, + 146962461710962.6, + 143927494208912.38, + 141072178412878.78, + 138383833459875.8, + 135850826229914.27, + 133462472282759.86, + 131208947226407.88, + 129081207321019.44, + 127070918269517.56, + 125170391272622.27, + 123372525535675.33, + 121670756518192.75, + 120059009296409.5, + 118531668753453.81, + 117083525311571.0, + 115709710318728.34, + 114405725785893.33, + 113167389473178.39, + 111990809312097.34, + 110872360170072.14, + 109808662725540.33, + 108796564236592.3, + 107833121017659.64, + 106915582459347.52, + 106041376431703.06, + 105208095947617.9, + 104413486959420.16, + 103655437183194.61, + 102931965854681.19, + 102241214329040.6, + 101581437448067.73, + 100950995603730.38, + 100348347436377.56, + 99772043108404.7, + 99220718105874.52, + 98693087518270.73, + 98187940757455.16, + 97704136675393.52, + 97240599050432.17, + 96796312405593.72, + 96370318136411.11, + 95961710919354.02, + 95569635379466.4, + 95193282993472.34, + 94831889213877.6, + 94484730792535.0, + 94151123288018.19, + 93830418746832.02, + 93522003537859.14, + 93225296336143.55, + 92939746236089.92, + 92664830994011.1, + 92400055380555.25, + 92144949643312.14, + 91899068064388.0, + 91661987613690.53, + 80088749700966.69 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 5 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/carbon6.json b/cherab/generomak/plasma/data/core/carbon6.json new file mode 100644 index 00000000..4c824909 --- /dev/null +++ b/cherab/generomak/plasma/data/core/carbon6.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 4.9983892951576294e+17, + 4.983879497133105e+17, + 4.967755866140605e+17, + 4.9510985466990605e+17, + 4.9341581359634624e+17, + 4.9170556331941075e+17, + 4.89986255096533e+17, + 4.8826259422505344e+17, + 4.86537891784407e+17, + 4.848145870922532e+17, + 4.8309453749605005e+17, + 4.8137919232444864e+17, + 4.796697037214771e+17, + 4.7796700064309997e+17, + 4.762718401528628e+17, + 4.745848440962568e+17, + 4.7290652600562976e+17, + 4.71237311271517e+17, + 4.695775525472951e+17, + 4.6792754170000864e+17, + 4.6628751920653805e+17, + 4.646576816249229e+17, + 4.63038187590711e+17, + 4.6142916266522874e+17, + 4.598307037636062e+17, + 4.582429064892481e+17, + 4.5666583570714464e+17, + 4.5509951511611744e+17, + 4.535439474786944e+17, + 4.519991158057632e+17, + 4.504649841775356e+17, + 4.4894149825499296e+17, + 4.474285854993411e+17, + 4.4592615597774957e+17, + 4.4443415477814e+17, + 4.42952515097652e+17, + 4.414811279853572e+17, + 4.400198704534008e+17, + 4.3856860516097184e+17, + 4.3712717986695616e+17, + 4.356954267048369e+17, + 4.3427316126968966e+17, + 4.3286018150378886e+17, + 4.314562255858729e+17, + 4.3006107253038886e+17, + 4.28674584595776e+17, + 4.272966235432586e+17, + 4.259268569438572e+17, + 4.2456491049926803e+17, + 4.23210364386893e+17, + 4.218627468670139e+17, + 4.205215267547799e+17, + 4.191861045425703e+17, + 4.178558019143855e+17, + 4.165300783172122e+17, + 4.152089127552762e+17, + 4.138915252178627e+17, + 4.1257692847745683e+17, + 4.112639828847147e+17, + 4.099513696238169e+17, + 4.0863755841905824e+17, + 4.0732076842764634e+17, + 4.059989634281084e+17, + 4.046715679784462e+17, + 4.0333761021624723e+17, + 4.0199477060236544e+17, + 4.006403654855438e+17, + 3.992712831648083e+17, + 3.978839075577596e+17, + 3.9647402693668077e+17, + 3.950367246761433e+17, + 3.935662483399666e+17, + 3.920558527100581e+17, + 3.9049761151576006e+17, + 3.888821916572605e+17, + 3.871989618946227e+17, + 3.854440535692542e+17, + 3.83608792883725e+17, + 3.816785306952975e+17, + 3.7963586382173274e+17, + 3.774600978175744e+17, + 3.751266060486219e+17, + 3.726060693144332e+17, + 3.69863581102174e+17, + 3.668576069457651e+17, + 3.635385522314936e+17, + 3.5986031537030906e+17, + 3.557990082407581e+17, + 3.5129187163841626e+17, + 3.462630238193415e+17, + 3.406247132754573e+17, + 3.342765850451636e+17, + 3.2710558017180826e+17, + 3.189869600468432e+17, + 3.098538368048117e+17, + 2.997091691578431e+17, + 2.884763976878227e+17, + 2.7609146834775462e+17, + 2.6251483616307402e+17, + 2.4774269151380362e+17, + 2.318189253723286e+17, + 2.1484628655656454e+17, + 1.9699454486519517e+17, + 1.7850310137509254e+17, + 1.596756834262952e+17, + 1.4086575956198934e+17, + 1.2257942351734243e+17, + 1.0536119643932915e+17, + 8.945080677375304e+16, + 7.501077534396101e+16, + 6.213221597889738e+16, + 5.083876796328754e+16, + 4.109548571419219e+16, + 3.282073395211112e+16, + 2.58991771483556e+16, + 2.0194309163133372e+16, + 1.5559472132032808e+16, + 1.1846815437393852e+16, + 8918871356669974.0, + 6666488619767974.0, + 4952272490160231.0, + 3656693545661167.5, + 2684245219410593.0, + 1959293800683820.2, + 1422454462508854.5, + 1027495920737416.9, + 738735260847582.1, + 528869852835632.25, + 377246151542587.9, + 268208188769288.22, + 190158286219365.4, + 134519749871650.16, + 94999138095081.17, + 67016838139886.77, + 47388925728580.2, + 33655594842202.37, + 24010931117580.105, + 17211131680797.598, + 12397388136261.445, + 8975069389548.232, + 6531215810353.063, + 4778109970512.45, + 3514609743151.2197, + 2599594904029.761, + 1933687843450.649, + 1446636619284.179, + 1088581422766.1594, + 823990834577.6648, + 627438327748.4506, + 480650770721.95197, + 370438747494.49036, + 287240088062.0348, + 224091191132.38284, + 175897776716.16806, + 138915940488.82767, + 110381427410.77623, + 88243745695.27124, + 70974708829.00201, + 57430010951.75191, + 46748736133.543, + 38280102857.815254, + 31529840224.19217, + 26120769367.413773, + 21763705170.207542, + 18235884795.813892, + 15364908451.932526, + 13016733283.730207, + 11086658506.895058, + 9492528928.261642, + 8169588216.710768, + 7066704039.21469, + 6142970302.393482, + 5365759572.048553, + 4708966109.732259, + 4151558497.112541, + 3676522729.535672, + 3270038222.3734403, + 2920833184.218933, + 2619678032.075184, + 2358985990.8214483, + 2132496850.038897, + 1935025513.2003684, + 1762261779.2242439, + 1610609664.5842507, + 1477058481.0535715, + 1359078978.4888785, + 1254538959.6238554, + 1161635237.9576263, + 1078837828.5027132, + 1004844373.7736144, + 938543019.4518695, + 878981608.3420769, + 825342214.7196075, + 776920444.1419855, + 733107745.1355531, + 693376953.2984082, + 657270432.3485224, + 624389409.1422876, + 594385987.3914045, + 566955686.755834, + 541831025.8088642, + 518777281.941817, + 497587285.0481517, + 478077997.69254464, + 460087497.62123114, + 443471967.2421416, + 428103642.58286554, + 413868620.92078733, + 400665191.04849786, + 388402498.0855996, + 376998969.9038688, + 366381412.1133349, + 356484284.95387477, + 347248113.25952405, + 338619246.9022559, + 330549123.4969663, + 322993975.0574306, + 315913999.852501, + 309273169.99934995, + 303038445.91674054, + 297179891.09289044, + 291670181.76515394, + 286484267.6975641, + 281599400.4746377, + 276994518.5718484, + 272650410.9427122, + 268549411.96215296, + 264675181.73834926, + 261012759.79181898, + 257548445.94359872, + 254269423.21526513, + 251163844.2373063, + 248220801.75397795, + 245430340.81520307, + 242782931.8616595, + 240269948.92623258, + 237883261.9877548, + 235615478.3775228, + 233459505.95959592, + 231408820.5941001, + 229457393.87108496, + 227599572.65190783, + 225830024.39467406, + 224143772.6216756, + 222536269.81091282, + 221003094.60468093, + 219540278.05436647, + 218143888.0390999, + 216810518.66811267, + 215536728.0802908, + 214319215.59609443, + 213155316.59152457, + 212042071.98700914, + 210976871.09200314, + 209957208.5307115, + 173744949.17023906 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "carbon", + "charge": 6 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/electrons.json b/cherab/generomak/plasma/data/core/electrons.json new file mode 100644 index 00000000..7b5452e3 --- /dev/null +++ b/cherab/generomak/plasma/data/core/electrons.json @@ -0,0 +1,1292 @@ +{ + "temperature": [ + 3000.0, + 2998.52312403888, + 2992.7832506358122, + 2982.0621888623136, + 2966.1918094687762, + 2945.2429393424018, + 2919.419338214869, + 2889.003692058303, + 2854.3254277348515, + 2815.7398455505386, + 2773.6139136215174, + 2728.3163262527637, + 2680.2104553864137, + 2629.6493480144745, + 2576.972213977541, + 2522.5020223843676, + 2466.5439345047307, + 2409.3843734189736, + 2351.2905804738834, + 2292.5105439306594, + 2233.273210983751, + 2173.788913597856, + 2114.249953285668, + 2054.8313013065413, + 1995.6913796728331, + 1936.972895410661, + 1878.803706166504, + 1821.2976997951666, + 1764.5556742417446, + 1708.6662070172113, + 1653.7065059997449, + 1599.7432352775288, + 1546.8333113667693, + 1495.024666457186, + 1444.3569764096046, + 1394.8623520995304, + 1346.5659934019332, + 1299.4868056747446, + 1253.6379790455755, + 1209.0275311576331, + 1165.6588143030472, + 1123.5309880785605, + 1082.6394588511619, + 1042.9762874292182, + 1004.5305664061173, + 967.2887686849466, + 931.2350687100197, + 896.351637928748, + 862.6189159894875, + 830.0158591507654, + 798.5201673375811, + 768.1084912334692, + 738.7566207447438, + 710.4396561172668, + 683.1321629276453, + 656.8083121109125, + 631.4420061264298, + 607.0069923036725, + 583.4769643502522, + 560.8256529464521, + 539.0269062941003, + 518.0547614329445, + 497.88350708511757, + 478.4877387378806, + 459.84240662666764, + 441.9228572346464, + 424.7048688815144, + 408.16468193306855, + 392.2790241242251, + 377.0251314515118, + 362.3807650566294, + 348.3242244903237, + 334.83435771552126, + 321.890568180333, + 309.4728192650518, + 297.56163638257, + 286.1381069886319, + 275.1838787369333, + 264.68115599418036, + 254.6126949117557, + 244.96179723353092, + 235.71230300348657, + 226.84858232216044, + 218.35552628735832, + 210.21853724207088, + 202.42351844097678, + 194.95686323627902, + 187.8054438738336, + 180.95659998151527, + 174.3981268234994, + 168.11826338653523, + 162.10568035733561, + 156.34946804381812, + 150.8391242871043, + 145.56454240585458, + 140.51599920963352, + 135.6841431135791, + 131.0599823825858, + 126.63487352955082, + 122.40050988887697, + 118.3489103834026, + 114.47240850017948, + 110.76364148803557, + 107.21553978761827, + 103.82131670259133, + 100.57445831884021, + 97.46871367691406, + 94.49808520145595, + 91.65681939008829, + 88.93939776303986, + 86.34052807377111, + 83.8551357799498, + 81.47835577330721, + 79.20552436620889, + 77.03217153215161, + 74.95401339685759, + 72.9669449761771, + 71.06703315661875, + 69.25050991397345, + 67.51376576524152, + 65.85334344881281, + 64.26593182768406, + 62.74836001033597, + 61.2975916837819, + 59.91071965322365, + 58.58496058269206, + 57.31764993102628, + 56.10623707754045, + 54.948280631735244, + 53.84144392146098, + 52.78349065396557, + 51.77228074433782, + 50.805766305924465, + 49.88198779737891, + 48.999070321097264, + 48.15522006789341, + 47.348720902865914, + 46.57793108752631, + 45.841280133367995, + 45.137265782168555, + 44.46445110844881, + 43.82146173961798, + 43.206983189469376, + 42.61975830080557, + 42.05858479309852, + 41.522312911211856, + 41.00984317133116, + 40.520124200373346, + 40.05215066526151, + 39.604961288570934, + 39.17763694716649, + 38.7692988505653, + 38.379106795872445, + 38.00625749624137, + 37.64998297992401, + 37.30954905707338, + 36.98425385156884, + 36.67342639522852, + 36.37642528187246, + 36.09263737879576, + 35.82147659329382, + 35.56238269198415, + 35.314820170740816, + 35.07827717314717, + 34.852264455457785, + 34.63631439612891, + 34.42998004805659, + 34.23283423173999, + 34.044468667644786, + 33.864493146125206, + 33.69253473331596, + 33.52823701147538, + 33.371259352322355, + 33.221276221963834, + 33.07797651607301, + 32.94106292402577, + 32.81025132076462, + 32.685270185198156, + 32.56586004400546, + 32.45177293975274, + 32.34277192227911, + 32.23863056234819, + 32.13913248661057, + 32.04407093295307, + 31.953248325357347, + 31.866475867422928, + 31.783573153745696, + 31.704367798379554, + 31.62869507963904, + 31.556397600531596, + 31.48732496414222, + 31.421333463316515, + 31.3582857840232, + 31.298050721795224, + 31.24050291068117, + 31.18552256415908, + 31.132995227490305, + 31.082811541013847, + 31.034867013898335, + 30.98906180789872, + 30.94530053067335, + 30.903492038247204, + 30.863549246214866, + 30.82538894930257, + 30.788931648920514, + 30.754101388354496, + 30.720825595258578, + 30.68903493113094, + 30.658663147461834, + 30.62964694826175, + 30.60192585868675, + 30.575442099493, + 30.550140467063347, + 30.52596821875878, + 30.502874963360853, + 30.480812556378506, + 30.459735000005395, + 30.439598347523223, + 30.420360611949583, + 30.401981678749532, + 30.384423222424292, + 30.36764862680968, + 30.35162290891837, + 30.336312646168288, + 30.321685906848675, + 30.307712183679225, + 30.294362330325363, + 30.28160850073722, + 30.26942409119068, + 30.257783684906165, + 30.246662999132866, + 30.23603883458999, + 30.225889027158132, + 30.21619240172184, + 30.206928728068068, + 30.19807867874907, + 30.18962378882196, + 30.181546417381178, + 30.17382971080637, + 30.16645756764611, + 30.159414605066953, + 30.15268612679821, + 30.146258092504468, + 30.1401170885238, + 30.13425029990965, + 30.128645483719644, + 30.123290943494364, + 30.118175504875328, + 30.113288492308037, + 30.108619706785408, + 30.104159404583456, + 30.09989827694405, + 30.095827430664894, + 30.091938369554885, + 30.08822297671646, + 30.00874190034343 + ], + "density": [ + 5e+19, + 4.981243417835291e+19, + 4.960402826870333e+19, + 4.938876378646617e+19, + 4.916988294636239e+19, + 4.894895073575541e+19, + 4.872689168534975e+19, + 4.850431386174099e+19, + 4.82816450569505e+19, + 4.8059200450545025e+19, + 4.783722014076859e+19, + 4.761589167152264e+19, + 4.739536438387333e+19, + 4.7175758996665205e+19, + 4.6957174248046666e+19, + 4.673969164500353e+19, + 4.65233789497414e+19, + 4.630829279642529e+19, + 4.609448069324508e+19, + 4.588198258002084e+19, + 4.567083205796747e+19, + 4.546105737335663e+19, + 4.5252682213529305e+19, + 4.504572635781684e+19, + 4.484020621485682e+19, + 4.463613526993922e+19, + 4.443352446035942e+19, + 4.423238249261527e+19, + 4.403271611221671e+19, + 4.383453033457265e+19, + 4.3637828643671245e+19, + 4.34426131639279e+19, + 4.324888480953614e+19, + 4.305664341484428e+19, + 4.286588784864013e+19, + 4.2676616114717884e+19, + 4.248882544069383e+19, + 4.230251235670968e+19, + 4.2117672765396435e+19, + 4.193430200425467e+19, + 4.175239490142886e+19, + 4.157194582570674e+19, + 4.1392948731452695e+19, + 4.121539719908261e+19, + 4.103928447160288e+19, + 4.0864603487664456e+19, + 4.069134691152288e+19, + 4.051950716024378e+19, + 4.034907642845002e+19, + 4.018004671086943e+19, + 4.0012409822910185e+19, + 3.984615741946379e+19, + 3.968128101211149e+19, + 3.951777198489014e+19, + 3.935562160875532e+19, + 3.919482105486449e+19, + 3.903536140678914e+19, + 3.887723367175341e+19, + 3.872042879098642e+19, + 3.856493764926597e+19, + 3.841075108372399e+19, + 3.82578598919766e+19, + 3.810625483963539e+19, + 3.795592666725145e+19, + 3.780686609673816e+19, + 3.765906383731481e+19, + 3.751251059100921e+19, + 3.736719705775351e+19, + 3.7223113940105036e+19, + 3.708025194762049e+19, + 3.6938601800909865e+19, + 3.6798154235393765e+19, + 3.665890000478603e+19, + 3.652082988432173e+19, + 3.638393467374876e+19, + 3.6248205200099906e+19, + 3.611363232026088e+19, + 3.5980206923348574e+19, + 3.584791993291257e+19, + 3.5716762308972077e+19, + 3.55867250498995e+19, + 3.545779919416078e+19, + 3.532997582192232e+19, + 3.5203246056533017e+19, + 3.5077601065889927e+19, + 3.495303206369484e+19, + 3.4829530310609023e+19, + 3.470708711531262e+19, + 3.4585693835474727e+19, + 3.446534187864e+19, + 3.434602270303676e+19, + 3.422772781831188e+19, + 3.4110448786196742e+19, + 3.3994177221108638e+19, + 3.3878904790691725e+19, + 3.376462321630112e+19, + 3.3651324273433653e+19, + 3.3538999792108577e+19, + 3.3427641657201336e+19, + 3.331724180873318e+19, + 3.320779224211924e+19, + 3.3099285008377905e+19, + 3.299171221430335e+19, + 3.288506602260401e+19, + 3.277933865200853e+19, + 3.2674522377341583e+19, + 3.257060952957118e+19, + 3.246759249582918e+19, + 3.236546371940678e+19, + 3.22642156997264e+19, + 3.21638409922914e+19, + 3.2064332208615055e+19, + 3.1965682016130003e+19, + 3.1867883138079457e+19, + 3.1770928353391223e+19, + 3.1674810496535667e+19, + 3.157952245736852e+19, + 3.1485057180959785e+19, + 3.1391407667409125e+19, + 3.129856697164928e+19, + 3.1206528203237556e+19, + 3.1115284526136713e+19, + 3.1024829158485823e+19, + 3.093515537236158e+19, + 3.0846256493530964e+19, + 3.0758125901195686e+19, + 3.0670757027729146e+19, + 3.058414335840632e+19, + 3.049827843112693e+19, + 3.0413155836133044e+19, + 3.032876921572053e+19, + 3.0245112263945794e+19, + 3.016217872632765e+19, + 3.0079962399544726e+19, + 2.9998457131129012e+19, + 2.9917656819155902e+19, + 2.9837555411930575e+19, + 2.9758146907671814e+19, + 2.9679425354192785e+19, + 2.9601384848579527e+19, + 2.9524019536867484e+19, + 2.9447323613715747e+19, + 2.937129132207994e+19, + 2.9295916952883618e+19, + 2.922119484468826e+19, + 2.9147119383362347e+19, + 2.907368500174985e+19, + 2.9000886179337527e+19, + 2.8928717441922073e+19, + 2.885717336127708e+19, + 2.878624855481938e+19, + 2.8715937685275652e+19, + 2.8646235460349194e+19, + 2.857713663238656e+19, + 2.850863599804493e+19, + 2.844072839795962e+19, + 2.83734087164125e+19, + 2.830667188100075e+19, + 2.8240512862306402e+19, + 2.8174926673567138e+19, + 2.8109908370347393e+19, + 2.8045453050210898e+19, + 2.798155585239442e+19, + 2.7918211957482054e+19, + 2.7855416587081368e+19, + 2.779316500350069e+19, + 2.773145250942718e+19, + 2.7670274447607374e+19, + 2.7609626200527954e+19, + 2.7549503190099005e+19, + 2.748990087733828e+19, + 2.7430814762057028e+19, + 2.7372240382547927e+19, + 2.7314173315273884e+19, + 2.725660917455944e+19, + 2.719954361228283e+19, + 2.714297231757104e+19, + 2.7086891016495333e+19, + 2.7031295471769723e+19, + 2.697618148245014e+19, + 2.692154488363691e+19, + 2.686738154617714e+19, + 2.6813687376370782e+19, + 2.6760458315677737e+19, + 2.67076903404267e+19, + 2.665537946152642e+19, + 2.660352172417858e+19, + 2.65521132075925e+19, + 2.65011500247021e+19, + 2.645062832188477e+19, + 2.6400544278682247e+19, + 2.6350894107522163e+19, + 2.6301674053443994e+19, + 2.625288039382521e+19, + 2.620450943810967e+19, + 2.6156557527538237e+19, + 2.610902103488069e+19, + 2.6061896364171543e+19, + 2.6015179950445117e+19, + 2.5968868259474686e+19, + 2.5922957787512246e+19, + 2.5877445061031244e+19, + 2.5832326636470903e+19, + 2.578759909998139e+19, + 2.57432590671733e+19, + 2.5699303182867214e+19, + 2.5655728120845242e+19, + 2.561253058360556e+19, + 2.5569707302117368e+19, + 2.5527255035580244e+19, + 2.5485170571182055e+19, + 2.544345072386111e+19, + 2.54020923360703e+19, + 2.5361092277541216e+19, + 2.5320447445052424e+19, + 2.5280154762197893e+19, + 2.5240211179157164e+19, + 2.520061367247062e+19, + 2.5161359244810146e+19, + 2.5122444924759847e+19, + 2.5083867766590497e+19, + 2.5045624850041692e+19, + 2.500771328010387e+19, + 2.4970130186799833e+19, + 2.493287272497215e+19, + 2.489593807406909e+19, + 2.485932343793488e+19, + 2.482302604459721e+19, + 2.478704314606391e+19, + 2.475137201811416e+19, + 2.471600996009324e+19, + 2.4680954294713426e+19, + 2.464620236785044e+19, + 2.4611751548345704e+19, + 2.45775992278063e+19, + 2.4543742820412535e+19, + 2.451017976272195e+19, + 2.447690751347609e+19, + 2.444392355341035e+19, + 2.4411225385064636e+19, + 2.4378810532594893e+19, + 2.434667654158714e+19, + 2.4314820978872984e+19, + 2.4283241432347005e+19, + 2.42519355107831e+19, + 2.422090084365806e+19, + 2.419013508096743e+19, + 2.415963589305566e+19, + 2.4129400970434146e+19, + 2.4099428023610917e+19, + 2.406971478291697e+19, + 2.4040258998335894e+19, + 2.4011058439331717e+19, + 2.3982110894683005e+19, + 2.395341417231851e+19, + 2.0670269989725e+19 + ], + "vtor": [ + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10, + 1e-10 + ], + "vpol": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/hydrogen0.json b/cherab/generomak/plasma/data/core/hydrogen0.json new file mode 100644 index 00000000..754940c0 --- /dev/null +++ b/cherab/generomak/plasma/data/core/hydrogen0.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2240.0, + 2236.4504489807964, + 2226.309382068473, + 2210.299052071184, + 2189.0904205810994, + 2163.3046865525707, + 2133.515194552534, + 2100.249595432215, + 2063.992162943028, + 2025.1861932135298, + 1984.2364319190758, + 1941.5114877758033, + 1897.3462016693657, + 1852.0439490190533, + 1805.87885942599, + 1759.0979426720367, + 1711.9231140372276, + 1664.5531149286294, + 1617.1653271484834, + 1569.917480919263, + 1522.949258141365, + 1476.3837933749978, + 1430.3290757822574, + 1384.8792557946174, + 1340.1158606300705, + 1296.1089230088292, + 1252.9180275356005, + 1210.5932792532092, + 1169.1761988453034, + 1128.7005488900284, + 1089.193095453988, + 1050.674309176122, + 1013.1590098321976, + 976.6569581985458, + 941.1733988535289, + 906.7095573708474, + 873.2630951733505, + 840.828525131861, + 809.3975908125881, + 778.9596121003383, + 749.5017997540461, + 721.0095412868372, + 693.4666604055054, + 666.8556520941395, + 641.1578952839868, + 616.3538449164151, + 592.4232050780837, + 569.3450847669792, + 547.0981377346717, + 525.6606877437563, + 505.0108404797533, + 485.1265832634224, + 465.9858736222754, + 447.56671769870127, + 429.8472393962903, + 412.8057410953542, + 396.4207567029529, + 380.6710977417666, + 365.53589312550383, + 350.9946232160131, + 337.02714870861604, + 323.61373484709463, + 310.7350714280776, + 298.37228901601867, + 286.50697175433055, + 275.12116712535635, + 264.1973929815059, + 253.7186421419, + 243.66838482307517, + 234.0305691485461, + 224.78961996016457, + 215.93043613409628, + 207.4383865857546, + 199.29930513103795, + 191.4994843556246, + 184.02566862976738, + 176.86504639289873, + 170.00524182033894, + 163.43430597337053, + 157.14070752386905, + 151.113323135452, + 145.34142757467652, + 139.81468361812196, + 134.52313181416025, + 129.457180151816, + 124.60759368327163, + 119.96548414126156, + 115.52229958776202, + 111.26981412599169, + 107.20011770374389, + 103.30560603245968, + 99.57897064317038, + 96.01318909747529, + 92.60151536903889, + 89.33747040868218, + 86.21483290395201, + 83.22763024211139, + 80.37012968371364, + 77.63682975236918, + 75.02245184488561, + 72.52193206471382, + 70.13041328051168, + 67.84323741064236, + 65.6559379335423, + 63.564232623128014, + 61.56401650771693, + 59.65135505035933, + 57.82247754794227, + 56.07377074599886, + 54.40177266575983, + 52.80316663966101, + 51.27477555125246, + 49.81355627521912, + 48.416594313043035, + 47.081098619687594, + 45.80439661656459, + 44.5839293859712, + 43.417247042115314, + 42.302004273818525, + 41.23595605397633, + 40.216953510850516, + 39.24293995630613, + 38.311947066125406, + 37.422091207594626, + 36.571569909608, + 35.75865847061232, + 34.98170669978367, + 34.23913578692253, + 33.52943529662951, + 32.85116028243641, + 32.20292851664732, + 31.583417831760713, + 30.99136356943732, + 30.425556133091256, + 29.88483864028179, + 29.36810467119799, + 28.87429610962729, + 28.40240107291426, + 27.951451927516537, + 27.52052338687281, + 27.108730688404705, + 26.715227846572123, + 26.33920597900726, + 25.979891702853777, + 25.636545598528727, + 25.30846073823038, + 24.994961276600144, + 24.69540110104437, + 24.409162539308983, + 24.13565512198808, + 23.874314397731172, + 23.624600798996898, + 23.385998556284218, + 23.15801465884384, + 22.940177859954957, + 22.73203772492011, + 22.533163720006204, + 22.343144340625674, + 22.161586277118445, + 21.988113616562465, + 21.822367079097155, + 21.664003287311004, + 21.51269406729655, + 21.36812578003317, + 21.229998681815967, + 21.098026312495502, + 20.971934910344643, + 20.851462852421577, + 20.736360119335554, + 20.626387783375975, + 20.521317519002128, + 20.420931134733753, + 20.32502012552446, + 20.233385244735498, + 20.145836094864723, + 20.06219073622283, + 19.982275312781507, + 19.90592369444775, + 19.832977135057188, + 19.763283945401255, + 19.696699180637957, + 19.633084341458513, + 19.572307088415513, + 19.51424096883524, + 19.45876515576827, + 19.40576419845266, + 19.355127783786383, + 19.30675050832906, + 19.26053166037145, + 19.21637501163307, + 19.174188618164745, + 19.133884630054332, + 19.09537910954868, + 19.05859185722216, + 19.023446245839033, + 18.989869061570744, + 18.957790352245382, + 18.927143282320497, + 18.897863994281074, + 18.86989147618248, + 18.843167435065453, + 18.817636175985687, + 18.79324448640893, + 18.769941525736378, + 18.747678719732868, + 18.726409659641607, + 18.706090005778155, + 18.686677395406114, + 18.668131354704837, + 18.650413214648612, + 18.633486030623327, + 18.617314505615823, + 18.60186491681852, + 18.587105045495814, + 18.573004109970697, + 18.55953270159014, + 18.546662723539878, + 18.53436733238102, + 18.522620882187194, + 18.51139887116874, + 18.500677890671284, + 18.49043557644487, + 18.480650562082833, + 18.471302434532834, + 18.46237169158913, + 18.453839701278447, + 18.445688663053495, + 18.437901570715436, + 18.430462176988556, + 18.423354959672093, + 18.416565089300423, + 18.41007839824483, + 18.403881351191185, + 18.39796101693386, + 18.392305041426237, + 18.386901622033136, + 18.381739482930183, + 18.376807851599832, + 18.372096436376008, + 18.367595404988766, + 18.36329536406628, + 18.359187339550697, + 18.355262757987074, + 18.351513428646914, + 18.34793152644875, + 18.344509575640426, + 18.341240434209354, + 18.338117278988225, + 18.335133591424395, + 18.3322831439848, + 18.329559987167194, + 18.326958437090312, + 18.324473063638234, + 18.32209867913344, + 18.319830327515373, + 18.271305784128668 + ], + "density": [ + 204620001816.96503, + 203995340626.48117, + 203580415549.07077, + 203478214247.57297, + 203715877183.5095, + 204301348415.06805, + 205234420738.4918, + 206511241598.7883, + 208126529002.83133, + 210074752469.19244, + 212350782448.41467, + 214950246524.57938, + 217869717181.2876, + 221106800859.7011, + 224660168985.34265, + 228529555375.00076, + 232715734933.54498, + 237220492826.1153, + 242046589785.09818, + 247197727005.0989, + 252678512684.92447, + 258494431389.8997, + 264651816845.81558, + 271157828420.81525, + 278020413999.39655, + 285247320715.9104, + 292847439717.963, + 300831346075.9571, + 309210473286.05414, + 317997118815.822, + 327204455041.3841, + 336846543548.0578, + 346938352650.2804, + 357495722378.11005, + 368531748603.2294, + 380060992884.0296, + 392101369277.8834, + 404671758538.71185, + 417792030624.14746, + 431483075722.2703, + 445766837408.0821, + 460666347857.28577, + 476205765045.6582, + 492411668158.4271, + 509310082743.527, + 526922958785.0509, + 545270668436.08124, + 564382532826.562, + 584289351347.6514, + 605023347803.2103, + 626618224577.489, + 649109218441.2275, + 672533157882.3363, + 696928521829.5643, + 722330392907.0331, + 748761752833.0225, + 776263316106.7876, + 804879690534.3215, + 834657513313.9681, + 865645526272.6191, + 897894651906.1387, + 931458069962.6783, + 966390478339.3801, + 1002713570323.5283, + 1040458926521.3761, + 1079684516540.4954, + 1120450776084.2368, + 1162820677057.6262, + 1206859796754.8408, + 1252636384433.044, + 1300221424799.789, + 1349688697963.7998, + 1401114835449.2217, + 1454579371952.6218, + 1510164792644.5955, + 1567952481060.059, + 1627931114636.4526, + 1690139925687.1553, + 1754669353636.4456, + 1821613533247.6384, + 1891070371220.4712, + 1963141624989.376, + 2037932986753.0464, + 2115554176680.7764, + 2196119050183.666, + 2279748776152.387, + 2366500641895.066, + 2456287369484.3516, + 2549234395774.7485, + 2645490036768.047, + 2745210502439.3457, + 2848560231576.2715, + 2955712221053.107, + 3066848216768.975, + 3181983423138.0234, + 3300978418418.4097, + 3423964172116.361, + 3551081532136.3022, + 3682471863828.1035, + 3818274934361.4946, + 3958626240909.5186, + 4103653876103.972, + 4253475165354.2246, + 4408193462719.133, + 4567895603650.418, + 4732650517791.943, + 4902247847002.056, + 5076233402000.4795, + 5254598646625.475, + 5437349446630.464, + 5624489405383.76, + 5816019959574.812, + 6011939258098.727, + 6212239938237.438, + 6416906050351.422, + 6625909469387.242, + 6839206174537.927, + 7056732786183.625, + 7278311975421.752, + 7503119857298.018, + 7730869714380.9375, + 7961427820158.273, + 8194643345174.029, + 8430349935940.838, + 8668367358140.194, + 8908503068344.902, + 9150553621597.744, + 9394305867677.299, + 9639552766370.97, + 9886049104086.564, + 10133557734495.324, + 10381834826485.693, + 10630630439847.912, + 10879678469431.771, + 11128195788042.75, + 11375596059217.254, + 11621622648260.379, + 11866019968617.504, + 12108534650895.979, + 12348916749575.322, + 12586920956579.773, + 12822307795224.22, + 13054844772251.508, + 13284307469611.012, + 13510480561149.14, + 13733158742539.867, + 13952147565575.44, + 14167264170431.275, + 14378337911729.918, + 14585210876204.7, + 14787738291529.992, + 14985788827443.982, + 15179244791671.332, + 15368002224351.652, + 15551970895709.932, + 15731074212570.957, + 15905249040010.746, + 16074445444998.629, + 16238626369271.07, + 16397767238935.273, + 16551855518444.57, + 16700890216574.428, + 16844881351954.912, + 16983849385509.26, + 17117824626881.824, + 17246846621603.266, + 17370963525334.229, + 17490231471110.625, + 17604713935017.176, + 17714481105258.8, + 17819608453161.367, + 17920177950565.527, + 18016276628873.59, + 18107995459063.83, + 18195429156535.652, + 18278675646852.23, + 18357835555954.918, + 18433011726092.445, + 18504308758387.65, + 18571832582598.63, + 18635690054375.332, + 18695988580024.676, + 18752835768579.754, + 18806339110761.88, + 18856605684254.773, + 18903741884561.38, + 18947853180595.484, + 18989043894059.926, + 19027417001587.02, + 19063073958565.055, + 19096114543521.96, + 19126636721924.43, + 19154736528236.87, + 19180507965077.684, + 19204042918329.66, + 19225431087078.62, + 19244759927277.656, + 19262114608067.395, + 19277577979726.01, + 19291230552256.516, + 19303150483665.746, + 19313413577033.516, + 19322093285526.824, + 19329260724543.79, + 19334984690237.266, + 19339331683709.87, + 19342365940211.094, + 19344149462728.09, + 19344742059393.53, + 19344201384180.91, + 19342582980399.023, + 19339940326544.8, + 19336324363046.41, + 19331784091828.13, + 19326368145671.793, + 19320122234851.223, + 19313090256097.117, + 19305314345120.19, + 19296834929279.25, + 19287690780209.77, + 19277919066210.582, + 19267555404250.375, + 19256633911438.43, + 19245187255846.457, + 19233246706569.15, + 19220842182934.86, + 19208002302790.24, + 19194754429784.965, + 19181124719614.805, + 19167138165162.6, + 19152818640516.805, + 19138188943830.562, + 19123270839007.04, + 19108085096194.83, + 19092651531087.504, + 19076989043026.625, + 19061115651909.043, + 19045048533903.996, + 19028804056001.64, + 19012397809386.355, + 18995844641670.94, + 18979158687998.574, + 18962353401040.9, + 18945441579905.883, + 18928435397987.637, + 18911346429777.703, + 18894185676666.883, + 18876963591763.613, + 18859690103748.47, + 18842374639807.22, + 18825026147656.855, + 18807653116696.703, + 18790263598309.477, + 18772865225350.633, + 18755465230832.047, + 16384843817946.543 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "hydrogen", + "charge": 0 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/hydrogen1.json b/cherab/generomak/plasma/data/core/hydrogen1.json new file mode 100644 index 00000000..a23d3c11 --- /dev/null +++ b/cherab/generomak/plasma/data/core/hydrogen1.json @@ -0,0 +1,1294 @@ +{ + "temperature": [ + 2800.0, + 2795.6140361201715, + 2783.083337747409, + 2763.3003486616526, + 2737.094135213008, + 2705.2322750926364, + 2668.4232152414806, + 2627.3189416606197, + 2582.5178419123567, + 2534.5676700076638, + 2483.96854551181, + 2431.1759357517954, + 2376.6035832043513, + 2320.6263503869714, + 2263.5829625419237, + 2205.7786346035323, + 2147.4875737595085, + 2088.955352654985, + 2030.4011511730957, + 1972.019866937452, + 1913.9840963599631, + 1856.4459893126868, + 1799.5389814222144, + 1743.3794086390585, + 1688.0680091781107, + 1633.691318203845, + 1580.3229607811295, + 1528.0248486579108, + 1476.8482864126447, + 1426.8349924056063, + 1378.018039834137, + 1330.4227230192646, + 1284.0673538547676, + 1238.963993137122, + 1195.1191212721988, + 1152.5342526267314, + 1111.2064975634503, + 1071.1290759712253, + 1032.291785877979, + 994.6814305162284, + 958.2822070001829, + 923.0760595703174, + 889.0430001669228, + 856.1613989086177, + 824.4082468755387, + 793.759393429837, + 764.1897601482588, + 735.6735332927431, + 708.1843366049751, + 681.6953860793737, + 656.1796282458071, + 631.6098633780208, + 607.9588549360606, + 585.1994264504128, + 563.3045469619041, + 542.2474060441687, + 522.0014793543342, + 502.5405855822321, + 483.8389355984452, + 465.8711745366052, + 448.61241748523827, + 432.0382794087496, + 416.124899865624, + 400.8489630442867, + 386.1877135930409, + 372.11896867986945, + 358.6211266803817, + 345.67317285760663, + 333.2546823654665, + 321.34582087841227, + 309.927343122692, + 298.9805895598673, + 288.4874814503533, + 278.4305145037626, + 268.79275130356683, + 259.5578126759042, + 250.7098681561356, + 242.23362569190442, + 234.11432070782254, + 226.33770464446184, + 218.89003307292606, + 211.75805347586066, + 204.92899277624957, + 198.39054468665884, + 192.13085694367678, + 186.13851848507585, + 180.40254662065936, + 174.91237424177845, + 169.65783710907806, + 164.62916125309604, + 159.8169505178761, + 155.21217427370195, + 150.80615532139907, + 146.59055800733688, + 142.55737656528729, + 138.69892369858735, + 135.00781941365733, + 131.47698011372418, + 128.09960795968092, + 124.86918050324817, + 121.77944059606116, + 118.82438657692074, + 115.99826273821829, + 113.29555007145385, + 110.71095729082082, + 108.23941213297381, + 105.87605293038021, + 103.6162204549953, + 101.45545002847112, + 99.38946389462029, + 97.41416384945612, + 95.52562412379939, + 93.72008451315051, + 91.99394374930452, + 90.34375310799985, + 88.7662102467457, + 87.25815326688075, + 85.81655499383113, + 84.43851746950027, + 83.12126665071017, + 81.86214730760918, + 80.65861811600642, + 79.5082469376184, + 78.408706282292, + 77.35776894632689, + 76.35330382111951, + 75.39327186643368, + 74.47572224272074, + 73.59878859700572, + 72.76068549699494, + 71.95970500815942, + 71.19421340869229, + 70.46264803735373, + 69.7635142693571, + 69.0953826155724, + 68.45688594046604, + 67.84671679431725, + 67.26362485539421, + 66.70641447789761, + 66.17394234161189, + 65.6651151993376, + 65.1788877182984, + 64.7142604118468, + 64.27027765791827, + 63.84602580079542, + 63.440631332874815, + 63.05325915323381, + 62.68311089991544, + 62.32942335295714, + 61.9914669052981, + 61.66854409880293, + 61.359988222742814, + 61.065161972176995, + 60.78345616376737, + 60.51428850665944, + 60.2571024261477, + 60.01136593793548, + 59.776570570881596, + 59.55223033620793, + 59.33788074122536, + 59.13307784570519, + 58.937397359105915, + 58.75043377692946, + 58.571799554551646, + 58.40112431694312, + 58.23805410275451, + 58.08225064130303, + 57.93339066106313, + 57.7911652283102, + 57.65527911463285, + 57.525450192074885, + 57.40140885472106, + 57.28289746559213, + 57.16966982775895, + 57.06149067863122, + 56.95813520642253, + 56.85938858783372, + 56.765045546033065, + 56.674909928059456, + 56.588794300802036, + 56.50651956475268, + 56.427914584755875, + 56.35281583702141, + 56.281067071686564, + 56.21251899025332, + 56.14702893725086, + 56.08446060550164, + 56.02468375439783, + 55.96757394061782, + 55.91301226073971, + 55.8608851052287, + 55.81108392330204, + 55.76350499819346, + 55.718049232360165, + 55.67462194219673, + 55.63313266183696, + 55.593494955645, + 55.55562623901411, + 55.51944760710454, + 55.484883671174465, + 55.45186240216659, + 55.42031498123234, + 55.390175656886, + 55.361381608497936, + 55.333872815845915, + 55.307591934457406, + 55.282484176486456, + 55.258497196881336, + 55.23558098460842, + 55.21368775870917, + 55.19277186897538, + 55.17278970103895, + 55.153699585681665, + 55.13546171217539, + 55.11803804547773, + 55.101392247108336, + 55.08548959954608, + 55.070296933989404, + 55.05578256132999, + 55.04191620619953, + 55.028668943950365, + 55.01601314044173, + 55.003922394507015, + 54.99237148298145, + 54.98133630817787, + 54.970793847702375, + 54.96072210650347, + 54.951100071057624, + 54.941907665596375, + 54.93312571028218, + 54.9247358812481, + 54.91672067241873, + 54.90906335903125, + 54.90174796278291, + 54.8947592185312, + 54.888082542479076, + 54.881704001777535, + 54.87561028548313, + 54.869788676811275, + 54.86422702662521, + 54.858913728107794, + 54.85383769256265, + 54.84898832629396, + 54.8443555085177, + 54.83992957025756, + 54.83570127418218, + 54.83166179534185, + 54.82780270276455, + 54.824115941872016, + 54.82059381768116, + 54.81722897875463, + 54.81401440186672, + 54.81094337735412, + 54.80800949511986, + 54.80520663126194, + 54.7452478101525 + ], + "density": [ + 4.7000161116838035e+19, + 4.682128787215119e+19, + 4.662255270837901e+19, + 4.6417278208422126e+19, + 4.620855584730208e+19, + 4.5997877921630495e+19, + 4.5786126032580125e+19, + 4.557388001473378e+19, + 4.536154779862328e+19, + 4.514942993087138e+19, + 4.4937755363011396e+19, + 4.472670293288807e+19, + 4.451641505013135e+19, + 4.430700683219043e+19, + 4.409857243769369e+19, + 4.38911895956297e+19, + 4.368492293000609e+19, + 4.347982645523464e+19, + 4.327594548538098e+19, + 4.3073318119596106e+19, + 4.287197641493933e+19, + 4.267194732453969e+19, + 4.247325345683865e+19, + 4.227591369649927e+19, + 4.207994371652285e+19, + 4.18853563809563e+19, + 4.169216213518003e+19, + 4.150036932127918e+19, + 4.130998441718659e+19, + 4.1121012257462985e+19, + 4.093345622347993e+19, + 4.074731840811801e+19, + 4.056259975913072e+19, + 4.037930020368103e+19, + 4.019741870571682e+19, + 4.001695340964126e+19, + 3.983790175600905e+19, + 3.966026052904146e+19, + 3.948402592421399e+19, + 3.930919360878721e+19, + 3.91357587761679e+19, + 3.896371619490987e+19, + 3.879306025305037e+19, + 3.862378038885082e+19, + 3.8455872756913046e+19, + 3.828933304946574e+19, + 3.812415434913541e+19, + 3.796032969592534e+19, + 3.779785195844539e+19, + 3.763671385931872e+19, + 3.747690800145458e+19, + 3.731842689377711e+19, + 3.716126297679983e+19, + 3.700540864846064e+19, + 3.6850856060966724e+19, + 3.6697596750521393e+19, + 3.6545622931847754e+19, + 3.63949269560757e+19, + 3.624550126620646e+19, + 3.6097338433711747e+19, + 3.5950431200155054e+19, + 3.580477252521384e+19, + 3.5660355599881843e+19, + 3.551717212816437e+19, + 3.5375214199829737e+19, + 3.523447525545721e+19, + 3.509494910531205e+19, + 3.4956630001189454e+19, + 3.4819512721075306e+19, + 3.4683592669405524e+19, + 3.4548865996283527e+19, + 3.44153297397094e+19, + 3.4282981995701944e+19, + 3.4151822122170814e+19, + 3.402185098353278e+19, + 3.389307085841355e+19, + 3.3765477248415257e+19, + 3.36390706526775e+19, + 3.3513857808022893e+19, + 3.3389848457318638e+19, + 3.326705595137885e+19, + 3.3145497973701267e+19, + 3.3025197409857077e+19, + 3.2906183384364196e+19, + 3.27884924868037e+19, + 3.2672168857369743e+19, + 3.2557253870071173e+19, + 3.2443771047542505e+19, + 3.233178288004695e+19, + 3.222136754653978e+19, + 3.2112618260038386e+19, + 3.2005644930339512e+19, + 3.190057544359449e+19, + 3.179755610169293e+19, + 3.169667413331649e+19, + 3.1597941789312086e+19, + 3.1501468231219503e+19, + 3.1407358392679612e+19, + 3.1315701402899493e+19, + 3.122655976363501e+19, + 3.113995725177193e+19, + 3.1055866895317287e+19, + 3.0974201050364137e+19, + 3.0894806075100783e+19, + 3.0817464043937587e+19, + 3.074190310163784e+19, + 3.0667602748084875e+19, + 3.059388912084078e+19, + 3.0520502154565943e+19, + 3.0447256542588035e+19, + 3.0374032039505535e+19, + 3.0300769320810455e+19, + 3.0227459649422733e+19, + 3.0154130399978762e+19, + 3.0080828724751536e+19, + 3.00076055212318e+19, + 2.9934501515846365e+19, + 2.986153682499915e+19, + 2.9788666487587082e+19, + 2.9715601389175697e+19, + 2.9642293974170833e+19, + 2.9568746794147893e+19, + 2.9494953117879144e+19, + 2.942090418993194e+19, + 2.9346594991144505e+19, + 2.927202828423032e+19, + 2.919721694348071e+19, + 2.912218473740004e+19, + 2.9046961655055204e+19, + 2.897159522535346e+19, + 2.889613573005297e+19, + 2.8820638275299803e+19, + 2.8745160581640344e+19, + 2.866976018562403e+19, + 2.859446819509753e+19, + 2.8519338651384783e+19, + 2.8444437402203193e+19, + 2.8369821546424705e+19, + 2.8295540143670977e+19, + 2.822163499192403e+19, + 2.8148141418240123e+19, + 2.8075089045501837e+19, + 2.800250251202477e+19, + 2.7930402131116364e+19, + 2.785880448496708e+19, + 2.7787722952184242e+19, + 2.7717168171436e+19, + 2.764714844554494e+19, + 2.757767009134292e+19, + 2.750873774095989e+19, + 2.744035460018579e+19, + 2.7372522669268402e+19, + 2.730524293109751e+19, + 2.7238515511248703e+19, + 2.7172339813867467e+19, + 2.710671463689212e+19, + 2.704163826966439e+19, + 2.697710857556336e+19, + 2.6913123061929193e+19, + 2.684967893921741e+19, + 2.678677317103723e+19, + 2.6724402516482183e+19, + 2.666256356594527e+19, + 2.6601252771429528e+19, + 2.6540466472208925e+19, + 2.648020091656107e+19, + 2.642045228018025e+19, + 2.6361216681785897e+19, + 2.6302490196357964e+19, + 2.6244268866366194e+19, + 2.6186547167773954e+19, + 2.6129321911080337e+19, + 2.607258987087877e+19, + 2.6016347040942785e+19, + 2.596058941784374e+19, + 2.5905313005155705e+19, + 2.585051381702677e+19, + 2.5796187881207525e+19, + 2.5742331241620062e+19, + 2.568893996053235e+19, + 2.563601012039743e+19, + 2.5583537825404264e+19, + 2.5531519202783388e+19, + 2.5479950403901665e+19, + 2.5428827605176574e+19, + 2.5378147008834527e+19, + 2.5327904843536536e+19, + 2.5278097364889854e+19, + 2.522872085586001e+19, + 2.5179771627098292e+19, + 2.5131246017196376e+19, + 2.5083140392876634e+19, + 2.5035451149129798e+19, + 2.498817470930402e+19, + 2.4941307525154173e+19, + 2.4894846076854927e+19, + 2.484878687298473e+19, + 2.4803126450483405e+19, + 2.475786137458537e+19, + 2.4712988238736052e+19, + 2.466850366448876e+19, + 2.462440430138878e+19, + 2.4580686826843484e+19, + 2.4537347945982157e+19, + 2.4494384391507644e+19, + 2.445179292353768e+19, + 2.4409570329441386e+19, + 2.4367713423668306e+19, + 2.432621904757395e+19, + 2.428508406924054e+19, + 2.4244305383293637e+19, + 2.420387991071655e+19, + 2.4163804063775474e+19, + 2.4124074299375776e+19, + 2.4084688695258206e+19, + 2.4045644275574723e+19, + 2.4006938089881907e+19, + 2.3968567212948713e+19, + 2.393052874455778e+19, + 2.3892819809314083e+19, + 2.3855437556447207e+19, + 2.3818379159616664e+19, + 2.378164181671946e+19, + 2.374522274969222e+19, + 2.3709119204319965e+19, + 2.3673328450042348e+19, + 2.3637847779761213e+19, + 2.3602674509648167e+19, + 2.3567805978955555e+19, + 2.3533239549825778e+19, + 2.349897260710105e+19, + 2.346500255813818e+19, + 2.3431326832619577e+19, + 2.3397942882369196e+19, + 2.3364848181166285e+19, + 2.333204022456342e+19, + 2.3299516529704546e+19, + 2.326727463514042e+19, + 2.3235312100654547e+19, + 2.320362650708015e+19, + 2.3172215456123494e+19, + 2.3141076570189193e+19, + 2.3110207492203815e+19, + 2.3079605885444526e+19, + 2.3049269433364124e+19, + 2.3019195839420236e+19, + 2.2989382826907095e+19, + 2.295982813878743e+19, + 2.293052953752237e+19, + 2.290148480490682e+19, + 2.287269174190705e+19, + 2.2844148168495403e+19, + 2.2815851923484787e+19, + 2.278780086437519e+19, + 2.2759992867188072e+19, + 1.9578429198926516e+19 + ], + "vtor": [ + 100000.0, + 99544.32023364124, + 98251.15453696062, + 96235.68685722632, + 93614.68356191639, + 90502.18847954353, + 87006.32501749854, + 83227.06508922808, + 79254.813872946, + 75169.66123127713, + 71041.16042746486, + 66928.50919629741, + 62881.02482158773, + 58938.821939433255, + 55133.61820859918, + 51489.60808192722, + 48024.35830839606, + 44749.69035052268, + 41672.524623402256, + 38795.66945343124, + 36118.54407642795, + 33637.83003375026, + 31348.04917610031, + 29242.06933764292, + 27311.54077408969, + 25547.26782590489, + 23939.521110407513, + 22478.295982958116, + 21153.523137258686, + 19955.237120191036, + 18873.708284120235, + 17899.54334214529, + 17023.759270761642, + 16237.834851443324, + 15533.743681380842, + 14903.972031120154, + 14341.524495030524, + 13839.919977172622, + 13393.180184865832, + 12995.812467288095, + 12642.788537234375, + 12329.520349892675, + 12051.83418147994, + 11805.94375056095, + 11588.423053275488, + 11396.179437797053, + 11226.42732039882, + 11076.662842842592, + 10944.639685904545, + 10828.34618435543, + 10725.983832469563, + 10635.94722420132, + 10556.805436804136, + 10487.284839339893, + 10426.253286892172, + 10372.705646187464, + 10325.750587737686, + 10284.598572685094, + 10248.550958525679, + 10216.990146193662, + 10189.370691092356, + 10165.211302127113, + 10144.087655281786, + 10125.625951492917, + 10109.49715228246, + 10095.411830623676, + 10083.115578687914, + 10072.384918337477, + 10063.023664403154, + 10054.85969484832, + 10047.742085826912, + 10041.538573356505, + 10036.133306828673, + 10031.424862854412, + 10027.3244909875, + 10023.75456568367, + 10020.647221443161, + 10017.943150456194, + 10015.59054423511, + 10013.544162684711, + 10011.764515845945, + 10010.217145160046, + 10008.871992553453, + 10007.702846950711, + 10006.686858995241, + 10005.804115808194, + 10005.037268554323, + 10004.371206421612, + 10003.792771367725, + 10003.29050865027, + 10002.85444874773, + 10002.475916801126, + 10002.14736617021, + 10001.86223310832, + 10001.614809922945, + 10001.400134309408, + 10001.213892828007, + 10001.052336744104, + 10000.912208670363, + 10000.790678643563, + 10000.68528843837, + 10000.593903069897, + 10000.514668568, + 10000.445975221452, + 10000.386425591105, + 10000.334806679686, + 10000.290065723417, + 10000.25128913859, + 10000.217684215675, + 10000.188563205464, + 10000.16332948734, + 10000.141465549392, + 10000.122522544827, + 10000.106111219531, + 10000.091894031972, + 10000.079578309811, + 10000.068910307727, + 10000.05967004848, + 10000.051666844598, + 10000.044735411424, + 10000.038732493818, + 10000.033533939033, + 10000.029032157028, + 10000.02513391718, + 10000.021758437058, + 10000.018835724715, + 10000.016305140998, + 10000.01411415281, + 10000.012217252064, + 10000.010575018385, + 10000.009153306504, + 10000.007922541854, + 10000.00685710995, + 10000.005934827166, + 10000.005136482061, + 10000.004445437868, + 10000.003847288048, + 10000.003329557814, + 10000.002881445527, + 10000.00249359863, + 10000.002157919536, + 10000.001867397486, + 10000.001615962863, + 10000.001398361033, + 10000.001210043049, + 10000.001047070993, + 10000.000906036008, + 10000.000783987294, + 10000.000678370623, + 10000.000586975104, + 10000.000507887078, + 10000.000439450198, + 10000.000380230858, + 10000.000328988277, + 10000.000284648579, + 10000.000246282361, + 10000.000213085277, + 10000.00018436122, + 10000.000159507772, + 10000.000138003594, + 10000.000119397531, + 10000.000103299173, + 10000.000089370667, + 10000.000077319659, + 10000.000066893168, + 10000.000057872281, + 10000.000050067574, + 10000.00004331514, + 10000.00003747315, + 10000.000032418886, + 10000.000028046165, + 10000.00002426311, + 10000.000020990228, + 10000.000018158735, + 10000.00001570912, + 10000.000013589894, + 10000.000011756505, + 10000.00001017041, + 10000.00000879826, + 10000.000007611205, + 10000.00000658428, + 10000.000005695889, + 10000.000004927346, + 10000.000004262487, + 10000.000003687326, + 10000.000003189763, + 10000.000002759334, + 10000.000002386978, + 10000.000002064864, + 10000.000001786213, + 10000.00000154516, + 10000.000001336637, + 10000.00000115625, + 10000.000001000204, + 10000.000000865217, + 10000.000000748445, + 10000.000000647431, + 10000.00000056005, + 10000.000000484462, + 10000.000000419075, + 10000.000000362512, + 10000.000000313583, + 10000.000000271257, + 10000.000000234644, + 10000.000000202972, + 10000.000000175574, + 10000.000000151877, + 10000.000000131377, + 10000.000000113641, + 10000.000000098302, + 10000.000000085032, + 10000.000000073554, + 10000.000000063626, + 10000.000000055037, + 10000.000000047608, + 10000.000000041182, + 10000.000000035621, + 10000.000000030814, + 10000.000000026654, + 10000.000000023056, + 10000.000000019943, + 10000.000000017251, + 10000.000000014923, + 10000.000000012908, + 10000.000000011165, + 10000.000000009659, + 10000.000000008355, + 10000.000000007227, + 10000.00000000625, + 10000.000000005406, + 10000.000000004677, + 10000.000000004045, + 10000.0000000035, + 10000.000000003027, + 10000.000000002618, + 10000.000000002265, + 10000.000000001959, + 10000.000000001695, + 10000.000000001466, + 10000.000000001268, + 10000.000000001097, + 10000.00000000095, + 10000.00000000082, + 10000.00000000071, + 10000.000000000615, + 10000.000000000531, + 10000.00000000046, + 10000.000000000397, + 10000.000000000344, + 10000.000000000296, + 10000.000000000256, + 10000.000000000222, + 10000.000000000193, + 10000.000000000167, + 10000.000000000144, + 10000.0 + ], + "vpol": [ + 18462.326927732716, + 18514.99979366994, + 18565.939231880784, + 18615.197379613106, + 18662.8251518226, + 18708.87224398403, + 18753.38713728863, + 18796.417106004, + 18838.008226787257, + 18878.20538975573, + 18917.052311132516, + 18954.591547296637, + 18990.864510079347, + 19025.91148315922, + 19059.7716394193, + 19092.483059139577, + 19124.08274890752, + 19154.60666113828, + 19184.089714104663, + 19212.565812384706, + 19240.067867642374, + 19266.627819663598, + 19292.27665757659, + 19317.044441191432, + 19340.960322399642, + 19364.052566579816, + 19386.34857396039, + 19407.874900895215, + 19428.65728101207, + 19448.720646198086, + 19468.08914739, + 19486.78617514046, + 19504.834379934928, + 19522.2556922367, + 19539.07134224024, + 19555.301879315663, + 19570.96719112952, + 19586.08652242914, + 19600.678493479918, + 19614.761118146518, + 19628.351821610984, + 19641.467457721887, + 19654.124325970388, + 19666.33818809011, + 19678.124284279074, + 19689.497349042842, + 19700.47162665909, + 19711.060886264648, + 19721.278436566783, + 19731.137140181316, + 19740.649427600623, + 19749.827310795343, + 19758.68239645382, + 19767.225898864108, + 19775.4686524434, + 19783.421123920307, + 19791.093424175622, + 19798.49531974744, + 19805.63624400677, + 19812.52530800986, + 19819.171311033715, + 19825.582750801284, + 19831.76783340299, + 19837.73448292125, + 19843.49035076469, + 19849.042824718887, + 19854.399037720275, + 19859.565876360022, + 19864.549989124593, + 19869.357794379575, + 19873.99548810352, + 19878.469051378208, + 19882.78425764191, + 19886.946679712015, + 19890.961696583312, + 19894.83450000816, + 19898.570100864654, + 19902.173335318803, + 19905.64887078662, + 19909.00121170188, + 19912.234705095303, + 19915.353545990587, + 19918.361782622906, + 19921.263321485007, + 19924.061932206263, + 19926.76125226966, + 19929.36479157172, + 19931.875936830187, + 19934.297955844206, + 19936.634001611572, + 19938.887116307546, + 19941.060235129582, + 19943.156190012254, + 19945.17771321644, + 19947.12744079687, + 19949.00791595186, + 19950.821592259108, + 19952.570836801166, + 19954.25793318425, + 19955.885084453774, + 19957.454415910102, + 19958.967977827684, + 19960.427748080827, + 19961.835634679162, + 19963.193478215824, + 19964.503054231183, + 19965.76607549503, + 19966.984194209857, + 19968.159004137942, + 19969.292042654735, + 19970.384792731096, + 19971.438684846682, + 19972.455098836952, + 19973.435365675872, + 19974.380769196654, + 19975.29254775252, + 19976.1718958196, + 19977.019965543906, + 19977.83786823432, + 19978.6266758034, + 19979.38742215782, + 19980.121104540183, + 19980.828684823875, + 19981.511090762506, + 19982.169217195635, + 19982.80392721215, + 19983.41605327286, + 19984.006398293674, + 19984.575736690727, + 19985.124815388765, + 19985.654354794107, + 19986.165049733347, + 19986.657570359053, + 19987.132563023555, + 19987.590651121973, + 19988.03243590553, + 19988.458497266216, + 19988.869394493737, + 19989.265667005842, + 19989.647835052798, + 19990.01640039704, + 19990.371846968832, + 19990.714641498744, + 19991.045234127785, + 19991.36405899601, + 19991.67153481026, + 19991.968065391884, + 19992.254040205054, + 19992.52983486641, + 19992.795811636643, + 19993.05231989471, + 19993.299696595244, + 19993.538266709777, + 19993.768343652326, + 19993.9902296899, + 19994.2042163385, + 19994.410584745023, + 19994.60960605568, + 19994.801541771343, + 19994.986644090288, + 19995.165156238767, + 19995.33731278991, + 19995.503339971237, + 19995.66345596134, + 19995.817871175987, + 19995.96678854407, + 19996.110403773815, + 19996.248905609486, + 19996.38247607898, + 19996.511290732687, + 19996.6355188738, + 19996.75532378048, + 19996.870862920143, + 19996.98228815611, + 19997.089745946923, + 19997.1933775386, + 19997.29331915003, + 19997.389702151813, + 19997.482653238705, + 19997.572294595982, + 19997.65874405985, + 19997.74211527218, + 19997.822517829743, + 19997.90005742811, + 19997.974836000485, + 19998.046951851553, + 19998.11649978661, + 19998.183571236077, + 19998.248254375594, + 19998.31063424184, + 19998.370792844253, + 19998.42880927274, + 19998.4847598016, + 19998.538717989708, + 19998.590754777182, + 19998.64093857857, + 19998.68933537273, + 19998.736008789543, + 19998.78102019351, + 19998.824428764387, + 19998.866291574945, + 19998.906663665974, + 19998.945598118597, + 19998.983146124054, + 19999.019357050955, + 19999.054278510157, + 19999.087956417356, + 19999.120435053414, + 19999.151757122556, + 19999.181963808518, + 19999.21109482865, + 19999.239188486128, + 19999.26628172031, + 19999.292410155274, + 19999.317608146655, + 19999.34190882679, + 19999.36534414829, + 19999.387944926013, + 19999.409740877607, + 19999.430760662537, + 19999.451031919776, + 19999.470581304155, + 19999.489434521372, + 19999.5076163618, + 19999.525150733072, + 19999.54206069153, + 19999.558368472495, + 19999.57409551955, + 19999.58926251268, + 19999.603889395497, + 19999.61799540144, + 19999.63159907907, + 19999.644718316464, + 19999.657370364694, + 19999.669571860562, + 19999.681338848437, + 19999.69268680136, + 19999.703630641383, + 19999.7141847592, + 19999.72436303305, + 19999.73417884697, + 19999.743645108418, + 19999.752774265195, + 19999.76157832185, + 19999.770068855447, + 19999.778257030794, + 19999.78615361512, + 19999.793768992236, + 19999.801113176174, + 19999.808195824375, + 19999.815026250366, + 19999.82161343603, + 19999.827966043387, + 19999.834092426012, + 19999.84000064, + 20000.0 + ], + "vnorm": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "element": "hydrogen", + "charge": 1 +} \ No newline at end of file diff --git a/cherab/generomak/plasma/data/core/psi_norm.json b/cherab/generomak/plasma/data/core/psi_norm.json new file mode 100644 index 00000000..01760ebf --- /dev/null +++ b/cherab/generomak/plasma/data/core/psi_norm.json @@ -0,0 +1,260 @@ +{ + "psi_norm": [ + 0.0, + 0.03561162084555414, + 0.06995505415186065, + 0.10307546213272856, + 0.13501639870233162, + 0.1658198667493661, + 0.19552637337158152, + 0.2241749831433214, + 0.25180336948611703, + 0.27844786420989853, + 0.3041435052899555, + 0.3289240829424859, + 0.35282218405932053, + 0.3758692350602538, + 0.3980955432193337, + 0.41953033651945604, + 0.44020180208767157, + 0.4601371232617498, + 0.47936251533674257, + 0.49790326003855345, + 0.5157837387698492, + 0.5330274646720292, + 0.5496571135454165, + 0.5656945536683295, + 0.581160874554252, + 0.5960764146849096, + 0.6104607882557274, + 0.6243329109688405, + 0.637711024907571, + 0.6506127225250871, + 0.663054969778784, + 0.6750541284408158, + 0.6866259776141095, + 0.6977857344821621, + 0.7085480743199009, + 0.7189271497919272, + 0.7289366095635164, + 0.7385896162488508, + 0.7478988637200875, + 0.7568765938000214, + 0.7655346123602946, + 0.7738843048463258, + 0.7819366512493673, + 0.7897022405453866, + 0.7971912846197539, + 0.8044136316960491, + 0.8113787792866483, + 0.8180958866821177, + 0.8245737869958408, + 0.8308209987797164, + 0.8368457372262026, + 0.842655924971439, + 0.8482592025136506, + 0.8536629382605364, + 0.8588742382188548, + 0.8638999553389449, + 0.8687466985264773, + 0.8734208413332797, + 0.8779285303386681, + 0.882275693232307, + 0.8864680466092238, + 0.8905111034872313, + 0.8944101805566421, + 0.8981704051718096, + 0.9017967220936874, + 0.9052938999922776, + 0.9086665377175137, + 0.9119190703468294, + 0.915055775017362, + 0.9180807765504632, + 0.9209980528759103, + 0.9238114402629539, + 0.9265246383650785, + 0.9291412150851113, + 0.931664611267077, + 0.9340981452209673, + 0.9364450170863771, + 0.9387083130407426, + 0.9408910093577201, + 0.9429959763210364, + 0.9450259819989626, + 0.9469836958843723, + 0.9488716924051706, + 0.9506924543097125, + 0.9524483759316599, + 0.954141766338572, + 0.9557748523683696, + 0.9573497815576658, + 0.9588686249658143, + 0.960333379898388, + 0.9617459725336712, + 0.9631082604556176, + 0.9644220350966051, + 0.9656890240932012, + 0.9669108935580351, + 0.9680892502707645, + 0.9692256437910194, + 0.9703215684960996, + 0.9713784655461073, + 0.9723977247790973, + 0.9733806865387387, + 0.9743286434368901, + 0.9752428420534066, + 0.9761244845754142, + 0.9769747303782067, + 0.9777946975498447, + 0.9785854643614604, + 0.9793480706852037, + 0.9800835193616915, + 0.9807927775187607, + 0.9814767778432588, + 0.9821364198075426, + 0.9827725708523005, + 0.9833860675272521, + 0.9839777165912252, + 0.9845482960730585, + 0.9850985562947225, + 0.9856292208580062, + 0.9861409875960662, + 0.9866345294910889, + 0.9871104955592747, + 0.9875695117043048, + 0.9880121815404163, + 0.9884390871861642, + 0.988850790029919, + 0.989247831468101, + 0.9896307336171266, + 0.99, + 0.9903561162084555, + 0.9906995505415186, + 0.9910307546213273, + 0.9913501639870234, + 0.9916581986674937, + 0.9919552637337158, + 0.9922417498314332, + 0.9925180336948611, + 0.992784478642099, + 0.9930414350528995, + 0.9932892408294248, + 0.9935282218405932, + 0.9937586923506025, + 0.9939809554321933, + 0.9941953033651946, + 0.9944020180208767, + 0.9946013712326175, + 0.9947936251533674, + 0.9949790326003856, + 0.9951578373876985, + 0.9953302746467203, + 0.9954965711354542, + 0.9956569455366833, + 0.9958116087455425, + 0.9959607641468491, + 0.9961046078825573, + 0.9962433291096884, + 0.9963771102490757, + 0.9965061272252509, + 0.9966305496977879, + 0.9967505412844082, + 0.9968662597761411, + 0.9969778573448216, + 0.997085480743199, + 0.9971892714979192, + 0.9972893660956351, + 0.9973858961624885, + 0.9974789886372009, + 0.9975687659380003, + 0.997655346123603, + 0.9977388430484633, + 0.9978193665124937, + 0.9978970224054539, + 0.9979719128461976, + 0.9980441363169605, + 0.9981137877928665, + 0.9981809588668211, + 0.9982457378699584, + 0.9983082099877971, + 0.998368457372262, + 0.9984265592497144, + 0.9984825920251366, + 0.9985366293826053, + 0.9985887423821885, + 0.9986389995533894, + 0.9986874669852648, + 0.9987342084133328, + 0.9987792853033867, + 0.998822756932323, + 0.9988646804660922, + 0.9989051110348723, + 0.9989441018055665, + 0.998981704051718, + 0.9990179672209368, + 0.9990529389999228, + 0.9990866653771752, + 0.9991191907034683, + 0.9991505577501736, + 0.9991808077655047, + 0.9992099805287591, + 0.9992381144026296, + 0.9992652463836508, + 0.9992914121508512, + 0.9993166461126708, + 0.9993409814522096, + 0.9993644501708637, + 0.9993870831304075, + 0.9994089100935772, + 0.9994299597632104, + 0.9994502598199896, + 0.9994698369588437, + 0.9994887169240517, + 0.9995069245430971, + 0.9995244837593166, + 0.9995414176633857, + 0.9995577485236837, + 0.9995734978155767, + 0.9995886862496581, + 0.9996033337989839, + 0.9996174597253367, + 0.9996310826045561, + 0.999644220350966, + 0.999656890240932, + 0.9996691089355804, + 0.9996808925027076, + 0.9996922564379102, + 0.999703215684961, + 0.9997137846554611, + 0.9997239772477909, + 0.9997338068653874, + 0.9997432864343689, + 0.999752428420534, + 0.9997612448457541, + 0.9997697473037821, + 0.9997779469754985, + 0.9997858546436146, + 0.999793480706852, + 0.999800835193617, + 0.9998079277751876, + 0.9998147677784326, + 0.9998213641980754, + 0.999827725708523, + 0.9998338606752725, + 0.9998397771659122, + 0.9998454829607306, + 0.9998509855629473, + 0.9998562922085801, + 0.9998614098759606, + 0.9998663452949109, + 0.9998711049555927, + 0.999875695117043, + 0.9998801218154042, + 0.9998843908718617, + 0.9998885079002992, + 0.999892478314681, + 0.9998963073361713, + 0.9999, + 1.0 + ] +} \ No newline at end of file diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index ab7ef95d..45c5ed3e 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -125,7 +125,6 @@ def get_edge_interpolators(): mesh_interp["composition"][elem_name][stage]["temperature"] = t mesh_interp["composition"][elem_name][stage]["density"] = n mesh_interp["composition"][elem_name][stage]["velocity"] = v - mesh_interp["composition"][elem_name][stage]["element"] = stage_data["element"] return mesh_interp.freeze() @@ -195,6 +194,84 @@ def get_edge_plasma(atomic_data=None, parent=None, name="Generomak edge plasma") parent=parent, name=name) +def load_core_profiles(): + """ + Loads Generomak default core plasma profiles. + + Return a single dictionary with available core plasma species temperature and + density profiles on a magnetic surface coordinate grid. + + :return: dictionary with electron and plasma composition profiles + """ + profiles_dir = os.path.join(os.path.dirname(__file__), "data/core") + + core_data = RecursiveDict() + path = os.path.join(profiles_dir, "psi_norm.json") + with open(path, "r") as fhl: + core_data["psi_norm"] = json.load(fhl)["psi_norm"] + + path = os.path.join(profiles_dir, "electrons.json") + with open(path, "r") as fhl: + core_data["electron"] = json.load(fhl) + + saved_elements = (hydrogen, carbon) + + for element in saved_elements: + for chrg in range(element.atomic_number + 1): + path = os.path.join(profiles_dir, "{}{:d}.json".format(element.name, chrg)) + + with open(path, "r") as fhl: + file_data = json.load(fhl) + element_name = file_data["element"] + charge = file_data["charge"] + + core_data["composition"][element_name][charge] = file_data + + return core_data.freeze() + + +def get_core_interpolators(): + """ + Provides 1d interpolators for Generomak default core profiles. + + :return: dictionary holding 1D interpolators of density, + temperature and velocity for plasma species + """ + + profiles = load_core_profiles() + + core_interp = RecursiveDict() + + te = Interpolator1DArray(profiles["psi_norm"], profiles["electron"]["temperature"], 'cubic', 'nearest', 1.e-5) + ne = Interpolator1DArray(profiles["psi_norm"], profiles["electron"]["density"], 'cubic', 'nearest', 1.e-5) + ve_tor = Interpolator1DArray(profiles["psi_norm"], profiles["electron"]["vtor"], 'cubic', 'nearest', 1.e-5) + ve_pol = Interpolator1DArray(profiles["psi_norm"], profiles["electron"]["vpol"], 'cubic', 'nearest', 1.e-5) + ve_norm = Interpolator1DArray(profiles["psi_norm"], profiles["electron"]["vnorm"], 'cubic', 'nearest', 1.e-5) + + core_interp["electron"]["f1d_temperature"] = te + core_interp["electron"]["f1d_density"] = ne + core_interp["electron"]["f1d_vtor"] = ve_tor + core_interp["electron"]["f1d_vpol"] = ve_pol + core_interp["electron"]["f1d_vnorm"] = ve_norm + + for elem_name, elem_data in profiles["composition"].items(): + for stage, stage_data in elem_data.items(): + + t = Interpolator1DArray(profiles["psi_norm"], stage_data["temperature"], 'cubic', 'nearest', 1.e-5) + n = Interpolator1DArray(profiles["psi_norm"], stage_data["density"], 'cubic', 'nearest', 1.e-5) + vtor = Interpolator1DArray(profiles["psi_norm"], stage_data["vtor"], 'cubic', 'nearest', 1.e-5) + vpol = Interpolator1DArray(profiles["psi_norm"], stage_data["vpol"], 'cubic', 'nearest', 1.e-5) + vnorm = Interpolator1DArray(profiles["psi_norm"], stage_data["vnorm"], 'cubic', 'nearest', 1.e-5) + + core_interp["composition"][elem_name][stage]["f1d_temperature"] = t + core_interp["composition"][elem_name][stage]["f1d_density"] = n + core_interp["composition"][elem_name][stage]["f1d_vtor"] = vtor + core_interp["composition"][elem_name][stage]["f1d_vpol"] = vpol + core_interp["composition"][elem_name][stage]["f1d_vnorm"] = vnorm + + return core_interp.freeze() + + def get_double_parabola(v_min, v_max, convexity, concavity, xmin=0, xmax=1): """ Returns a 1d double-quadratic Function1D @@ -417,7 +494,7 @@ def get_core_profiles_description(lcfs_values=None, core_args=None): # solve ionisation balance openadas = OpenADAS(permit_extrapolation=True) - psin_1d = np.linspace(0, 1, 256) + psin_1d = np.append(1. - np.geomspace(1.e-4, 1, 1023)[::-1], [1.]) # density profiles are sharp near psin=1 density_profiles = {} density_profiles["carbon"] = interpolators1d_from_elementdensity(openadas, carbon, psin_1d, carbon_total_density, profiles["electron"]["f1d_density"], @@ -451,15 +528,17 @@ def get_core_distributions(profiles=None, equilibrium=None): """ Returns a dictionary of core plasma species Maxwellian distributions. - :param profiles: Dictionary of core particle profiles. The dictionary has to have the same form - as the one returned by the function get_core_profiles_description. - The default value is the value returned by the call get_core_profiles_description(). + :param profiles: Dictionary with core interpolators. The dictionary has to have + the same form as the one returned by the function + get_core_profiles_description or get_core_interpolators. + The default value is the value returned by the call + get_core_interpolators(). :param equilibrium: an instance of EFITEquilibrium. :return: dictionary of core plasma species with Maxwellian distribution """ # get core profile data if not passed sa argument if profiles is None: - profiles = get_core_profiles_description() + profiles = get_core_interpolators() # load plasma equilibrium if not passed as argument if equilibrium is None: @@ -504,11 +583,11 @@ def get_full_profiles(equilibrium=None, core_profiles=None, edge_profiles=None, :param equilibrium: an instance of EFITEquilibrium. The default value is the value returned by load_equilibrium(). - :param core_profiles: Dictionary of core particle profiles. The dictionary has to have + :param core_profiles: Dictionary with core interpolators. The dictionary has to have the same form as the one returned by the function - get_core_profiles_description. + get_core_profiles_description or get_core_interpolators. The default value is the value returned by the call - get_core_profiles_description(). + get_core_interpolators(). :param edge_profiles: Dictionary with edge interpolators in the shape returned by the get_edge_interpolators function. If not specified, will use the value returned by @@ -524,7 +603,7 @@ def get_full_profiles(equilibrium=None, core_profiles=None, edge_profiles=None, equilibrium = equilibrium or load_equilibrium() - core_profiles = core_profiles or get_core_profiles_description() + core_profiles = core_profiles or get_core_interpolators() edge_profiles = edge_profiles or get_edge_interpolators() diff --git a/demos/generomak/plasma/plot_2d_plasma.py b/demos/generomak/plasma/plot_2d_plasma.py index 24750242..12f58866 100755 --- a/demos/generomak/plasma/plot_2d_plasma.py +++ b/demos/generomak/plasma/plot_2d_plasma.py @@ -28,7 +28,7 @@ from cherab.core.math import sample3d from cherab.core.atomic.elements import hydrogen, carbon -from cherab.generomak.plasma.plasma import get_core_plasma, get_edge_plasma, get_plasma +from cherab.generomak.plasma import get_core_plasma, get_edge_plasma, get_plasma def plot_profiles(core_profile, edge_profile, full_profile, r_range, z_range, label): diff --git a/demos/generomak/plasma/plot_2d_profiles.py b/demos/generomak/plasma/plot_2d_profiles.py index 34daa025..07fb7683 100755 --- a/demos/generomak/plasma/plot_2d_profiles.py +++ b/demos/generomak/plasma/plot_2d_profiles.py @@ -30,7 +30,7 @@ from cherab.core.utility import RecursiveDict from cherab.generomak.equilibrium import load_equilibrium -from cherab.generomak.plasma.plasma import get_core_profiles_description, load_edge_profiles, get_full_profiles +from cherab.generomak.plasma.plasma import get_core_interpolators, load_edge_profiles, get_full_profiles def plot_profiles(core_profile, edge_mesh, edge_data, full_profile, label): @@ -90,7 +90,7 @@ def plot_profiles(core_profile, edge_mesh, edge_data, full_profile, label): equilibrium = load_equilibrium() # load 1D core profiles, f(psi_norm) -core_profiles_1d = get_core_profiles_description() +core_profiles_1d = get_core_interpolators() # load 2D edge profiles defined on a quadrilateral mesh edge_data = load_edge_profiles() diff --git a/demos/generomak/plasma/plot_core_profiles.py b/demos/generomak/plasma/plot_core_profiles.py index c12f0d5a..23fd4486 100644 --- a/demos/generomak/plasma/plot_core_profiles.py +++ b/demos/generomak/plasma/plot_core_profiles.py @@ -22,9 +22,9 @@ import matplotlib.pyplot as plt from cherab.core.math.samplers import sample1d_points -from cherab.generomak.plasma.plasma import get_core_profiles_description +from cherab.generomak.plasma.plasma import get_core_interpolators -profiles = get_core_profiles_description() +profiles = get_core_interpolators() # setup temperature plot _, ax_t = plt.subplots() @@ -35,11 +35,12 @@ # setup density plot _, ax_n = plt.subplots() ax_n.set_yscale("log") +ax_n.set_ylim(1.e-1, 1.e21) ax_n.set_title("Species Core Density Profiles") ax_n.set_xlabel("psin") ax_n.set_ylabel("m^-3") -psin = np.linspace(0, 1, 30) +psin = np.append(1. - np.geomspace(1.e-4, 1, 127)[::-1], [1.]) # add hydrogen curves for chrg, desc in profiles["composition"]["hydrogen"].items(): vals = sample1d_points(desc["f1d_temperature"], psin) From d7fa6380c8338647846b534128c431a6d5ac9977 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 22 Dec 2022 16:36:59 +0000 Subject: [PATCH 53/59] Update CI with new python and numpy versions (#391) * Update python and numpy versions in CI - Remove Python 3.6 - Only test oldest and newest supported numpy versions * Update pyopencl version in CI v2022.2.4 fixes POCL library not found issue. * Use newest pre-built numpy in CI Add --prefer-binary flag when installing dependencies, to avoid trying to build newer numpy versions from source on older Pythons. --- .github/workflows/ci.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56bc565f..a1350bf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,8 @@ jobs: strategy: fail-fast: false matrix: - numpy-version: ["1.15.0", "1.16.6", "1.19.2"] - python-version: ["3.6", "3.7", "3.8"] - exclude: - - python-version: "3.8" - numpy-version: "1.15.0" + numpy-version: ["oldest-supported-numpy", "numpy"] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - name: Checkout code uses: actions/checkout@v2 @@ -26,9 +23,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib pyopencl[pocl]==2022.1 - - name: Work around PyOpenCL issue 537 - run: echo OCL_ICD_VENDORS=$(python -c 'import os, pyopencl; print(os.path.join(*pyopencl.__path__, ".libs"))') >> $GITHUB_ENV + run: python -m pip install --prefer-binary cython>=0.28 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4" - name: Install Raysect from pypi run: pip install raysect==0.7.1 - name: Build cherab From a7caca2e0f2593017c7085a32bd8446c7c10b977 Mon Sep 17 00:00:00 2001 From: Matej Tomes Date: Thu, 22 Dec 2022 17:42:05 +0100 Subject: [PATCH 54/59] Rewritten TS model with segmented laser geometry (#219) I decided to do new PR, because at the end there were many changes: laser geometry is now segmented cylinder as suggested by @mattngc. This propagated into handling of information between plasma, laser node, laser model and scattering model. This was neccessary to simplify handling of notifications, deletions and etc. laser model and spectrum hold no plasma and laser reference. Their methods return requested properties (e.g. laser power) in laser node frame. Laser material now obtains all the information from plasma, laser spectrum and laser model needed by the scattering model. Scattering model works in the frame of reference of the laser node. Method for scattered spectrum calcullation must now get all the neccessary plasma and laser variables (e.g. Te, ne, directions, etc.) laser models were rewritten and new added (uniform, bivariate and trivariate normal spatial distributions, Gaussian beam model). Lasers are now described by pulse energy and pulse temporal lengh, this is transformed to spatial power densities using given distributions. laser spectrum power spectral density was corrected. Unittests checking the following were added: laser node initialisation propagation of notifications between laser model, laser spectrum, scattering model, laser geometry and laser material position of the laser segments compared to the expected one laser spectrum initialisation and changes normalisation of models laser models initialisation polarisation direction correct values of power for spatial points integration of laser power over pulse spatial extent is compared to expected pulse energy scattering model scaterred spectrum traced by a ray is compared to expected values calculated by semi-analytical approach. --- CHANGELOG.md | 1 + cherab/core/laser/__init__.pxd | 4 + cherab/core/laser/__init__.py | 4 + cherab/core/laser/laserspectrum.pxd | 47 ++ cherab/core/laser/laserspectrum.pyx | 192 +++++ cherab/core/laser/material.pxd | 31 + cherab/core/laser/material.pyx | 66 ++ cherab/core/laser/model.pxd | 37 + cherab/core/laser/model.pyx | 78 ++ cherab/core/laser/node.pxd | 55 ++ cherab/core/laser/node.pyx | 261 ++++++ cherab/core/laser/profile.pxd | 41 + cherab/core/laser/profile.pyx | 163 ++++ cherab/core/laser/tests/__init__.py | 0 cherab/core/laser/tests/test_laser.py | 110 +++ cherab/core/laser/tests/test_laserspectrum.py | 62 ++ cherab/core/model/laser/__init__.pxd | 4 + cherab/core/model/laser/__init__.py | 4 + cherab/core/model/laser/laserspectrum.pxd | 34 + cherab/core/model/laser/laserspectrum.pyx | 133 +++ cherab/core/model/laser/math_functions.pxd | 48 ++ cherab/core/model/laser/math_functions.pyx | 304 +++++++ cherab/core/model/laser/model.pxd | 40 + cherab/core/model/laser/model.pyx | 220 +++++ cherab/core/model/laser/profile.pxd | 57 ++ cherab/core/model/laser/profile.pyx | 765 ++++++++++++++++++ cherab/core/model/laser/tests/__init__.py | 0 .../model/laser/tests/test_laserspectrum.py | 46 ++ cherab/core/model/laser/tests/test_model.py | 104 +++ .../core/model/laser/tests/test_profiles.py | 198 +++++ demos/laser/laser_profile.py | 86 ++ demos/laser/laser_spectrum.py | 34 + demos/laser/model_seldenmatoba.py | 72 ++ demos/laser/thomson_scattering.py | 61 ++ docs/source/models/emission_models.rst | 1 + docs/source/models/laser/laser.rst | 28 + docs/source/plasmas/laser.rst | 20 + docs/source/plasmas/plasmas.rst | 1 + 38 files changed, 3412 insertions(+) create mode 100644 cherab/core/laser/__init__.pxd create mode 100644 cherab/core/laser/__init__.py create mode 100644 cherab/core/laser/laserspectrum.pxd create mode 100644 cherab/core/laser/laserspectrum.pyx create mode 100644 cherab/core/laser/material.pxd create mode 100644 cherab/core/laser/material.pyx create mode 100644 cherab/core/laser/model.pxd create mode 100644 cherab/core/laser/model.pyx create mode 100644 cherab/core/laser/node.pxd create mode 100644 cherab/core/laser/node.pyx create mode 100644 cherab/core/laser/profile.pxd create mode 100644 cherab/core/laser/profile.pyx create mode 100644 cherab/core/laser/tests/__init__.py create mode 100644 cherab/core/laser/tests/test_laser.py create mode 100644 cherab/core/laser/tests/test_laserspectrum.py create mode 100644 cherab/core/model/laser/__init__.pxd create mode 100644 cherab/core/model/laser/__init__.py create mode 100644 cherab/core/model/laser/laserspectrum.pxd create mode 100644 cherab/core/model/laser/laserspectrum.pyx create mode 100644 cherab/core/model/laser/math_functions.pxd create mode 100644 cherab/core/model/laser/math_functions.pyx create mode 100644 cherab/core/model/laser/model.pxd create mode 100644 cherab/core/model/laser/model.pyx create mode 100644 cherab/core/model/laser/profile.pxd create mode 100644 cherab/core/model/laser/profile.pyx create mode 100644 cherab/core/model/laser/tests/__init__.py create mode 100644 cherab/core/model/laser/tests/test_laserspectrum.py create mode 100644 cherab/core/model/laser/tests/test_model.py create mode 100644 cherab/core/model/laser/tests/test_profiles.py create mode 100644 demos/laser/laser_profile.py create mode 100644 demos/laser/laser_spectrum.py create mode 100644 demos/laser/model_seldenmatoba.py create mode 100644 demos/laser/thomson_scattering.py create mode 100644 docs/source/models/laser/laser.rst create mode 100644 docs/source/plasmas/laser.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index dbe69a21..d6d704ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ New: * Add group observer class for each of Raysect's 0D observers. (#332) * Add a demo for observer group handling and plotting. * Add verbose parameter to SartOpencl solver (default is False). (#358) +* Add Thomson Scattering model. (#97) * Add Generomak core plasma profiles. (#360) * Add toroidal_mesh_from_polygon for making mesh for not fully-360 degrees axisymmetric elements. (#365) diff --git a/cherab/core/laser/__init__.pxd b/cherab/core/laser/__init__.pxd new file mode 100644 index 00000000..2760a2ea --- /dev/null +++ b/cherab/core/laser/__init__.pxd @@ -0,0 +1,4 @@ +from cherab.core.laser.node cimport Laser +from cherab.core.laser.model cimport LaserModel +from cherab.core.laser.laserspectrum cimport LaserSpectrum +from cherab.core.laser.profile cimport LaserProfile \ No newline at end of file diff --git a/cherab/core/laser/__init__.py b/cherab/core/laser/__init__.py new file mode 100644 index 00000000..911a64b1 --- /dev/null +++ b/cherab/core/laser/__init__.py @@ -0,0 +1,4 @@ +from .node import Laser +from .model import LaserModel +from .laserspectrum import LaserSpectrum +from .profile import LaserProfile \ No newline at end of file diff --git a/cherab/core/laser/laserspectrum.pxd b/cherab/core/laser/laserspectrum.pxd new file mode 100644 index 00000000..a00945eb --- /dev/null +++ b/cherab/core/laser/laserspectrum.pxd @@ -0,0 +1,47 @@ +# 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 raysect.core.math.function.float cimport Function1D + +from cherab.core.utility.constants cimport SPEED_OF_LIGHT, PLANCK_CONSTANT + +from numpy cimport ndarray + + +cdef class LaserSpectrum(Function1D): + + cdef: + double _min_wavelength, _max_wavelength, _delta_wavelength + int _bins + ndarray _power, _power_spectral_density, _wavelengths # power_spectral_density [w/nm] + double[::1] power_mv, power_spectral_density_mv, wavelengths_mv + + cpdef double evaluate_integral(self, double lower_limit, double upper_limit) + + cpdef void _update_cache(self) + + cpdef double get_min_wavelenth(self) + + cpdef double get_max_wavelenth(self) + + cpdef int get_spectral_bins(self) + + cpdef double get_delta_wavelength(self) + + cpdef double _get_bin_power_spectral_density(self, double wavelength_lower, double wavelength_upper) \ No newline at end of file diff --git a/cherab/core/laser/laserspectrum.pyx b/cherab/core/laser/laserspectrum.pyx new file mode 100644 index 00000000..e196fba1 --- /dev/null +++ b/cherab/core/laser/laserspectrum.pyx @@ -0,0 +1,192 @@ +# 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 raysect.core.math.function.float cimport Function1D +from raysect.optical cimport Point3D, Vector3D + +from cherab.core.utility import Notifier +from cherab.core.utility.constants cimport SPEED_OF_LIGHT, PLANCK_CONSTANT + +import numpy as np +cimport numpy as np + + +cdef class LaserSpectrum(Function1D): + """ + Laser spectrum base class. + + This is an abstract class and cannot be used for observing. + + A 1D function holding information about the spectral properties + of a laser. The scattered spectrum is calculated as an iteration + over the laser spectrum. + + + .. warning:: + When adding a LaserSpectrum, a special care should be given + to the integral power of the laser spectrum. During the + scattering calculation, the spectral power can be multiplied + by the power spatial distribution [W * m ** -3] of the laser + power from the LaserProfile. If the integral power + of the LaserSpectrum is not 1, unexpected values + might be obtained. + + .. note:: + It is expected that majority of the fusion applications can + neglect the influence of the spectral shape of the + laser and can use laser spectrum with a single + bin, which approximates an infinitely narrow laser spectrum. + + :param float min_wavelength: The minimum wavelength of the laser + spectrum in nm. + :param float max_wavelength: The maximum wavelength of the laser + spectrum in nm. + :param int bins: The number of spectral bins of the laser spectrum. + :ivar float min_wavelength: The minimum wavelength of the laser + spectrum in nm. + :ivar float max_wavelength: The maximum wavelength of the laser + spectrum in nm. + :ivar int bins: The number of specral bins of the laser spectrum + :ivar ndarray wavelengths: The wavelengt coordinate vector in nm. + :ivar ndarray power_spectral_density: The values of the power + spectral density in W / nm. + :ivar float delta_wavelength: Spectral width of the bins in nm. + """ + + def __init__(self, double min_wavelength, double max_wavelength, int bins): + + super().__init__() + + self._check_wavelength_validity(min_wavelength, max_wavelength) + + self._min_wavelength = min_wavelength + self._max_wavelength = max_wavelength + + self.bins = bins + + @property + def min_wavelength(self): + return self._min_wavelength + + @min_wavelength.setter + def min_wavelength(self, double value): + + self._check_wavelength_validity(value, self.max_wavelength) + self._min_wavelength = value + self._update_cache() + + @property + def max_wavelength(self): + return self._max_wavelength + + @max_wavelength.setter + def max_wavelength(self, double value): + + self._check_wavelength_validity(self.min_wavelength, value) + self._max_wavelength = value + self._update_cache() + + @property + def bins(self): + return self._bins + + @bins.setter + def bins(self, int value): + if value <= 0: + raise ValueError("Value has to be larger than 0") + + self._bins = value + self._update_cache() + + @property + def wavelengths(self): + return self._wavelengths + + @property + def power_spectral_density(self): + return self._power_spectral_density + + @property + def delta_wavelength(self): + return self._delta_wavelength + + def _check_wavelength_validity(self, min_wavelength, max_wavelength): + + if min_wavelength <= 0: + raise ValueError("min_wavelength has to be larger than 0, but {} passed.".format(min_wavelength)) + if max_wavelength <= 0: + raise ValueError("min_wavelength has to be larger than 0, but {} passed.".format(max_wavelength)) + + if min_wavelength >= max_wavelength: + raise ValueError("min_wavelength has to be smaller than max_wavelength: min_wavelength={} > max_wavelength={}".format(min_wavelength, max_wavelength)) + + cpdef double get_min_wavelenth(self): + return self._min_wavelength + + cpdef double get_max_wavelenth(self): + return self._min_wavelength + + cpdef int get_spectral_bins(self): + return self._bins + + cpdef double get_delta_wavelength(self): + return self._delta_wavelength + + cpdef void _update_cache(self): + + cdef: + Py_ssize_t index + double delta_wvl_half, wvl_lower, wvl_upper, wvl + + self._delta_wavelength = (self._max_wavelength - self._min_wavelength) / self._bins + self._wavelengths = np.zeros(self.bins, dtype=np.double) + self.wavelengths_mv = self._wavelengths + + for index in range(self._bins): + self._wavelengths[index] = self._min_wavelength + (0.5 + index) * self._delta_wavelength + + self._power_spectral_density = np.zeros(self._bins, dtype=np.double) # power spectral density (PSD) + self.power_spectral_density_mv = self._power_spectral_density + + self._power = np.zeros(self._bins, dtype=np.double) # power in a spectral bin (PSD * delta wavelength) + self.power_mv = self._power + + delta_wvl_half = self._delta_wavelength * 0.5 + wvl_lower = self.wavelengths_mv[0] - delta_wvl_half + + for index in range(self._bins): + wvl = wvl_lower + delta_wvl_half + wvl_upper = wvl_lower + self._delta_wavelength + + self.power_spectral_density_mv[index] = self._get_bin_power_spectral_density(wvl_lower, wvl_upper) + self.power_mv[index] = self.power_spectral_density_mv[index] * self._delta_wavelength # power in the spectral bin for scattering calculations + + wvl_lower = wvl_upper + + cpdef double evaluate_integral(self, double lower_limit, double upper_limit): + raise NotImplementedError('Virtual method must be implemented in a sub-class.') + + cpdef double _get_bin_power_spectral_density(self, double wavelength_lower, double wavelength_upper): + """ + Returns the power spectral density in a bin. + + This method can be overidden if a better precision is needed. + For example for distributions with known cumulative distribution function. + """ + return 0.5 * (self.evaluate(wavelength_lower) + self.evaluate(wavelength_upper)) diff --git a/cherab/core/laser/material.pxd b/cherab/core/laser/material.pxd new file mode 100644 index 00000000..c91fa416 --- /dev/null +++ b/cherab/core/laser/material.pxd @@ -0,0 +1,31 @@ +# 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 raysect.core.scenegraph._nodebase cimport _NodeBase +from raysect.core.math cimport AffineMatrix3D +from raysect.optical.material.emitter cimport InhomogeneousVolumeEmitter + +from cherab.core.laser.node cimport Laser + + +cdef class LaserMaterial(InhomogeneousVolumeEmitter): + + cdef: + AffineMatrix3D _laser_to_plasma, _laser_segment_to_laser_node + list _models diff --git a/cherab/core/laser/material.pyx b/cherab/core/laser/material.pyx new file mode 100644 index 00000000..4732e01a --- /dev/null +++ b/cherab/core/laser/material.pyx @@ -0,0 +1,66 @@ +# 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 raysect.core.scenegraph._nodebase cimport _NodeBase +from raysect.optical cimport World, Primitive, Ray, Spectrum, Point3D, Vector3D, AffineMatrix3D +from raysect.optical.material.emitter cimport InhomogeneousVolumeEmitter +from raysect.optical.material.emitter.inhomogeneous cimport VolumeIntegrator + +from cherab.core.laser.node cimport Laser +from cherab.core.laser.model cimport LaserModel + + +cdef class LaserMaterial(InhomogeneousVolumeEmitter): + + def __init__(self, Laser laser not None, _NodeBase laser_segment not None, list models, VolumeIntegrator integrator not None): + + super().__init__(integrator) + + self._laser_segment_to_laser_node = laser_segment.to(laser) + self._laser_to_plasma = laser_segment.to(laser.plasma) + self.importance = laser.importance + + #validate and set models + for model in models: + if not isinstance(model, LaserModel): + raise TypeError("Model supplied to laser are not LaserMaterial is not LaserModel") + model.plasma = laser.plasma + model.laser_profile = laser.laser_profile + model.laser_spectrum = laser.laser_spectrum + + self._models = models + + cpdef Spectrum emission_function(self, Point3D point, Vector3D direction, Spectrum spectrum, + World world, Ray ray, Primitive primitive, + AffineMatrix3D to_local, AffineMatrix3D to_world): + + cdef: + Point3D point_plasma, point_laser + Vector3D direction_plasma, direction_laser + LaserModel model + + point_laser = point.transform(self._laser_segment_to_laser_node) + direction_laser = direction.transform(self._laser_segment_to_laser_node) # observation vector in the laser frame + point_plasma = point.transform(self._laser_to_plasma) + direction_plasma = direction.transform(self._laser_to_plasma) + + for model in self._models: + spectrum = model.emission(point_plasma, direction_plasma, point_laser, direction_laser, spectrum) + + return spectrum diff --git a/cherab/core/laser/model.pxd b/cherab/core/laser/model.pxd new file mode 100644 index 00000000..71cdf9a8 --- /dev/null +++ b/cherab/core/laser/model.pxd @@ -0,0 +1,37 @@ +# 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 raysect.optical cimport Vector3D, Point3D +from raysect.optical.spectrum cimport Spectrum + +from cherab.core cimport Plasma +from cherab.core.laser.profile cimport LaserProfile +from cherab.core.laser.laserspectrum cimport LaserSpectrum + + +cdef class LaserModel: + cdef: + Plasma _plasma + LaserSpectrum _laser_spectrum + LaserProfile _laser_profile + + cpdef Spectrum emission(self, Point3D point_plasma, Vector3D observation_plasma, Point3D point_laser, + Vector3D observation_laser, Spectrum spectrum) + + cdef object __weakref__ diff --git a/cherab/core/laser/model.pyx b/cherab/core/laser/model.pyx new file mode 100644 index 00000000..c7d76cae --- /dev/null +++ b/cherab/core/laser/model.pyx @@ -0,0 +1,78 @@ +# 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 raysect.optical cimport Vector3D, Point3D +from raysect.optical.spectrum cimport Spectrum + +from cherab.core cimport Plasma +from cherab.core.laser.profile cimport LaserProfile +from cherab.core.laser.laserspectrum cimport LaserSpectrum + + +cdef class LaserModel: + """ + Laser spectrum base class. + + This is an abstract class and cannot be used for observing. + + Calculates the contribution to a spectrum caused by a laser. + + :param laser_profile: LaserProfile object + :param plasma: Plasma object + :param laser_spectrum: LaserSpectrum object + + :ivar laser_profile: LaserProfile object + :ivar plasma: Plasma object + :ivar laser_spectrum: LaserSpectrum object + """ + def __init__(self, LaserProfile laser_profile=None, LaserSpectrum laser_spectrum=None, Plasma plasma=None): + + self._laser_profile = laser_profile + self._laser_spectrum = laser_spectrum + self._plasma = plasma + + cpdef Spectrum emission(self, Point3D point_plasma, Vector3D observation_plasma, Point3D point_laser, Vector3D observation_laser, + Spectrum spectrum): + + raise NotImplementedError('Virtual method must be implemented in a sub-class.') + + @property + def laser_profile(self): + return self._laser_profile + + @laser_profile.setter + def laser_profile(self, LaserProfile value): + self._laser_profile = value + + @property + def plasma(self): + return self._plasma + + @plasma.setter + def plasma(self, Plasma value): + self._plasma = value + + @property + def laser_spectrum(self): + return self._laser_spectrum + + @laser_spectrum.setter + def laser_spectrum(self, LaserSpectrum value): + + self._laser_spectrum = value diff --git a/cherab/core/laser/node.pxd b/cherab/core/laser/node.pxd new file mode 100644 index 00000000..6045fb4a --- /dev/null +++ b/cherab/core/laser/node.pxd @@ -0,0 +1,55 @@ +# 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 raysect.optical cimport Point3D, Vector3D, Node, Spectrum, Primitive +from raysect.optical.material.emitter.inhomogeneous cimport VolumeIntegrator +from raysect.primitive cimport Cylinder + +from cherab.core.plasma cimport Plasma +from cherab.core.laser.profile cimport LaserProfile +from cherab.core.laser.laserspectrum cimport LaserSpectrum +from cherab.core.laser.model cimport LaserModel + + +cdef class ModelManager: + + cdef: + list _models + readonly object notifier + + cpdef object set(self, object models) + + cpdef object add(self, LaserModel model) + + cpdef object clear(self) + + +cdef class Laser(Node): + + cdef: + readonly object notifier + double _importance + Plasma _plasma + ModelManager _models + LaserProfile _laser_profile + LaserSpectrum _laser_spectrum + list _geometry + VolumeIntegrator _integrator + + cdef object __weakref__ \ No newline at end of file diff --git a/cherab/core/laser/node.pyx b/cherab/core/laser/node.pyx new file mode 100644 index 00000000..b0b821b5 --- /dev/null +++ b/cherab/core/laser/node.pyx @@ -0,0 +1,261 @@ +# 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 raysect.primitive cimport Cylinder +from raysect.optical cimport World, AffineMatrix3D, Primitive, Ray +from raysect.optical.material.emitter.inhomogeneous cimport NumericalIntegrator +from raysect.core cimport translate, Material + +from cherab.core.laser.material cimport LaserMaterial +from cherab.core.laser.model cimport LaserModel +from cherab.core.laser.profile import LaserProfile +from cherab.core.laser.laserspectrum import LaserSpectrum +from cherab.core.utility import Notifier +from libc.math cimport M_PI + +from math import ceil + +cdef double DEGREES_TO_RADIANS = (M_PI / 180) + + +cdef class ModelManager: + + def __init__(self): + self._models = [] + self.notifier = Notifier() + + def __iter__(self): + return iter(self._models) + + cpdef object set(self, object models): + + # copy models and test it is an iterable + models = list(models) + + # check contents of list are laser models + for model in models: + if not isinstance(model, LaserModel): + raise TypeError('The model list must consist of only LaserModel objects.') + + self._models = models + self.notifier.notify() + + cpdef object add(self, LaserModel model): + + if not model: + raise ValueError('Model must not be None type.') + + self._models.append(model) + self.notifier.notify() + + cpdef object clear(self): + self._models = [] + self.notifier.notify() + + +cdef class Laser(Node): + """ + A scene-graph object representing a laser of laser light. + + The Cherab laser object holds basic information about the laser and connects + the components which are needed for the laser description. With specified + emission models it can contribute to observed radiation. + + The Laser object is a Raysect scene-graph node and lives in it's own + coordinate space. This coordinate space is defined relative to it's parent + scene-graph object by an AffineTransform. The beam parameters are defined + in the Laser object coordinate space. Models using the beam object must + convert any spatial coordinates into beam space before requesting values + from the Laser object. + + The main physical properties of the laser are defined by the three + attributes laser_spectrum, laser_profile and models. The laser_spectrum + has to be an instance of LaserSpectrum and defines the spectral properties + of the laser light. The laser_profile has to be an instance of LaserProfile + and it holds all the space related definitions as volumetric distribution + of laser light energy polarisation direction. In the models a list of LaserModels + can be stored, which calculate the contribution of the laser ligth to the observed + radiation. The models can cover various applications as for example + Thomson scattering. Please see the documentation of individual classes + for more detail. + + The shape of the laser (e.g. cylinder) and its parameters (e.g. radius) + is controled by the LaserProfile. + + The plasma reference has to be specified to attach the any models. + + :param Node parent: The parent node in the Raysect scene-graph. + See the Raysect documentation for more guidance. + :param AffineMatrix3D transform: The transform defining the spatial position + and orientation of this laser. See the Raysect documentation if you need + guidance on how to use AffineMatrix3D transforms. + :param str name: The name for this laser object. + :ivar Plasma plasma: The plasma instance with which this laser interacts. + :ivar float importance: The importance sampling factor. + :ivar LaserSpectrum laser_spectrum: The LaserSpectrum instance with which this laser interacts. + :ivar LaserProfile laser_profile: The LaserProfile instance with which this laser interacts. + :ivar ModelManager models: The manager class that sets and provides access to the + emission models for this laser. + :ivar VolumeIntegrator integrator: The configurable method for doing + volumetric integration through the laser along a Ray's path. Defaults to + a numerical integrator with 1mm step size, NumericalIntegrator(step=0.001). + """ + + def __init__(self, object parent=None, AffineMatrix3D transform=None, str name=None): + + super().__init__(parent, transform, name) + + self._set_init_values() + + self.notifier = Notifier() + + self._models = ModelManager() + + self._integrator = NumericalIntegrator(step=1e-3) + + self._importance = 1. + + def _set_init_values(self): + """ + Sets initial values of the laser shape to avoid errors. + """ + self._importance = 0. + self._geometry = [] + + @property + def plasma(self): + return self._plasma + + @plasma.setter + def plasma(self, Plasma value not None): + + #unregister from old plasma notifier + if self._plasma is not None: + self._plasma.notifier.remove(self._plasma_changed) + + self._plasma = value + self._plasma.notifier.add(self._plasma_changed) + + self._configure_materials() + + @property + def importance(self): + return self._importance + + @importance.setter + def importance(self, double value): + + self._importance = value + self._configure_materials() + + @property + def laser_spectrum(self): + return self._laser_spectrum + + @laser_spectrum.setter + def laser_spectrum(self, LaserSpectrum value): + self._laser_spectrum = value + self._configure_materials() + + @property + def laser_profile(self): + return self._laser_profile + + @laser_profile.setter + def laser_profile(self, LaserProfile value): + + if self._laser_profile is not None: + self._laser_profile.notifier.remove(self.configure_geometry) + + self._laser_profile = value + self._laser_profile.notifier.add(self.configure_geometry) + + self.configure_geometry() + + @property + def models(self): + return list(self._models) + + @models.setter + def models(self, value): + + # check necessary data is available + if not all([self._plasma, self._laser_profile, self._laser_spectrum]): + raise ValueError("The plasma, laser_profile and laser_spectrum must be set before before specifying any models.") + + self._models.set(value) + self._configure_materials() + + @property + def integrator(self): + return self._integrator + + @integrator.setter + def integrator(self, VolumeIntegrator value): + self._integrator = value + + for i in self._geometry: + i.material.integrator = value + + def configure_geometry(self): + """ + Reconfigure the laser primitives and materials. + """ + + self._build_geometry() + self._configure_materials() + + def _build_geometry(self): + """ + Delete and build new laser segments + """ + # remove old laser segments in any case + for i in self._geometry: + i.parent = None + self._geometry = [] + + # no point in adding segments if there is no model and profile + if self._laser_profile is None: + return + + # rebuild geometry + self._geometry = self._laser_profile.generate_geometry() + + for i in self._geometry: + i.parent = self + + def _configure_materials(self): + """ + Configure laser segment materials + """ + if not list(self._models) or self._plasma is None or self._laser_spectrum is None: + return + + for i in self._geometry: + i.material = LaserMaterial(self, i, list(self._models), self._integrator) + + def get_geometry(self): + return self._geometry + + def _plasma_changed(self): + """React to change of plasma and propagate the information.""" + self._configure_materials() + + def _modified(self): + self._configure_materials() diff --git a/cherab/core/laser/profile.pxd b/cherab/core/laser/profile.pxd new file mode 100644 index 00000000..c634a2f8 --- /dev/null +++ b/cherab/core/laser/profile.pxd @@ -0,0 +1,41 @@ +# 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 raysect.core.math.function.float cimport Function3D +from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D + +from raysect.optical cimport Spectrum, Point3D, Vector3D + +from cherab.core.laser.node cimport Laser + + +cdef class LaserProfile: + + cdef: + VectorFunction3D _polarization3d, _pointing3d + Function3D _energy_density3d + readonly object notifier + + cpdef Vector3D get_pointing(self, double x, double y, double z) + + cpdef Vector3D get_polarization(self, double x, double y, double z) + + cpdef double get_energy_density(self, double x, double y, double z) + + cpdef list generate_geometry(self) \ No newline at end of file diff --git a/cherab/core/laser/profile.pyx b/cherab/core/laser/profile.pyx new file mode 100644 index 00000000..6356d1cb --- /dev/null +++ b/cherab/core/laser/profile.pyx @@ -0,0 +1,163 @@ +# 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 raysect.core.math.function.float cimport Function3D +from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D + +from raysect.optical cimport SpectralFunction, Spectrum, InterpolatedSF, Point3D, Vector3D + +from cherab.core.laser.node cimport Laser +from cherab.core.utility import Notifier + + +cdef class LaserProfile: + """ + LaserProfile base class. + + This is an abstract class and cannot be used for observing. + + Provides information about spatial properties of the laser beam: + direction of the laser propagation (direction + of the Poynting vector), polarisation of the ligth as the direction + of the electric component vector and volumetric energy density of + the laser light. + + All the laser properties are evaluated in the frame of reference of + the laser. + + .. warning:: + When combining a LaserProfile with a LaserSpectrum for a laser, + a special care has to be given to obtain the correct power + of the scattered spectrum. Scattering models can multiply + both the spectral power density given by the LaserProfile and + the volumetric energy density given by the LaserProfile. + Combination of incompatible cases may yield incorrect + values of scattered power. + + :ivar Laser laser: The Laser scenegraph node the LaserProfile + is connected to. + """ + + def __init__(self): + + self.notifier = Notifier() + + def set_polarization_function(self, VectorFunction3D function): + """ + Assigns the 3D vector function describing the polarisation vector. + + The polarisation is given as the direction of the electric + component of the electromagnetic wave. + + The function is specified in the laser space. + + :param VectorFunction3D function: A 3D vector function describing + the polarisation vector. + """ + self._polarization3d = function + + def set_pointing_function(self, VectorFunction3D function): + """ + Assings the 3D vector function describing the direction of the laser propagation. + + The direction of the laser light propagation is the direction + of the Poynting vector. + + :param VectorFunction3D function: A 3D vector function describing + the laser light propagation direction + """ + self._pointing3d = function + + def set_energy_density_function(self, Function3D function): + """ + Assigns the 3D scalar function describing the laser energy distribution. + + The laser power distribution is the value of the volumetric + energy density of the laser light. + """ + self._energy_density3d = function + + cpdef Vector3D get_pointing(self, double x, double y, double z): + """ + Returns the laser light propagation direction. + + At the point (x, y, z) in the laser space. + + :param x: x coordinate in meters. + :param y: y coordinate in meters. + :param z: z coordinate in meters. + :return: Intensity in m^-3. + """ + + return self._pointing3d.evaluate(x, y, z) + + cpdef Vector3D get_polarization(self, double x, double y, double z): + """ + Returns a vector denoting the laser polarisation. + + The polarisation direction is the direction of the electric + component of the electromagnetic wave for the point (x, y, z) + in the laser space. + + :param x: x coordinate in meters. + :param y: y coordinate in meters. + :param z: z coordinate in meters. + :return: power density in Wm^-3. + """ + + return self._polarization3d(x, y, z) + + cpdef double get_energy_density(self, double x, double y, double z): + """ + Returns the volumetric energy density of the laser light in W*m^-3. + + At the point (x, y, z) in the laser space. + + :param x: x coordinate in meters in the laser frame. + :param y: y coordinate in meters in the laser frame. + :param z: z coordinate in meters in the laser frame. + :return: power density in W*m^-3. + """ + + return self._energy_density3d.evaluate(x, y, z) + + cpdef list generate_geometry(self): + """ + returns list of raysect primitives composing the laser geometry + + This method is called from the Laser instance to which the instance + of Profile is attached to. The Laser instance will be assigned as + the parent to the returned primitives in the Laser._configure method. + The Laser._configure method does not change any transforms. This is + why the returned primitives have to have their transforms already + initialised in the frame of the laser, when returned. + """ + + raise NotImplementedError("Virtual function density not defined.") + + def _change(self): + """ + Called if the laser properties change. + + If the model caches calculation data that would be invalidated if its + source data changes then this method may be overridden to clear the + cache. + """ + + pass diff --git a/cherab/core/laser/tests/__init__.py b/cherab/core/laser/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cherab/core/laser/tests/test_laser.py b/cherab/core/laser/tests/test_laser.py new file mode 100644 index 00000000..f9f85732 --- /dev/null +++ b/cherab/core/laser/tests/test_laser.py @@ -0,0 +1,110 @@ +import unittest + +from raysect.optical import World +from raysect.optical.material.emitter.inhomogeneous import NumericalIntegrator + +from cherab.core import Plasma +from cherab.core.laser.node import Laser +from cherab.core.model.laser.laserspectrum import ConstantSpectrum +from cherab.core.model.laser.model import SeldenMatobaThomsonSpectrum +from cherab.core.model.laser.profile import UniformEnergyDensity + + +class TestLaser(unittest.TestCase): + + def test_laser_init(self): + """ + Test correct initialisation of a laser instance. + """ + + world = World() + laser = Laser(parent=world) + + with self.assertRaises(ValueError, msg="Model was attached before Plasma, Profile and LaserSpectrum were specified."): + laser.models = [SeldenMatobaThomsonSpectrum()] + + laser.laser_profile = UniformEnergyDensity() + with self.assertRaises(ValueError, msg="Model was attached before Plasma, Profile and LaserSpectrum were specified."): + laser.models = [SeldenMatobaThomsonSpectrum()] + + laser.laser_spectrum = ConstantSpectrum(min_wavelength=1059, max_wavelength=1061, bins=10) + with self.assertRaises(ValueError, msg="Model was attached before Plasma, Profile and LaserSpectrum were specified."): + laser.models = [SeldenMatobaThomsonSpectrum()] + + laser.plasma = Plasma(parent=world) + laser.models = [SeldenMatobaThomsonSpectrum()] + + def test_reference_change(self): + + world = World() + + laser_profile = UniformEnergyDensity(laser_length=1, laser_radius=0.1) + laser_spectrum = ConstantSpectrum(min_wavelength=1059, max_wavelength=1061, bins=10) + plasma = Plasma(parent=world) + models = [SeldenMatobaThomsonSpectrum()] + + laser_profile2 = UniformEnergyDensity() + laser_spectrum2 = ConstantSpectrum(min_wavelength=1059, max_wavelength=1061, bins=10) + plasma2 = Plasma(parent=world) + models2 = [SeldenMatobaThomsonSpectrum()] + + laser = Laser(parent=world) + + laser.laser_spectrum = laser_spectrum + laser.plasma = plasma + laser.laser_profile = laser_profile + laser.models = models + + for mod in list(laser.models): + self.assertIs(mod.laser_profile, laser_profile, msg="laser_profile reference in emission model" + "is not set correctly.") + self.assertIs(mod.plasma, plasma, msg="plasma reference in emission model" + "is not set correctly.") + self.assertIs(mod.laser_spectrum, laser_spectrum, msg="laser_spectrum reference in emission model" + "is not set correctly.") + + laser.laser_spectrum = laser_spectrum2 + laser.plasma = plasma2 + laser.laser_profile = laser_profile2 + + for mod in list(laser.models): + self.assertIs(mod.laser_profile, laser_profile2, msg="laser_profile reference in emission model" + "is not set correctly.") + self.assertIs(mod.plasma, plasma2, msg="plasma reference in emission model" + "is not set correctly.") + self.assertIs(mod.laser_spectrum, laser_spectrum2, msg="laser_spectrum reference in emission model" + "is not set correctly.") + + laser.models = models + models2 + + for mod in list(laser.models): + self.assertIs(mod.laser_profile, laser_profile2, msg="laser_profile reference in emission model" + "is not set correctly.") + self.assertIs(mod.plasma, plasma2, msg="plasma reference in emission model" + "is not set correctly.") + self.assertIs(mod.laser_spectrum, laser_spectrum2, msg="laser_spectrum reference in emission model" + "is not set correctly.") + + def test_integrator_change(self): + + world = World() + + laser_profile = UniformEnergyDensity(laser_length=1, laser_radius=0.1) + laser_spectrum = ConstantSpectrum(min_wavelength=1059, max_wavelength=1061, bins=10) + plasma = Plasma(parent=world) + models = [SeldenMatobaThomsonSpectrum()] + + laser = Laser(parent=world) + + laser.laser_spectrum = laser_spectrum + laser.plasma = plasma + laser.laser_profile = laser_profile + laser.models = models + + integrator = NumericalIntegrator(1e-4) + + laser.integrator = integrator + + for i in laser.get_geometry(): + self.assertIs(i.material.integrator, integrator, msg="Integrator not updated properly") + diff --git a/cherab/core/laser/tests/test_laserspectrum.py b/cherab/core/laser/tests/test_laserspectrum.py new file mode 100644 index 00000000..9473e43d --- /dev/null +++ b/cherab/core/laser/tests/test_laserspectrum.py @@ -0,0 +1,62 @@ +import unittest +import numpy as np + +from cherab.core.laser.laserspectrum import LaserSpectrum +from raysect.optical.spectrum import Spectrum + +class TestLaserSpectrum(unittest.TestCase): + + def test_laserspectrum_init(self): + # test min_wavelength boundaries + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise a ValueError with min_wavelength being zero."): + LaserSpectrum(0., 100, 200) + LaserSpectrum(-1, 100, 200) + + # test max_wavelength boundaries + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise a ValueError with max_wavelength being zero."): + LaserSpectrum(10, 0, 200) + LaserSpectrum(10, -1, 200) + + # test min_wavelength >= max_wavelength + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise a ValueError with max_wavelength < min_wavelength."): + LaserSpectrum(40, 30, 200) + LaserSpectrum(30, 30, 200) + + # test bins > 0 + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise a ValueError with max_wavelength < min_wavelength."): + LaserSpectrum(30, 30, 0) + LaserSpectrum(30, 30, -1) + + def test_laserspectrum_changes(self): + laser_spectrum = LaserSpectrum(100, 200, 100) + + # change min_wavelength to be larger or equal to max_wavelength + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise ValueError for min_wavelength change " + "with min_wavelength >= max_wavelength."): + laser_spectrum.min_wavelength = 300 + laser_spectrum.min_wavelength = 200 + + # change max_wavelength to be smaller than max_wavelength + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise ValueError for max_wavelength change " + "with min_wavelength > max_wavelength."): + laser_spectrum.max_wavelength = 50 + laser_spectrum.max_wavelength = 100 + + with self.assertRaises(ValueError, + msg="LaserSpectrum did not raise a ValueError with max_wavelength < min_wavelength."): + laser_spectrum.bins = -1 + laser_spectrum.bins = 0 + + # laser spectrum should have same behaviour as Spectrum from raysect.optical + spectrum = Spectrum(laser_spectrum.min_wavelength, laser_spectrum.max_wavelength, laser_spectrum.bins) + + # test caching of spectrum data, behaviour should be consistent with raysect.optical.spectrum.Spectrum + self.assertTrue(np.array_equal(laser_spectrum.wavelengths, spectrum.wavelengths), + "LaserSpectrum.wavelengths values are not equal to Spectrum.wavelengths " + "with same boundaries and number of bins") \ No newline at end of file diff --git a/cherab/core/model/laser/__init__.pxd b/cherab/core/model/laser/__init__.pxd new file mode 100644 index 00000000..3b0e3818 --- /dev/null +++ b/cherab/core/model/laser/__init__.pxd @@ -0,0 +1,4 @@ +from cherab.core.model.laser.laserspectrum import ConstantSpectrum, GaussianSpectrum +from cherab.core.model.laser.profile import UniformEnergyDensity, ConstantAxisymmetricGaussian +from cherab.core.model.laser.profile import ConstantBivariateGaussian, TrivariateGaussian, GaussianBeamAxisymmetric +from cherab.core.model.laser.model import SeldenMatobaThomsonSpectrum \ No newline at end of file diff --git a/cherab/core/model/laser/__init__.py b/cherab/core/model/laser/__init__.py new file mode 100644 index 00000000..930b55a7 --- /dev/null +++ b/cherab/core/model/laser/__init__.py @@ -0,0 +1,4 @@ +from .laserspectrum import ConstantSpectrum, GaussianSpectrum +from .profile import UniformEnergyDensity, ConstantAxisymmetricGaussian +from .profile import ConstantBivariateGaussian, TrivariateGaussian, GaussianBeamAxisymmetric +from .model import SeldenMatobaThomsonSpectrum diff --git a/cherab/core/model/laser/laserspectrum.pxd b/cherab/core/model/laser/laserspectrum.pxd new file mode 100644 index 00000000..e21d36fb --- /dev/null +++ b/cherab/core/model/laser/laserspectrum.pxd @@ -0,0 +1,34 @@ +# 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 cherab.core.laser cimport LaserSpectrum + + +cdef class ConstantSpectrum(LaserSpectrum): + + cdef double evaluate(self, double x) except? -1e999 + + +cdef class GaussianSpectrum(LaserSpectrum): + + cdef: + double _stddev, _recip_stddev, _normalisation, _mean + double _norm_cdf + + cdef double evaluate(self, double x) except? -1e999 diff --git a/cherab/core/model/laser/laserspectrum.pyx b/cherab/core/model/laser/laserspectrum.pyx new file mode 100644 index 00000000..58c1cada --- /dev/null +++ b/cherab/core/model/laser/laserspectrum.pyx @@ -0,0 +1,133 @@ +# 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 cherab.core.laser cimport LaserSpectrum +from libc.math cimport sqrt, exp, M_PI, erf, M_SQRT2 + + +cdef class ConstantSpectrum(LaserSpectrum): + """ + A laser spectrum with constant power. + + Has a constant, non-zero distribution of power spectral density + between the min_wavelength and max_wavelength. The integral value + of the power is 1 W. + + .. note:: + The ConstantSpectrum class is suitable for approximation + of an infinitely thin laser spectrum, e.g.: + ConstantSpectrum(1063.9, 1064.1, 1) + """ + + def __init__(self, double min_wavelength, double max_wavelength, int bins): + + super().__init__(min_wavelength, max_wavelength, bins) + + cdef double evaluate(self, double x) except? -1e999: + """ + Returns the spectral power density for the given wavelength. + + :param float x: Wavelength in nm. + + :return: Power spectral density in W/nm. + """ + + cdef: + double spectrum_width + int index + + if self._min_wavelength <= x <= self._max_wavelength: + return 1.0 / (self._max_wavelength - self._min_wavelength) + else: + return 0 + + +cdef class GaussianSpectrum(LaserSpectrum): + """ + A laser spectrum with a normally distributed power spectral density. + + Has a Gaussian-like spectral shape. The inegral value of power is 1 W. + + :param float mean: The mean value of the Gaussian distribution + of the laser spectrum in nm, can be thought of as the central + wavelength of the laser. + :param float stddev: Standard deviation of the Gaussian + distribution of the laser spectrum. + + :ivar float stddev: Standard deviation of the Gaussian + distribution of the laser spectrum. + :ivar float mean: The mean value of the Gaussian distribution + of the laser spectrum in nm, can be thought of as the central + wavelength of the laser. + """ + + def __init__(self, double min_wavelength, double max_wavelength, int bins, double mean, double stddev): + + self.stddev = stddev + self.mean = mean + super().__init__(min_wavelength, max_wavelength, bins) + + @property + def stddev(self): + return self._stddev + + @stddev.setter + def stddev(self, value): + if value <= 0: + raise ValueError("Value has to be larger than 0") + + self._stddev = value + self._recip_stddev = 1 / value + self._normalisation = 1 / (value * sqrt(2 * M_PI)) + self._norm_cdf = 1 / (value * M_SQRT2) + + @property + def mean(self): + return self._mean + + @mean.setter + def mean(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0") + + self._mean = value + + cdef double evaluate(self, double x) except? -1e999: + """ + Returns the spectral power density for the given wavelength. + + :param float x: Wavelength in nm. + + :return: Power spectral density in W/nm. + """ + return self._normalisation * exp(-0.5 * ((x - self._mean) * self._recip_stddev) ** 2) + + cpdef double _get_bin_power_spectral_density(self, double wavelength_lower, double wavelength_upper): + """ + Returns the power spectral density in a bin. + + Overrides the parent method to deliver better precision. + """ + + cdef: + double val_lower, val_upper + + val_lower = erf((wavelength_lower - self._mean) * self._norm_cdf) + val_upper = erf((wavelength_upper - self._mean) * self._norm_cdf) + return 0.5 * (val_upper - val_lower) / self._delta_wavelength \ No newline at end of file diff --git a/cherab/core/model/laser/math_functions.pxd b/cherab/core/model/laser/math_functions.pxd new file mode 100644 index 00000000..08827094 --- /dev/null +++ b/cherab/core/model/laser/math_functions.pxd @@ -0,0 +1,48 @@ +# 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 raysect.core.math.function.float cimport Function3D + +cdef class ConstantAxisymmetricGaussian3D(Function3D): + + cdef: + double _stddev, _normalisation, _kr + + cdef double evaluate(self, double x, double y, double z) except? -1e999 + + +cdef class ConstantBivariateGaussian3D(Function3D): + + cdef: + double _stddev_x, _stddev_y, _kx, _ky, _normalisation + + +cdef class TrivariateGaussian3D(Function3D): + + cdef: + double _mean_z, _stddev_x, _stddev_y, _stddev_z, _kx, _ky + double _kz, _normalisation + + +cdef class GaussianBeamModel(Function3D): + + cdef: + double _waist_z, _stddev_waist, _stddev_waist2, _wavelength, _rayleigh_range + + cdef double evaluate(self, double x, double y, double z) except? -1e999 diff --git a/cherab/core/model/laser/math_functions.pyx b/cherab/core/model/laser/math_functions.pyx new file mode 100644 index 00000000..1ed73a61 --- /dev/null +++ b/cherab/core/model/laser/math_functions.pyx @@ -0,0 +1,304 @@ +# 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. + + +cimport cython + +from raysect.core.math.function.float cimport Function3D +from raysect.core.math.cython.utility cimport find_index + +from libc.math cimport sqrt, exp, pi + + +cdef class ConstantAxisymmetricGaussian3D(Function3D): + """ + A function with a 2D Gaussian in the x-y plane and equal standard deviations in x and y directions. + + .. math:: + F(x, y, z) = \\frac{1}{2 * \\pi \\sigma^2} exp\\left(-\\frac{x^2 + y^2}{2 * \\sigma^2}\\right) + + The function value has a Gaussian shape in the x-y plane with the standard deviations in + x and y direction being equal. The integral over an x-y plane is equal to 1 + and the mean values in x and y directions are equal to 0. + + :param float stddev: The standard deviation in both the x and y directions. + """ + + def __init__(self, stddev): + + super().__init__() + + self.stddev = stddev + + @property + def stddev(self): + + return self._stddev + + @stddev.setter + def stddev(self, value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev = value + self._kr = -1 / (2 * value ** 2) + self._normalisation = 1 / (2 * pi * value ** 2) + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + cdef: + double r2 + r2 = x ** 2 + y ** 2 + return self._normalisation * exp(r2 * self._kr) + + +cdef class ConstantBivariateGaussian3D(Function3D): + """ + A function with a 2D Gaussian in the x-y plane. + + .. math:: + F(x, y, z) = \\frac{1}{2 * \\pi \\sigma_x \\sigma_y} exp\\left(-\\frac{x^2 + y^2}{2 * \\sigma_x \\sigma_y}\\right) + + The function value has a Gaussian shape in the x-y plane. The integral over an x-y plane is equal to 1 + and the mean values in x and y directions are equal to 0. + The correlation between the standard deviations in x and y directions is equal to 0. + + :param float stddev_x: The standard deviation in the x directions. + :param float stddev_y: The standard deviation in the y directions. + """ + + def __init__(self, stddev_x, stddev_y): + + super().__init__() + self._init_params() + + self.stddev_x = stddev_x + self.stddev_y = stddev_y + + def _init_params(self): + self._stddev_x = 1 + self._stddev_y = 1 + + @property + def stddev_x(self): + return self._stddev_x + + @stddev_x.setter + def stddev_x(self, value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev_x = value + + self._cache_constants() + + @property + def stddev_y(self): + return self._stddev_y + + @stddev_y.setter + def stddev_y(self, value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev_y = value + + self._cache_constants() + + def _cache_constants(self): + self._kx = -1 / (2 * self._stddev_x ** 2) + self._ky = -1 / (2 * self._stddev_y ** 2) + self._normalisation = 1 / (2 * pi * self._stddev_x * self._stddev_y) + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + return self._normalisation * exp(x ** 2 * self._kx + + y ** 2 * self._ky) + + +cdef class TrivariateGaussian3D(Function3D): + """ + A function with a 3D Gaussian shape. + + .. math:: + F(x, y, z) = \\frac{1}{\\sqrt{2 \\pi^3} \\sigma_x \\sigma_y \\sigma_z} exp\\left(-\\frac{x^2}{2 \\sigma_x^2} -\\frac{y^2}{2 \\sigma_y^2} - \\frac{(z - \\mu_z)^2}{2 \\sigma_z^2}\\right) + + The integral over the whole 3D space is equal to 1.The correlation between the standard deviations in x and y directions is equal to 0. The mean value in the + x and y directions are equal to 0. + + :param float mean_z: Mean value in the z direction. + :param float stddev_x: The standard deviation in the x directions. + :param float stddev_y: The standard deviation in the y directions. + :param float stddev_z: The standard deviation in the z directions. + """ + + def __init__(self, mean_z, stddev_x, stddev_y, stddev_z): + + super().__init__() + self._init_params() + + self.stddev_x = stddev_x + self.stddev_y = stddev_y + self.stddev_z = stddev_z + self.mean_z = mean_z + + def _init_params(self): + self._mean_z = 1 + self._stddev_x = 1 + self._stddev_y = 1 + self._stddev_z = 1 + + @property + def stddev_x(self): + return self._stddev_x + + @stddev_x.setter + def stddev_x(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev_x = value + + self._cache_constants() + + @property + def stddev_y(self): + return self._stddev_y + + @stddev_y.setter + def stddev_y(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev_y = value + + self._cache_constants() + + @property + def stddev_z(self): + return self._stddev_z + + @stddev_z.setter + def stddev_z(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._stddev_z = value + + self._cache_constants() + + @property + def mean_z(self): + return self._mean_z + + @mean_z.setter + def mean_z(self, double value): + self._mean_z = value + + def _cache_constants(self): + self._kx = -1 / (2 * self._stddev_x ** 2) + self._ky = -1 / (2 * self._stddev_y ** 2) + self._kz = -1 / (2 * self._stddev_z ** 2) + self._normalisation = 1 / (sqrt((2 * pi) ** 3) * self._stddev_x * self._stddev_y * self._stddev_z) + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + return self._normalisation * exp(x ** 2 * self._kx + + y ** 2 * self._ky + + (z - self._mean_z) ** 2 * self._kz) + + +cdef class GaussianBeamModel(Function3D): + """ + A Gaussian beam function (https://en.wikipedia.org/wiki/Gaussian_beam) + + .. math:: + F(x, y, z) = \\frac{1}{2 \\pi \\sigma^2_z} exp\\left( -\\frac{x^2 + y^2}{2 \\sigma_z(z)^2 }\\right) + + where the standard deviation in the z direction + + .. math:: + \\sigma_z(z) = \\sigma_0 \\sqrt{1 + \\left(\\frac{z - z_0}{z_R}\\right)^2} + + is a function of position and the + + .. math:: + z_R = \\frac{\\pi \\omega_0^2 n}{\\lambda_l} + + is the Rayleigh range. + """ + + def __init__(self, double wavelength, double waist_z, double stddev_waist): + + # preset default values + self._wavelength = 1e3 + self._waist_z = 0 + self._stddev_waist = 1e-3 + + self.wavelength = wavelength + self.waist_z = waist_z + self.stddev_waist = stddev_waist + + @property + def wavelength(self): + return self._wavelength + + @wavelength.setter + def wavelength(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0, but {0} passed.".format(value)) + + self._wavelength = value + self._cache_constants() + + @property + def waist_z(self): + return self._waist_z + + @waist_z.setter + def waist_z(self, double value): + self._waist_z = value + + @property + def stddev_waist(self): + return self._stddev_waist + + @stddev_waist.setter + def stddev_waist(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0, but {0} passed.".format(value)) + + self._stddev_waist = value + self._stddev_waist2 = self._stddev_waist ** 2 + self._cache_constants() + + def _cache_constants(self): + + n = 1 # refractive index of vacuum + self._rayleigh_range = 2 * pi * n * self._stddev_waist2 / self._wavelength / 1e-9 + + @cython.cdivision(True) + cdef double evaluate(self, double x, double y, double z) except? -1e999: + + cdef: + double r2, stddev_z2, z_prime + + # shift to correct gaussiam beam model coords, it works with waist at z=0 + z_prime = z - self._waist_z + + r2 = x ** 2 + y ** 2 + stddev_z2 = self._stddev_waist2 * (1 + ((z_prime) / self._rayleigh_range) ** 2) + + return 1 / (2 * pi * stddev_z2) * exp(r2 / (-2 * stddev_z2)) diff --git a/cherab/core/model/laser/model.pxd b/cherab/core/model/laser/model.pxd new file mode 100644 index 00000000..27e0230f --- /dev/null +++ b/cherab/core/model/laser/model.pxd @@ -0,0 +1,40 @@ +# 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 raysect.optical cimport Vector3D, Point3D +from raysect.optical.spectrum cimport Spectrum + +from cherab.core.laser cimport Laser, LaserModel, LaserSpectrum, LaserProfile + + +cdef class SeldenMatobaThomsonSpectrum(LaserModel): + + cdef: + double _CONST_ALPHA, _RATE_TS, _RECIP_M_PI + + cpdef Spectrum emission(self, Point3D point_plasma, Vector3D observation_plasma, Point3D point_laser, + Vector3D observation_laser, Spectrum spectrum) + + cdef double seldenmatoba_spectral_shape(self, double epsilon, double cos_theta, double alpha) + + cdef Spectrum _add_spectral_contribution(self, double ne, double te, double laser_energy, double angle_pointing, + double angle_polarization, double laser_wavelength, Spectrum spectrum) + + cpdef Spectrum calculate_spectrum(self, double ne, double te, double laser_energy_density, double laser_wavelength, + double observation_angle, double angle_polarization, Spectrum spectrum) \ No newline at end of file diff --git a/cherab/core/model/laser/model.pyx b/cherab/core/model/laser/model.pyx new file mode 100644 index 00000000..dbab8803 --- /dev/null +++ b/cherab/core/model/laser/model.pyx @@ -0,0 +1,220 @@ +# 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 libc.math cimport exp, sqrt, cos, M_PI, sin +cimport cython + +from raysect.optical cimport Vector3D, Point3D +from raysect.optical.spectrum cimport Spectrum + +from cherab.core cimport Plasma +from cherab.core.laser cimport LaserModel, LaserProfile, LaserSpectrum +from cherab.core.utility.constants cimport DEGREES_TO_RADIANS +from cherab.core.utility.constants cimport SPEED_OF_LIGHT, ELECTRON_CLASSICAL_RADIUS, ELECTRON_REST_MASS, ELEMENTARY_CHARGE + + +cdef class SeldenMatobaThomsonSpectrum(LaserModel): + """ + Thomson Scattering based on Selden-Matoba. + + The class calculates Thomson scattering of the laser to the spectrum. The model of the scattered spectrum used is based on + the semi-empirical model by Selden and the Thomson scattering cross-section is taken from Matoba articles. The spectral contribution + of the scattered laser light c is calculated as a sum of contributions of all laser wavelengths + + .. math:: + c(\lambda) = c r_e^2 n_e cos^2\\theta \\sum_{\\lambda_L} \\frac{E_L(\\lambda_l) S(\\frac{\\lambda}{\\lambda_L} - 1, \\varphi, T_e)}{\\lambda_L}, + + + where :math:`\\lambda` is the spectrum's wavelength, :math:`r_e` is the classical electron radius, :math:`n_e` is the electron delsity, + :math:`\\theta` is the angle between the laser polarisation and scattering vectors, :math:`c` is the vacuum speed of light + :math:`\\lambda_L` is the laser wavelength, :math:`E_L` is the laser energy density, :math:`\\varphi` is the scattering angle and :math:`T_e` is the electron + temperature. The scattering function S is taken from the Matoba article. The multiplication by the speed of light is added to transfer the Thomson scattering + cross section into a reaction rate. + + .. seealso:: + The Prunty article provides a thorough introduction into the phyiscs of Thomson scattering. The articles by Selden and Matoba were used to build + this model. + + :Selden: `Selden, A.C., 1980. Simple analytic form of the relativistic Thomson scattering spectrum. Physics Letters A, 79(5-6), pp.405-406.` + :Matoba: `Matoba, T., et al., 1979. Analytical approximations in the theory of relativistic Thomson scattering for high temperature fusion plasma. + Japanese Journal of Applied Physics, 18(6), p.1127.` + :Prunty: `Prunty, S.L., 2014. A primer on the theory of Thomson scattering for high-temperature fusion plasmas. Physica Scripta, 89(12), p.128001.` + + """ + + def __init__(self, LaserProfile laser_profile=None, LaserSpectrum laser_spectrum=None, Plasma plasma=None): + + super().__init__(laser_profile, laser_spectrum, plasma) + + # Selden, A.C., 1980. Simple analytic form of the relativistic Thomson scattering spectrum. Physics Letters A, 79(5-6), pp.405-406. + self._CONST_ALPHA = ELECTRON_REST_MASS * SPEED_OF_LIGHT ** 2 / (2 * ELEMENTARY_CHARGE) #constant alpha, rewritten for Te in eV + + # from: Prunty, S. L. "A primer on the theory of Thomson scattering for high-temperature fusion plasmas." + # TS cross section equiation ~ 3.28 or + # Matoba, T., et al., 1979. Analytical approximations in the theory of relativistic Thomson scattering for high temperature fusion plasma. + # Japanese Journal of Applied Physics, 18(6), p.1127., TS cross section equiation 18 + # speed of light for correct normalisation of the scattered intensity calculation (from x-section to rate constant) + self._RATE_TS = ELECTRON_CLASSICAL_RADIUS ** 2 * SPEED_OF_LIGHT + + self._RECIP_M_PI = 1 / M_PI + + @cython.cdivision(True) + cdef double seldenmatoba_spectral_shape(self, double epsilon, double const_theta, double alpha): + + cdef: + double c, a, b + + # const_theta is 2 * (1 - cos(theta)) + + c = sqrt(alpha * self._RECIP_M_PI) * (1 - 15. / (16. * alpha) + 345. / (512. * alpha ** 2)) + a = (1 + epsilon) ** 3 * sqrt(const_theta * (1 + epsilon) + epsilon ** 2) + b = sqrt(1 + epsilon ** 2 / (const_theta * (1 + epsilon))) - 1 + + return c / a * exp(-2 * alpha * b) + + @cython.boundscheck(False) + @cython.wraparound(False) + cpdef Spectrum emission(self, Point3D point_plasma, Vector3D observation_plasma, Point3D point_laser, + Vector3D observation_laser, Spectrum spectrum): + cdef: + double angle_scattering, angle_pointing, angle_polarization + double te, ne, laser_energy_density, laser_energy + double plasma_x, plasma_y, plasma_z, laser_x, laser_y, laser_z + double[::1] laser_wavelength_mv, laser_spectrum_power_mv + int bins + Vector3D pointing_vector, polarisation_vector + Py_ssize_t index + + plasma_x = point_plasma.x + plasma_y = point_plasma.y + plasma_z = point_plasma.z + + # get electron parameters for the plasma point + te = self._plasma.get_electron_distribution().effective_temperature(plasma_x, plasma_y, plasma_z) + + #terminate early if electron temperature is 0 + if te <= 0: + return spectrum + + ne = self._plasma.get_electron_distribution().density(plasma_x, plasma_y, plasma_z) + + #terminate early if electron density is 0 + if ne <= 0: + return spectrum + + laser_x = point_laser.x + laser_y = point_laser.y + laser_z = point_laser.z + + #get laser volumetric power + laser_energy_density = self._laser_profile.get_energy_density(laser_x, laser_y, laser_z) + + #terminate early if laser power is 0 + if laser_energy_density == 0: + return spectrum + + pointing_vector = self._laser_profile.get_pointing(laser_x, laser_y, laser_z) + + #angle between observation and pointing vector + angle_pointing = observation_laser.angle(pointing_vector) # angle between observation and pointing vector of laser + + angle_scattering = (180. - angle_pointing) # scattering direction is the opposite to obervation direction + + # angle between polarisation and observation + polarisation_vector = self._laser_profile.get_polarization(laser_x, laser_y, laser_z) + angle_polarization = observation_laser.angle(polarisation_vector) # scattering direction is the opposite to obervation direction + + laser_wavelength_mv = self._laser_spectrum.wavelengths_mv + laser_spectrum_power_mv = self._laser_spectrum.power_mv # power in spectral bins (PSD * delta wavelength) + bins = self._laser_spectrum.get_spectral_bins() + + for index in range(bins): + laser_energy = laser_spectrum_power_mv[index] * laser_energy_density + if laser_energy > 0: + spectrum = self._add_spectral_contribution(ne, te, laser_energy, angle_scattering, + angle_polarization, laser_wavelength_mv[index], spectrum) + + return spectrum + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.cdivision(True) + cdef Spectrum _add_spectral_contribution(self, double ne, double te, double laser_energy, double angle_scattering, + double angle_polarization, double laser_wavelength, Spectrum spectrum): + + cdef: + int index, nbins + double alpha, epsilon, cos_anglescat, wavelength, min_wavelength, delta_wavelength + double const_theta, recip_laser_wavelength, scattered_power, spectrum_norm + double sin2_angle_pol + + alpha = self._CONST_ALPHA / te + # scattering angle of the photon = pi - observation_angle + cos_anglescat = cos(angle_scattering * DEGREES_TO_RADIANS) + + # pre-calculate constants for Selden-Matoba shape + const_theta = 2 * (1 - cos_anglescat) + + nbins = spectrum.bins + min_wavelength = spectrum.min_wavelength + delta_wavelength = spectrum.delta_wavelength + recip_laser_wavelength = 1 / laser_wavelength + + # dipole radiation has a cos ** 2 characteristic, here angle shifted by 90 deg + sin2_angle_pol = sin(angle_polarization * DEGREES_TO_RADIANS) ** 2 + + #from d_lambda to d_epsilon:d_epsilon = d_lambda / laser_wavelength + scattered_power = ne * self._RATE_TS * laser_energy * recip_laser_wavelength * sin2_angle_pol + for index in range(nbins): + wavelength = min_wavelength + (0.5 + index) * delta_wavelength + epsilon = (wavelength * recip_laser_wavelength) - 1 + spectrum_norm = self.seldenmatoba_spectral_shape(epsilon, const_theta, alpha) + spectrum.samples_mv[index] += spectrum_norm * scattered_power + + return spectrum + + cpdef Spectrum calculate_spectrum(self, double ne, double te, double laser_energy_density, double laser_wavelength, + double observation_angle, double angle_polarization, Spectrum spectrum): + """ + Calculates scattered spectrum for the given parameters. + + The method returns the Thomson scattered spectrum given the plasma parameters, without the need of specifying + plasma or laser. + + :param float ne: Plasma electron density in m**-3 + :param float te: Plasma electron temperature in eV + :param float laser_energy_density: Energy density of the laser light in J * m**-3 + :param float laser_wavelength: The laser light wavelength in nm + :param float observation_angle: The angle of observation is the angle between the observation direction and the direction + of the Poynting vector. + :param float angle_polarization: The angle between the observation direction and the polarisation direction of the laser light. + + :return: Spectrum + """ + # check for nonzero laser power, ne, te, wavelength + if ne <= 0 or te <= 0 or not laser_energy_density > 0: + return spectrum + if laser_wavelength <= 0: + raise ValueError("laser wavelength has to be larger than 0") + + angle_scattering = (180. - observation_angle) # scattering direction is the opposite to obervation direction + + return self._add_spectral_contribution(ne, te, laser_energy_density, angle_scattering, angle_polarization, laser_wavelength, spectrum) + + \ No newline at end of file diff --git a/cherab/core/model/laser/profile.pxd b/cherab/core/model/laser/profile.pxd new file mode 100644 index 00000000..12379335 --- /dev/null +++ b/cherab/core/model/laser/profile.pxd @@ -0,0 +1,57 @@ +# 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 raysect.core.math.function.float cimport Function3D +from raysect.optical cimport Spectrum, Point3D, Vector3D + +from cherab.core.laser cimport LaserProfile + + +cdef class UniformEnergyDensity(LaserProfile): + + cdef: + double _energy_density, _laser_length, _laser_radius + + +cdef class ConstantAxisymmetricGaussian(LaserProfile): + + cdef: + double _stddev, _pulse_energy, _pulse_length, _laser_length, _laser_radius + Function3D _distribution + + +cdef class ConstantBivariateGaussian(LaserProfile): + + cdef: + double _stddev_x, _stddev_y, _pulse_energy, _pulse_length, _laser_length, _laser_radius + Function3D _distribution + + +cdef class TrivariateGaussian(LaserProfile): + + cdef: + double _stddev_x, _stddev_y, _stddev_z, _mean_z, _pulse_energy, _pulse_length, _laser_length, _laser_radius + Function3D _distribution + + +cdef class GaussianBeamAxisymmetric(LaserProfile): + + cdef: + double _pulse_energy, _pulse_length, _stddev_waist, _waist_z, _laser_wavelength, _laser_length, _laser_radius + Function3D _distribution diff --git a/cherab/core/model/laser/profile.pyx b/cherab/core/model/laser/profile.pyx new file mode 100644 index 00000000..82376980 --- /dev/null +++ b/cherab/core/model/laser/profile.pyx @@ -0,0 +1,765 @@ +from raysect.core.math.function.float import Constant3D +from raysect.core.math.function.vector3d cimport Constant3D as ConstantVector3D +from raysect.primitive import Cylinder +from raysect.optical cimport Spectrum, Vector3D, translate + +from cherab.core.laser cimport Laser, LaserProfile +from cherab.core.model.laser.math_functions cimport ConstantAxisymmetricGaussian3D, ConstantBivariateGaussian3D, TrivariateGaussian3D, GaussianBeamModel + +from cherab.core.utility.constants cimport SPEED_OF_LIGHT + +from libc.math cimport M_PI, sqrt, exp + + +cdef class UniformEnergyDensity(LaserProfile): + """ + LaserProfile with a constant volumetric energy density. + + Returns a laser with a cylindrical shape within which the laser volumentric energy density is constant. + The laser starts at z=0 and extends in the positive z direction. + + .. note: + The methods get_pointing, get_polarization and get_energy_density are not limited to the inside + of the laser cylinder. If called alone for position (x, y, z) outisde the laser cylinder, + they will still return non-zero values. + + In the following example, a laser of length of 2 m (extending from z=0 to z=2 m) with a radius of 3 cm + and volumetric energy density of 5 J*m^-3 and polarisation in the y direction is created: + + .. code-block:: pycon + + >>> from raysect.core import Vector3D + >>> from cherab.core.model.laser import UniformEnergyDensity + + >>> energy = 5 # energy density in J + >>> radius = 3e-2 # laser radius in m + >>> length = 2 # laser length in m + >>> polarisation = Vector3D(0, 1, 0) # polarisation direction + + # create the laser profile + >>> laser_profile = UniformEnergyDensity(energy, radius, length, polarisation) + + :param float energy_density: The volumetric energy density of the laser light. + :param float laser_length: The length of the laser cylinder. + :param float laser_radius: The radius of the laser cylinder. + :param Vector3D polarization: The direction of the laser polarization: + + :ivar float energy_density: The volumetric energy density of the laser light. + :ivar float laser_radius: The radius of the laser cylinder. + :ivar float laser_length: The length of the laser cylinder. + """ + + def __init__(self, double energy_density=1., double laser_length=1., double laser_radius=0.05, Vector3D polarization=Vector3D(0, 1, 0)): + super().__init__() + + self.set_polarization(polarization) + self.set_pointing_function(ConstantVector3D(Vector3D(0, 0, 1))) + self.energy_density = energy_density + + self._laser_radius = 0.05 + self._laser_length = 1. + + self.laser_radius = laser_radius + self.laser_length = laser_length + + def set_polarization(self, Vector3D value): + value = value.normalise() + self.set_polarization_function(ConstantVector3D(value)) + + @property + def laser_length(self): + return self._laser_length + + @laser_length.setter + def laser_length(self, value): + + if value <= 0: + raise ValueError("Laser length has to be larger than 0.") + + self._laser_length = value + self.notifier.notify() + + @property + def laser_radius(self): + return self._laser_radius + + @laser_radius.setter + def laser_radius(self, value): + + if value <= 0: + raise ValueError("Laser radius has to be larger than 0.") + + self._laser_radius = value + self.notifier.notify() + + @property + def energy_density(self): + return self._energy_density + + @energy_density.setter + def energy_density(self, value): + if value <= 0: + raise ValueError("Laser power density has to be larger than 0.") + + self._energy_density = value + funct = Constant3D(value) + self.set_energy_density_function(funct) + + cpdef list generate_geometry(self): + + return generate_segmented_cylinder(self.laser_radius, self.laser_length) + + +cdef class ConstantBivariateGaussian(LaserProfile): + """ + LaserProfile with a Gaussian-shaped volumetric energy density distribution in the xy plane + and constant pulse intensity. + + Returns a laser with a cylindrical shape and the propagation of the laser light in the positive z direction. + + The model imitates a laser beam with a uniform power output within a single pulse. This results + in the distribution of the energy density along the propagation direction of the laser (z-axis) to be also + uniform. The integral value of laser energy Exy in an x-y plane is given by + + .. math:: + E_{xy} = \\frac{E_p}{(c * \\tau)}, + + where Ep is the energy of the laser pulse, tau is the temporal pulse length and c is the speed of light in vacuum. + In an x-y plane, the volumetric energy density follows a bivariate Gaussian with a zero correlation: + + .. math:: + E(x, y) = \\frac{E_{xy}}{2 \\pi \\sigma_x \\sigma_y} exp\\left(-\\frac{x^2 + y^2}{2 \\sigma_x \\sigma_y}\\right). + + The sigma_x and sigma_y are standard deviations in x and y directions, respectively. + + .. note:: + The height of the cylinder, forming the laser beam, is given by the laser_length and is independent from the + temporal length of the laser pulse given by pulse_length. This gives the possibility to independently control + the size of the laser primitive and the value of the volumetric energy density. + + The methods get_pointing, get_polarization and get_energy_density are not limited to the inside + of the laser cylinder. If called for position (x, y, z) outisde the laser cylinder, they can still + return non-zero values. + + + The following example shows how to create a laser with sigma_x= 1 cm and sigma_y=2 cm, which makes the laser + profile in x-y plane to be elliptical. The pulse energy is 5 J and the laser temporal pulse length is 10 ns: + + .. code-block:: pycon + + >>> from raysect.core import Vector3D + >>> from cherab.core.model.laser import ConstantBivariateGaussian + + >>> radius = 3e-2 # laser radius in m + >>> length = 2 # laser length in m + >>> polarisation = Vector3D(0, 1, 0) # polarisation direction + >>> pulse_energy = 5 # energy in a laser pulse in J + >>> pulse_length = 1e-8 # pulse length in s + >>> width_x = 1e-2 # standard deviation in x direction in m + >>> width_y = 2e-2 # standard deviation in y direction in m + + # create the laser profile + >>> laser_profile = ConstantBivariateGaussian(pulse_energy, pulse_length, radius, length, width_x, width_y, polarisation) + + :param float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :param float pulse_length: The temporal length of the laser pulse in seconds. + :param float laser_length: The length of the laser cylinder. + :param float laser_radius: The radius of the laser cylinder. + :param float stddev_x: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the x axis in meters. + :param float stddev_y: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the y axis in meters. + :param Vector3D polarization: The direction of the laser polarization: + + :ivar float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :ivar float pulse_length: The temporal length of the laser pulse in seconds. + :ivar float laser_radius: The radius of the laser cylinder. + :ivar float laser_length: The length of the laser cylinder. + :ivar float stddev_x: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the x axis in meters. + :ivar float stddev_y: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the y axis in meters. + """ + def __init__(self, double pulse_energy=1, double pulse_length=1, double laser_radius=0.05, double laser_length=1., + double stddev_x=0.01, double stddev_y=0.01, Vector3D polarization=Vector3D(0, 1, 0)): + + super().__init__() + # set initial values + self._pulse_energy = 1 + self._pulse_length = 1 + self._stddev_x = 0.1 + self._stddev_y = 0.1 + + self._laser_radius = 0.05 + self._laser_length = 1 + + self.laser_radius = laser_radius + self.laser_length = laser_length + + self.set_polarization(polarization) + self.set_pointing_function(ConstantVector3D(Vector3D(0, 0, 1))) + + self.stddev_x = stddev_x + self.stddev_y = stddev_y + + self.pulse_energy = pulse_energy + self.pulse_length = pulse_length + + # set laser constants + self.set_polarization(polarization) + + def set_polarization(self, Vector3D value): + value = value.normalise() + self.set_polarization_function(ConstantVector3D(value)) + + @property + def laser_length(self): + return self._laser_length + + @laser_length.setter + def laser_length(self, value): + + if value <= 0: + raise ValueError("Laser length has to be larger than 0.") + + self._laser_length = value + self.notifier.notify() + + @property + def laser_radius(self): + return self._laser_radius + + @laser_radius.setter + def laser_radius(self, value): + + if value <= 0: + raise ValueError("Laser radius has to be larger than 0.") + + self._laser_radius = value + self.notifier.notify() + + @property + def pulse_energy(self): + return self._pulse_energy + + @pulse_energy.setter + def pulse_energy(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_energy = value + self.notifier.notify() + + @property + def pulse_length(self): + return self._pulse_length + + @pulse_length.setter + def pulse_length(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_length = value + self._function_changed() + + @property + def stddev_x(self): + return self._stddev_x + + @stddev_x.setter + def stddev_x(self, value): + if value <= 0: + raise ValueError("Standard deviation of the laser power has to be larger than 0.") + + self._stddev_x = value + self._function_changed() + + @property + def stddev_y(self): + return self._stddev_y + + @stddev_y.setter + def stddev_y(self, value): + if value <= 0: + raise ValueError("Standard deviation of the laser power has to be larger than 0.") + + self._stddev_y = value + self._function_changed() + + def _function_changed(self): + """ + Energy density should be returned in units [J/m ** 3]. Energy shape in xy + plane is defined by normal distribution (integral over xy plane for + constant z is 1). The units of such distribution are [m ** -2]. + In the z axis direction (direction of laser propagation), + the laser_energy is spread along the z axis using the velocity + of light SPEED_OF_LIGHT and the temporal duration of the pulse: + length = SPEED_OF_LIGTH * pulse_length. Combining the normal distribution with the normalisation + pulse_energy / length gives the units [J / m ** 3]. + """ + self._distribution = ConstantBivariateGaussian3D(self._stddev_x, self._stddev_y) + + length = SPEED_OF_LIGHT * self._pulse_length # convert from temporal to spatial length of pulse + normalisation = self._pulse_energy / length # normalisation to have correct spatial energy density [J / m**3] + + function = normalisation * self._distribution + self.set_energy_density_function(function) + + cpdef list generate_geometry(self): + + return generate_segmented_cylinder(self.laser_radius, self.laser_length) + + +cdef class TrivariateGaussian(LaserProfile): + """ + LaserProfile with a trivariate Gaussian-shaped volumetric energy density. + + Returns a laser with a cylindrical shape and the propagation of the laser light in the positive z direction. + This model imitates a laser beam with a Gaussian distribution of power output within a single pulse frozen in time: + + .. math:: + E(x, y, z) = \\frac{E_p}{\\sqrt{2 \\pi^3} \\sigma_x \\sigma_y \\sigma_z} exp\\left(-\\frac{x^2}{2 \\sigma_x^2} -\\frac{y^2}{2 \\sigma_y^2} -\\frac{(z - \\mu_z)^2}{2 \\sigma_z^2}\\right). + + + The sigma_x and sigma_y are standard deviations in x and y directions, respectively, and E_p is the energy deliverd by laser in a + single laser pulse. The mu_z is the mean of the distribution in the z direction and controls th position of the laser pulse along the z direction. + The standard deviation in z direction sigma_z is calculated from the pulse length tau_p, which is the + standard deviation of the Gaussian distributed ouput power of the laser within a single pulse: + + .. math:: + \\sigma_z = \\tau_p c. + + The c stands for the speed of light in vacuum. + + .. note:: + The height of the cylinder, forming the laser beam, is given by the laser_length and is independent from the + temporal length of the laser pulse given by pulse_length. This gives the possibility to independently control + the size of the laser primitive and the value of the volumetric energy density. + + The methods get_pointing, get_polarization and get_energy_density are not limited to the inside + of the laser cylinder. If called alone for position (x, y, z) outisde the laser cylinder, they can still + return non-zero values. + + + The following example shows how to create a laser with sigma_x = 1 cm and sigma_y = 2 cm, which makes the laser + profile in an x-y plane to be elliptical. The pulse energy is 5 J and the laser temporal pulse length is 10 ns. + The position of the laser pulse maximum mean_z is set to 0.5: + + .. code-block:: pycon + + >>> from raysect.core import Vector3D + >>> from cherab.core.model.laser import ConstantBivariateGaussian + + >>> radius = 3e-2 # laser radius in m + >>> length = 2 # laser length in m + >>> polarisation = Vector3D(0, 1, 0) # polarisation direction + >>> pulse_energy = 5 # energy in a laser pulse in J + >>> pulse_length = 1e-8 # pulse length in s + >>> pulse_z = 0.5 # position of the pulse mean + >>> width_x = 1e-2 # standard deviation in x direction in m + >>> width_y = 2e-2 # standard deviation in y direction in m + + # create the laser profile + >>> laser_profile = ConstantBivariateGaussian(pulse_energy, pulse_length, pulse_z, radius, length, width_x, width_y, polarisation) + + + :param float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :param float pulse_length: The standard deviation of the laser pulse length in the temporal domain. + :param float mean_z: Position of the mean value of the laser pulse in the z direction. Can be used to control the + position of the laser pulse along the laser propagation. + :param float laser_length: The length of the laser cylinder. + :param float laser_radius: The radius of the laser cylinder. + :param float stddev_x: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the x axis in meters. + :param float stddev_y: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the y axis in meters. + :param Vector3D polarization: The direction of the laser polarization. + + :ivar float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :ivar float pulse_length: The standard deviation of the laser pulse length in the temporal domain. + :ivar float mean_z: Position of the mean value of the laser pulse in the z direction. + Can be used to control the position of the laser pulse along the laser propagation. + :ivar float laser_radius: The radius of the laser cylinder. + :ivar float laser_length: The length of the laser cylinder. + :ivar float stddev_x: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the x axis in meters. + :ivar float stddev_y: The standard deviation of the bivariate Gaussian distribution of the volumetric energy + density distribution of the laser light in the y axis in meters. + """ + def __init__(self, double pulse_energy=1, double pulse_length=1, double mean_z=0, + double laser_length=1., double laser_radius=0.05, + double stddev_x=0.01, double stddev_y=0.01, + Vector3D polarization=Vector3D(0, 1, 0)): + + super().__init__() + # set initial values + self._pulse_energy = 1 + self._pulse_length = 1 + self._stddev_x = 0.1 + self._stddev_y = 0.1 + self._stddev_z = 1 + self._laser_radius = 0.05 + self._laser_length = 1 + self._mean_z = mean_z + + + self.laser_radius = laser_radius + self.laser_length = laser_length + self.stddev_x = stddev_x + self.stddev_y = stddev_y + self.mean_z = mean_z + + self.pulse_energy = pulse_energy + self.pulse_length = pulse_length + + self.set_polarization(polarization) + self.set_pointing_function(ConstantVector3D(Vector3D(0, 0, 1))) + + def set_polarization(self, Vector3D value): + value = value.normalise() + self.set_polarization_function(ConstantVector3D(value)) + + @property + def laser_length(self): + return self._laser_length + + @laser_length.setter + def laser_length(self, value): + + if value <= 0: + raise ValueError("Laser length has to be larger than 0.") + + self._laser_length = value + self.notifier.notify() + + @property + def laser_radius(self): + return self._laser_radius + + @laser_radius.setter + def laser_radius(self, value): + + if value <= 0: + raise ValueError("Laser radius has to be larger than 0.") + + self._laser_radius = value + self.notifier.notify() + + @property + def pulse_energy(self): + return self._pulse_energy + + @pulse_energy.setter + def pulse_energy(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_energy = value + self._function_changed() + + @property + def pulse_length(self): + return self._pulse_length + + @pulse_length.setter + def pulse_length(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_length = value + self._stddev_z = self._pulse_length * SPEED_OF_LIGHT + self._function_changed() + + @property + def stddev_x(self): + return self._stddev_x + + @stddev_x.setter + def stddev_x(self, value): + if value <= 0: + raise ValueError("Standard deviation of the laser power has to be larger than 0.") + + self._stddev_x = value + self._function_changed() + + @property + def stddev_y(self): + return self._stddev_y + + @stddev_y.setter + def stddev_y(self, value): + if value <= 0: + raise ValueError("Standard deviation of the laser power has to be larger than 0.") + + self._stddev_y = value + self._function_changed() + + @property + def mean_z(self): + return self._mean_z + + @mean_z.setter + def mean_z(self, double value): + self._mean_z = value + self._function_changed() + + def _function_changed(self): + """ + Energy density should be returned in units [J/m ** 3]. The integral value of the _distribution + is 1, thus multiplying _distribution by _pulse_energy gives correct values. + """ + + self._distribution = TrivariateGaussian3D(self._mean_z, self._stddev_x, self._stddev_y, + self._stddev_z) + + normalisation = self._pulse_energy + + function = normalisation * self._distribution + self.set_energy_density_function(function) + + cpdef list generate_geometry(self): + + return generate_segmented_cylinder(self.laser_radius, self.laser_length) + +cdef class GaussianBeamAxisymmetric(LaserProfile): + """ + LaserProfile with volumetric energy density following the Gaussian beam model. + + Returns a laser with a cylindrical shape and the propagation of the laser light in the positive z direction. This model implements + the axisymmetrical Gaussian beam model. It immitates a focused axis symmetrical laser beam with a uniform power ouput in a laser pulse. + The volumetric energy density is given by + + .. math:: + E(x, y, z) = \\frac{E_{xy}}{2 \\pi \\sigma^2(z)} exp\\left( -\\frac{x^2 + y^2}{2 \\sigma^2(z) }\\right) \\\\ + + where the sigma is the standard deviation of the Gaussian shape in the xy plane and is given by + + .. math:: + sigma(z) = \\sigma_0 \\sqrt{1 + \\left(\\frac{z - z_0}{z_R}\\right)^2}. + + The z_0 is the position of the beam focus and z_R is the Rayleigh length + + .. math:: + z_R = \\frac{\\pi \\omega_0^2 n}{\\lambda_l} + + where the omega_0 is the standard deviation in the xy plane in the focal point (beam waist) and lambda_l is the central wavelength of + the laser. The E_xy stand for the laser energy in an xy plane and is calculated as: + + .. math:: + E_{xy} = \\frac{E_p}{(c * \\tau)}, + + where the E_p is the energy in a single laser pulse and tau is the temporal pulse length. + + .. note:: + For more information about the Gaussian beam model see https://en.wikipedia.org/wiki/Gaussian_beam + + The methods get_pointing, get_polarization and get_energy_density are not limited to the inside + of the laser cylinder. If called alone for position (x, y, z) outisde the laser cylinder, they can still + return non-zero values. + + The following example shows how to create a laser with pulse energy 5J, pulse length 10 ns and with the laser cylinder primitive + being 2m long with 5 cm in diameter. The the standard deviation of the beam in the focal point (waist) is 5mm and the position of the + waist is z=50 cm. The laser wavelength is 1060 nm. + + .. code-block:: pycon + + >>> from raysect.core import Vector3D + >>> from cherab.core.model.laser import GaussianBeamAxisymmetric + + >>> radius = 5e-2 # laser radius in m + >>> length = 2 # laser length in m + >>> polarisation = Vector3D(0, 1, 0) # polarisation direction + >>> pulse_energy = 5 # energy in a laser pulse in J + >>> pulse_length = 1e-8 # pulse length in s + >>> waist_width = 5e-3 # standard deviation in the waist + >>> waist_z = 0.5 # position of the pulse mean + >>> width_x = 1e-2 # standard deviation in x direction in m + >>> width_y = 2e-2 # standard deviation in y direction in m + >>> laser_wlen = 1060 # laser wavelength in nm + + # create the laser profile + >>> laser_profile = GaussianBeamAxisymmetric(pulse_energy, pulse_length, length, radius, waist_z, waist_width, laser_wlen) + + :param float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :param float pulse_length: The temporal length of the laser pulse in seconds. + :param float laser_length: The length of the laser cylinder in meters. + :param float laser_radius: The radius of the laser cylinder in meters. + :param float waist_z: Position of the laser waist along the z axis in m. + :param float stddev_waist: The standard deviation of the laser width in the focal point (waist) in m. + :param float laser_wavelength: The central wavelength of the laser light in nanometers. + :param Vector3D polarization: The direction of the laser polarization. + + :ivar float pulse_energy: The energy of the laser in Joules delivered in a single laser pulse. + :ivar float pulse_length: The temporal length of the laser pulse in seconds. + :ivar float laser_length: The length of the laser cylinder in meters. + :ivar float laser_radius: The radius of the laser cylinder in meters. + :ivar float waist_z: Position of the laser waist along the z axis in m. + :ivar float stddev_waist: The standard deviation of the laser width in the focal point (waist) in m. + :ivar float laser_wavelength: The central wavelength of the laser light in nanometers. + :ivar Vector3D polarization: The direction of the laser polarization. + """ + + def __init__(self, double pulse_energy=1, double pulse_length=1, + double laser_length=1., double laser_radius=0.05, + double waist_z=0, double stddev_waist=0.01, + double laser_wavelength=1e3, Vector3D polarization=Vector3D(0, 1, 0)): + + super().__init__() + # set initial values + self._pulse_energy = 1 + self._pulse_length = 1 + self._stddev_waist = 0.1 + self._waist_z = waist_z + self._laser_wavelength = 1e3 + self._laser_radius = 0.05 + self._laser_length = 1 + + self.laser_length = laser_length + self.laser_radius = laser_radius + + self.set_polarization(polarization) + self.set_pointing_function(ConstantVector3D(Vector3D(0, 0, 1))) + + self.stddev_waist = stddev_waist + self.waist_z = waist_z + + self.pulse_energy = pulse_energy + self.pulse_length = pulse_length + self.laser_wavelength = laser_wavelength + + def set_polarization(self, Vector3D value): + value = value.normalise() + self.set_polarization_function(ConstantVector3D(value)) + + @property + def laser_length(self): + return self._laser_length + + @laser_length.setter + def laser_length(self, value): + + if value <= 0: + raise ValueError("Laser length has to be larger than 0.") + + self._laser_length = value + self.notifier.notify() + + @property + def laser_radius(self): + return self._laser_radius + + @laser_radius.setter + def laser_radius(self, value): + + if value <= 0: + raise ValueError("Laser radius has to be larger than 0.") + + self._laser_radius = value + self.notifier.notify() + + @property + def pulse_energy(self): + return self._pulse_energy + + @pulse_energy.setter + def pulse_energy(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_energy = value + self._function_changed() + + @property + def pulse_length(self): + return self._pulse_length + + @pulse_length.setter + def pulse_length(self, double value): + if value <= 0: + raise ValueError("Value has to be larger than 0.") + + self._pulse_length = value + self._function_changed() + + @property + def waist_z(self): + return self._waist_z + + @waist_z.setter + def waist_z(self, double value): + self._waist_z = value + self._function_changed() + + @property + def stddev_waist(self): + return self._stddev_waist + + @stddev_waist.setter + def stddev_waist(self, double value): + self._stddev_waist = value + self._function_changed() + + @property + def laser_wavelength(self): + return self._laser_wavelength + + @laser_wavelength.setter + def laser_wavelength(self, double value): + self._laser_wavelength = value + self._function_changed() + + def _function_changed(self): + """ + Energy density should be returned in units [J/m ** 3]. Energy shape in xy + plane is defined by normal distribution (integral over xy plane for + constant z is 1). The units of such distribution are [m ** -2]. + In the z axis direction (direction of laser propagation), + the laser_energy is spread along the z axis using the velocity + of light SPEED_OF_LIGHT and the temporal duration of the pulse: + length = SPEED_OF_LIGTH * pulse_length. Combining the normal distribution with the normalisation + pulse_energy / length gives the units [J / m ** 3]. + """ + + self._distribution = GaussianBeamModel(self._laser_wavelength, self._waist_z, self._stddev_waist) + # calculate volumetric power dentiy + length = SPEED_OF_LIGHT * self._pulse_length # convert from temporal to spatial length of pulse + normalisation = self._pulse_energy / length # normalisation to have correct spatial energy density [J / m**3] + + function = normalisation * self._distribution + self.set_energy_density_function(function) + + cpdef list generate_geometry(self): + + return generate_segmented_cylinder(self.laser_radius, self.laser_length) + + +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 + 2 * cylinder radius. + + :return: List of cylinders + """ + + n_segments = int(length // (2 * radius)) # number of segments + geometry = [] + + #length of segment is either length / n_segments if length > radius or length if length < radius + if n_segments > 1: + segment_length = length / n_segments + for i in range(n_segments): + segment = Cylinder(name="Laser segment {0:d}".format(i), radius=radius, height=segment_length, + transform=translate(0, 0, i * segment_length)) + + geometry.append(segment) + elif 0 <= n_segments < 2: + segment = Cylinder(name="Laser segment {0:d}".format(0), radius=radius, height=length) + + geometry.append(segment) + else: + raise ValueError("Incorrect number of segments calculated.") + + return geometry \ No newline at end of file diff --git a/cherab/core/model/laser/tests/__init__.py b/cherab/core/model/laser/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cherab/core/model/laser/tests/test_laserspectrum.py b/cherab/core/model/laser/tests/test_laserspectrum.py new file mode 100644 index 00000000..1f72e666 --- /dev/null +++ b/cherab/core/model/laser/tests/test_laserspectrum.py @@ -0,0 +1,46 @@ +import unittest +from scipy.integrate import nquad + +from cherab.core.model.laser.laserspectrum import GaussianSpectrum, ConstantSpectrum + +class TestLaserSpectrum(unittest.TestCase): + + def test_constantspectrum(self): + """ + Laser spectrum should be normalized, i.e. integral from minuns inf. to inf. should be one. + :return: + """ + min_wavelength = 1039.9 + max_wavelength = 1040.1 + bins = 10 + + spectrum = ConstantSpectrum(min_wavelength, max_wavelength, bins) + + # check if the power_spectral density is normalized + + integral = spectrum.power_spectral_density.sum() * spectrum.delta_wavelength + self.assertTrue(integral == 1, msg="Power spectral density is not normalised.") + + def test_gaussian_spectrum(self): + """ + Laser spectrum should be normalized, i.e. integral from minuns inf. to inf. should be one. + :return: + """ + min_wavelength = 1035 + max_wavelength = 1045 + bins = 100 + mean = 1040 + stddev = 0.5 + + spectrum = GaussianSpectrum(min_wavelength, max_wavelength, bins, mean, stddev) + integral = nquad(spectrum, [(min_wavelength, max_wavelength)])[0] + + # check if the power_spectral density is normalized + self.assertAlmostEqual(integral, 1., 8, msg="Power spectral density function is not normalised.") + + psd = spectrum.power_spectral_density + integral = 0 + for index in range(0, spectrum.bins - 1): + integral += (psd[index] + psd[index + 1]) / 2 * spectrum.delta_wavelength + + self.assertAlmostEqual(integral, 1, 8, msg="Power spectral density is not normalised.") diff --git a/cherab/core/model/laser/tests/test_model.py b/cherab/core/model/laser/tests/test_model.py new file mode 100644 index 00000000..992d5d52 --- /dev/null +++ b/cherab/core/model/laser/tests/test_model.py @@ -0,0 +1,104 @@ +import unittest +import numpy as np +from math import cos, sin, sqrt, radians, exp +from scipy.constants import pi, c, e, m_e, epsilon_0 + +from raysect.optical import World, Point3D, Vector3D, translate, Ray + +from cherab.core.laser import Laser +from cherab.core.model.laser import ConstantSpectrum, SeldenMatobaThomsonSpectrum, UniformEnergyDensity + +from cherab.tools.plasmas.slab import build_constant_slab_plasma + +class TestScatteringModel(unittest.TestCase): + laser_wavelength = 1040 + wavelength = np.linspace(600, 1200, 800) + scatangle = 45 + + def test_selden_matoba_scattered_spectrum(self): + + # calculate TS cross section and constants + r_e = e ** 2 / (4 * pi * epsilon_0 * m_e * c ** 2) # classical electron radius + + # re ** 2 is the cross section, c transforms xsection into rate constant + scat_const = r_e ** 2 * c + + ray_origin = Point3D(0, 0, 0) + + # angle of scattering + observation_angle = [45, 90, 135] + for obsangle in observation_angle: + + # pointing vector is in +z direction, angle of observation is 180 - obsangle + z = cos((obsangle) / 180 * pi) + x = sin((obsangle) / 180 * pi) + ray_direction = Vector3D(x, 0, z).normalise() + + # ray spectrum properties + min_wavelength = 600 + max_wavelength = 1200 + bins = 800 + + # plasma properties + e_density = 8e19 + t_e = [100, 1e3, 1e4] + + for vte in t_e: + # setup scene + world = World() + plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=e_density, + electron_temperature=vte, plasma_species=[], parent=world) + + # setup laser + laser_spectrum = ConstantSpectrum(1059, 1061, 1) + laser_profile = UniformEnergyDensity(laser_length=1, laser_radius=0.015) + scattering_model = SeldenMatobaThomsonSpectrum() + laser = Laser() + laser.parent = world + laser.transform = translate(0.05, 0, -0.5) + laser.laser_profile = laser_profile + laser.laser_spectrum = laser_spectrum + laser.plasma = plasma + laser.models = [scattering_model] + + # trace a single ray through the laser + ray = Ray(origin=ray_origin, direction=ray_direction, min_wavelength=min_wavelength, + max_wavelength=max_wavelength, bins=bins) + traced_spectrum = ray.trace(world) + + + # calculate spectrum ray-tracing should deliver + dl = 2 * laser_profile.laser_radius / sin((obsangle) / 180 * pi) # ray-laser cross section length + intensity_test = np.zeros_like(traced_spectrum.wavelengths) + + for vl in laser.laser_spectrum.wavelengths: + intensity_const = scat_const * e_density / vl * dl + for iwvl, vwvl in enumerate(traced_spectrum.wavelengths): + intensity_test[iwvl] += _selden_matoba_shape(vwvl, vte, obsangle, vl) * intensity_const + + for index, (vtest, vray) in enumerate(zip(intensity_test, traced_spectrum.samples)): + # skip the test for too low values of power spectral density, max is approx. 3e-5 + if vray > 1e-30: + rel_error = np.abs((vtest - vray) / vray) + self.assertLess(rel_error, 1e-7, + msg="Traced and test spectrum value do not match: " + "scattering angle = {0} deg, Te = {1} eV, wavelength = {2} nm." + .format(180 - obsangle, vte, traced_spectrum.wavelengths[index])) + + +def _selden_matoba_shape(wavelength, te, obsangle, laser_wavelength): + """ + Returns Selden-Matoba Spectral shape + """ + epsilon = wavelength / laser_wavelength - 1 + + alpha = m_e * c ** 2 / (2 * e * te) + + scat_angle = 180 - obsangle + cos_scat = cos(radians(scat_angle)) + + const_c = sqrt(alpha / pi) * (1 - 15 / 16 / alpha + 345 / 512 / alpha ** 2) + const_a = (1 + epsilon) ** 3 * sqrt(2 * (1 - cos_scat) * (1 + epsilon) + epsilon ** 2) + const_b = sqrt(1 + epsilon ** 2 / (2 * (1 - cos_scat) * (1 + epsilon))) - 1 + + return const_c / const_a * exp(-2 * alpha * const_b) diff --git a/cherab/core/model/laser/tests/test_profiles.py b/cherab/core/model/laser/tests/test_profiles.py new file mode 100644 index 00000000..6f2c9fc3 --- /dev/null +++ b/cherab/core/model/laser/tests/test_profiles.py @@ -0,0 +1,198 @@ +import unittest +import numpy as np +from math import exp, sqrt + +from raysect.core import Vector3D + +from cherab.core.model.laser.profile import UniformEnergyDensity, ConstantBivariateGaussian +from cherab.core.model.laser.profile import TrivariateGaussian, GaussianBeamAxisymmetric, generate_segmented_cylinder + +from scipy.integrate import nquad +from scipy.constants import c, pi + +class TestSegmentedCylinder(unittest.TestCase): + + def test_number_of_primitives(self): + + # for r > l there should be 1 cylinder segment only + primitives = generate_segmented_cylinder(radius=1, length=0.5) + self.assertEqual(len(primitives), 1, msg="Wrong nuber of laser segments, expected 1.") + + # for r < l tehre should be length // (2 * radius) segments + primitives = generate_segmented_cylinder(radius=0.5, length=10) + self.assertEqual(len(primitives), 10, msg="Wrong nuber of laser segments, expected 20.") + + +class TestLaserProfile(unittest.TestCase): + + def test_uniform_energy_density(self): + polarisation = Vector3D(1, 3, 8).normalise() + energy_density = 2 + model = UniformEnergyDensity(energy_density=energy_density, polarization=polarisation) + + # test polarisation + pol_model = model.get_polarization(1, 1, 1) + self.assertEqual(pol_model.x, polarisation.x, + msg="Model polarization x vector component does not agreee with input value.") + self.assertEqual(pol_model.y, polarisation.y, + msg="Model polarization y vector component does not agreee with input value.") + self.assertEqual(pol_model.z, polarisation.z, + msg="Model polarization z vector component does not agreee with input value.") + + # test power + self.assertEqual(model.get_energy_density(3, 4, 1), energy_density, + msg="Model power density distribution does not agree with input.") + + def test_bivariate_gaussian(self): + + pulse_energy = 2 + pulse_length = 1e-8 + stddev_x = 0.03 + stddev_y = 0.06 + polarisation = Vector3D(2, 3, 4).normalise() + model = ConstantBivariateGaussian(pulse_energy=pulse_energy, pulse_length=pulse_length, stddev_x=stddev_x, + stddev_y=stddev_y, polarization=polarisation) + + # test polarisation + pol_model = model.get_polarization(1, 1, 1) + self.assertEqual(pol_model.x, polarisation.x, + msg="Model polarization x vector component does not agreee with input value.") + self.assertEqual(pol_model.y, polarisation.y, + msg="Model polarization y vector component does not agreee with input value.") + self.assertEqual(pol_model.z, polarisation.z, + msg="Model polarization z vector component does not agreee with input value.") + + # Integrate over laser volume to check energy + xlim = [-5 * stddev_x, 5 * stddev_x] + ylim = [-5 * stddev_y, 5 * stddev_y] + zlim = [0, pulse_length * c] + energy_integrated = nquad(model.get_energy_density, [xlim, ylim, zlim])[0] + self.assertTrue(np.isclose(energy_integrated / pulse_energy, 1, 1e-3), + msg="Integrated laser energy of the model does not give results close to input energy") + + # Check laser power density profile + x = np.linspace(-3 * stddev_x, 3 * stddev_x, 30) + y = np.linspace(-3 * stddev_y, 3 * stddev_y, 30) + for vx in x: + for vy in y: + tmp = _constant_bivariate_gaussian2d(vx, vy, pulse_energy, pulse_length, stddev_x, stddev_y) + tmp2 = model.get_energy_density(vx, vy, 0) + self.assertTrue(np.isclose(tmp, tmp2, 1e-9), + msg="Model power density distribution for ({},{},{}) does not agree with input.".format( + vx, vy, 0)) + + def test_trivariate_gaussian(self): + + pulse_energy = 2 + pulse_length = 1e-8 + mean_z = 0 + stddev_x = 0.03 + stddev_y = 0.06 + polarisation = Vector3D(2, 3, 4).normalise() + model = TrivariateGaussian(pulse_energy=pulse_energy, pulse_length=pulse_length, mean_z=mean_z, + stddev_x=stddev_x, stddev_y=stddev_y, polarization=polarisation) + + # test polarisation + pol_model = model.get_polarization(1, 1, 1) + self.assertEqual(pol_model.x, polarisation.x, + msg="Model polarization x vector component does not agreee with input value.") + self.assertEqual(pol_model.y, polarisation.y, + msg="Model polarization y vector component does not agreee with input value.") + self.assertEqual(pol_model.z, polarisation.z, + msg="Model polarization z vector component does not agreee with input value.") + + # Integrate over laser volume to check energy + xlim = [-5 * stddev_x, 5 * stddev_x] + ylim = [-5 * stddev_y, 5 * stddev_y] + zlim = [mean_z - 5 * pulse_length * c, mean_z + 5 * pulse_length * c] + energy_integrated = nquad(model.get_energy_density, [xlim, ylim, zlim])[0] + + self.assertTrue(np.isclose(energy_integrated / pulse_energy, 1, 1e-3), + msg="Integrated laser energy of the model does not give results close to input energy") + + # Check laser power density profile + x = np.linspace(-3 * stddev_x, 3 * stddev_x, 30) + y = np.linspace(-3 * stddev_y, 3 * stddev_y, 30) + z = np.linspace(-3 * pulse_length * c, 3 * pulse_length * c, 30) + + for vx in x: + for vy in y: + for vz in z: + tmp = _constant_trivariate_gaussian3d(vx, vy, vz, pulse_energy, pulse_length, mean_z, stddev_x, + stddev_y) + tmp2 = model.get_energy_density(vx, vy, vz) + self.assertTrue(np.isclose(tmp, tmp2, 1e-9), + msg="Model power density distribution for ({},{},{})" + " does not agree with input.".format(vx, vy, vz)) + + def test_gaussianbeam(self): + + pulse_energy = 2 + pulse_length = 1e-9 + waist_z = 0 + stddev_waist = 0.003 + laser_wavelength = 1040 + polarisation = Vector3D(2, 3, 4).normalise() + + model = GaussianBeamAxisymmetric(pulse_energy=pulse_energy, pulse_length=pulse_length, waist_z=waist_z, + stddev_waist=stddev_waist, laser_wavelength=laser_wavelength, + polarization=polarisation) + + # test polarisation + pol_model = model.get_polarization(1, 1, 1) + self.assertEqual(pol_model.x, polarisation.x, + msg="Model polarization x vector component does not agreee with input value.") + self.assertEqual(pol_model.y, polarisation.y, + msg="Model polarization y vector component does not agreee with input value.") + self.assertEqual(pol_model.z, polarisation.z, + msg="Model polarization z vector component does not agreee with input value.") + + # Integrate over laser volume to check energy + xlim = [-20 * stddev_waist, 20 * stddev_waist] + zlim = [-1 * pulse_length / 2 * c, pulse_length / 2 * c] + energy_integrated = nquad(model.get_energy_density, [xlim, xlim, zlim])[0] + + self.assertTrue(np.isclose(energy_integrated / pulse_energy, 1, 1e-3), + msg="Integrated laser energy of the model does not give results close to input energy") + + # Check laser power density profile + x = np.linspace(-3 * stddev_waist, 3 * stddev_waist, 30) + y = np.linspace(-3 * stddev_waist, 3 * stddev_waist, 30) + z = np.linspace(-3 * pulse_length * c, 3 * pulse_length * c, 30) + + for vx in x: + for vy in y: + for vz in z: + tmp = _gaussian_beam_model(vx, vy, vz, pulse_energy, pulse_length, waist_z, stddev_waist, + laser_wavelength * 1e-9) + tmp2 = model.get_energy_density(vx, vy, vz) + self.assertTrue(np.isclose(tmp, tmp2, 1e-9), + msg="Model power density distribution for ({},{},{}) " + "does not agree with input.".format(vx, vy, vz)) + + +def _gaussian_beam_model(x, y, z, pulse_energy, pulse_length, waist_z, stddev_waist, wavelength): + laser_power_axis = pulse_energy / (pulse_length * c) + + n = 1 # refractive index + rayleigh_distance = 2 * pi * stddev_waist ** 2 * n / wavelength + + z_prime = z - waist_z + + stddev_z2 = stddev_waist ** 2 * (1 + (z_prime / rayleigh_distance) ** 2) + + r2 = x ** 2 + y ** 2 + + return laser_power_axis / (2 * pi * stddev_z2) * exp(r2 / (-2 * stddev_z2)) + + +def _constant_trivariate_gaussian3d(x, y, z, pulse_energy, pulse_length, mean_z, stddev_x, stddev_y): + stddev_z = pulse_length * c + return (pulse_energy / (sqrt((2 * pi) ** 3) * stddev_x * stddev_y * stddev_z) * + exp(-1 / 2 * ((x / stddev_x) ** 2 + (y / stddev_y) ** 2 + ((z - mean_z) / stddev_z) ** 2))) + + +def _constant_bivariate_gaussian2d(x, y, pulse_energy, pulse_length, stddev_x, stddev_y): + length = pulse_length * c + normalisation = pulse_energy / length + return normalisation / (stddev_x * stddev_y * 2 * pi) * exp(-1 / 2 * ((x / stddev_x) ** 2 + (y / stddev_y) ** 2)) diff --git a/demos/laser/laser_profile.py b/demos/laser/laser_profile.py new file mode 100644 index 00000000..fd5a08ad --- /dev/null +++ b/demos/laser/laser_profile.py @@ -0,0 +1,86 @@ +from random import gauss +from raysect.core import Vector3D + +from cherab.core.math.samplers import sample3d_grid +from cherab.core.model.laser.profile import (UniformEnergyDensity, ConstantBivariateGaussian, + TrivariateGaussian, GaussianBeamAxisymmetric) + +import numpy as np +import matplotlib.pyplot as plt + +def plot_profiles(profile, title=""): + """Plot the laser profile + + Produces 2d energy density plot in the x-z plane at y=0. + Produces plots of energy density profiles along x, y and z + directions. + """ + + # set coordinate grid and zero indices + x = np.linspace(-0.01, 0.01, 31) + z = np.linspace(-1, 1, 101) + x_zero = np.abs(x).argmin() + z_zero = np.abs(z).argmin() + + e_xz_profile = sample3d_grid(profile.get_energy_density, x, [0], z)[:, 0, :] + e_y_profile = sample3d_grid(profile.get_energy_density, [0], x, [0])[0, :, 0] + + # plot + fig = plt.figure(constrained_layout=True) + fig.suptitle(title) + spec = fig.add_gridspec(4, 3) + + # profile along y at z=0 + axz = fig.add_subplot(spec[0, 0:2]) + axz.plot(x, e_y_profile, color="C2", ls="dashed") + axz.set_xlabel("y [m]") + axz.set_ylabel("Energy [J/m^3]") + + # x-y plane + ax2d = fig.add_subplot(spec[1:3, 0:2]) + im = ax2d.pcolormesh(x, z, e_xz_profile.T) + fig.colorbar(im, ax=ax2d, label="Energy [J/m^3]") + ax2d.axhline(z[z_zero], color="C0", ls="dashed") + ax2d.axvline(x[x_zero], color="C1", ls="dashed") + ax2d.plot([x[x_zero]], z[z_zero], marker="x", color="C2") + ax2d.set_xlabel("x [m]") + ax2d.set_ylabel("z [m]") + + # profile along z at x=0 + axz = fig.add_subplot(spec[1:3, -1]) + axz.plot(e_xz_profile[x_zero, :], z, color="C0", ls="dashed") + axz.set_ylabel("z [m]") + axz.set_xlabel("Energy [J/m^3]") + + # profile along x at z=0 + axz = fig.add_subplot(spec[-1, 0:2]) + axz.plot(x, e_xz_profile[:, z_zero].T, color="C1", ls="dashed") + axz.set_xlabel("x [m]") + axz.set_ylabel("Energy [J/m^3]") + +# UniformEnergyDensity profile has constant parameters in the whole x, y, z space +uniform = UniformEnergyDensity(energy_density=1e3, laser_length=2., laser_radius=0.005, + polarization=Vector3D(0, 1, 0)) +plot_profiles(uniform, "Uniform Energy Profile") + +# Example of ConstantBivariteGaussian. With energy 2J, pulse temporal length 5 ns +# and different standard deviations in the x and y plane. The mean value in both +# dimensions is equal to 0. There is no correlation between x and y. + +gauss2d = ConstantBivariateGaussian(pulse_energy=2, pulse_length=5e-9, + stddev_x=2e-3, stddev_y=4e-3) +plot_profiles(gauss2d, "ConstantBivariateGaussian Energy Profile") + +# Example of TrivariageGaussian profile. With the pulse mean at z=0.2 +gauss3d = TrivariateGaussian(pulse_energy=2, pulse_length=2e-9, + mean_z=0.2, stddev_x=2e-3, stddev_y=4e-3 ) +plot_profiles(gauss3d, "TrivariateGaussian Energy Profile") + +# Example of GaussianBeamModel with wavelength 1e4 nm and 1 mm standard deviation +# in the waist. The wavelength is too high +# but was selected to produce nice plots in the defined x, y, z dimensions. + +gaussbeam = GaussianBeamAxisymmetric(stddev_waist=1e-3, waist_z=0.2, laser_wavelength=1e4) +plot_profiles(gaussbeam, "GaussianBeam Energy Profile") + +plt.show() \ No newline at end of file diff --git a/demos/laser/laser_spectrum.py b/demos/laser/laser_spectrum.py new file mode 100644 index 00000000..7904bbdc --- /dev/null +++ b/demos/laser/laser_spectrum.py @@ -0,0 +1,34 @@ +from cherab.core.model.laser import ConstantSpectrum, GaussianSpectrum + +import matplotlib.pyplot as plt + + +# construct a ConstantSpectrum with 10 spectral bins +constant_wide = ConstantSpectrum(min_wavelength=1059.9, max_wavelength=1060.1, bins=10) + +# plot the power_spectral_density attribute of the laser +_, ax = plt.subplots() +ax.plot(constant_wide.wavelengths, constant_wide.power_spectral_density) + +ax.set_xlabel("Wavelength [nm]") +ax.set_ylabel("W / nm") + +ax.set_title("Energy Spectral Density") +plt.show() + +# construct a narrow laser spectrum +constant_narrow = ConstantSpectrum(min_wavelength=1059.999, max_wavelength=1060.001, bins=1) +print("narow spectrum wavelengths: {}, power spectral density: {}".format(constant_narrow.wavelengths, + constant_narrow.power_spectral_density)) + +# construct a GaussianSpectrum with 20 bins +gaussian = GaussianSpectrum(min_wavelength=1059, max_wavelength=1061, bins=30, + mean=1060, stddev=0.3) + +_, ax = plt.subplots() +ax.plot(gaussian.wavelengths, gaussian.power_spectral_density) +ax.set_xlabel("Wavelength [nm]") +ax.set_ylabel("W / nm") + +ax.set_title("Energy Spectral Density") +plt.show() diff --git a/demos/laser/model_seldenmatoba.py b/demos/laser/model_seldenmatoba.py new file mode 100644 index 00000000..0491cd66 --- /dev/null +++ b/demos/laser/model_seldenmatoba.py @@ -0,0 +1,72 @@ +import matplotlib.pyplot as plt + +from raysect.optical.spectrum import Spectrum + +from cherab.core.model.laser import SeldenMatobaThomsonSpectrum +from cherab.core.utility import RecursiveDict + + +density = 3e19 # electron density +temperatures = [100, 2.e3] # electron temperatures in eV +laser_wavelength = 1060 # wavelength of the laser light in nm +laser_energy = 1 # energy density of the laser light in J/m^3 + +# angle between observation direction and electric field +angles_pol = [90, 45] + +# angles between propagation direction and observation direction +angles_obs = [45, 90, 135, 120] + +# define spectrum of observed scattering +min_wavelength = 650 +max_wavelength = 1300 +bins = 1000 + + +# calculate Thomson scattered spectra for the specified combinations of el. properties and observation +model = SeldenMatobaThomsonSpectrum() +scattered = RecursiveDict() + +for te in temperatures: + for ap in angles_pol: + for ao in angles_obs: + spectrum = Spectrum(min_wavelength, max_wavelength, bins) + scattered[te][ap][ao] = model.calculate_spectrum(density, te, laser_energy, laser_wavelength, + ao, ap, spectrum).samples +scattered = scattered.freeze() + +wvls = spectrum.wavelengths + +# plot temperature influence on scattered spectra +ap, ao = 90, 90 +_, ax = plt.subplots() +ax.set_title("Scattered spectrum, pol. angl.={:d}, obs. angl = {:d}".format(ap, ao)) +for te in temperatures: + rad = scattered[te][ap][ao] + ax.plot(wvls, rad, label="Te = {:3.1f} eV".format(te)) +ax.set_xlabel("Wavelength [nm]") +ax.set_ylabel("Spectral Radiance [W/m^3/sr]") +ax.legend() + +# plot influence of angle between polarisation and observation on scattered spectra +te, ao = 100, 90 +_, ax = plt.subplots() +ax.set_title("Scattered spectrum, te = {:3.1f}, obs. angl = {:d}".format(te, ao)) +for ap in angles_pol: + rad = scattered[te][ap][ao] + ax.plot(wvls, rad, label="pol. angl. = {:d} deg".format(ap)) +ax.set_xlabel("Wavelength [nm]") +ax.set_ylabel("Spectral Radiance [W/m^3/sr]") +ax.legend() + +# plot influence of observation angle on scattered spectra +te, ap = 2e3, 90 +_, ax = plt.subplots() +ax.set_title("Scattered spectrum, te = {:3.1f}, obs. angl = {:d}".format(te, ao)) +for ao in angles_obs: + rad = scattered[te][ap][ao] + ax.plot(wvls, rad, label="obs. angl. = {:d} deg".format(ao)) +ax.set_xlabel("Wavelength [nm]") +ax.set_ylabel("Spectral Radiance [W/m^3/sr]") +ax.legend() +plt.show() diff --git a/demos/laser/thomson_scattering.py b/demos/laser/thomson_scattering.py new file mode 100644 index 00000000..f9198d8e --- /dev/null +++ b/demos/laser/thomson_scattering.py @@ -0,0 +1,61 @@ +import numpy as np + +from raysect.optical import World, translate, Point3D, rotate_basis, Vector3D +from raysect.optical.observer import FibreOptic + +from cherab.core.model.laser import ConstantBivariateGaussian, ConstantSpectrum, SeldenMatobaThomsonSpectrum +from cherab.core.laser import Laser + +from cherab.generomak.plasma import get_core_plasma +from cherab.generomak.equilibrium import load_equilibrium + +import matplotlib.pyplot as plt + +world = World() + +plasma = get_core_plasma(parent=world) +equilibrium = load_equilibrium() + +# set up the laser +laser = Laser(name="Thomson Scattering laser", parent=world) +laser.transform = translate(equilibrium.magnetic_axis[0], 0, -2) +laser.plasma = plasma +laser.laser_profile = ConstantBivariateGaussian(pulse_energy=2, pulse_length=1e-8, + laser_length=4, laser_radius=1e-2, + stddev_x=3e-3, stddev_y=2e-3) +laser.laser_spectrum = ConstantSpectrum(min_wavelength=1059.9, max_wavelength=1060.1, bins=1) +laser.models = [SeldenMatobaThomsonSpectrum()] + +# generate points on laser in world space to measure on +laser_points = [Point3D(0, 0, round(z, 2)).transform(laser.to_root()) for z in np.linspace(2, 2.8, 5)] +te = [plasma.electron_distribution.effective_temperature(*point) for point in laser_points] +ne = [plasma.electron_distribution.density(*point) for point in laser_points] + +# place fibres 0.1m outside sep on lfs +fibre_position = Point3D(equilibrium.psin_to_r(1) + 0.1, 0, 0) + +# generate fibres list and observe +fibres = {} +for point in laser_points: + + direction = fibre_position.vector_to(point) + transform = translate(*fibre_position) * rotate_basis(direction, Vector3D(0, 0, 1)) + + fibre = FibreOptic(radius=1e-3, acceptance_angle=0.25, + parent=world, transform=transform, + min_wavelength=800, max_wavelength=1200, + spectral_bins=1000, + pixel_samples=1000) + + fibre.pipelines[0].display_progress = False + fibre.observe() + fibres[point.z] = fibre + +_, ax = plt.subplots() +for z, fibre in fibres.items(): + pipeline = fibre.pipelines[0] + ax.plot(pipeline.wavelengths, pipeline.samples.mean, label="z={:1.2f}m".format(z)) +ax.set_xlabel("wavelength [nm]") +ax.set_ylabel("spectral power W/nm") +ax.legend() +plt.show() diff --git a/docs/source/models/emission_models.rst b/docs/source/models/emission_models.rst index 73f76cc2..3a2c3b9c 100644 --- a/docs/source/models/emission_models.rst +++ b/docs/source/models/emission_models.rst @@ -13,3 +13,4 @@ Cherab contains a number of independent physics models for spectroscopic emissio brem/bremsstrahlung basic_line/basic_line_emission line_shapes/spectral_line_shapes + laser/laser diff --git a/docs/source/models/laser/laser.rst b/docs/source/models/laser/laser.rst new file mode 100644 index 00000000..da9f7f79 --- /dev/null +++ b/docs/source/models/laser/laser.rst @@ -0,0 +1,28 @@ +Laser +====== + +Laser Spectrum +-------------- + +.. autoclass:: cherab.core.model.laser.laserspectrum.ConstantSpectrum + :members: +.. autoclass:: cherab.core.model.laser.laserspectrum.GaussianSpectrum + :members: + +Laser Profile +------------- + +.. autoclass:: cherab.core.model.laser.profile.UniformEnergyDensity + :members: +.. autoclass:: cherab.core.model.laser.profile.ConstantBivariateGaussian + :members: +.. autoclass:: cherab.core.model.laser.profile.TrivariateGaussian + :members: +.. autoclass:: cherab.core.model.laser.profile.GaussianBeamAxisymmetric + :members: + +Laser Model +------------- + +.. autoclass:: cherab.core.model.laser.model.SeldenMatobaThomsonSpectrum + :members: diff --git a/docs/source/plasmas/laser.rst b/docs/source/plasmas/laser.rst new file mode 100644 index 00000000..6c94c6d8 --- /dev/null +++ b/docs/source/plasmas/laser.rst @@ -0,0 +1,20 @@ +Laser +====== + +Laser Spectrum +-------------- + +.. autoclass:: cherab.core.laser.laserspectrum.LaserSpectrum + :members: + +Laser Profile +------------- + +.. autoclass:: cherab.core.laser.profile.LaserProfile + :members: + +Laser Model +------------- + +.. autoclass:: cherab.core.laser.model.LaserModel + :members: diff --git a/docs/source/plasmas/plasmas.rst b/docs/source/plasmas/plasmas.rst index 07198e71..98e3d9c8 100644 --- a/docs/source/plasmas/plasmas.rst +++ b/docs/source/plasmas/plasmas.rst @@ -7,3 +7,4 @@ Plasmas core_plasma_classes equilibrium particle_beams + laser From ab6df92d62e511582d508c7437a1134291de7c1e Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 23 Dec 2022 14:46:30 +0000 Subject: [PATCH 55/59] Fixup changelog --- CHANGELOG.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb38c25..d7bf7c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ Release 1.4.0 (TBD) API changes: * Spectroscopic observers and their groups are deprecated and replaced by groups based on Raysect's 0D observers. (#332) +* Support for Python 3.6 is dropped. It may still work, but is no longer actively tested. Bug fixes: * Fixed generomak plasma edge data paths. * Fix and improve OpenCL utility functions. (#358) -* Fix wavelength indexing in Bremsstrahlung emission model. (#352) +* Fixed Bremsstrahlung trapezium evaluation (#384). +* Fixed generomak plasma edge data paths. New: * Make f_profile (current flux) a read-only attribute of EFITEquilibrium. (#355) @@ -26,12 +28,8 @@ New: * Add integrator attribute to LineShapeModel to use with lineshapes that cannot be analytically integrated over a spectral bin. (#366) * Add a numerical integration of StarkBroadenedLine over the spectral bin. (#366) * Add Generomak full plasma profiles obtained by blending the core and edge profiles. (#372) - -Bug Fixes: ----------- -* Fixed Bremsstrahlung trapezium evaluation (#384). -* Fixed generomak plasma edge data paths. -* Fix and improve OpenCL utility functions. (#358) +* Clean up build/install dependencies. (#353) +* Test against Python 3.10 and latest released Numpy. Drop Python 3.6 and older Numpy from tests. (#391) Release 1.3.0 (8 Dec 2021) From f63dcda02509659409ef24588a794d4820c741ab Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 23 Dec 2022 15:35:11 +0000 Subject: [PATCH 56/59] Update version in docs and VERSION file --- cherab/core/VERSION | 2 +- docs/source/conf.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cherab/core/VERSION b/cherab/core/VERSION index f0bb29e7..40b48802 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.3.0 +1.4.0rc1 diff --git a/docs/source/conf.py b/docs/source/conf.py index 7dfb5f04..60589037 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -55,16 +55,16 @@ # General information about the project. project = 'Cherab' -copyright = '2021, Cherab Team' +copyright = '2022, Cherab Team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1.3' +version = '1.4' # The full version, including alpha/beta/rc tags. -release = '1.3.0' +release = '1.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 365c211acc1a3bfc45b273ab52d03f593b1fc4e2 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 23 Dec 2022 19:08:17 +0000 Subject: [PATCH 57/59] Add some notes on how to build wheels for PyPI --- dev/pypi_notes.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 dev/pypi_notes.md diff --git a/dev/pypi_notes.md b/dev/pypi_notes.md new file mode 100644 index 00000000..c79e6fe8 --- /dev/null +++ b/dev/pypi_notes.md @@ -0,0 +1,80 @@ +Building wheels for publishing on PyPI +====================================== + +Linux wheels published on PyPI must be for one of the manylinux variants. +This means they should be built inside a manylinux Docker container. + +For Cherab versions 1.4 and earlier (which depend on raysect 0.7.1 or earlier), +the procedure varies slightly depending on whether there is a raysect wheel +available for the Python version or not. + +When Raysect wheels are available +--------------------------------- + +1. Retrieve the appropriate manylinux container (we'll use manylinux1 here as + an example, but any appropriate version works the same way). + ```bash + sudo docker run -ti -v :/cherab_core quay.io/pypa/manylinux1_x86_64 /bin/bash + ``` + Or, if using singularity instead of Docker (where sudo rights are not available): + ```bash + singularity pull docker://quay.io/pypa/manylinux1_x86_64 + singularity run -B :/cherab_core -W /tmp -c ./manylinux1_x86_64_latest.sif + ``` +2. Inside the container, change to the top level directory of this repository. + ```bash + cd /cherab_core + ``` +3. Configure pip to prefer older binary wheels over newer sdists. This is because + the manylinux images have very few libraries installed so fail to build many + packages. + ```bash + export PIP_PREFER_BINARY=1 + ``` +4. (Optional) Configure a cache directory for pip that doesn't have a small quota. + This will prevent `No space left on device` errors when collecting dependencies, + or when pip does have to build certain packages (Numpy for example does successfully + build on certain manylinux versions for certain Python versions). + ```bash + export PIP_CACHE_DIR=/tmp/pipcache + ``` +5. Use PyPA's `build` package to build the sdists and wheels, for each Python version + we're building the wheels for. For example, for Python 3.8: + ```bash + /opt/python/cp38-cp38/bin/python -m build . + ``` + Ensure you've done step 3 above before running this command! +6. Once the build has finished, repair the wheel to give it the correct manylinux tag. + Once again, using Python 3.8 as an example: + ```bash + auditwheel repair ./dist/cherab-1.4.0rc1-cp38-cp38-linux_x86_64.whl + ``` + This will produce a wheel in the `./wheelhouse` directory which can be uploaded to PyPI. + +When Raysect wheels aren't available +------------------------------------ + +For Raysect 0.8 and later, the same procedure applies as above. +For Raysect 0.7.1 and below, a couple of additional steps are required. + +1. Follow steps 1-4 above to set up the container environment. +2. Create a virtual environment for building the wheel. For example, for Python 3.10: + ```bash + /opt/python/cp310-cp310/bin/python -m venv /tmp/cp310 + . /tmp/cp310/bin/activate + ``` +3. Install build, wheel Cython and the oldest supported Numpy into this environment. + ```bash + pip install build wheel cython oldest-supported-numpy + ``` +4. Install the required Raysect version. + ```bash + pip install raysect==0.7.1 + ``` +5. Use PyPA's `build` to build the wheel, but tell it to use this virtual + environment rather than creating a new isolated one. + ```bash + python -m build -n . + ``` +6. Run the auditwheel command as given in step 6 above to produce wheels with + the correct tag for uploading to PyPI. From 7711680667273edb895d313fc7968925d0897df0 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 3 Feb 2023 15:28:48 +0000 Subject: [PATCH 58/59] Remove duplicate changelog entry --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bf7c6d..1a9d87c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ Bug fixes: * Fixed generomak plasma edge data paths. * Fix and improve OpenCL utility functions. (#358) * Fixed Bremsstrahlung trapezium evaluation (#384). -* Fixed generomak plasma edge data paths. New: * Make f_profile (current flux) a read-only attribute of EFITEquilibrium. (#355) From cf77c57f11b88e50b7bb9eb2377449203da9991f Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 3 Feb 2023 17:06:52 +0000 Subject: [PATCH 59/59] Update version to 1.4.0 --- CHANGELOG.md | 2 +- cherab/core/VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a9d87c1..1ae0a7d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Project Changelog ================= -Release 1.4.0 (TBD) +Release 1.4.0 (3 Feb 2023) ------------------- API changes: diff --git a/cherab/core/VERSION b/cherab/core/VERSION index 40b48802..88c5fb89 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.4.0rc1 +1.4.0