From 27e99f1083027c698731289cdcb07bf4744248f2 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 14 Dec 2021 11:37:14 +0000 Subject: [PATCH 001/134] Add compatibility with raysect 0.8.0 Only changes needed to build the project without errors are included. --- .github/workflows/ci.yml | 4 ++-- cherab/core/VERSION | 2 +- cherab/tools/raytransfer/roughconductor.pyx | 5 +++-- pyproject.toml | 2 +- requirements.txt | 2 +- setup.py | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19ec060b..5fc5e55f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install Python dependencies run: python -m pip install cython>=0.28 numpy==${{ matrix.numpy-version }} scipy matplotlib - - name: Install Raysect from pypi - run: pip install raysect + - name: Install Raysect from Github + run: pip install git+https://github.com/raysect/source@development --global-option=build_ext --global-option="-j2" - name: Build cherab run: dev/build.sh - name: Run tests diff --git a/cherab/core/VERSION b/cherab/core/VERSION index f0bb29e7..fa710d85 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.3.0 +1.4.0dev1 diff --git a/cherab/tools/raytransfer/roughconductor.pyx b/cherab/tools/raytransfer/roughconductor.pyx index 04edf6f2..faef9cf1 100644 --- a/cherab/tools/raytransfer/roughconductor.pyx +++ b/cherab/tools/raytransfer/roughconductor.pyx @@ -18,7 +18,7 @@ # under the Licence. # -from raysect.optical cimport Point3D, Vector3D, AffineMatrix3D, World, Ray, Spectrum, new_vector3d +from raysect.optical cimport Point3D, Vector3D, AffineMatrix3D, World, Ray, Spectrum, new_vector3d, Intersection from libc.math cimport M_PI, sqrt from raysect.optical.material cimport RoughConductor cimport cython @@ -34,7 +34,8 @@ cdef class RToptimisedRoughConductor(RoughConductor): @cython.cdivision(True) cpdef Spectrum evaluate_shading(self, World world, Ray ray, Vector3D s_incoming, Vector3D s_outgoing, Point3D w_reflection_origin, Point3D w_transmission_origin, bint back_face, - AffineMatrix3D world_to_surface, AffineMatrix3D surface_to_world): + AffineMatrix3D world_to_surface, AffineMatrix3D surface_to_world, + Intersection intersection): cdef: double n, k diff --git a/pyproject.toml b/pyproject.toml index 8c92c2a1..503024fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["setuptools", "wheel", "numpy>=1.14", "cython>=0.28", "raysect==0.7.1"] +requires = ["setuptools", "wheel", "numpy>=1.14", "cython>=0.28", "raysect==0.8.0"] diff --git a/requirements.txt b/requirements.txt index 2b5e7b4e..d9462771 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ cython>=0.28 numpy>=1.14 scipy matplotlib -raysect==0.7.1 \ No newline at end of file +raysect==0.8.0 diff --git a/setup.py b/setup.py index db4bb706..76674a0a 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ "numpy>=1.14", "scipy", "matplotlib", - "raysect==0.7.1", + "raysect==0.8.0", "cython>=0.28", ], packages=find_packages(), From 6584c90aba35accbf6c56f17439c740d73a7ea2b Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 18 Nov 2022 17:16:37 +0300 Subject: [PATCH 002/134] Add ThermalCXLine model. --- cherab/core/atomic/rates.pxd | 4 +- cherab/core/atomic/rates.pyx | 24 ++++- cherab/core/model/plasma/__init__.pxd | 1 + cherab/core/model/plasma/__init__.py | 1 + cherab/core/model/plasma/thermal_cx.pxd | 35 +++++++ cherab/core/model/plasma/thermal_cx.pyx | 133 ++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 cherab/core/model/plasma/thermal_cx.pxd create mode 100644 cherab/core/model/plasma/thermal_cx.pyx diff --git a/cherab/core/atomic/rates.pxd b/cherab/core/atomic/rates.pxd index 1f844122..239fafc1 100644 --- a/cherab/core/atomic/rates.pxd +++ b/cherab/core/atomic/rates.pxd @@ -46,8 +46,8 @@ cdef class RecombinationPEC(_PECRate): pass -cdef class ThermalCXPEC(_PECRate): - pass +cdef class ThermalCXPEC: + cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999 cdef class BeamCXPEC: diff --git a/cherab/core/atomic/rates.pyx b/cherab/core/atomic/rates.pyx index 25b94e7e..5cd1916c 100644 --- a/cherab/core/atomic/rates.pyx +++ b/cherab/core/atomic/rates.pyx @@ -101,8 +101,8 @@ cdef class _PECRate: cpdef double evaluate(self, double density, double temperature) except? -1e999: """Returns a photon emissivity coefficient at given conditions. - :param temperature: Receiver ion temperature in eV. - :param density: Receiver ion density in m^-3 + :param density: Electron density in m^-3. + :param temperature: Electron temperature in eV. :return: The effective PEC rate in W/m^3. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -130,11 +130,27 @@ cdef class RecombinationPEC(_PECRate): pass -cdef class ThermalCXPEC(_PECRate): +cdef class ThermalCXPEC: """ Thermal charge exchange rate coefficient. """ - pass + + def __call__(self, double electron_density, double electron_temperature, donor_temperature): + """Returns a CX photon emissivity coefficient at the specified plasma conditions. + + This function just wraps the cython evaluate() method. + """ + return self.evaluate(electron_density, electron_temperature, donor_temperature) + + cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999: + """Returns a CX photon emissivity coefficient at given conditions. + + :param electron_density: Electron density in m^-3. + :param electron_temperature: Electron temperature in eV. + :param donor_temperature: Donor temperature in eV. + :return: The effective CX PEC rate in W/m^3. + """ + raise NotImplementedError("The evaluate() virtual method must be implemented.") cdef class BeamCXPEC: diff --git a/cherab/core/model/plasma/__init__.pxd b/cherab/core/model/plasma/__init__.pxd index 6971f828..c398fd30 100644 --- a/cherab/core/model/plasma/__init__.pxd +++ b/cherab/core/model/plasma/__init__.pxd @@ -20,4 +20,5 @@ from cherab.core.model.plasma.bremsstrahlung cimport Bremsstrahlung from cherab.core.model.plasma.impact_excitation cimport ExcitationLine from cherab.core.model.plasma.recombination cimport RecombinationLine +from cherab.core.model.plasma.thermal_cx cimport ThermalCXLine from cherab.core.model.plasma.total_radiated_power cimport TotalRadiatedPower diff --git a/cherab/core/model/plasma/__init__.py b/cherab/core/model/plasma/__init__.py index 52766032..436060b5 100644 --- a/cherab/core/model/plasma/__init__.py +++ b/cherab/core/model/plasma/__init__.py @@ -20,4 +20,5 @@ from .bremsstrahlung import Bremsstrahlung from .impact_excitation import ExcitationLine from .recombination import RecombinationLine +from .thermal_cx import ThermalCXLine from .total_radiated_power import TotalRadiatedPower diff --git a/cherab/core/model/plasma/thermal_cx.pxd b/cherab/core/model/plasma/thermal_cx.pxd new file mode 100644 index 00000000..636fddaa --- /dev/null +++ b/cherab/core/model/plasma/thermal_cx.pxd @@ -0,0 +1,35 @@ +# Copyright 2016-2018 Euratom +# Copyright 2016-2018 United Kingdom Atomic Energy Authority +# Copyright 2016-2018 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.atomic cimport Line +from cherab.core.plasma cimport PlasmaModel +from cherab.core.species cimport Species +from cherab.core.model.lineshape cimport LineShapeModel + + +cdef class ThermalCXLine(PlasmaModel): + + cdef: + Line _line + double _wavelength + Species _target_species + list _rates + LineShapeModel _lineshape + object _lineshape_class, _lineshape_args, _lineshape_kwargs + + cdef int _populate_cache(self) except -1 diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx new file mode 100644 index 00000000..f290649a --- /dev/null +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -0,0 +1,133 @@ +# Copyright 2016-2018 Euratom +# Copyright 2016-2018 United Kingdom Atomic Energy Authority +# Copyright 2016-2018 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 Spectrum, Point3D, Vector3D +from cherab.core cimport Plasma, AtomicData +from cherab.core.atomic cimport ThermalCXPEC +from cherab.core.model.lineshape cimport GaussianLine, LineShapeModel +from cherab.core.utility.constants cimport RECIP_4_PI + + +cdef class ThermalCXLine(PlasmaModel): + + def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, + object lineshape_args=None, object lineshape_kwargs=None): + + super().__init__(plasma, atomic_data) + + self._line = line + + self._lineshape_class = lineshape or GaussianLine + if not issubclass(self._lineshape_class, LineShapeModel): + raise TypeError("The attribute lineshape must be a subclass of LineShapeModel.") + + if lineshape_args: + self._lineshape_args = lineshape_args + else: + self._lineshape_args = [] + if lineshape_kwargs: + self._lineshape_kwargs = lineshape_kwargs + else: + self._lineshape_kwargs = {} + + # ensure that cache is initialised + self._change() + + def __repr__(self): + return ''.format(self._line.element.name, self._line.charge, self._line.transition) + + cpdef Spectrum emission(self, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef: + double ne, te, receiver_density, donor_density, donor_temperature, weighted_rate, radiance + Species species + ThermalCXPEC rate + + # cache data on first run + if self._target_species is None: + self._populate_cache() + + ne = self._plasma.get_electron_distribution().density(point.x, point.y, point.z) + if ne <= 0.0: + return spectrum + + te = self._plasma.get_electron_distribution().effective_temperature(point.x, point.y, point.z) + if te <= 0.0: + return spectrum + + receiver_density = self._target_species.distribution.density(point.x, point.y, point.z) + if receiver_density <= 0.0: + return spectrum + + weighted_rate = 0 + for species, rate in self._rates: + donor_density = species.distribution.density(point.x, point.y, point.z) + donor_temperature = species.distribution.effective_temperature(point.x, point.y, point.z) + weighted_rate += donor_density * rate.evaluate(ne, te, donor_temperature) + + # add emission line to spectrum + radiance = RECIP_4_PI * weighted_rate * receiver_density + return self._lineshape.add_line(radiance, point, direction, spectrum) + + cdef int _populate_cache(self) except -1: + + cdef: + int receiver_charge + Species species + ThermalCXPEC rate + + # sanity checks + 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._line is None: + raise RuntimeError("The emission line has not been set.") + + # locate target species + receiver_charge = self._line.charge + 1 + try: + self._target_species = self._plasma.composition.get(self._line.element, receiver_charge) + except ValueError: + raise RuntimeError("The plasma object does not contain the ion species for the specified CX line " + "(element={}, ionisation={}).".format(self._line.element.symbol, receiver_charge)) + + # obtain rate functions + self._rates = [] + for species in self._plasma.composition: + if species.charge < species.element.atomic_number: + rate = self._atomic_data.thermal_cx_pec(species.element, species.charge, # donor + self._line.element, receiver_charge, # receiver + self._line.transition) + self._rates.append((species, rate)) + + # identify wavelength + self._wavelength = self._atomic_data.wavelength(self._line.element, self._line.charge, self._line.transition) + + # instance line shape renderer + self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, + *self._lineshape_args, **self._lineshape_kwargs) + + def _change(self): + + # clear cache to force regeneration on first use + self._target_species = None + self._wavelength = 0.0 + self._rates = None + self._lineshape = None From 7b357e10b3b945d6aaad1a2f61e86b317f56ca20 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 18 Nov 2022 17:21:02 +0300 Subject: [PATCH 003/134] Add ThermalCXPEC and update atomic data interface. --- cherab/core/atomic/interface.pxd | 2 + cherab/core/atomic/interface.pyx | 3 + cherab/openadas/openadas.py | 42 ++++++++++ cherab/openadas/rates/pec.pxd | 18 ++-- cherab/openadas/rates/pec.pyx | 59 ++++++++++++- cherab/openadas/repository/pec.py | 134 +++++++++++++++++++++++++----- 6 files changed, 230 insertions(+), 28 deletions(-) diff --git a/cherab/core/atomic/interface.pxd b/cherab/core/atomic/interface.pxd index 9ada928e..b072bd1a 100644 --- a/cherab/core/atomic/interface.pxd +++ b/cherab/core/atomic/interface.pxd @@ -44,6 +44,8 @@ cdef class AtomicData: cpdef RecombinationPEC recombination_pec(self, Element ion, int charge, tuple transition) + cpdef ThermalCXPEC thermal_cx_pec(self, Element donor_ion, int donor_charge, Element receiver_ion, int receiver_charge, tuple transition) + cpdef TotalRadiatedPower total_radiated_power(self, Element element) cpdef LineRadiationPower line_radiated_power_rate(self, Element element, int charge) diff --git a/cherab/core/atomic/interface.pyx b/cherab/core/atomic/interface.pyx index e5219205..1a9702b6 100644 --- a/cherab/core/atomic/interface.pyx +++ b/cherab/core/atomic/interface.pyx @@ -75,6 +75,9 @@ cdef class AtomicData: cpdef RecombinationPEC recombination_pec(self, Element ion, int charge, tuple transition): raise NotImplementedError("The recombination() virtual method is not implemented for this atomic data source.") + cpdef ThermalCXPEC thermal_cx_pec(self, Element donor_ion, int donor_charge, Element receiver_ion, int receiver_charge, tuple transition): + raise NotImplementedError("The thermal_cx_pec() virtual method is not implemented for this atomic data source.") + cpdef TotalRadiatedPower total_radiated_power(self, Element element): raise NotImplementedError("The total_radiated_power() virtual method is not implemented for this atomic data source.") diff --git a/cherab/openadas/openadas.py b/cherab/openadas/openadas.py index 7e850bc4..5d42935a 100644 --- a/cherab/openadas/openadas.py +++ b/cherab/openadas/openadas.py @@ -386,6 +386,48 @@ def recombination_pec(self, ion, charge, transition): return RecombinationPEC(wavelength, data, extrapolate=self._permit_extrapolation) + def thermal_cx_pec(self, donor_element, donor_charge, receiver_element, receiver_charge, transition): + """ + Thermal CX photon emission coefficient for a given species. + + Open-ADAS data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used when permit_extrapolation is True. + + :param donor_element: Element object defining the donor ion type. + :param donor_charge: Charge state of the donor ion. + :param receiver_element: Element object defining the receiver ion type. + :param receiver_charge: Charge state of the receiver ion. + :param transition: Tuple containing (initial level, final level) of the receiver + in charge state receiver_charge - 1. + :return: Thermal charge exchange photon emission coefficient in W.m^3 + as a function of electron density, electron temperature and donor temperature. + """ + + # extract elements from isotopes because there are no isotope rates in ADAS + if isinstance(donor_element, Isotope): + donor_element = donor_element.element + + if isinstance(receiver_element, Isotope): + receiver_element = receiver_element.element + + try: + # read thermal CX rate from json file in the repository + data = repository.get_pec_thermal_cx_rate(donor_element, donor_charge, + receiver_element, receiver_charge, + transition, + repository_path=self._data_path) + + except RuntimeError: + if self._missing_rates_return_null: + return NullThermalCXPEC() + raise + + # obtain isotope's rest wavelength for a given transition + # the wavelength is used ot convert the PEC from photons/s/m3 to W/m3 + wavelength = self.wavelength(receiver_element, receiver_charge - 1, transition) + + return ThermalCXPEC(wavelength, data, extrapolate=self._permit_extrapolation) + def line_radiated_power_rate(self, ion, charge): """ Line radiated power coefficient for a given species. diff --git a/cherab/openadas/rates/pec.pxd b/cherab/openadas/rates/pec.pxd index 46a54cbe..57bc7560 100644 --- a/cherab/openadas/rates/pec.pxd +++ b/cherab/openadas/rates/pec.pxd @@ -19,7 +19,7 @@ from cherab.core cimport ImpactExcitationPEC as CoreImpactExcitationPEC from cherab.core cimport RecombinationPEC as CoreRecombinationPEC from cherab.core cimport ThermalCXPEC as CoreThermalCXPEC -from cherab.core.math cimport Function2D +from cherab.core.math cimport Function2D, Function3D cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): @@ -48,8 +48,14 @@ cdef class NullRecombinationPEC(CoreRecombinationPEC): pass -# cdef class CXThermalRate(CoreCXThermalRate): -# pass -# -# cdef class ThermalCXRate(CoreThermalCXRate): -# pass +cdef class ThermalCXPEC(CoreThermalCXPEC): + + cdef: + readonly dict raw_data + readonly double wavelength + readonly tuple density_range, temperature_range, donor_temperature_range + Function3D _rate + + +cdef class NullThermalCXPEC(CoreThermalCXPEC): + pass diff --git a/cherab/openadas/rates/pec.pyx b/cherab/openadas/rates/pec.pyx index eafc6c64..7a17b6a6 100644 --- a/cherab/openadas/rates/pec.pyx +++ b/cherab/openadas/rates/pec.pyx @@ -20,7 +20,7 @@ import numpy as np from libc.math cimport INFINITY, log10 -from raysect.core.math.function.float cimport Interpolator2DArray +from raysect.core.math.function.float cimport Interpolator2DArray, Interpolator3DArray from cherab.core.utility.conversion import PhotonToJ @@ -126,3 +126,60 @@ cdef class NullRecombinationPEC(CoreRecombinationPEC): cpdef double evaluate(self, double density, double temperature) except? -1e999: return 0.0 + + +cdef class ThermalCXPEC(CoreThermalCXPEC): + + def __init__(self, double wavelength, dict data, extrapolate=False): + """ + :param wavelength: Resting wavelength of corresponding emission line in nm. + :param data: Dictionary containing rate data. + :param extrapolate: Enable extrapolation (default=False). + """ + + self.wavelength = wavelength + self.raw_data = data + + # unpack + ne = data['ne'] + te = data['te'] + td = data['td'] + rate = data['rate'] + + # pre-convert data to W m^3 from Photons s^-1 cm^3 prior to interpolation + rate = np.log10(PhotonToJ.to(rate, wavelength)) + + # store limits of data + self.density_range = ne.min(), ne.max() + self.temperature_range = te.min(), te.max() + self.donor_temperature_range = td.min(), td.max() + + # interpolate rate + # using nearest extrapolation to avoid infinite values at 0 for some rates + extrapolation_type = 'nearest' if extrapolate else 'none' + self._rate = Interpolator3DArray(np.log10(ne), np.log10(te), np.log10(td), rate, 'cubic', extrapolation_type, INFINITY, INFINITY, INFINITY) + + cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999: + + # need to handle zeros, also density and temperature can become negative due to cubic interpolation + if electron_density < 1.e-300: + electron_density = 1.e-300 + + if electron_temperature < 1.e-300: + electron_temperature = 1.e-300 + + if donor_temperature < 1.e-300: + donor_temperature = 1.e-300 + + # calculate rate and convert from log10 space to linear space + return 10 ** self._rate.evaluate(log10(electron_density), log10(electron_temperature), log10(donor_temperature)) + + +cdef class NullThermalCXPEC(CoreThermalCXPEC): + """ + A PEC rate that always returns zero. + Needed for use cases where the required atomic data is missing. + """ + + cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999: + return 0.0 diff --git a/cherab/openadas/repository/pec.py b/cherab/openadas/repository/pec.py index 8eb867fc..cd07dbea 100644 --- a/cherab/openadas/repository/pec.py +++ b/cherab/openadas/repository/pec.py @@ -82,44 +82,39 @@ def add_pec_recombination_rate(element, charge, transition, rate, repository_pat }, repository_path) -def add_pec_thermalcx_rate(element, charge, transition, rate, repository_path=None): +def add_pec_thermal_cx_rate(donor_element, donor_charge, receiver_element, receiver_charge, transition, rate, repository_path=None): """ - Adds a single PEC thermalcx rate to the repository. + Adds a single PEC thermal CX rate to the repository. - If adding multiple rate, consider using the update_pec_rates() function + If adding multiple rate, consider using the update_pec_thermal_rates() function instead. The update function avoid repeatedly opening and closing the rate files. - :param element: - :param charge: + :param donor_element: + :param donor_charge: + :param receiver_element: + :param receiver_charge: :param transition: :param rate: :param repository_path: :return: """ + rates2update = RecursiveDict() + rates2update[donor_element][donor_charge][receiver_element][receiver_charge][transition] = rate - update_pec_rates({ - 'thermalcx': { - element: { - charge: { - transition: rate - } - } - } - }, repository_path) + update_pec_thermal_cx_rates(rates2update, repository_path) def update_pec_rates(rates, repository_path=None): """ - PEC rate file structure + Excitation and recombination PEC rate file structure /pec///.json """ valid_classes = [ 'excitation', - 'recombination', - 'thermalcx' + 'recombination' ] repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -183,6 +178,83 @@ def update_pec_rates(rates, repository_path=None): json.dump(content, f, indent=2, sort_keys=True) +def update_pec_thermal_cx_rates(rates, repository_path=None): + """ + Thermal CX PEC rate file structure + + /pec/thermal_cx////.json + """ + repository_path = repository_path or DEFAULT_REPOSITORY_PATH + + for donor_element, donor_charge_states in rates.items(): + for donor_charge, receiver_elements in donor_charge_states.items(): + for receiver_element, receiver_charge_states in receiver_elements.items(): + for receiver_charge, transitions in receiver_charge_states.items(): + + # sanitise and validate + if not isinstance(donor_element, Element): + raise TypeError('The donor element must be an Element object.') + + if not valid_charge(donor_element, donor_charge + 1): + raise ValueError('Donor charge state is equal to or larger than the number of protons in the element.') + + if not isinstance(receiver_element, Element): + raise TypeError('The receiver element must be an Element object.') + + if not valid_charge(receiver_element, receiver_charge): + raise ValueError('Receiver charge state is larger than the number of protons in the element.') + + rate_path = 'pec/thermal_cx/{0}/{1}/{2}/{3}.json'.format(donor_element.symbol.lower(), donor_charge, + receiver_element.symbol.lower(), receiver_charge) + path = os.path.join(repository_path, rate_path) + + # read in any existing rates + try: + with open(path, 'r') as f: + content = RecursiveDict.from_dict(json.load(f)) + except FileNotFoundError: + content = RecursiveDict() + + # add/replace data for a transition + for transition, data in transitions.items(): + key = encode_transition(transition) + + # sanitise/validate data + ne = np.array(data['ne'], np.float64) + te = np.array(data['te'], np.float64) + td = np.array(data['td'], np.float64) + rate = np.array(data['rate'], np.float64) + + if ne.ndim != 1: + raise ValueError('Electron density array must be a 1D array.') + + if te.ndim != 1: + raise ValueError('Electron temperature array must be a 1D array.') + + if td.ndim != 1: + raise ValueError('Donor temperature array must be a 1D array.') + + if (ne.shape[0], te.shape[0], td.shape[0]) != rate.shape: + raise ValueError('Electron density, electron temperature, donor temperature' + ' and rate data arrays have inconsistent sizes.') + + content[key] = { + 'ne': ne.tolist(), + 'te': te.tolist(), + 'td': td.tolist(), + 'rate': rate.tolist() + } + + # create directory structure if missing + directory = os.path.dirname(path) + if not os.path.isdir(directory): + os.makedirs(directory) + + # write new data + with open(path, 'w') as f: + json.dump(content, f, indent=2, sort_keys=True) + + def get_pec_excitation_rate(element, charge, transition, repository_path=None): return _get_pec_rate('excitation', element, charge, transition, repository_path) @@ -191,10 +263,6 @@ def get_pec_recombination_rate(element, charge, transition, repository_path=None return _get_pec_rate('recombination', element, charge, transition, repository_path) -def get_pec_thermalcx_rate(element, charge, transition, repository_path=None): - return _get_pec_rate('thermalcx', element, charge, transition, repository_path) - - def _get_pec_rate(cls, element, charge, transition, repository_path=None): repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -214,3 +282,27 @@ def _get_pec_rate(cls, element, charge, transition, repository_path=None): return d + +def get_pec_thermal_cx_rate(donor_element, donor_charge, receiver_element, receiver_charge, transition, repository_path=None): + + repository_path = repository_path or DEFAULT_REPOSITORY_PATH + + rate_path = 'pec/thermal_cx/{0}/{1}/{2}/{3}.json'.format(donor_element.symbol.lower(), donor_charge, + receiver_element.symbol.lower(), receiver_charge) + path = os.path.join(repository_path, rate_path) + try: + with open(path, 'r') as f: + content = json.load(f) + d = content[encode_transition(transition)] + except (FileNotFoundError, KeyError): + raise RuntimeError('Requested thermal charge-exchange PEC (donor={}, donor charge={}, receiver={}, receiver charge={})' + ' is not available.' + ''.format(donor_element.symbol, donor_charge, receiver_element.symbol, receiver_charge)) + + # convert to numpy arrays + d['ne'] = np.array(d['ne'], np.float64) + d['te'] = np.array(d['te'], np.float64) + d['td'] = np.array(d['td'], np.float64) + d['rate'] = np.array(d['rate'], np.float64) + + return d From f69b74c8784f6bfda988b71ec27be161e74ade85 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 23 Nov 2022 22:01:12 +0300 Subject: [PATCH 004/134] Add cylindrical and periodic mappers. --- cherab/core/math/__init__.py | 13 +- cherab/core/math/mappers.pxd | 81 +++++-- cherab/core/math/mappers.pyx | 447 ++++++++++++++++++++++++++++++++++- 3 files changed, 514 insertions(+), 27 deletions(-) diff --git a/cherab/core/math/__init__.py b/cherab/core/math/__init__.py index b3e9a031..4f821332 100644 --- a/cherab/core/math/__init__.py +++ b/cherab/core/math/__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"); @@ -31,6 +31,11 @@ from .caching import Caching1D, Caching2D, Caching3D from .clamp import ClampOutput1D, ClampOutput2D, ClampOutput3D from .clamp import ClampInput1D, ClampInput2D, ClampInput3D -from .mappers import IsoMapper2D, IsoMapper3D, Swizzle2D, Swizzle3D, AxisymmetricMapper, VectorAxisymmetricMapper +from .mappers import IsoMapper2D, IsoMapper3D +from .mappers import Swizzle2D, Swizzle3D +from .mappers import AxisymmetricMapper, VectorAxisymmetricMapper +from .mappers import CylindricalMapper, VectorCylindricalMapper +from .mappers import PeriodicMapper1D, PeriodicMapper2D, PeriodicMapper3D +from .mappers import VectorPeriodicMapper1D, VectorPeriodicMapper2D, VectorPeriodicMapper3D from .mask import PolygonMask2D from .slice import Slice2D, Slice3D diff --git a/cherab/core/math/mappers.pxd b/cherab/core/math/mappers.pxd index 5b455d9d..4fee0cbf 100644 --- a/cherab/core/math/mappers.pxd +++ b/cherab/core/math/mappers.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"); @@ -16,7 +16,11 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from cherab.core.math.function cimport Function1D, Function2D, Function3D, VectorFunction2D, VectorFunction3D +from libc.math cimport fmod +from raysect.core.math.function.float cimport Function1D, Function2D, Function3D +from raysect.core.math.function.vector3d cimport Function1D as VectorFunction1D +from raysect.core.math.function.vector3d cimport Function2D as VectorFunction2D +from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D from raysect.core cimport Vector3D @@ -26,8 +30,6 @@ cdef class IsoMapper2D(Function2D): readonly Function1D function1d readonly Function2D function2d - cdef double evaluate(self, double x, double y) except? -1e999 - cdef class IsoMapper3D(Function3D): @@ -35,15 +37,11 @@ cdef class IsoMapper3D(Function3D): readonly Function3D function3d readonly Function1D function1d - cdef double evaluate(self, double x, double y, double z) except? -1e999 - cdef class Swizzle2D(Function2D): cdef readonly Function2D function2d - cdef double evaluate(self, double x, double y) except? -1e999 - cdef class Swizzle3D(Function3D): @@ -51,18 +49,71 @@ cdef class Swizzle3D(Function3D): readonly Function3D function3d int shape[3] - cdef double evaluate(self, double x, double y, double z) except? -1e999 - cdef class AxisymmetricMapper(Function3D): cdef readonly Function2D function2d - cdef double evaluate(self, double x, double y, double z) except? -1e999 - cdef class VectorAxisymmetricMapper(VectorFunction3D): cdef readonly VectorFunction2D function2d - cdef Vector3D evaluate(self, double x, double y, double z) \ No newline at end of file + +cdef class CylindricalMapper(Function3D): + + cdef readonly Function3D function3d + + +cdef class VectorCylindricalMapper(VectorFunction3D): + + cdef readonly VectorFunction3D function3d + + +cdef inline double remainder(double x1, double x2) nogil: + if x2 == 0: + return x1 + x1 = fmod(x1, x2) + return x1 + x2 if (x1 < 0) else x1 + + +cdef class PeriodicMapper1D(Function1D): + + cdef: + readonly Function1D function1d + readonly double period + + +cdef class PeriodicMapper2D(Function2D): + + cdef: + readonly Function2D function2d + double period_x, period_y + + +cdef class PeriodicMapper3D(Function3D): + + cdef: + readonly Function3D function3d + readonly double period_x, period_y, period_z + + +cdef class VectorPeriodicMapper1D(VectorFunction1D): + + cdef: + readonly VectorFunction1D function1d + readonly double period + + +cdef class VectorPeriodicMapper2D(VectorFunction2D): + + cdef: + readonly VectorFunction2D function2d + readonly double period_x, period_y + + +cdef class VectorPeriodicMapper3D(VectorFunction3D): + + cdef: + readonly VectorFunction3D function3d + readonly double period_x, period_y, period_z diff --git a/cherab/core/math/mappers.pyx b/cherab/core/math/mappers.pyx index 5ff224a3..63f868a9 100644 --- a/cherab/core/math/mappers.pyx +++ b/cherab/core/math/mappers.pyx @@ -20,7 +20,10 @@ from libc.math cimport sqrt, atan2, M_PI -from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d, autowrap_function3d, autowrap_vectorfunction2d +from raysect.core.math.function.float cimport autowrap_function1d, autowrap_function2d, autowrap_function3d +from raysect.core.math.function.vector3d cimport autowrap_function1d as autowrap_vectorfunction1d +from raysect.core.math.function.vector3d cimport autowrap_function2d as autowrap_vectorfunction2d +from raysect.core.math.function.vector3d cimport autowrap_function3d as autowrap_vectorfunction3d from raysect.core cimport rotate_z cimport cython @@ -251,13 +254,10 @@ cdef class AxisymmetricMapper(Function3D): def __init__(self, object function2d): if not callable(function2d): - raise TypeError("Function3D is not callable.") + raise TypeError("Function2D is not callable.") self.function2d = autowrap_function2d(function2d) - def __call__(self, double x, double y, double z): - return self.evaluate(x, y, z) - cdef double evaluate(self, double x, double y, double z) except? -1e999: """Return the value of function2d when it is y-axis symmetrically extended to the 3D space.""" @@ -299,13 +299,11 @@ cdef class VectorAxisymmetricMapper(VectorFunction3D): self.function2d = autowrap_vectorfunction2d(vectorfunction2d) - def __call__(self, double x, double y, double z): - return self.evaluate(x, y, z) - @cython.cdivision(True) cdef Vector3D evaluate(self, double x, double y, double z): """Return the value of function2d when it is y-axis symmetrically extended to the 3D space.""" + cdef double r, phi # convert to cylindrical coordinates phi = atan2(y, x) / M_PI * 180 @@ -313,3 +311,436 @@ cdef class VectorAxisymmetricMapper(VectorFunction3D): # perform axisymmetric rotation return self.function2d.evaluate(r, z).transform(rotate_z(phi)) + + +cdef class CylindricalMapper(Function3D): + """ + Converts Cartesian coordinates to cylindrical coordinates and calls a 3D function + defined in cylindrical coordinates, f(r, :math:`\\phi`, z). + + The angular coordinate is given in radians. + + Positive angular coordinate is measured counterclockwise from the xz plane. + + :param Function3D function3d: The function to be mapped. Must be defined + in the interval (:math:`-\\pi`, :math:`\\pi`] + on the angular axis. + + .. code-block:: pycon + + >>> from math import sqrt, cos + >>> from cherab.core.math import CylindricalMapper + >>> + >>> def my_func(r, phi, z): + >>> return r * cos(phi) + >>> + >>> f = CylindricalMapper(my_func) + >>> + >>> f(1, 0, 0) + 1.0 + >>> f(0.5 * sqrt(3), 0.5, 0) + 0.8660254037844385 + """ + + def __init__(self, object function3d): + + if not callable(function3d): + raise TypeError("Function3D is not callable.") + + self.function3d = autowrap_function3d(function3d) + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + """ + Converts to cylindrical coordinates and evaluates the function + defined in cylindrical coordinates. + """ + cdef double r, phi + + r = sqrt(x * x + y * y) + phi = atan2(y, x) + + return self.function3d.evaluate(r, phi, z) + + +cdef class VectorCylindricalMapper(VectorFunction3D): + """ + Converts Cartesian coordinates to cylindrical coordinates, calls + a 3D vector function defined in cylindrical coordinates, f(r, :math:`\\phi`, z), + then converts the returned 3D vector to Cartesian coordinates. + + The angular coordinate is given in radians. + + Positive angular coordinate is measured counterclockwise from the xz plane. + + :param VectorFunction3D function3d: The function to be mapped. Must be defined + in the interval (:math:`-\\pi`, :math:`\\pi`] + on the angular axis. + + .. code-block:: pycon + + >>> from math import sqrt, cos + >>> from raysect.core.math import Vector3D + >>> from cherab.core.math import VectorCylindricalMapper + >>> + >>> def my_vec_func(r, phi, z): + >>> v = Vector3D(0, 1, 0) + >>> v.length = r * abs(cos(phi)) + >>> return v + >>> + >>> f = VectorCylindricalMapper(my_vec_func) + >>> + >>> f(1, 0, 0) + Vector3D(0.0, 1.0, 0.0) + >>> f(1/sqrt(2), 1/sqrt(2), 0) + Vector3D(-0.5, 0.5, 0.0) + """ + + def __init__(self, object function3d): + + if not callable(function3d): + raise TypeError("Function3D is not callable.") + + self.function3d = autowrap_vectorfunction3d(function3d) + + @cython.cdivision(True) + cdef Vector3D evaluate(self, double x, double y, double z): + """ + Converts to cylindrical coordinates, evaluates the vector function + defined in cylindrical coordinates and rotates the resulting vector + around z-axis. + """ + cdef double r, phi + + r = sqrt(x * x + y * y) + phi = atan2(y, x) + + return self.function3d.evaluate(r, phi, z).transform(rotate_z(phi / M_PI * 180)) + + +cdef class PeriodicMapper1D(Function1D): + """ + Maps a periodic 1D function into 1D space. + + :param Function1D function1d: The periodic 1D function to map defined + in the [0, period) interval. + :param double period: The period of the function. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicMapper1D + >>> + >>> def f1(x): + >>> return x + >>> + >>> f2 = PeriodicMapper1D(f1, 1.) + >>> + >>> f2(1.5) + 0.5 + >>> f2(-0.3) + 0.7 + """ + + def __init__(self, object function1d, double period): + + if not callable(function1d): + raise TypeError("function1d is not callable.") + + self.function1d = autowrap_function1d(function1d) + + if period <= 0: + raise ValueError("Argument period must be positive.") + + self.period = period + + cdef double evaluate(self, double x) except? -1e999: + """Return the value of periodic function.""" + + return self.function1d.evaluate(remainder(x, self.period)) + + +cdef class PeriodicMapper2D(Function2D): + """ + Maps a periodic 2D function into 2D space. + + Set period_x/period_y to 0 if the function is not periodic along x/y axis. + + :param Function2D function2d: The periodic 2D function to map defined + in the ([0, period_x), [0, period_y)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicMapper2D + >>> + >>> def f1(x, y): + >>> return x * y + >>> + >>> f2 = PeriodicMapper2D(f1, 1., 1.) + >>> + >>> f2(1.5, 1.5) + 0.25 + >>> f2(-0.3, -1.3) + 0.49 + >>> + >>> f3 = PeriodicMapper2D(f1, 1., 0) + >>> + >>> f3(1.5, 1.5) + 0.75 + >>> f3(-0.3, -1.3) + -0.91 + """ + + def __init__(self, object function2d, double period_x, double period_y): + + if not callable(function2d): + raise TypeError("function2d is not callable.") + + self.function2d = autowrap_function2d(function2d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + + cdef double evaluate(self, double x, double y) except? -1e999: + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + + return self.function2d.evaluate(x, y) + + +cdef class PeriodicMapper3D(Function3D): + """ + Maps a periodic 3D function into 3D space. + + Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. + + :param Function3D function3d: The periodic 3D function to map defined in the + ([0, period_x), [0, period_y), [0, period_z)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + :param double period_z: The period of the function along z-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicMapper3D + >>> + >>> def f1(x, y, z): + >>> return x * y * z + >>> + >>> f2 = PeriodicMapper3D(f1, 1., 1., 1.) + >>> + >>> f2(1.5, 1.5, 1.5) + 0.125 + >>> f2(-0.3, -1.3, -2.3) + 0.343 + >>> + >>> f3 = PeriodicMapper3D(f1, 0, 1., 0) + >>> + >>> f3(1.5, 1.5, 1.5) + 1.125 + >>> f3(-0.3, -1.3, -0.3) + 0.063 + """ + + def __init__(self, object function3d, double period_x, double period_y, double period_z): + + if not callable(function3d): + raise TypeError("function2d is not callable.") + + self.function3d = autowrap_function3d(function3d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + if period_z < 0: + raise ValueError("Argument period_z must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + self.period_z = period_z + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + z = remainder(z, self.period_z) + + return self.function3d.evaluate(x, y, z) + + +cdef class VectorPeriodicMapper1D(VectorFunction1D): + """ + Maps a periodic 1D vector function into 1D space. + + :param VectorFunction1D function1d: The periodic 1D vector function to map + defined in the [0, period) interval. + :param double period: The period of the function. + + .. code-block:: pycon + + >>> from raysect.core.math import Vector3D + >>> from cherab.core.math import VectorPeriodicMapper1D + >>> + >>> def f1(x): + >>> return Vector3D(x, 0, 0) + >>> + >>> f2 = VectorPeriodicMapper1D(f1, 1.) + >>> + >>> f2(1.5) + Vector3D(0.5, 0, 0) + >>> f2(-0.3) + Vector3D(0.7, 0, 0) + """ + + def __init__(self, object function1d, double period): + + if not callable(function1d): + raise TypeError("function1d is not callable.") + + self.function1d = autowrap_vectorfunction1d(function1d) + + if period <= 0: + raise ValueError("Argument period must be positive.") + + self.period = period + + cdef Vector3D evaluate(self, double x): + """Return the value of periodic function.""" + + return self.function1d.evaluate(remainder(x, self.period)) + + +cdef class VectorPeriodicMapper2D(VectorFunction2D): + """ + Maps a periodic 2D vector function into 2D space. + + Set period_x/period_y to 0 if the function is not periodic along x/y axis. + + :param VectorFunction2D function2d: The periodic 2D vector function to map defined in + the ([0, period_x), [0, period_y)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import VectorPeriodicMapper2D + >>> + >>> def f1(x, y): + >>> return Vector3D(x, y, 0) + >>> + >>> f2 = VectorPeriodicMapper2D(f1, 1., 1.) + >>> + >>> f2(1.5, 1.5) + Vector3D(0.5, 0.5, 0) + >>> f2(-0.3, -1.3) + Vector3D(0.7, 0.7, 0) + >>> + >>> f3 = VectorPeriodicMapper2D(f1, 1., 0) + >>> + >>> f3(1.5, 1.5) + Vector3D(0.5, 1.5, 0) + >>> f3(-0.3, -1.3) + Vector3D(0.7, -1.3, 0) + """ + + def __init__(self, object function2d, double period_x, double period_y): + + if not callable(function2d): + raise TypeError("function2d is not callable.") + + self.function2d = autowrap_vectorfunction2d(function2d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + + cdef Vector3D evaluate(self, double x, double y): + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + + return self.function2d.evaluate(x, y) + + +cdef class VectorPeriodicMapper3D(VectorFunction3D): + """ + Maps a periodic 3D vector function into 3D space. + + Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. + + :param VectorFunction3D function3d: The periodic 3D vector function to map defined in the + ([0, period_x), [0, period_y), [0, period_z)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + :param double period_z: The period of the function along z-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicMapper3D + >>> + >>> def f1(x, y, z): + >>> return Vector3D(x, y, z) + >>> + >>> f2 = VectorPeriodicMapper3D(f1, 1., 1., 1.) + >>> + >>> f2(1.5, 1.5, 1.5) + Vector3D(0.5, 0.5, 0.5) + >>> f2(-0.3, -1.3, -2.3) + Vector3D(0.7, 0.7, 0.7) + >>> + >>> f3 = VectorPeriodicMapper3D(f1, 0, 1., 0) + >>> + >>> f3(1.5, 0.5, 1.5) + Vector3D(1.5, 0.5, 1.5) + """ + + def __init__(self, object function3d, double period_x, double period_y, double period_z): + + if not callable(function3d): + raise TypeError("function2d is not callable.") + + self.function3d = autowrap_vectorfunction3d(function3d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + if period_z < 0: + raise ValueError("Argument period_z must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + self.period_z = period_z + + cdef Vector3D evaluate(self, double x, double y, double z): + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + z = remainder(z, self.period_z) + + return self.function3d.evaluate(x, y, z) From 251b11b457cb1daa9cd5c82017a9bfd6c41aa94d Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 25 Nov 2022 22:50:35 +0300 Subject: [PATCH 005/134] Add tests for periodic and cylindrical mappers. --- cherab/core/math/tests/test_mappers.py | 114 ++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 3 deletions(-) diff --git a/cherab/core/math/tests/test_mappers.py b/cherab/core/math/tests/test_mappers.py index 777a6a85..69a5f771 100644 --- a/cherab/core/math/tests/test_mappers.py +++ b/cherab/core/math/tests/test_mappers.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"); @@ -16,6 +16,7 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. +from raysect.core.math import Vector3D from cherab.core.math import mappers import numpy as np import unittest @@ -142,5 +143,112 @@ def test_axisymmetric_mapper_invalid_arg(self): """An error must be raised if the given argument is not callable.""" self.assertRaises(TypeError, mappers.AxisymmetricMapper, "blah") + def test_cylindrical_mapper(self): + """Cylindrical mapper.""" + def f3d(r, phi, z): return r * np.cos(phi) + z + cyl_func = mappers.CylindricalMapper(f3d) + self.assertAlmostEqual(cyl_func(1., 1., 0.5), + f3d(np.sqrt(2.), 0.25 * np.pi, 0.5), + places=10) + + def test_cylindrical_mapper_invalid_arg(self): + """An error must be raised if the given argument is not callable.""" + self.assertRaises(TypeError, mappers.CylindricalMapper, "blah") + + def test_vector_cylindrical_mapper(self): + """Cylindrical mapper.""" + def f3d(r, phi, z): return Vector3D(np.sin(phi), r * z, np.cos(phi)) + cyl_func = mappers.VectorCylindricalMapper(f3d) + vec1 = cyl_func(1., 1., 1.) + vec2 = Vector3D(-0.5, 1.5, 1 / np.sqrt(2)) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + def test_vector_cylindrical_mapper_invalid_arg(self): + """An error must be raised if the given argument is not callable.""" + self.assertRaises(TypeError, mappers.VectorCylindricalMapper, "blah") + + def test_periodic_mapper_1d(self): + """1D periodic mapper""" + period_func = mappers.PeriodicMapper1D(self.function1d, np.pi) + self.assertAlmostEqual(period_func(1.4 * np.pi), + self.function1d(0.4 * np.pi), + places=10) + self.assertAlmostEqual(period_func(-0.4 * np.pi), + self.function1d(0.6 * np.pi), + places=10) + + def test_periodic_mapper_1d_invalid_arg(self): + """1D periodic mapper. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, mappers.PeriodicMapper1D, "blah", np.pi) + # period is not a number + self.assertRaises(TypeError, mappers.PeriodicMapper1D, self.function1d, "blah") + # period is negative + self.assertRaises(ValueError, mappers.PeriodicMapper1D, self.function1d, -1) + + def test_periodic_mapper_2d(self): + """2D periodic mapper""" + period_func = mappers.PeriodicMapper2D(self.function2d, 1, np.pi) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(0.6, 0.4 * np.pi), + places=10) + # Periodic only along x + period_func = mappers.PeriodicMapper2D(self.function2d, 1., 0) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(0.6, 1.4 * np.pi), + places=10) + # Periodic only along y + period_func = mappers.PeriodicMapper2D(self.function2d, 0, np.pi) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(-0.4, 0.4 * np.pi), + places=10) + + def test_periodic_mapper_2d_invalid_arg(self): + """2D periodic mapper. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, mappers.PeriodicMapper2D, "blah", np.pi, np.pi) + # period is not a number + self.assertRaises(TypeError, mappers.PeriodicMapper2D, self.function2d, "blah", np.pi) + self.assertRaises(TypeError, mappers.PeriodicMapper2D, self.function2d, np.pi, "blah") + # period is negative + self.assertRaises(ValueError, mappers.PeriodicMapper2D, self.function2d, -1, np.pi) + self.assertRaises(ValueError, mappers.PeriodicMapper2D, self.function2d, np.pi, -1) + + def test_periodic_mapper_3d(self): + """3D periodic mapper""" + period_func = mappers.PeriodicMapper3D(self.function3d, 1, 1, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 0.4, 0.1), + places=10) + # Periodic only along y and z + period_func = mappers.PeriodicMapper3D(self.function3d, 0, 1, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(-0.4, 0.4, 0.1), + places=10) + # Periodic only along x and z + period_func = mappers.PeriodicMapper3D(self.function3d, 1, 0, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 1.4, 0.1), + places=10) + # Periodic only along x and y + period_func = mappers.PeriodicMapper3D(self.function3d, 1, 1, 0) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 0.4, 2.1), + places=10) + + def test_periodic_mapper_3d_invalid_arg(self): + """2D periodic mapper. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, mappers.PeriodicMapper3D, "blah", np.pi, np.pi, np.pi) + # period is not a number + self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, "blah", np.pi, np.pi) + self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, np.pi, "blah", np.pi) + self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, np.pi, np.pi, "blah") + # period is negative + self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, -1, np.pi, np.pi) + self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, np.pi, -1, np.pi) + self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, np.pi, np.pi, -1) + + if __name__ == '__main__': unittest.main() \ No newline at end of file From c0a60eeda1d7c64921767aae9cc4c4ca449a9fcf Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 25 Nov 2022 23:14:16 +0300 Subject: [PATCH 006/134] Update CHANGELOG. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbe69a21..b8a3bc5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ 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 PeriodicMapperXD and VectorPeriodicMapperXD to support the data simulated with periodic boundary conditions. (#387) +* Add CylindricalMapper and VectorCylindricalMapper to map functions from cylindrical to Cartesian coordinates. (#387) Bug Fixes: ---------- From 73b87df644b09e8b536e14f60b95ab5976ba9f2f Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 26 Dec 2022 14:06:40 +0300 Subject: [PATCH 007/134] Rename periodic/cylindrical mappers to periodic/cylindrical transforms and move them to cherab.core.math.transform module. --- cherab/core/math/__init__.pxd | 1 + cherab/core/math/__init__.py | 6 +- cherab/core/math/mappers.pxd | 62 --- cherab/core/math/mappers.pyx | 436 +-------------------- cherab/core/math/tests/test_mappers.py | 114 +----- cherab/core/math/tests/test_transform.py | 157 ++++++++ cherab/core/math/transform/__init__.pxd | 20 + cherab/core/math/transform/__init__.py | 21 + cherab/core/math/transform/cylindrical.pxd | 30 ++ cherab/core/math/transform/cylindrical.pyx | 128 ++++++ cherab/core/math/transform/periodic.pxd | 72 ++++ cherab/core/math/transform/periodic.pyx | 352 +++++++++++++++++ 12 files changed, 796 insertions(+), 603 deletions(-) create mode 100644 cherab/core/math/tests/test_transform.py create mode 100644 cherab/core/math/transform/__init__.pxd create mode 100644 cherab/core/math/transform/__init__.py create mode 100644 cherab/core/math/transform/cylindrical.pxd create mode 100644 cherab/core/math/transform/cylindrical.pyx create mode 100644 cherab/core/math/transform/periodic.pxd create mode 100644 cherab/core/math/transform/periodic.pyx diff --git a/cherab/core/math/__init__.pxd b/cherab/core/math/__init__.pxd index 99e92fbe..710ee381 100644 --- a/cherab/core/math/__init__.pxd +++ b/cherab/core/math/__init__.pxd @@ -25,3 +25,4 @@ from cherab.core.math.clamp cimport * from cherab.core.math.mappers cimport * from cherab.core.math.mask cimport * from cherab.core.math.slice cimport * +from cherab.core.math.transform cimport * diff --git a/cherab/core/math/__init__.py b/cherab/core/math/__init__.py index 4f821332..85336afa 100644 --- a/cherab/core/math/__init__.py +++ b/cherab/core/math/__init__.py @@ -34,8 +34,8 @@ from .mappers import IsoMapper2D, IsoMapper3D from .mappers import Swizzle2D, Swizzle3D from .mappers import AxisymmetricMapper, VectorAxisymmetricMapper -from .mappers import CylindricalMapper, VectorCylindricalMapper -from .mappers import PeriodicMapper1D, PeriodicMapper2D, PeriodicMapper3D -from .mappers import VectorPeriodicMapper1D, VectorPeriodicMapper2D, VectorPeriodicMapper3D from .mask import PolygonMask2D from .slice import Slice2D, Slice3D +from .transform import CylindricalTransform, VectorCylindricalTransform +from .transform import PeriodicTransform1D, PeriodicTransform2D, PeriodicTransform3D +from .transform import VectorPeriodicTransform1D, VectorPeriodicTransform2D, VectorPeriodicTransform3D diff --git a/cherab/core/math/mappers.pxd b/cherab/core/math/mappers.pxd index 4fee0cbf..3835e949 100644 --- a/cherab/core/math/mappers.pxd +++ b/cherab/core/math/mappers.pxd @@ -16,12 +16,9 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from libc.math cimport fmod from raysect.core.math.function.float cimport Function1D, Function2D, Function3D -from raysect.core.math.function.vector3d cimport Function1D as VectorFunction1D from raysect.core.math.function.vector3d cimport Function2D as VectorFunction2D from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D -from raysect.core cimport Vector3D cdef class IsoMapper2D(Function2D): @@ -58,62 +55,3 @@ cdef class AxisymmetricMapper(Function3D): cdef class VectorAxisymmetricMapper(VectorFunction3D): cdef readonly VectorFunction2D function2d - - -cdef class CylindricalMapper(Function3D): - - cdef readonly Function3D function3d - - -cdef class VectorCylindricalMapper(VectorFunction3D): - - cdef readonly VectorFunction3D function3d - - -cdef inline double remainder(double x1, double x2) nogil: - if x2 == 0: - return x1 - x1 = fmod(x1, x2) - return x1 + x2 if (x1 < 0) else x1 - - -cdef class PeriodicMapper1D(Function1D): - - cdef: - readonly Function1D function1d - readonly double period - - -cdef class PeriodicMapper2D(Function2D): - - cdef: - readonly Function2D function2d - double period_x, period_y - - -cdef class PeriodicMapper3D(Function3D): - - cdef: - readonly Function3D function3d - readonly double period_x, period_y, period_z - - -cdef class VectorPeriodicMapper1D(VectorFunction1D): - - cdef: - readonly VectorFunction1D function1d - readonly double period - - -cdef class VectorPeriodicMapper2D(VectorFunction2D): - - cdef: - readonly VectorFunction2D function2d - readonly double period_x, period_y - - -cdef class VectorPeriodicMapper3D(VectorFunction3D): - - cdef: - readonly VectorFunction3D function3d - readonly double period_x, period_y, period_z diff --git a/cherab/core/math/mappers.pyx b/cherab/core/math/mappers.pyx index 63f868a9..f1326f12 100644 --- a/cherab/core/math/mappers.pyx +++ b/cherab/core/math/mappers.pyx @@ -20,10 +20,9 @@ from libc.math cimport sqrt, atan2, M_PI +from raysect.core.math cimport Vector3D from raysect.core.math.function.float cimport autowrap_function1d, autowrap_function2d, autowrap_function3d -from raysect.core.math.function.vector3d cimport autowrap_function1d as autowrap_vectorfunction1d from raysect.core.math.function.vector3d cimport autowrap_function2d as autowrap_vectorfunction2d -from raysect.core.math.function.vector3d cimport autowrap_function3d as autowrap_vectorfunction3d from raysect.core cimport rotate_z cimport cython @@ -311,436 +310,3 @@ cdef class VectorAxisymmetricMapper(VectorFunction3D): # perform axisymmetric rotation return self.function2d.evaluate(r, z).transform(rotate_z(phi)) - - -cdef class CylindricalMapper(Function3D): - """ - Converts Cartesian coordinates to cylindrical coordinates and calls a 3D function - defined in cylindrical coordinates, f(r, :math:`\\phi`, z). - - The angular coordinate is given in radians. - - Positive angular coordinate is measured counterclockwise from the xz plane. - - :param Function3D function3d: The function to be mapped. Must be defined - in the interval (:math:`-\\pi`, :math:`\\pi`] - on the angular axis. - - .. code-block:: pycon - - >>> from math import sqrt, cos - >>> from cherab.core.math import CylindricalMapper - >>> - >>> def my_func(r, phi, z): - >>> return r * cos(phi) - >>> - >>> f = CylindricalMapper(my_func) - >>> - >>> f(1, 0, 0) - 1.0 - >>> f(0.5 * sqrt(3), 0.5, 0) - 0.8660254037844385 - """ - - def __init__(self, object function3d): - - if not callable(function3d): - raise TypeError("Function3D is not callable.") - - self.function3d = autowrap_function3d(function3d) - - cdef double evaluate(self, double x, double y, double z) except? -1e999: - """ - Converts to cylindrical coordinates and evaluates the function - defined in cylindrical coordinates. - """ - cdef double r, phi - - r = sqrt(x * x + y * y) - phi = atan2(y, x) - - return self.function3d.evaluate(r, phi, z) - - -cdef class VectorCylindricalMapper(VectorFunction3D): - """ - Converts Cartesian coordinates to cylindrical coordinates, calls - a 3D vector function defined in cylindrical coordinates, f(r, :math:`\\phi`, z), - then converts the returned 3D vector to Cartesian coordinates. - - The angular coordinate is given in radians. - - Positive angular coordinate is measured counterclockwise from the xz plane. - - :param VectorFunction3D function3d: The function to be mapped. Must be defined - in the interval (:math:`-\\pi`, :math:`\\pi`] - on the angular axis. - - .. code-block:: pycon - - >>> from math import sqrt, cos - >>> from raysect.core.math import Vector3D - >>> from cherab.core.math import VectorCylindricalMapper - >>> - >>> def my_vec_func(r, phi, z): - >>> v = Vector3D(0, 1, 0) - >>> v.length = r * abs(cos(phi)) - >>> return v - >>> - >>> f = VectorCylindricalMapper(my_vec_func) - >>> - >>> f(1, 0, 0) - Vector3D(0.0, 1.0, 0.0) - >>> f(1/sqrt(2), 1/sqrt(2), 0) - Vector3D(-0.5, 0.5, 0.0) - """ - - def __init__(self, object function3d): - - if not callable(function3d): - raise TypeError("Function3D is not callable.") - - self.function3d = autowrap_vectorfunction3d(function3d) - - @cython.cdivision(True) - cdef Vector3D evaluate(self, double x, double y, double z): - """ - Converts to cylindrical coordinates, evaluates the vector function - defined in cylindrical coordinates and rotates the resulting vector - around z-axis. - """ - cdef double r, phi - - r = sqrt(x * x + y * y) - phi = atan2(y, x) - - return self.function3d.evaluate(r, phi, z).transform(rotate_z(phi / M_PI * 180)) - - -cdef class PeriodicMapper1D(Function1D): - """ - Maps a periodic 1D function into 1D space. - - :param Function1D function1d: The periodic 1D function to map defined - in the [0, period) interval. - :param double period: The period of the function. - - .. code-block:: pycon - - >>> from cherab.core.math import PeriodicMapper1D - >>> - >>> def f1(x): - >>> return x - >>> - >>> f2 = PeriodicMapper1D(f1, 1.) - >>> - >>> f2(1.5) - 0.5 - >>> f2(-0.3) - 0.7 - """ - - def __init__(self, object function1d, double period): - - if not callable(function1d): - raise TypeError("function1d is not callable.") - - self.function1d = autowrap_function1d(function1d) - - if period <= 0: - raise ValueError("Argument period must be positive.") - - self.period = period - - cdef double evaluate(self, double x) except? -1e999: - """Return the value of periodic function.""" - - return self.function1d.evaluate(remainder(x, self.period)) - - -cdef class PeriodicMapper2D(Function2D): - """ - Maps a periodic 2D function into 2D space. - - Set period_x/period_y to 0 if the function is not periodic along x/y axis. - - :param Function2D function2d: The periodic 2D function to map defined - in the ([0, period_x), [0, period_y)) intervals. - :param double period_x: The period of the function along x-axis. - 0 if not periodic. - :param double period_y: The period of the function along y-axis. - 0 if not periodic. - - .. code-block:: pycon - - >>> from cherab.core.math import PeriodicMapper2D - >>> - >>> def f1(x, y): - >>> return x * y - >>> - >>> f2 = PeriodicMapper2D(f1, 1., 1.) - >>> - >>> f2(1.5, 1.5) - 0.25 - >>> f2(-0.3, -1.3) - 0.49 - >>> - >>> f3 = PeriodicMapper2D(f1, 1., 0) - >>> - >>> f3(1.5, 1.5) - 0.75 - >>> f3(-0.3, -1.3) - -0.91 - """ - - def __init__(self, object function2d, double period_x, double period_y): - - if not callable(function2d): - raise TypeError("function2d is not callable.") - - self.function2d = autowrap_function2d(function2d) - - if period_x < 0: - raise ValueError("Argument period_x must be >= 0.") - if period_y < 0: - raise ValueError("Argument period_y must be >= 0.") - - self.period_x = period_x - self.period_y = period_y - - cdef double evaluate(self, double x, double y) except? -1e999: - """Return the value of periodic function.""" - - x = remainder(x, self.period_x) - y = remainder(y, self.period_y) - - return self.function2d.evaluate(x, y) - - -cdef class PeriodicMapper3D(Function3D): - """ - Maps a periodic 3D function into 3D space. - - Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. - - :param Function3D function3d: The periodic 3D function to map defined in the - ([0, period_x), [0, period_y), [0, period_z)) intervals. - :param double period_x: The period of the function along x-axis. - 0 if not periodic. - :param double period_y: The period of the function along y-axis. - 0 if not periodic. - :param double period_z: The period of the function along z-axis. - 0 if not periodic. - - .. code-block:: pycon - - >>> from cherab.core.math import PeriodicMapper3D - >>> - >>> def f1(x, y, z): - >>> return x * y * z - >>> - >>> f2 = PeriodicMapper3D(f1, 1., 1., 1.) - >>> - >>> f2(1.5, 1.5, 1.5) - 0.125 - >>> f2(-0.3, -1.3, -2.3) - 0.343 - >>> - >>> f3 = PeriodicMapper3D(f1, 0, 1., 0) - >>> - >>> f3(1.5, 1.5, 1.5) - 1.125 - >>> f3(-0.3, -1.3, -0.3) - 0.063 - """ - - def __init__(self, object function3d, double period_x, double period_y, double period_z): - - if not callable(function3d): - raise TypeError("function2d is not callable.") - - self.function3d = autowrap_function3d(function3d) - - if period_x < 0: - raise ValueError("Argument period_x must be >= 0.") - if period_y < 0: - raise ValueError("Argument period_y must be >= 0.") - if period_z < 0: - raise ValueError("Argument period_z must be >= 0.") - - self.period_x = period_x - self.period_y = period_y - self.period_z = period_z - - cdef double evaluate(self, double x, double y, double z) except? -1e999: - """Return the value of periodic function.""" - - x = remainder(x, self.period_x) - y = remainder(y, self.period_y) - z = remainder(z, self.period_z) - - return self.function3d.evaluate(x, y, z) - - -cdef class VectorPeriodicMapper1D(VectorFunction1D): - """ - Maps a periodic 1D vector function into 1D space. - - :param VectorFunction1D function1d: The periodic 1D vector function to map - defined in the [0, period) interval. - :param double period: The period of the function. - - .. code-block:: pycon - - >>> from raysect.core.math import Vector3D - >>> from cherab.core.math import VectorPeriodicMapper1D - >>> - >>> def f1(x): - >>> return Vector3D(x, 0, 0) - >>> - >>> f2 = VectorPeriodicMapper1D(f1, 1.) - >>> - >>> f2(1.5) - Vector3D(0.5, 0, 0) - >>> f2(-0.3) - Vector3D(0.7, 0, 0) - """ - - def __init__(self, object function1d, double period): - - if not callable(function1d): - raise TypeError("function1d is not callable.") - - self.function1d = autowrap_vectorfunction1d(function1d) - - if period <= 0: - raise ValueError("Argument period must be positive.") - - self.period = period - - cdef Vector3D evaluate(self, double x): - """Return the value of periodic function.""" - - return self.function1d.evaluate(remainder(x, self.period)) - - -cdef class VectorPeriodicMapper2D(VectorFunction2D): - """ - Maps a periodic 2D vector function into 2D space. - - Set period_x/period_y to 0 if the function is not periodic along x/y axis. - - :param VectorFunction2D function2d: The periodic 2D vector function to map defined in - the ([0, period_x), [0, period_y)) intervals. - :param double period_x: The period of the function along x-axis. - 0 if not periodic. - :param double period_y: The period of the function along y-axis. - 0 if not periodic. - - .. code-block:: pycon - - >>> from cherab.core.math import VectorPeriodicMapper2D - >>> - >>> def f1(x, y): - >>> return Vector3D(x, y, 0) - >>> - >>> f2 = VectorPeriodicMapper2D(f1, 1., 1.) - >>> - >>> f2(1.5, 1.5) - Vector3D(0.5, 0.5, 0) - >>> f2(-0.3, -1.3) - Vector3D(0.7, 0.7, 0) - >>> - >>> f3 = VectorPeriodicMapper2D(f1, 1., 0) - >>> - >>> f3(1.5, 1.5) - Vector3D(0.5, 1.5, 0) - >>> f3(-0.3, -1.3) - Vector3D(0.7, -1.3, 0) - """ - - def __init__(self, object function2d, double period_x, double period_y): - - if not callable(function2d): - raise TypeError("function2d is not callable.") - - self.function2d = autowrap_vectorfunction2d(function2d) - - if period_x < 0: - raise ValueError("Argument period_x must be >= 0.") - if period_y < 0: - raise ValueError("Argument period_y must be >= 0.") - - self.period_x = period_x - self.period_y = period_y - - cdef Vector3D evaluate(self, double x, double y): - """Return the value of periodic function.""" - - x = remainder(x, self.period_x) - y = remainder(y, self.period_y) - - return self.function2d.evaluate(x, y) - - -cdef class VectorPeriodicMapper3D(VectorFunction3D): - """ - Maps a periodic 3D vector function into 3D space. - - Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. - - :param VectorFunction3D function3d: The periodic 3D vector function to map defined in the - ([0, period_x), [0, period_y), [0, period_z)) intervals. - :param double period_x: The period of the function along x-axis. - 0 if not periodic. - :param double period_y: The period of the function along y-axis. - 0 if not periodic. - :param double period_z: The period of the function along z-axis. - 0 if not periodic. - - .. code-block:: pycon - - >>> from cherab.core.math import PeriodicMapper3D - >>> - >>> def f1(x, y, z): - >>> return Vector3D(x, y, z) - >>> - >>> f2 = VectorPeriodicMapper3D(f1, 1., 1., 1.) - >>> - >>> f2(1.5, 1.5, 1.5) - Vector3D(0.5, 0.5, 0.5) - >>> f2(-0.3, -1.3, -2.3) - Vector3D(0.7, 0.7, 0.7) - >>> - >>> f3 = VectorPeriodicMapper3D(f1, 0, 1., 0) - >>> - >>> f3(1.5, 0.5, 1.5) - Vector3D(1.5, 0.5, 1.5) - """ - - def __init__(self, object function3d, double period_x, double period_y, double period_z): - - if not callable(function3d): - raise TypeError("function2d is not callable.") - - self.function3d = autowrap_vectorfunction3d(function3d) - - if period_x < 0: - raise ValueError("Argument period_x must be >= 0.") - if period_y < 0: - raise ValueError("Argument period_y must be >= 0.") - if period_z < 0: - raise ValueError("Argument period_z must be >= 0.") - - self.period_x = period_x - self.period_y = period_y - self.period_z = period_z - - cdef Vector3D evaluate(self, double x, double y, double z): - """Return the value of periodic function.""" - - x = remainder(x, self.period_x) - y = remainder(y, self.period_y) - z = remainder(z, self.period_z) - - return self.function3d.evaluate(x, y, z) diff --git a/cherab/core/math/tests/test_mappers.py b/cherab/core/math/tests/test_mappers.py index 69a5f771..a1d0eb73 100644 --- a/cherab/core/math/tests/test_mappers.py +++ b/cherab/core/math/tests/test_mappers.py @@ -36,6 +36,9 @@ def f2d(x, y): return x*np.sin(y) def f3d(x, y, z): return x*x*np.exp(y)-2*z*y self.function3d = f3d + def vecf2d(r, z): return Vector3D(0, r, z) + self.vectorfunction2d = vecf2d + def test_iso_mapper_2d(self): """Composition of a 1D and a 2D function.""" @@ -143,112 +146,17 @@ def test_axisymmetric_mapper_invalid_arg(self): """An error must be raised if the given argument is not callable.""" self.assertRaises(TypeError, mappers.AxisymmetricMapper, "blah") - def test_cylindrical_mapper(self): - """Cylindrical mapper.""" - def f3d(r, phi, z): return r * np.cos(phi) + z - cyl_func = mappers.CylindricalMapper(f3d) - self.assertAlmostEqual(cyl_func(1., 1., 0.5), - f3d(np.sqrt(2.), 0.25 * np.pi, 0.5), - places=10) - - def test_cylindrical_mapper_invalid_arg(self): - """An error must be raised if the given argument is not callable.""" - self.assertRaises(TypeError, mappers.CylindricalMapper, "blah") - - def test_vector_cylindrical_mapper(self): - """Cylindrical mapper.""" - def f3d(r, phi, z): return Vector3D(np.sin(phi), r * z, np.cos(phi)) - cyl_func = mappers.VectorCylindricalMapper(f3d) - vec1 = cyl_func(1., 1., 1.) - vec2 = Vector3D(-0.5, 1.5, 1 / np.sqrt(2)) + def test_vector_axisymmetric_mapper(self): + """Vector axisymmetric mapper.""" + symm_func = mappers.VectorAxisymmetricMapper(self.vectorfunction2d) + vec1 = symm_func(1., 1., 1.) + vec2 = Vector3D(-1., 1., 1.) np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) - def test_vector_cylindrical_mapper_invalid_arg(self): + def test_vector_axisymmetric_mapper_invalid_arg(self): """An error must be raised if the given argument is not callable.""" - self.assertRaises(TypeError, mappers.VectorCylindricalMapper, "blah") - - def test_periodic_mapper_1d(self): - """1D periodic mapper""" - period_func = mappers.PeriodicMapper1D(self.function1d, np.pi) - self.assertAlmostEqual(period_func(1.4 * np.pi), - self.function1d(0.4 * np.pi), - places=10) - self.assertAlmostEqual(period_func(-0.4 * np.pi), - self.function1d(0.6 * np.pi), - places=10) - - def test_periodic_mapper_1d_invalid_arg(self): - """1D periodic mapper. Invalid arguments.""" - # 1st argument is not callable - self.assertRaises(TypeError, mappers.PeriodicMapper1D, "blah", np.pi) - # period is not a number - self.assertRaises(TypeError, mappers.PeriodicMapper1D, self.function1d, "blah") - # period is negative - self.assertRaises(ValueError, mappers.PeriodicMapper1D, self.function1d, -1) - - def test_periodic_mapper_2d(self): - """2D periodic mapper""" - period_func = mappers.PeriodicMapper2D(self.function2d, 1, np.pi) - self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), - self.function2d(0.6, 0.4 * np.pi), - places=10) - # Periodic only along x - period_func = mappers.PeriodicMapper2D(self.function2d, 1., 0) - self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), - self.function2d(0.6, 1.4 * np.pi), - places=10) - # Periodic only along y - period_func = mappers.PeriodicMapper2D(self.function2d, 0, np.pi) - self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), - self.function2d(-0.4, 0.4 * np.pi), - places=10) - - def test_periodic_mapper_2d_invalid_arg(self): - """2D periodic mapper. Invalid arguments.""" - # 1st argument is not callable - self.assertRaises(TypeError, mappers.PeriodicMapper2D, "blah", np.pi, np.pi) - # period is not a number - self.assertRaises(TypeError, mappers.PeriodicMapper2D, self.function2d, "blah", np.pi) - self.assertRaises(TypeError, mappers.PeriodicMapper2D, self.function2d, np.pi, "blah") - # period is negative - self.assertRaises(ValueError, mappers.PeriodicMapper2D, self.function2d, -1, np.pi) - self.assertRaises(ValueError, mappers.PeriodicMapper2D, self.function2d, np.pi, -1) - - def test_periodic_mapper_3d(self): - """3D periodic mapper""" - period_func = mappers.PeriodicMapper3D(self.function3d, 1, 1, 1) - self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), - self.function3d(0.6, 0.4, 0.1), - places=10) - # Periodic only along y and z - period_func = mappers.PeriodicMapper3D(self.function3d, 0, 1, 1) - self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), - self.function3d(-0.4, 0.4, 0.1), - places=10) - # Periodic only along x and z - period_func = mappers.PeriodicMapper3D(self.function3d, 1, 0, 1) - self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), - self.function3d(0.6, 1.4, 0.1), - places=10) - # Periodic only along x and y - period_func = mappers.PeriodicMapper3D(self.function3d, 1, 1, 0) - self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), - self.function3d(0.6, 0.4, 2.1), - places=10) - - def test_periodic_mapper_3d_invalid_arg(self): - """2D periodic mapper. Invalid arguments.""" - # 1st argument is not callable - self.assertRaises(TypeError, mappers.PeriodicMapper3D, "blah", np.pi, np.pi, np.pi) - # period is not a number - self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, "blah", np.pi, np.pi) - self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, np.pi, "blah", np.pi) - self.assertRaises(TypeError, mappers.PeriodicMapper3D, self.function3d, np.pi, np.pi, "blah") - # period is negative - self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, -1, np.pi, np.pi) - self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, np.pi, -1, np.pi) - self.assertRaises(ValueError, mappers.PeriodicMapper3D, self.function3d, np.pi, np.pi, -1) + self.assertRaises(TypeError, mappers.VectorAxisymmetricMapper, "blah") if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/cherab/core/math/tests/test_transform.py b/cherab/core/math/tests/test_transform.py new file mode 100644 index 00000000..2ee4eefc --- /dev/null +++ b/cherab/core/math/tests/test_transform.py @@ -0,0 +1,157 @@ +# 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 import Vector3D +from cherab.core.math import transform +import numpy as np +import unittest + + +class TestCylindricalTransform(unittest.TestCase): + """Cylindrical transform tests.""" + + def setUp(self): + """Initialisation with functions to map.""" + + def f3d(r, phi, z): return r * np.cos(phi) + z + self.function3d = f3d + + def vecf3d(r, phi, z): return Vector3D(np.sin(phi), r * z, np.cos(phi)) + self.vectorfunction3d = vecf3d + + def test_cylindrical_transform(self): + """Cylindrical transform.""" + cyl_func = transform.CylindricalTransform(self.function3d) + self.assertAlmostEqual(cyl_func(1., 1., 0.5), + self.function3d(np.sqrt(2.), 0.25 * np.pi, 0.5), + places=10) + + def test_cylindrical_transform_invalid_arg(self): + """An error must be raised if the given argument is not callable.""" + self.assertRaises(TypeError, transform.CylindricalTransform, "blah") + + def test_vector_cylindrical_transform(self): + """Cylindrical transform.""" + cyl_func = transform.VectorCylindricalTransform(self.vectorfunction3d) + vec1 = cyl_func(1., 1., 1.) + vec2 = Vector3D(-0.5, 1.5, 1 / np.sqrt(2)) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + def test_vector_cylindrical_transform_invalid_arg(self): + """An error must be raised if the given argument is not callable.""" + self.assertRaises(TypeError, transform.VectorCylindricalTransform, "blah") + + +class TestPeriodicTransform(unittest.TestCase): + """Periodic transform tests.""" + + def setUp(self): + """Initialisation with functions to map.""" + + def f1d(x): return x*np.cos(x-3) + self.function1d = f1d + def f2d(x, y): return x*np.sin(y) + self.function2d = f2d + def f3d(x, y, z): return x*x*np.exp(y)-2*z*y + self.function3d = f3d + + def test_periodic_transform_1d(self): + """1D periodic transform""" + period_func = transform.PeriodicTransform1D(self.function1d, np.pi) + self.assertAlmostEqual(period_func(1.4 * np.pi), + self.function1d(0.4 * np.pi), + places=10) + self.assertAlmostEqual(period_func(-0.4 * np.pi), + self.function1d(0.6 * np.pi), + places=10) + + def test_periodic_transform_1d_invalid_arg(self): + """1D periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.PeriodicTransform1D, "blah", np.pi) + # period is not a number + self.assertRaises(TypeError, transform.PeriodicTransform1D, self.function1d, "blah") + # period is negative + self.assertRaises(ValueError, transform.PeriodicTransform1D, self.function1d, -1) + + def test_periodic_transform_2d(self): + """2D periodic transform""" + period_func = transform.PeriodicTransform2D(self.function2d, 1, np.pi) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(0.6, 0.4 * np.pi), + places=10) + # Periodic only along x + period_func = transform.PeriodicTransform2D(self.function2d, 1., 0) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(0.6, 1.4 * np.pi), + places=10) + # Periodic only along y + period_func = transform.PeriodicTransform2D(self.function2d, 0, np.pi) + self.assertAlmostEqual(period_func(-0.4, 1.4 * np.pi), + self.function2d(-0.4, 0.4 * np.pi), + places=10) + + def test_periodic_transform_2d_invalid_arg(self): + """2D periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.PeriodicTransform2D, "blah", np.pi, np.pi) + # period is not a number + self.assertRaises(TypeError, transform.PeriodicTransform2D, self.function2d, "blah", np.pi) + self.assertRaises(TypeError, transform.PeriodicTransform2D, self.function2d, np.pi, "blah") + # period is negative + self.assertRaises(ValueError, transform.PeriodicTransform2D, self.function2d, -1, np.pi) + self.assertRaises(ValueError, transform.PeriodicTransform2D, self.function2d, np.pi, -1) + + def test_periodic_transform_3d(self): + """3D periodic transform""" + period_func = transform.PeriodicTransform3D(self.function3d, 1, 1, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 0.4, 0.1), + places=10) + # Periodic only along y and z + period_func = transform.PeriodicTransform3D(self.function3d, 0, 1, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(-0.4, 0.4, 0.1), + places=10) + # Periodic only along x and z + period_func = transform.PeriodicTransform3D(self.function3d, 1, 0, 1) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 1.4, 0.1), + places=10) + # Periodic only along x and y + period_func = transform.PeriodicTransform3D(self.function3d, 1, 1, 0) + self.assertAlmostEqual(period_func(-0.4, 1.4, 2.1), + self.function3d(0.6, 0.4, 2.1), + places=10) + + def test_periodic_transform_3d_invalid_arg(self): + """2D periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.PeriodicTransform3D, "blah", np.pi, np.pi, np.pi) + # period is not a number + self.assertRaises(TypeError, transform.PeriodicTransform3D, self.function3d, "blah", np.pi, np.pi) + self.assertRaises(TypeError, transform.PeriodicTransform3D, self.function3d, np.pi, "blah", np.pi) + self.assertRaises(TypeError, transform.PeriodicTransform3D, self.function3d, np.pi, np.pi, "blah") + # period is negative + self.assertRaises(ValueError, transform.PeriodicTransform3D, self.function3d, -1, np.pi, np.pi) + self.assertRaises(ValueError, transform.PeriodicTransform3D, self.function3d, np.pi, -1, np.pi) + self.assertRaises(ValueError, transform.PeriodicTransform3D, self.function3d, np.pi, np.pi, -1) + + +if __name__ == '__main__': + unittest.main() diff --git a/cherab/core/math/transform/__init__.pxd b/cherab/core/math/transform/__init__.pxd new file mode 100644 index 00000000..3ce39b6b --- /dev/null +++ b/cherab/core/math/transform/__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.transform.periodic cimport * +from cherab.core.math.transform.cylindrical cimport * \ No newline at end of file diff --git a/cherab/core/math/transform/__init__.py b/cherab/core/math/transform/__init__.py new file mode 100644 index 00000000..168bb305 --- /dev/null +++ b/cherab/core/math/transform/__init__.py @@ -0,0 +1,21 @@ +# 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 .cylindrical import CylindricalTransform, VectorCylindricalTransform +from .periodic import PeriodicTransform1D, PeriodicTransform2D, PeriodicTransform3D +from .periodic import VectorPeriodicTransform1D, VectorPeriodicTransform2D, VectorPeriodicTransform3D diff --git a/cherab/core/math/transform/cylindrical.pxd b/cherab/core/math/transform/cylindrical.pxd new file mode 100644 index 00000000..504b2fd5 --- /dev/null +++ b/cherab/core/math/transform/cylindrical.pxd @@ -0,0 +1,30 @@ +# 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 cimport Function3D +from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D + + +cdef class CylindricalTransform(Function3D): + + cdef readonly Function3D function3d + + +cdef class VectorCylindricalTransform(VectorFunction3D): + + cdef readonly VectorFunction3D function3d diff --git a/cherab/core/math/transform/cylindrical.pyx b/cherab/core/math/transform/cylindrical.pyx new file mode 100644 index 00000000..cd716cad --- /dev/null +++ b/cherab/core/math/transform/cylindrical.pyx @@ -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. + +from libc.math cimport sqrt, atan2, M_PI + +from raysect.core.math cimport Vector3D +from raysect.core.math.function.float cimport autowrap_function3d +from raysect.core.math.function.vector3d cimport autowrap_function3d as autowrap_vectorfunction3d +from raysect.core cimport rotate_z +cimport cython + +cdef class CylindricalTransform(Function3D): + """ + Converts Cartesian coordinates to cylindrical coordinates and calls a 3D function + defined in cylindrical coordinates, f(r, :math:`\\phi`, z). + + The angular coordinate is given in radians. + + Positive angular coordinate is measured counterclockwise from the xz plane. + + :param Function3D function3d: The function to be mapped. Must be defined + in the interval (:math:`-\\pi`, :math:`\\pi`] + on the angular axis. + + .. code-block:: pycon + + >>> from math import sqrt, cos + >>> from cherab.core.math import CylindricalTransform + >>> + >>> def my_func(r, phi, z): + >>> return r * cos(phi) + >>> + >>> f = CylindricalTransform(my_func) + >>> + >>> f(1, 0, 0) + 1.0 + >>> f(0.5 * sqrt(3), 0.5, 0) + 0.8660254037844385 + """ + + def __init__(self, object function3d): + + if not callable(function3d): + raise TypeError("Function3D is not callable.") + + self.function3d = autowrap_function3d(function3d) + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + """ + Converts to cylindrical coordinates and evaluates the function + defined in cylindrical coordinates. + """ + cdef double r, phi + + r = sqrt(x * x + y * y) + phi = atan2(y, x) + + return self.function3d.evaluate(r, phi, z) + + +cdef class VectorCylindricalTransform(VectorFunction3D): + """ + Converts Cartesian coordinates to cylindrical coordinates, calls + a 3D vector function defined in cylindrical coordinates, f(r, :math:`\\phi`, z), + then converts the returned 3D vector to Cartesian coordinates. + + The angular coordinate is given in radians. + + Positive angular coordinate is measured counterclockwise from the xz plane. + + :param VectorFunction3D function3d: The function to be mapped. Must be defined + in the interval (:math:`-\\pi`, :math:`\\pi`] + on the angular axis. + + .. code-block:: pycon + + >>> from math import sqrt, cos + >>> from raysect.core.math import Vector3D + >>> from cherab.core.math import VectorCylindricalTransform + >>> + >>> def my_vec_func(r, phi, z): + >>> v = Vector3D(0, 1, 0) + >>> v.length = r * abs(cos(phi)) + >>> return v + >>> + >>> f = VectorCylindricalTransform(my_vec_func) + >>> + >>> f(1, 0, 0) + Vector3D(0.0, 1.0, 0.0) + >>> f(1/sqrt(2), 1/sqrt(2), 0) + Vector3D(-0.5, 0.5, 0.0) + """ + + def __init__(self, object function3d): + + if not callable(function3d): + raise TypeError("Function3D is not callable.") + + self.function3d = autowrap_vectorfunction3d(function3d) + + @cython.cdivision(True) + cdef Vector3D evaluate(self, double x, double y, double z): + """ + Converts to cylindrical coordinates, evaluates the vector function + defined in cylindrical coordinates and rotates the resulting vector + around z-axis. + """ + cdef double r, phi + + r = sqrt(x * x + y * y) + phi = atan2(y, x) + + return self.function3d.evaluate(r, phi, z).transform(rotate_z(phi / M_PI * 180)) diff --git a/cherab/core/math/transform/periodic.pxd b/cherab/core/math/transform/periodic.pxd new file mode 100644 index 00000000..e97e4602 --- /dev/null +++ b/cherab/core/math/transform/periodic.pxd @@ -0,0 +1,72 @@ +# 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 libc.math cimport fmod +from raysect.core.math.function.float cimport Function1D, Function2D, Function3D +from raysect.core.math.function.vector3d cimport Function1D as VectorFunction1D +from raysect.core.math.function.vector3d cimport Function2D as VectorFunction2D +from raysect.core.math.function.vector3d cimport Function3D as VectorFunction3D + + +cdef inline double remainder(double x1, double x2) nogil: + if x2 == 0: + return x1 + x1 = fmod(x1, x2) + return x1 + x2 if (x1 < 0) else x1 + + +cdef class PeriodicTransform1D(Function1D): + + cdef: + readonly Function1D function1d + readonly double period + + +cdef class PeriodicTransform2D(Function2D): + + cdef: + readonly Function2D function2d + double period_x, period_y + + +cdef class PeriodicTransform3D(Function3D): + + cdef: + readonly Function3D function3d + readonly double period_x, period_y, period_z + + +cdef class VectorPeriodicTransform1D(VectorFunction1D): + + cdef: + readonly VectorFunction1D function1d + readonly double period + + +cdef class VectorPeriodicTransform2D(VectorFunction2D): + + cdef: + readonly VectorFunction2D function2d + readonly double period_x, period_y + + +cdef class VectorPeriodicTransform3D(VectorFunction3D): + + cdef: + readonly VectorFunction3D function3d + readonly double period_x, period_y, period_z diff --git a/cherab/core/math/transform/periodic.pyx b/cherab/core/math/transform/periodic.pyx new file mode 100644 index 00000000..13869458 --- /dev/null +++ b/cherab/core/math/transform/periodic.pyx @@ -0,0 +1,352 @@ +# 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 cimport Vector3D +from raysect.core.math.function.float cimport autowrap_function1d, autowrap_function2d, autowrap_function3d +from raysect.core.math.function.vector3d cimport autowrap_function1d as autowrap_vectorfunction1d +from raysect.core.math.function.vector3d cimport autowrap_function2d as autowrap_vectorfunction2d +from raysect.core.math.function.vector3d cimport autowrap_function3d as autowrap_vectorfunction3d + + +cdef class PeriodicTransform1D(Function1D): + """ + Extends a periodic 1D function to an infinite 1D space. + + :param Function1D function1d: The periodic 1D function defined + in the [0, period) interval. + :param double period: The period of the function. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicTransform1D + >>> + >>> def f1(x): + >>> return x + >>> + >>> f2 = PeriodicTransform1D(f1, 1.) + >>> + >>> f2(1.5) + 0.5 + >>> f2(-0.3) + 0.7 + """ + + def __init__(self, object function1d, double period): + + if not callable(function1d): + raise TypeError("function1d is not callable.") + + self.function1d = autowrap_function1d(function1d) + + if period <= 0: + raise ValueError("Argument period must be positive.") + + self.period = period + + cdef double evaluate(self, double x) except? -1e999: + """Return the value of periodic function.""" + + return self.function1d.evaluate(remainder(x, self.period)) + + +cdef class PeriodicTransform2D(Function2D): + """ + Extends a periodic 2D function to an infinite 2D space. + + Set period_x/period_y to 0 if the function is not periodic along x/y axis. + + :param Function2D function2d: The periodic 2D function defined + in the ([0, period_x), [0, period_y)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicTransform2D + >>> + >>> def f1(x, y): + >>> return x * y + >>> + >>> f2 = PeriodicTransform2D(f1, 1., 1.) + >>> + >>> f2(1.5, 1.5) + 0.25 + >>> f2(-0.3, -1.3) + 0.49 + >>> + >>> f3 = PeriodicTransform2D(f1, 1., 0) + >>> + >>> f3(1.5, 1.5) + 0.75 + >>> f3(-0.3, -1.3) + -0.91 + """ + + def __init__(self, object function2d, double period_x, double period_y): + + if not callable(function2d): + raise TypeError("function2d is not callable.") + + self.function2d = autowrap_function2d(function2d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + + cdef double evaluate(self, double x, double y) except? -1e999: + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + + return self.function2d.evaluate(x, y) + + +cdef class PeriodicTransform3D(Function3D): + """ + Extends a periodic 3D function to an infinite 3D space. + + Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. + + :param Function3D function3d: The periodic 3D function defined in the + ([0, period_x), [0, period_y), [0, period_z)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + :param double period_z: The period of the function along z-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicTransform3D + >>> + >>> def f1(x, y, z): + >>> return x * y * z + >>> + >>> f2 = PeriodicTransform3D(f1, 1., 1., 1.) + >>> + >>> f2(1.5, 1.5, 1.5) + 0.125 + >>> f2(-0.3, -1.3, -2.3) + 0.343 + >>> + >>> f3 = PeriodicTransform3D(f1, 0, 1., 0) + >>> + >>> f3(1.5, 1.5, 1.5) + 1.125 + >>> f3(-0.3, -1.3, -0.3) + 0.063 + """ + + def __init__(self, object function3d, double period_x, double period_y, double period_z): + + if not callable(function3d): + raise TypeError("function2d is not callable.") + + self.function3d = autowrap_function3d(function3d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + if period_z < 0: + raise ValueError("Argument period_z must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + self.period_z = period_z + + cdef double evaluate(self, double x, double y, double z) except? -1e999: + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + z = remainder(z, self.period_z) + + return self.function3d.evaluate(x, y, z) + + +cdef class VectorPeriodicTransform1D(VectorFunction1D): + """ + Extends a periodic 1D vector function to an infinite 1D space. + + :param VectorFunction1D function1d: The periodic 1D vector function + defined in the [0, period) interval. + :param double period: The period of the function. + + .. code-block:: pycon + + >>> from raysect.core.math import Vector3D + >>> from cherab.core.math import VectorPeriodicTransform1D + >>> + >>> def f1(x): + >>> return Vector3D(x, 0, 0) + >>> + >>> f2 = VectorPeriodicTransform1D(f1, 1.) + >>> + >>> f2(1.5) + Vector3D(0.5, 0, 0) + >>> f2(-0.3) + Vector3D(0.7, 0, 0) + """ + + def __init__(self, object function1d, double period): + + if not callable(function1d): + raise TypeError("function1d is not callable.") + + self.function1d = autowrap_vectorfunction1d(function1d) + + if period <= 0: + raise ValueError("Argument period must be positive.") + + self.period = period + + cdef Vector3D evaluate(self, double x): + """Return the value of periodic function.""" + + return self.function1d.evaluate(remainder(x, self.period)) + + +cdef class VectorPeriodicTransform2D(VectorFunction2D): + """ + Extends a periodic 2D vector function to an infinite 2D space. + + Set period_x/period_y to 0 if the function is not periodic along x/y axis. + + :param VectorFunction2D function2d: The periodic 2D vector function defined in + the ([0, period_x), [0, period_y)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import VectorPeriodicTransform2D + >>> + >>> def f1(x, y): + >>> return Vector3D(x, y, 0) + >>> + >>> f2 = VectorPeriodicTransform2D(f1, 1., 1.) + >>> + >>> f2(1.5, 1.5) + Vector3D(0.5, 0.5, 0) + >>> f2(-0.3, -1.3) + Vector3D(0.7, 0.7, 0) + >>> + >>> f3 = VectorPeriodicTransform2D(f1, 1., 0) + >>> + >>> f3(1.5, 1.5) + Vector3D(0.5, 1.5, 0) + >>> f3(-0.3, -1.3) + Vector3D(0.7, -1.3, 0) + """ + + def __init__(self, object function2d, double period_x, double period_y): + + if not callable(function2d): + raise TypeError("function2d is not callable.") + + self.function2d = autowrap_vectorfunction2d(function2d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + + cdef Vector3D evaluate(self, double x, double y): + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + + return self.function2d.evaluate(x, y) + + +cdef class VectorPeriodicTransform3D(VectorFunction3D): + """ + Extends a periodic 3D vector function to an infinite 3D space. + + Set period_x/period_y/period_z to 0 if the function is not periodic along x/y/z axis. + + :param VectorFunction3D function3d: The periodic 3D vector function defined in the + ([0, period_x), [0, period_y), [0, period_z)) intervals. + :param double period_x: The period of the function along x-axis. + 0 if not periodic. + :param double period_y: The period of the function along y-axis. + 0 if not periodic. + :param double period_z: The period of the function along z-axis. + 0 if not periodic. + + .. code-block:: pycon + + >>> from cherab.core.math import PeriodicTransform3D + >>> + >>> def f1(x, y, z): + >>> return Vector3D(x, y, z) + >>> + >>> f2 = VectorPeriodicTransform3D(f1, 1., 1., 1.) + >>> + >>> f2(1.5, 1.5, 1.5) + Vector3D(0.5, 0.5, 0.5) + >>> f2(-0.3, -1.3, -2.3) + Vector3D(0.7, 0.7, 0.7) + >>> + >>> f3 = VectorPeriodicTransform3D(f1, 0, 1., 0) + >>> + >>> f3(1.5, 0.5, 1.5) + Vector3D(1.5, 0.5, 1.5) + """ + + def __init__(self, object function3d, double period_x, double period_y, double period_z): + + if not callable(function3d): + raise TypeError("function2d is not callable.") + + self.function3d = autowrap_vectorfunction3d(function3d) + + if period_x < 0: + raise ValueError("Argument period_x must be >= 0.") + if period_y < 0: + raise ValueError("Argument period_y must be >= 0.") + if period_z < 0: + raise ValueError("Argument period_z must be >= 0.") + + self.period_x = period_x + self.period_y = period_y + self.period_z = period_z + + cdef Vector3D evaluate(self, double x, double y, double z): + """Return the value of periodic function.""" + + x = remainder(x, self.period_x) + y = remainder(y, self.period_y) + z = remainder(z, self.period_z) + + return self.function3d.evaluate(x, y, z) From 04b7813c82f577e5438cd985ebe6462cb86685ed Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 28 Dec 2022 11:59:45 +0300 Subject: [PATCH 008/134] Add docs for math.transfrom sub-module. --- docs/source/math/math.rst | 1 + docs/source/math/transform.rst | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 docs/source/math/transform.rst diff --git a/docs/source/math/math.rst b/docs/source/math/math.rst index eab9a1fa..836cbe84 100644 --- a/docs/source/math/math.rst +++ b/docs/source/math/math.rst @@ -18,6 +18,7 @@ utilities that Cherab provides for slicing, dicing and projecting these function function interpolators mappers + transform mask samplers slice diff --git a/docs/source/math/transform.rst b/docs/source/math/transform.rst new file mode 100644 index 00000000..f3d6cfa8 --- /dev/null +++ b/docs/source/math/transform.rst @@ -0,0 +1,11 @@ + +Transformations +--------------- + +These functions perform coordinate transformations as wrappers for other functions. Unlike mappers, these wrappers do not change the dimensionality of the wrapped functions. + +.. automodule:: cherab.core.math.transform.cylindrical + :members: + +.. automodule:: cherab.core.math.transform.periodic + :members: From 0964da44fe8fd1d8251a77e043ec3dc1dc6f3351 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 30 Dec 2022 19:55:12 +0300 Subject: [PATCH 009/134] Move lineshapes to a dedicated submodule. Add Doppler and Zeeman effects to StarkBroadenedLine. --- cherab/core/model/beam/__init__.pxd | 1 + cherab/core/model/beam/beam_emission.pyx | 3 +- cherab/core/model/lineshape.pxd | 112 -- cherab/core/model/lineshape.pyx | 968 ------------------ cherab/core/model/lineshape/__init__.pxd | 25 + cherab/core/model/lineshape/__init__.py | 25 + cherab/core/model/lineshape/base.pxd | 37 + cherab/core/model/lineshape/base.pyx | 43 + cherab/core/model/lineshape/beam/__init__.pxd | 20 + cherab/core/model/lineshape/beam/__init__.py | 20 + cherab/core/model/lineshape/beam/base.pxd | 35 + cherab/core/model/lineshape/beam/base.pyx | 39 + cherab/core/model/lineshape/beam/mse.pxd | 30 + cherab/core/model/lineshape/beam/mse.pyx | 134 +++ cherab/core/model/lineshape/doppler.pxd | 26 + cherab/core/model/lineshape/doppler.pyx | 59 ++ cherab/core/model/lineshape/gaussian.pxd | 29 + cherab/core/model/lineshape/gaussian.pyx | 138 +++ cherab/core/model/lineshape/multiplet.pxd | 31 + cherab/core/model/lineshape/multiplet.pyx | 116 +++ cherab/core/model/lineshape/stark.pxd | 36 + cherab/core/model/lineshape/stark.pyx | 331 ++++++ cherab/core/model/lineshape/zeeman.pxd | 48 + cherab/core/model/lineshape/zeeman.pyx | 417 ++++++++ .../core/model/plasma/impact_excitation.pyx | 2 +- cherab/core/utility/constants.pxd | 2 + cherab/core/utility/constants.pyx | 2 + 27 files changed, 1647 insertions(+), 1082 deletions(-) delete mode 100644 cherab/core/model/lineshape.pxd delete mode 100644 cherab/core/model/lineshape.pyx create mode 100644 cherab/core/model/lineshape/__init__.pxd create mode 100644 cherab/core/model/lineshape/__init__.py create mode 100644 cherab/core/model/lineshape/base.pxd create mode 100644 cherab/core/model/lineshape/base.pyx create mode 100644 cherab/core/model/lineshape/beam/__init__.pxd create mode 100644 cherab/core/model/lineshape/beam/__init__.py create mode 100644 cherab/core/model/lineshape/beam/base.pxd create mode 100644 cherab/core/model/lineshape/beam/base.pyx create mode 100644 cherab/core/model/lineshape/beam/mse.pxd create mode 100644 cherab/core/model/lineshape/beam/mse.pyx create mode 100644 cherab/core/model/lineshape/doppler.pxd create mode 100644 cherab/core/model/lineshape/doppler.pyx create mode 100644 cherab/core/model/lineshape/gaussian.pxd create mode 100644 cherab/core/model/lineshape/gaussian.pyx create mode 100644 cherab/core/model/lineshape/multiplet.pxd create mode 100644 cherab/core/model/lineshape/multiplet.pyx create mode 100644 cherab/core/model/lineshape/stark.pxd create mode 100644 cherab/core/model/lineshape/stark.pyx create mode 100644 cherab/core/model/lineshape/zeeman.pxd create mode 100644 cherab/core/model/lineshape/zeeman.pyx diff --git a/cherab/core/model/beam/__init__.pxd b/cherab/core/model/beam/__init__.pxd index 8a3c6e22..c5a0ebbe 100644 --- a/cherab/core/model/beam/__init__.pxd +++ b/cherab/core/model/beam/__init__.pxd @@ -1 +1,2 @@ from cherab.core.model.beam.charge_exchange cimport BeamCXLine +from cherab.core.model.beam.beam_emission cimport BeamEmissionLine diff --git a/cherab/core/model/beam/beam_emission.pyx b/cherab/core/model/beam/beam_emission.pyx index 8ca55331..67c41ac1 100644 --- a/cherab/core/model/beam/beam_emission.pyx +++ b/cherab/core/model/beam/beam_emission.pyx @@ -22,7 +22,8 @@ cimport cython from libc.math cimport sqrt from raysect.core cimport Point3D, Vector3D -from cherab.core cimport Species, Plasma, Beam, Element, BeamEmissionPEC, Spectrum, AtomicData +from raysect.optical cimport Spectrum +from cherab.core cimport Species, Plasma, Beam, Element, BeamEmissionPEC, AtomicData from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d from cherab.core.atomic.elements import Isotope, hydrogen from cherab.core.model.lineshape cimport BeamEmissionMultiplet diff --git a/cherab/core/model/lineshape.pxd b/cherab/core/model/lineshape.pxd deleted file mode 100644 index 2591efe3..00000000 --- a/cherab/core/model/lineshape.pxd +++ /dev/null @@ -1,112 +0,0 @@ -# cython: language_level=3 - -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 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 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 - - -cpdef double doppler_shift(double wavelength, Vector3D observation_direction, Vector3D velocity) - -cpdef double thermal_broadening(double wavelength, double temperature, double atomic_weight) - -cpdef Spectrum add_gaussian_line(double radiance, double wavelength, double sigma, Spectrum spectrum) - - -cdef class LineShapeModel: - - cdef: - Line line - double wavelength - Species target_species - Plasma plasma - Integrator1D integrator - - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum) - - -cdef class GaussianLine(LineShapeModel): - pass - - -cdef class MultipletLineShape(LineShapeModel): - - cdef: - int _number_of_lines - np.ndarray _multiplet - double[:,::1] _multiplet_mv - - -cdef class StarkBroadenedLine(LineShapeModel): - - cdef double _aij, _bij, _cij - - pass - - -cdef class ZeemanLineShapeModel(LineShapeModel): - - cdef double _polarisation - - pass - - -cdef class ZeemanTriplet(ZeemanLineShapeModel): - - pass - - -cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): - - cdef double _alpha, _beta, _gamma - - pass - - -cdef class ZeemanMultiplet(ZeemanLineShapeModel): - - cdef ZeemanStructure _zeeman_structure - - pass - - -cdef class BeamLineShapeModel: - - cdef: - - Line line - double wavelength - Beam beam - - cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, - Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum) - - -cdef class BeamEmissionMultiplet(BeamLineShapeModel): - - cdef: - - Function2D _sigma_to_pi - Function1D _sigma1_to_sigma0, _pi2_to_pi3, _pi4_to_pi3 diff --git a/cherab/core/model/lineshape.pyx b/cherab/core/model/lineshape.pyx deleted file mode 100644 index 69278560..00000000 --- a/cherab/core/model/lineshape.pyx +++ /dev/null @@ -1,968 +0,0 @@ -# cython: language_level=3 - -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 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 hyp2f1 - -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 - -# required by numpy c-api -np.import_array() - - -cdef double RECIP_ATOMIC_MASS = 1 / ATOMIC_MASS - - -cdef double evamu_to_ms(double x): - return sqrt(2 * x * ELEMENTARY_CHARGE * RECIP_ATOMIC_MASS) - - -@cython.cdivision(True) -cpdef double doppler_shift(double wavelength, Vector3D observation_direction, Vector3D velocity): - """ - Calculates the Doppler shifted wavelength for a given velocity and observation direction. - - :param wavelength: The wavelength to Doppler shift in nanometers. - :param observation_direction: A Vector defining the direction of observation. - :param velocity: A Vector defining the relative velocity of the emitting source in m/s. - :return: The Doppler shifted wavelength in nanometers. - """ - cdef double projected_velocity - - # flow velocity projected on the direction of observation - observation_direction = observation_direction.normalise() - projected_velocity = velocity.dot(observation_direction) - - return wavelength * (1 + projected_velocity / SPEED_OF_LIGHT) - - -@cython.cdivision(True) -cpdef double thermal_broadening(double wavelength, double temperature, double atomic_weight): - """ - Returns the line width for a gaussian line as a standard deviation. - - :param wavelength: Central wavelength. - :param temperature: Temperature in eV. - :param atomic_weight: Atomic weight in AMU. - :return: Standard deviation of gaussian line. - """ - - # todo: add input sanity checks - return sqrt(temperature * ELEMENTARY_CHARGE / (atomic_weight * ATOMIC_MASS)) * wavelength / SPEED_OF_LIGHT - - -# 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 - - -@cython.cdivision(True) -@cython.initializedcheck(False) -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef Spectrum add_gaussian_line(double radiance, double wavelength, double sigma, Spectrum spectrum): - r""" - Adds a Gaussian line to the given spectrum and returns the new spectrum. - - The formula used is based on the following definite integral: - :math:`\frac{1}{\sigma \sqrt{2 \pi}} \int_{\lambda_0}^{\lambda_1} \exp(-\frac{(x-\mu)^2}{2\sigma^2}) dx = \frac{1}{2} \left[ -Erf(\frac{a-\mu}{\sqrt{2}\sigma}) +Erf(\frac{b-\mu}{\sqrt{2}\sigma}) \right]` - - :param float radiance: Intensity of the line in radiance. - :param float wavelength: central wavelength of the line in nm. - :param float sigma: width of the line in nm. - :param Spectrum spectrum: the current spectrum to which the gaussian line is added. - :return: - """ - - cdef double temp - cdef double cutoff_lower_wavelength, cutoff_upper_wavelength - cdef double lower_wavelength, upper_wavelength - cdef double lower_integral, upper_integral - cdef int start, end, i - - if sigma <= 0: - return spectrum - - # calculate and check end of limits - cutoff_lower_wavelength = wavelength - GAUSSIAN_CUTOFF_SIGMA * sigma - if spectrum.max_wavelength < cutoff_lower_wavelength: - return spectrum - - cutoff_upper_wavelength = wavelength + GAUSSIAN_CUTOFF_SIGMA * sigma - if spectrum.min_wavelength > cutoff_upper_wavelength: - return spectrum - - # locate range of bins where there is significant contribution from the gaussian (plus a health margin) - 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)) - - # add line to spectrum - temp = 1 / (M_SQRT2 * sigma) - lower_wavelength = spectrum.min_wavelength + start * spectrum.delta_wavelength - lower_integral = erf((lower_wavelength - wavelength) * temp) - for i in range(start, end): - - upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) - upper_integral = erf((upper_wavelength - wavelength) * temp) - - spectrum.samples_mv[i] += radiance * 0.5 * (upper_integral - lower_integral) / spectrum.delta_wavelength - - lower_wavelength = upper_wavelength - lower_integral = upper_integral - - return spectrum - - -cdef class LineShapeModel: - """ - A base class for building line shapes. - - :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 None. - """ - - 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 GaussianLine(LineShapeModel): - """ - Produces Gaussian line shape. - - :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. - - .. code-block:: pycon - - >>> from cherab.core.atomic import Line, deuterium - >>> from cherab.core.model import ExcitationLine, GaussianLine - >>> - >>> # Adding Gaussian line to the plasma model. - >>> d_alpha = Line(deuterium, 0, (3, 2)) - >>> excit = ExcitationLine(d_alpha, lineshape=GaussianLine) - >>> plasma.models.add(excit) - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma): - - super().__init__(line, wavelength, target_species, plasma) - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - @cython.cdivision(True) - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - - cdef double ts, sigma, shifted_wavelength - cdef Vector3D ion_velocity - - ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) - if ts <= 0.0: - return spectrum - - ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) - - # calculate emission line central wavelength, doppler shifted along observation direction - shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) - - # calculate the line width - sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) - - return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) - - -DEF MULTIPLET_WAVELENGTH = 0 -DEF MULTIPLET_RATIO = 1 - - -cdef class MultipletLineShape(LineShapeModel): - """ - Produces Multiplet line shapes. - - The lineshape radiance is calculated from a base PEC rate that is unresolved. This - radiance is then divided over a number of components as specified in the multiplet - argument. The multiplet components are specified with an Nx2 array where N is the - number of components in the multiplet. The first axis of the array contains the - wavelengths of each component, the second contains the line ratio for each component. - The component line ratios must sum to one. For example: - - :param Line line: The emission line object for the base rate radiance calculation. - :param float wavelength: The rest wavelength of the base emission line. - :param Species target_species: The target plasma species that is emitting. - :param Plasma plasma: The emitting plasma object. - :param multiplet: An Nx2 array that specifies the multiplet wavelengths and line ratios. - - .. code-block:: pycon - - >>> from cherab.core.atomic import Line, nitrogen - >>> from cherab.core.model import ExcitationLine, MultipletLineShape - >>> - >>> # multiplet specification in Nx2 array - >>> multiplet = [[403.509, 404.132, 404.354, 404.479, 405.692], [0.205, 0.562, 0.175, 0.029, 0.029]] - >>> - >>> # Adding the multiplet to the plasma model. - >>> nitrogen_II_404 = Line(nitrogen, 1, ("2s2 2p1 4f1 3G13.0", "2s2 2p1 3d1 3F10.0")) - >>> excit = ExcitationLine(nitrogen_II_404, lineshape=MultipletLineShape, lineshape_args=[multiplet]) - >>> plasma.models.add(excit) - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, - object multiplet): - - super().__init__(line, wavelength, target_species, plasma) - - multiplet = np.array(multiplet, dtype=np.float64) - - if not (len(multiplet.shape) == 2 and multiplet.shape[0] == 2): - raise ValueError("The multiplet specification must be an array of shape (Nx2).") - - if not multiplet[1,:].sum() == 1.0: - raise ValueError("The multiplet line ratios should sum to one.") - - self._number_of_lines = multiplet.shape[1] - self._multiplet = multiplet - self._multiplet_mv = self._multiplet - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - - cdef double ts, sigma, shifted_wavelength, component_wavelength, component_radiance - cdef Vector3D ion_velocity - - ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) - if ts <= 0.0: - return spectrum - - ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) - - # calculate the line width - sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) - - for i in range(self._number_of_lines): - - component_wavelength = self._multiplet_mv[MULTIPLET_WAVELENGTH, i] - component_radiance = radiance * self._multiplet_mv[MULTIPLET_RATIO, i] - - # calculate emission line central wavelength, doppler shifted along observation direction - shifted_wavelength = doppler_shift(component_wavelength, direction, ion_velocity) - - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - return spectrum - - -DEF LORENZIAN_CUTOFF_GAMMA = 50.0 - - -cdef class StarkFunction(Function1D): - """ - Normalised Stark function for the StarkBroadenedLine line shape. - """ - - 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 self._norm / ((fabs(x - self._x0))**2.5 + self._a) - - -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. - Only Balmer and Paschen series are supported by default. - See B. Lomanowski, et al. "Inferring divertor plasma properties from hydrogen Balmer - and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) - `123028 `_. - - Call `show_supported_transitions()` to see the list of supported transitions and - default model coefficients. - - :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 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 = { - Line(hydrogen, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(hydrogen, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(hydrogen, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(hydrogen, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(hydrogen, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(hydrogen, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(hydrogen, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(hydrogen, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(hydrogen, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(hydrogen, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(hydrogen, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(hydrogen, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - Line(hydrogen, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), - Line(deuterium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(deuterium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(deuterium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(deuterium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(deuterium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(deuterium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(deuterium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(deuterium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(deuterium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(deuterium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(deuterium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(deuterium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - Line(deuterium, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), - Line(tritium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(tritium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(tritium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(tritium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(tritium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(tritium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(tritium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(tritium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(tritium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(tritium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(tritium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(tritium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - 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, integrator=GaussianQuadrature()): - - stark_model_coefficients = stark_model_coefficients or self.STARK_MODEL_COEFFICIENTS_DEFAULT - - try: - # Fitted Stark Constants - cij, aij, bij = stark_model_coefficients[line] - if cij <= 0: - raise ValueError('Coefficient c_ij must be positive.') - if aij <= 0: - raise ValueError('Coefficient a_ij must be positive.') - if bij <= 0: - raise ValueError('Coefficient b_ij must be positive.') - self._aij = aij - self._bij = bij - self._cij = cij - except IndexError: - raise ValueError('Stark broadening coefficients for {} is not currently available.'.format(line)) - - super().__init__(line, wavelength, target_species, plasma, integrator) - - def show_supported_transitions(self): - """ Prints all supported transitions.""" - for line, coeff in self.STARK_MODEL_COEFFICIENTS_DEFAULT.items(): - print('{}: c_ij={}, a_ij={}, b_ij={}'.format(line, coeff[0], coeff[1], coeff[2])) - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - @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 - double cutoff_lower_wavelength, cutoff_upper_wavelength - double lower_wavelength, upper_wavelength - double bin_integral - int start, end, i - Spectrum raw_lineshape - - ne = self.plasma.get_electron_distribution().density(point.x, point.y, point.z) - if ne <= 0.0: - return spectrum - - te = self.plasma.get_electron_distribution().effective_temperature(point.x, point.y, point.z) - if te <= 0.0: - return spectrum - - lambda_1_2 = self._cij * ne**self._aij / (te**self._bij) - - 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 - if spectrum.max_wavelength < cutoff_lower_wavelength: - return spectrum - - cutoff_upper_wavelength = self.wavelength + LORENZIAN_CUTOFF_GAMMA * lambda_1_2 - if spectrum.min_wavelength > cutoff_upper_wavelength: - return spectrum - - # locate range of bins where there is significant contribution from the gaussian (plus a health margin) - 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)) - - # add line to spectrum - lower_wavelength = spectrum.min_wavelength + start * spectrum.delta_wavelength - - for i in range(start, end): - upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) - - bin_integral = self.integrator.evaluate(lower_wavelength, upper_wavelength) - spectrum.samples_mv[i] += radiance * bin_integral / spectrum.delta_wavelength - - lower_wavelength = upper_wavelength - - return spectrum - - -DEF BOHR_MAGNETON = 5.78838180123e-5 # in eV/T -DEF HC_EV_NM = 1239.8419738620933 # (Planck constant in eV s) x (speed of light in nm/s) - -DEF PI_POLARISATION = 0 -DEF SIGMA_POLARISATION = 1 -DEF SIGMA_PLUS_POLARISATION = 1 -DEF SIGMA_MINUS_POLARISATION = -1 -DEF NO_POLARISATION = 2 - - -cdef class ZeemanLineShapeModel(LineShapeModel): - r""" - A base class for building Zeeman line shapes. - - :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 str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: - "pi" - leave only :math:`\pi`-polarised components, - "sigma" - leave only :math:`\sigma`-polarised components, - "no" - leave all components (default). - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, polarisation): - super().__init__(line, wavelength, target_species, plasma) - - self.polarisation = polarisation - - @property - def polarisation(self): - if self._polarisation == PI_POLARISATION: - return 'pi' - if self._polarisation == SIGMA_POLARISATION: - return 'sigma' - if self._polarisation == NO_POLARISATION: - return 'no' - - @polarisation.setter - def polarisation(self, value): - if value.lower() == 'pi': - self._polarisation = PI_POLARISATION - elif value.lower() == 'sigma': - self._polarisation = SIGMA_POLARISATION - elif value.lower() == 'no': - self._polarisation = NO_POLARISATION - else: - raise ValueError('Select between "pi", "sigma" or "no", {} is unsupported.'.format(value)) - - -cdef class ZeemanTriplet(ZeemanLineShapeModel): - r""" - Simple Doppler-Zeeman triplet (Paschen-Back effect). - - :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 str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: - "pi" - leave central component, - "sigma" - leave side components, - "no" - all components (default). - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, polarisation='no'): - - super().__init__(line, wavelength, target_species, plasma, polarisation) - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - @cython.cdivision(True) - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - - cdef double ts, sigma, shifted_wavelength, photon_energy, b_magn, component_radiance, cos_sqr, sin_sqr - cdef Vector3D ion_velocity, b_field - - ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) - if ts <= 0.0: - return spectrum - - ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) - - # calculate emission line central wavelength, doppler shifted along observation direction - shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) - - # calculate the line width - sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) - - # obtain magnetic field - b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) - b_magn = b_field.get_length() - - if b_magn == 0: - # no splitting if magnetic field strength is zero - if self._polarisation == NO_POLARISATION: - return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) - - return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) - - # coefficients for intensities parallel and perpendicular to magnetic field - cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 - sin_sqr = 1. - cos_sqr - - # adding pi component of the Zeeman triplet in case of NO_POLARISATION or PI_POLARISATION - if self._polarisation != SIGMA_POLARISATION: - component_radiance = 0.5 * sin_sqr * radiance - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - # adding sigma +/- components of the Zeeman triplet in case of NO_POLARISATION or SIGMA_POLARISATION - if self._polarisation != PI_POLARISATION: - component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance - - photon_energy = HC_EV_NM / self.wavelength - - shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy - BOHR_MAGNETON * b_magn), direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy + BOHR_MAGNETON * b_magn), direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - return spectrum - - -cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): - r""" - Parametrised Doppler-Zeeman triplet. It takes into account additional broadening due to - the line's fine structure without resolving the individual components of the fine - structure. The model is described with three parameters: :math:`\alpha`, - :math:`\beta` and :math:`\gamma`. - - The distance between :math:`\sigma^+` and :math:`\sigma^-` peaks: - :math:`\Delta \lambda_{\sigma} = \alpha B`, - where `B` is the magnetic field strength. - The ratio between Zeeman and thermal broadening line widths: - :math:`\frac{W_{Zeeman}}{W_{Doppler}} = \beta T^{\gamma}`, - where `T` is the species temperature in eV. - - Call `show_supported_transitions()` to see the list of supported transitions and - default parameters of the model. - - For details see A. Blom and C. Jupén, Parametrisation of the Zeeman effect - for hydrogen-like spectra in high-temperature plasmas, - Plasma Phys. Control. Fusion 44 (2002) `1229-1241 - `_. - - :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 dict line_parameters: Alternative parameters of the model in the form - {line_i: (alpha_i, beta_i, gamma_i), ...}. - If None, the default model parameters will be used. - :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: - "pi" - leave central component, - "sigma" - leave side components, - "no" - all components (default). - """ - - LINE_PARAMETERS_DEFAULT = { # alpha, beta, gamma parameters for selected lines - Line(hydrogen, 0, (3, 2)): (0.0402267, 0.3415, -0.5247), - Line(hydrogen, 0, (4, 2)): (0.0220724, 0.2837, -0.5346), - Line(deuterium, 0, (3, 2)): (0.0402068, 0.4384, -0.5015), - Line(deuterium, 0, (4, 2)): (0.0220610, 0.3702, -0.5132), - Line(helium3, 1, (4, 3)): (0.0205200, 1.4418, -0.4892), - Line(helium3, 1, (5, 3)): (0.0095879, 1.2576, -0.5001), - Line(helium3, 1, (6, 4)): (0.0401980, 0.8976, -0.4971), - Line(helium3, 1, (7, 4)): (0.0273538, 0.8529, -0.5039), - Line(helium, 1, (4, 3)): (0.0205206, 1.6118, -0.4838), - Line(helium, 1, (5, 3)): (0.0095879, 1.4294, -0.4975), - Line(helium, 1, (6, 4)): (0.0401955, 1.0058, -0.4918), - Line(helium, 1, (7, 4)): (0.0273521, 0.9563, -0.4981), - Line(beryllium, 3, (5, 4)): (0.0060354, 2.1245, -0.3190), - Line(beryllium, 3, (6, 5)): (0.0202754, 1.6538, -0.3192), - Line(beryllium, 3, (7, 5)): (0.0078966, 1.7017, -0.3348), - Line(beryllium, 3, (8, 6)): (0.0205025, 1.4581, -0.3450), - Line(boron, 4, (6, 5)): (0.0083423, 2.0519, -0.2960), - Line(boron, 4, (7, 6)): (0.0228379, 1.6546, -0.2941), - Line(boron, 4, (8, 6)): (0.0084065, 1.8041, -0.3177), - Line(boron, 4, (8, 7)): (0.0541883, 1.4128, -0.2966), - Line(boron, 4, (9, 7)): (0.0190781, 1.5440, -0.3211), - Line(boron, 4, (10, 8)): (0.0391914, 1.3569, -0.3252), - Line(carbon, 5, (6, 5)): (0.0040900, 2.4271, -0.2818), - Line(carbon, 5, (7, 6)): (0.0110398, 1.9785, -0.2816), - Line(carbon, 5, (8, 6)): (0.0040747, 2.1776, -0.3035), - Line(carbon, 5, (8, 7)): (0.0261405, 1.6689, -0.2815), - Line(carbon, 5, (9, 7)): (0.0092096, 1.8495, -0.3049), - Line(carbon, 5, (10, 8)): (0.0189020, 1.6191, -0.3078), - Line(carbon, 5, (11, 8)): (0.0110428, 1.6600, -0.3162), - Line(carbon, 5, (10, 9)): (0.0359009, 1.4464, -0.3104), - Line(nitrogen, 6, (7, 6)): (0.0060010, 2.4789, -0.2817), - Line(nitrogen, 6, (8, 7)): (0.0141271, 2.0249, -0.2762), - Line(nitrogen, 6, (9, 8)): (0.0300127, 1.7415, -0.2753), - Line(nitrogen, 6, (10, 8)): (0.0102089, 1.9464, -0.2975), - Line(nitrogen, 6, (11, 9)): (0.0193799, 1.7133, -0.2973), - Line(oxygen, 7, (8, 7)): (0.0083081, 2.4263, -0.2747), - Line(oxygen, 7, (9, 8)): (0.0176049, 2.0652, -0.2721), - Line(oxygen, 7, (10, 8)): (0.0059933, 2.3445, -0.2944), - Line(oxygen, 7, (10, 9)): (0.0343805, 1.8122, -0.2718), - Line(oxygen, 7, (11, 9)): (0.0113640, 2.0268, -0.2911), - Line(neon, 9, (9, 8)): (0.0072488, 2.8838, -0.2758), - Line(neon, 9, (10, 9)): (0.0141002, 2.4755, -0.2718), - Line(neon, 9, (11, 9)): (0.0046673, 2.8410, -0.2917), - Line(neon, 9, (11, 10)): (0.0257292, 2.1890, -0.2715) - } - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, dict line_parameters=None, polarisation='no'): - - super().__init__(line, wavelength, target_species, plasma, polarisation) - - line_parameters = line_parameters or self.LINE_PARAMETERS_DEFAULT - - try: - alpha, beta, gamma = line_parameters[self.line] - if alpha <= 0: - raise ValueError('Parameter alpha must be positive.') - if beta < 0: - raise ValueError('Parameter beta must be non-negative.') - self._alpha = alpha - self._beta = beta - self._gamma = gamma - - except KeyError: - raise ValueError('Data for {} is not available.'.format(self.line)) - - def show_supported_transitions(self): - """ Prints all supported transitions.""" - for line, param in self.LINE_PARAMETERS_DEFAULT.items(): - print('{}: alpha={}, beta={}, gamma={}'.format(line, param[0], param[1], param[2])) - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - @cython.cdivision(True) - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - - cdef double ts, sigma, shifted_wavelength, b_magn, component_radiance, cos_sqr, sin_sqr - cdef Vector3D ion_velocity, b_field - - ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) - if ts <= 0.0: - return spectrum - - ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) - - # calculate emission line central wavelength, doppler shifted along observation direction - shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) - - # calculate the line width - sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) - - # fine structure broadening correction - sigma *= sqrt(1. + self._beta * self._beta * ts**(2. * self._gamma)) - - # obtain magnetic field - b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) - b_magn = b_field.get_length() - - if b_magn == 0: - # no splitting if magnetic filed strength is zero - if self._polarisation == NO_POLARISATION: - return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) - - return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) - - # coefficients for intensities parallel and perpendicular to magnetic field - cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 - sin_sqr = 1. - cos_sqr - - # adding pi component of the Zeeman triplet in case of NO_POLARISATION or PI_POLARISATION - if self._polarisation != SIGMA_POLARISATION: - component_radiance = 0.5 * sin_sqr * radiance - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - # adding sigma +/- components of the Zeeman triplet in case of NO_POLARISATION or SIGMA_POLARISATION - if self._polarisation != PI_POLARISATION: - component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance - - shifted_wavelength = doppler_shift(self.wavelength + 0.5 * self._alpha * b_magn, direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - shifted_wavelength = doppler_shift(self.wavelength - 0.5 * self._alpha * b_magn, direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) - - return spectrum - - -cdef class ZeemanMultiplet(ZeemanLineShapeModel): - r""" - Doppler-Zeeman Multiplet. - - The lineshape radiance is calculated from a base PEC rate that is unresolved. This - radiance is then divided over a number of components as specified in the ``zeeman_structure`` - argument. The ``zeeman_structure`` specifies wavelengths and ratios of - :math:`\pi`-/:math:`\sigma`-polarised components as functions of the magnetic field strength. - These functions can be obtained using the output of the ADAS603 routines. - - :param Line line: The emission line object for the base rate radiance calculation. - :param float wavelength: The rest wavelength of the base emission line. - :param Species target_species: The target plasma species that is emitting. - :param Plasma plasma: The emitting plasma object. - :param zeeman_structure: A ``ZeemanStructure`` object that provides wavelengths and ratios - of :math:`\pi`-/:math:`\sigma^{+}`-/:math:`\sigma^{-}`-polarised - components for any given magnetic field strength. - :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: - "pi" - leave only :math:`\pi`-polarised components, - "sigma" - leave only :math:`\sigma`-polarised components, - "no" - leave all components (default). - - """ - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, - ZeemanStructure zeeman_structure, polarisation='no'): - - super().__init__(line, wavelength, target_species, plasma, polarisation) - - self._zeeman_structure = zeeman_structure - - @cython.boundscheck(False) - @cython.wraparound(False) - @cython.initializedcheck(False) - @cython.cdivision(True) - cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): - - cdef int i - cdef double ts, sigma, shifted_wavelength, component_radiance - cdef Vector3D ion_velocity - cdef double[:, :] multiplet_pi_mv, multiplet_sigma_mv - - ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) - if ts <= 0.0: - return spectrum - - ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) - - # calculate the line width - sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) - - # obtain magnetic field - b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) - b_magn = b_field.get_length() - - if b_magn == 0: - # no splitting if magnetic filed strength is zero - shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) - if self._polarisation == NO_POLARISATION: - return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) - - return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) - - # coefficients for intensities parallel and perpendicular to magnetic field - cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 - sin_sqr = 1. - cos_sqr - - # adding pi components of the Zeeman multiplet in case of NO_POLARISATION or PI_POLARISATION - if self._polarisation != SIGMA_POLARISATION: - component_radiance = 0.5 * sin_sqr * radiance - multiplet_mv = self._zeeman_structure.evaluate(b_magn, PI_POLARISATION) - - for i in range(multiplet_mv.shape[1]): - shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) - - # adding sigma components of the Zeeman multiplet in case of NO_POLARISATION or SIGMA_POLARISATION - if self._polarisation != PI_POLARISATION: - component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance - - multiplet_mv = self._zeeman_structure.evaluate(b_magn, SIGMA_PLUS_POLARISATION) - - for i in range(multiplet_mv.shape[1]): - shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) - - multiplet_mv = self._zeeman_structure.evaluate(b_magn, SIGMA_MINUS_POLARISATION) - - for i in range(multiplet_mv.shape[1]): - shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) - spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) - - return spectrum - - -cdef class BeamLineShapeModel: - """ - A base class for building beam emission line shapes. - - :param Line line: The emission line object for this line shape. - :param float wavelength: The rest wavelength for this emission line. - :param Beam beam: The beam class that is emitting. - """ - - def __init__(self, Line line, double wavelength, Beam beam): - - self.line = line - self.wavelength = wavelength - self.beam = beam - - cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, - Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum): - raise NotImplementedError('Child lineshape class must implement this method.') - - -DEF STARK_SPLITTING_FACTOR = 2.77e-8 - - -cdef class BeamEmissionMultiplet(BeamLineShapeModel): - """ - Produces Beam Emission Multiplet line shape, also known as the Motional Stark Effect spectrum. - """ - - def __init__(self, Line line, double wavelength, Beam beam, object sigma_to_pi, - object sigma1_to_sigma0, object pi2_to_pi3, object pi4_to_pi3): - - super().__init__(line, wavelength, beam) - - self._sigma_to_pi = autowrap_function2d(sigma_to_pi) - self._sigma1_to_sigma0 = autowrap_function1d(sigma1_to_sigma0) - self._pi2_to_pi3 = autowrap_function1d(pi2_to_pi3) - self._pi4_to_pi3 = autowrap_function1d(pi4_to_pi3) - - @cython.cdivision(True) - cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, - Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum): - - cdef double x, y, z - cdef Plasma plasma - cdef double te, ne, beam_energy, sigma, stark_split, beam_ion_mass, beam_temperature - cdef double natural_wavelength, central_wavelength - cdef double sigma_to_pi, d, intensity_sig, intensity_pi, e_field - cdef double s1_to_s0, intensity_s0, intensity_s1 - cdef double pi2_to_pi3, pi4_to_pi3, intensity_pi2, intensity_pi3, intensity_pi4 - cdef Vector3D b_field, beam_velocity - - # extract for more compact code - x = plasma_point.x - y = plasma_point.y - z = plasma_point.z - - plasma = self.beam.get_plasma() - - te = plasma.get_electron_distribution().effective_temperature(x, y, z) - if te <= 0.0: - return spectrum - - ne = plasma.get_electron_distribution().density(x, y, z) - if ne <= 0.0: - return spectrum - - beam_energy = self.beam.get_energy() - - # calculate Stark splitting - b_field = plasma.get_b_field().evaluate(x, y, z) - beam_velocity = beam_direction.normalise().mul(evamu_to_ms(beam_energy)) - e_field = beam_velocity.cross(b_field).get_length() - stark_split = fabs(STARK_SPLITTING_FACTOR * e_field) # TODO - calculate splitting factor? Reject other lines? - - # calculate emission line central wavelength, doppler shifted along observation direction - natural_wavelength = self.wavelength - central_wavelength = doppler_shift(natural_wavelength, observation_direction, beam_velocity) - - # calculate doppler broadening - beam_ion_mass = self.beam.get_element().atomic_weight - beam_temperature = self.beam.get_temperature() - sigma = thermal_broadening(self.wavelength, beam_temperature, beam_ion_mass) - - # calculate relative intensities of sigma and pi lines - sigma_to_pi = self._sigma_to_pi.evaluate(ne, beam_energy) - d = 1 / (1 + sigma_to_pi) - intensity_sig = sigma_to_pi * d * radiance - intensity_pi = 0.5 * d * radiance - - # add Sigma lines to output - s1_to_s0 = self._sigma1_to_sigma0.evaluate(ne) - intensity_s0 = 1 / (s1_to_s0 + 1) - intensity_s1 = 0.5 * s1_to_s0 * intensity_s0 - - spectrum = add_gaussian_line(intensity_sig * intensity_s0, central_wavelength, sigma, spectrum) - spectrum = add_gaussian_line(intensity_sig * intensity_s1, central_wavelength + stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_sig * intensity_s1, central_wavelength - stark_split, sigma, spectrum) - - # add Pi lines to output - pi2_to_pi3 = self._pi2_to_pi3.evaluate(ne) - pi4_to_pi3 = self._pi4_to_pi3.evaluate(ne) - intensity_pi3 = 1 / (1 + pi2_to_pi3 + pi4_to_pi3) - intensity_pi2 = pi2_to_pi3 * intensity_pi3 - intensity_pi4 = pi4_to_pi3 * intensity_pi3 - - spectrum = add_gaussian_line(intensity_pi * intensity_pi2, central_wavelength + 2 * stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_pi * intensity_pi2, central_wavelength - 2 * stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_pi * intensity_pi3, central_wavelength + 3 * stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_pi * intensity_pi3, central_wavelength - 3 * stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_pi * intensity_pi4, central_wavelength + 4 * stark_split, sigma, spectrum) - spectrum = add_gaussian_line(intensity_pi * intensity_pi4, central_wavelength - 4 * stark_split, sigma, spectrum) - - return spectrum diff --git a/cherab/core/model/lineshape/__init__.pxd b/cherab/core/model/lineshape/__init__.pxd new file mode 100644 index 00000000..1eeb4f7c --- /dev/null +++ b/cherab/core/model/lineshape/__init__.pxd @@ -0,0 +1,25 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.model.lineshape.beam cimport BeamLineShapeModel, BeamEmissionMultiplet +from cherab.core.model.lineshape.base cimport LineShapeModel +from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening +from cherab.core.model.lineshape.gaussian cimport add_gaussian_line, GaussianLine +from cherab.core.model.lineshape.multiplet cimport MultipletLineShape +from cherab.core.model.lineshape.stark cimport StarkBroadenedLine +from cherab.core.model.lineshape.zeeman cimport ZeemanLineShapeModel, ZeemanTriplet, ParametrisedZeemanTriplet, ZeemanMultiplet diff --git a/cherab/core/model/lineshape/__init__.py b/cherab/core/model/lineshape/__init__.py new file mode 100644 index 00000000..e0ad6a24 --- /dev/null +++ b/cherab/core/model/lineshape/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 .beam import BeamLineShapeModel, BeamEmissionMultiplet +from .base import LineShapeModel +from .doppler import doppler_shift, thermal_broadening +from .gaussian import add_gaussian_line, GaussianLine +from .multiplet import MultipletLineShape +from .stark import StarkBroadenedLine +from .zeeman import ZeemanLineShapeModel, ZeemanTriplet, ParametrisedZeemanTriplet, ZeemanMultiplet diff --git a/cherab/core/model/lineshape/base.pxd b/cherab/core/model/lineshape/base.pxd new file mode 100644 index 00000000..4ec2d2e6 --- /dev/null +++ b/cherab/core/model/lineshape/base.pxd @@ -0,0 +1,37 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 Spectrum, Point3D, Vector3D +from cherab.core.atomic cimport Line +from cherab.core.species cimport Species +from cherab.core.plasma cimport Plasma +from cherab.core.math.integrators cimport Integrator1D + + +cdef class LineShapeModel: + + cdef: + Line line + double wavelength + Species target_species + Plasma plasma + Integrator1D integrator + + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum) diff --git a/cherab/core/model/lineshape/base.pyx b/cherab/core/model/lineshape/base.pyx new file mode 100644 index 00000000..7577508e --- /dev/null +++ b/cherab/core/model/lineshape/base.pyx @@ -0,0 +1,43 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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. + + +cdef class LineShapeModel: + """ + A base class for building line shapes. + + :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 None. + """ + + 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.') diff --git a/cherab/core/model/lineshape/beam/__init__.pxd b/cherab/core/model/lineshape/beam/__init__.pxd new file mode 100644 index 00000000..6172d6c3 --- /dev/null +++ b/cherab/core/model/lineshape/beam/__init__.pxd @@ -0,0 +1,20 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.model.lineshape.beam.base cimport * +from cherab.core.model.lineshape.beam.mse cimport * diff --git a/cherab/core/model/lineshape/beam/__init__.py b/cherab/core/model/lineshape/beam/__init__.py new file mode 100644 index 00000000..48d34582 --- /dev/null +++ b/cherab/core/model/lineshape/beam/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 .base import BeamLineShapeModel +from .mse import BeamEmissionMultiplet diff --git a/cherab/core/model/lineshape/beam/base.pxd b/cherab/core/model/lineshape/beam/base.pxd new file mode 100644 index 00000000..b3da2323 --- /dev/null +++ b/cherab/core/model/lineshape/beam/base.pxd @@ -0,0 +1,35 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 Spectrum, Point3D, Vector3D +from cherab.core.atomic cimport Line +from cherab.core.beam cimport Beam + + +cdef class BeamLineShapeModel: + + cdef: + + Line line + double wavelength + Beam beam + + cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, + Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum) diff --git a/cherab/core/model/lineshape/beam/base.pyx b/cherab/core/model/lineshape/beam/base.pyx new file mode 100644 index 00000000..793e17d4 --- /dev/null +++ b/cherab/core/model/lineshape/beam/base.pyx @@ -0,0 +1,39 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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. + + +cdef class BeamLineShapeModel: + """ + A base class for building beam emission line shapes. + + :param Line line: The emission line object for this line shape. + :param float wavelength: The rest wavelength for this emission line. + :param Beam beam: The beam class that is emitting. + """ + + def __init__(self, Line line, double wavelength, Beam beam): + + self.line = line + self.wavelength = wavelength + self.beam = beam + + cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, + Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum): + raise NotImplementedError('Child lineshape class must implement this method.') diff --git a/cherab/core/model/lineshape/beam/mse.pxd b/cherab/core/model/lineshape/beam/mse.pxd new file mode 100644 index 00000000..a7b6131a --- /dev/null +++ b/cherab/core/model/lineshape/beam/mse.pxd @@ -0,0 +1,30 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 Function1D, Function2D +from cherab.core.model.lineshape.beam.base cimport BeamLineShapeModel + + +cdef class BeamEmissionMultiplet(BeamLineShapeModel): + + cdef: + + Function2D _sigma_to_pi + Function1D _sigma1_to_sigma0, _pi2_to_pi3, _pi4_to_pi3 diff --git a/cherab/core/model/lineshape/beam/mse.pyx b/cherab/core/model/lineshape/beam/mse.pyx new file mode 100644 index 00000000..3075cff2 --- /dev/null +++ b/cherab/core/model/lineshape/beam/mse.pyx @@ -0,0 +1,134 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 fabs, sqrt +from raysect.optical cimport Spectrum, Point3D, Vector3D + +from cherab.core.plasma cimport Plasma +from cherab.core.beam cimport Beam +from cherab.core.atomic cimport Line +from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d +from cherab.core.utility.constants cimport ATOMIC_MASS, ELEMENTARY_CHARGE +from cherab.core.model.lineshape.gaussian cimport add_gaussian_line +from cherab.core.model.lineshape.doppler cimport thermal_broadening, doppler_shift + +cimport cython + + +cdef double RECIP_ATOMIC_MASS = 1 / ATOMIC_MASS + + +cdef double evamu_to_ms(double x): + return sqrt(2 * x * ELEMENTARY_CHARGE * RECIP_ATOMIC_MASS) + + +DEF STARK_SPLITTING_FACTOR = 2.77e-8 + + +cdef class BeamEmissionMultiplet(BeamLineShapeModel): + """ + Produces Beam Emission Multiplet line shape, also known as the Motional Stark Effect spectrum. + """ + + def __init__(self, Line line, double wavelength, Beam beam, object sigma_to_pi, + object sigma1_to_sigma0, object pi2_to_pi3, object pi4_to_pi3): + + super().__init__(line, wavelength, beam) + + self._sigma_to_pi = autowrap_function2d(sigma_to_pi) + self._sigma1_to_sigma0 = autowrap_function1d(sigma1_to_sigma0) + self._pi2_to_pi3 = autowrap_function1d(pi2_to_pi3) + self._pi4_to_pi3 = autowrap_function1d(pi4_to_pi3) + + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, + Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum): + + cdef double x, y, z + cdef Plasma plasma + cdef double te, ne, beam_energy, sigma, stark_split, beam_ion_mass, beam_temperature + cdef double natural_wavelength, central_wavelength + cdef double sigma_to_pi, d, intensity_sig, intensity_pi, e_field + cdef double s1_to_s0, intensity_s0, intensity_s1 + cdef double pi2_to_pi3, pi4_to_pi3, intensity_pi2, intensity_pi3, intensity_pi4 + cdef Vector3D b_field, beam_velocity + + # extract for more compact code + x = plasma_point.x + y = plasma_point.y + z = plasma_point.z + + plasma = self.beam.get_plasma() + + te = plasma.get_electron_distribution().effective_temperature(x, y, z) + if te <= 0.0: + return spectrum + + ne = plasma.get_electron_distribution().density(x, y, z) + if ne <= 0.0: + return spectrum + + beam_energy = self.beam.get_energy() + + # calculate Stark splitting + b_field = plasma.get_b_field().evaluate(x, y, z) + beam_velocity = beam_direction.normalise().mul(evamu_to_ms(beam_energy)) + e_field = beam_velocity.cross(b_field).get_length() + stark_split = fabs(STARK_SPLITTING_FACTOR * e_field) # TODO - calculate splitting factor? Reject other lines? + + # calculate emission line central wavelength, doppler shifted along observation direction + natural_wavelength = self.wavelength + central_wavelength = doppler_shift(natural_wavelength, observation_direction, beam_velocity) + + # calculate doppler broadening + beam_ion_mass = self.beam.get_element().atomic_weight + beam_temperature = self.beam.get_temperature() + sigma = thermal_broadening(self.wavelength, beam_temperature, beam_ion_mass) + + # calculate relative intensities of sigma and pi lines + sigma_to_pi = self._sigma_to_pi.evaluate(ne, beam_energy) + d = 1 / (1 + sigma_to_pi) + intensity_sig = sigma_to_pi * d * radiance + intensity_pi = 0.5 * d * radiance + + # add Sigma lines to output + s1_to_s0 = self._sigma1_to_sigma0.evaluate(ne) + intensity_s0 = 1 / (s1_to_s0 + 1) + intensity_s1 = 0.5 * s1_to_s0 * intensity_s0 + + spectrum = add_gaussian_line(intensity_sig * intensity_s0, central_wavelength, sigma, spectrum) + spectrum = add_gaussian_line(intensity_sig * intensity_s1, central_wavelength + stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_sig * intensity_s1, central_wavelength - stark_split, sigma, spectrum) + + # add Pi lines to output + pi2_to_pi3 = self._pi2_to_pi3.evaluate(ne) + pi4_to_pi3 = self._pi4_to_pi3.evaluate(ne) + intensity_pi3 = 1 / (1 + pi2_to_pi3 + pi4_to_pi3) + intensity_pi2 = pi2_to_pi3 * intensity_pi3 + intensity_pi4 = pi4_to_pi3 * intensity_pi3 + + spectrum = add_gaussian_line(intensity_pi * intensity_pi2, central_wavelength + 2 * stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_pi * intensity_pi2, central_wavelength - 2 * stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_pi * intensity_pi3, central_wavelength + 3 * stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_pi * intensity_pi3, central_wavelength - 3 * stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_pi * intensity_pi4, central_wavelength + 4 * stark_split, sigma, spectrum) + spectrum = add_gaussian_line(intensity_pi * intensity_pi4, central_wavelength - 4 * stark_split, sigma, spectrum) + + return spectrum diff --git a/cherab/core/model/lineshape/doppler.pxd b/cherab/core/model/lineshape/doppler.pxd new file mode 100644 index 00000000..3d4438fc --- /dev/null +++ b/cherab/core/model/lineshape/doppler.pxd @@ -0,0 +1,26 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 + + +cpdef double doppler_shift(double wavelength, Vector3D observation_direction, Vector3D velocity) + +cpdef double thermal_broadening(double wavelength, double temperature, double atomic_weight) diff --git a/cherab/core/model/lineshape/doppler.pyx b/cherab/core/model/lineshape/doppler.pyx new file mode 100644 index 00000000..93755733 --- /dev/null +++ b/cherab/core/model/lineshape/doppler.pyx @@ -0,0 +1,59 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 sqrt + +from cherab.core.utility.constants cimport ATOMIC_MASS, ELEMENTARY_CHARGE, SPEED_OF_LIGHT + +cimport cython + + +@cython.cdivision(True) +cpdef double doppler_shift(double wavelength, Vector3D observation_direction, Vector3D velocity): + """ + Calculates the Doppler shifted wavelength for a given velocity and observation direction. + + :param wavelength: The wavelength to Doppler shift in nanometers. + :param observation_direction: A Vector defining the direction of observation. + :param velocity: A Vector defining the relative velocity of the emitting source in m/s. + :return: The Doppler shifted wavelength in nanometers. + """ + cdef double projected_velocity + + # flow velocity projected on the direction of observation + observation_direction = observation_direction.normalise() + projected_velocity = velocity.dot(observation_direction) + + return wavelength * (1 + projected_velocity / SPEED_OF_LIGHT) + + +@cython.cdivision(True) +cpdef double thermal_broadening(double wavelength, double temperature, double atomic_weight): + """ + Returns the line width for a gaussian line as a standard deviation. + + :param wavelength: Central wavelength. + :param temperature: Temperature in eV. + :param atomic_weight: Atomic weight in AMU. + :return: Standard deviation of gaussian line. + """ + + # todo: add input sanity checks + return sqrt(temperature * ELEMENTARY_CHARGE / (atomic_weight * ATOMIC_MASS)) * wavelength / SPEED_OF_LIGHT diff --git a/cherab/core/model/lineshape/gaussian.pxd b/cherab/core/model/lineshape/gaussian.pxd new file mode 100644 index 00000000..cd8d0091 --- /dev/null +++ b/cherab/core/model/lineshape/gaussian.pxd @@ -0,0 +1,29 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 Spectrum +from cherab.core.model.lineshape.base cimport LineShapeModel + + +cpdef Spectrum add_gaussian_line(double radiance, double wavelength, double sigma, Spectrum spectrum) + + +cdef class GaussianLine(LineShapeModel): + pass diff --git a/cherab/core/model/lineshape/gaussian.pyx b/cherab/core/model/lineshape/gaussian.pyx new file mode 100644 index 00000000..936d70df --- /dev/null +++ b/cherab/core/model/lineshape/gaussian.pyx @@ -0,0 +1,138 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 erf, M_SQRT2, floor, ceil +from raysect.optical cimport Point3D, Vector3D + +from cherab.core.atomic cimport Line +from cherab.core.species cimport Species +from cherab.core.plasma cimport Plasma +from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening + +cimport cython + + +# 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 + + +@cython.cdivision(True) +@cython.initializedcheck(False) +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef Spectrum add_gaussian_line(double radiance, double wavelength, double sigma, Spectrum spectrum): + r""" + Adds a Gaussian line to the given spectrum and returns the new spectrum. + + The formula used is based on the following definite integral: + :math:`\frac{1}{\sigma \sqrt{2 \pi}} \int_{\lambda_0}^{\lambda_1} \exp(-\frac{(x-\mu)^2}{2\sigma^2}) dx = \frac{1}{2} \left[ -Erf(\frac{a-\mu}{\sqrt{2}\sigma}) +Erf(\frac{b-\mu}{\sqrt{2}\sigma}) \right]` + + :param float radiance: Intensity of the line in radiance. + :param float wavelength: central wavelength of the line in nm. + :param float sigma: width of the line in nm. + :param Spectrum spectrum: the current spectrum to which the gaussian line is added. + :return: + """ + + cdef double temp + cdef double cutoff_lower_wavelength, cutoff_upper_wavelength + cdef double lower_wavelength, upper_wavelength + cdef double lower_integral, upper_integral + cdef int start, end, i + + if sigma <= 0: + return spectrum + + # calculate and check end of limits + cutoff_lower_wavelength = wavelength - GAUSSIAN_CUTOFF_SIGMA * sigma + if spectrum.max_wavelength < cutoff_lower_wavelength: + return spectrum + + cutoff_upper_wavelength = wavelength + GAUSSIAN_CUTOFF_SIGMA * sigma + if spectrum.min_wavelength > cutoff_upper_wavelength: + return spectrum + + # locate range of bins where there is significant contribution from the gaussian (plus a health margin) + 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)) + + # add line to spectrum + temp = 1 / (M_SQRT2 * sigma) + lower_wavelength = spectrum.min_wavelength + start * spectrum.delta_wavelength + lower_integral = erf((lower_wavelength - wavelength) * temp) + for i in range(start, end): + + upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) + upper_integral = erf((upper_wavelength - wavelength) * temp) + + spectrum.samples_mv[i] += radiance * 0.5 * (upper_integral - lower_integral) / spectrum.delta_wavelength + + lower_wavelength = upper_wavelength + lower_integral = upper_integral + + return spectrum + + +cdef class GaussianLine(LineShapeModel): + """ + Produces Gaussian line shape. + + :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. + + .. code-block:: pycon + + >>> from cherab.core.atomic import Line, deuterium + >>> from cherab.core.model import ExcitationLine, GaussianLine + >>> + >>> # Adding Gaussian line to the plasma model. + >>> d_alpha = Line(deuterium, 0, (3, 2)) + >>> excit = ExcitationLine(d_alpha, lineshape=GaussianLine) + >>> plasma.models.add(excit) + """ + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma): + + super().__init__(line, wavelength, target_species, plasma) + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef double ts, sigma, shifted_wavelength + cdef Vector3D ion_velocity + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + if ts <= 0.0: + return spectrum + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate emission line central wavelength, doppler shifted along observation direction + shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) + + # calculate the line width + sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) + + return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) diff --git a/cherab/core/model/lineshape/multiplet.pxd b/cherab/core/model/lineshape/multiplet.pxd new file mode 100644 index 00000000..577afadc --- /dev/null +++ b/cherab/core/model/lineshape/multiplet.pxd @@ -0,0 +1,31 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 numpy as np + +from cherab.core.model.lineshape.base cimport LineShapeModel + + +cdef class MultipletLineShape(LineShapeModel): + + cdef: + int _number_of_lines + np.ndarray _multiplet + double[:, ::1] _multiplet_mv diff --git a/cherab/core/model/lineshape/multiplet.pyx b/cherab/core/model/lineshape/multiplet.pyx new file mode 100644 index 00000000..e30093eb --- /dev/null +++ b/cherab/core/model/lineshape/multiplet.pyx @@ -0,0 +1,116 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 raysect.optical cimport Spectrum, Point3D, Vector3D + +from cherab.core.atomic cimport Line +from cherab.core.species cimport Species +from cherab.core.plasma cimport Plasma +from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening +from cherab.core.model.lineshape.gaussian cimport add_gaussian_line + +cimport cython + +# required by numpy c-api +np.import_array() + + +DEF MULTIPLET_WAVELENGTH = 0 +DEF MULTIPLET_RATIO = 1 + + +cdef class MultipletLineShape(LineShapeModel): + """ + Produces Multiplet line shapes. + + The lineshape radiance is calculated from a base PEC rate that is unresolved. This + radiance is then divided over a number of components as specified in the multiplet + argument. The multiplet components are specified with an Nx2 array where N is the + number of components in the multiplet. The first axis of the array contains the + wavelengths of each component, the second contains the line ratio for each component. + The component line ratios must sum to one. For example: + + :param Line line: The emission line object for the base rate radiance calculation. + :param float wavelength: The rest wavelength of the base emission line. + :param Species target_species: The target plasma species that is emitting. + :param Plasma plasma: The emitting plasma object. + :param multiplet: An Nx2 array that specifies the multiplet wavelengths and line ratios. + + .. code-block:: pycon + + >>> from cherab.core.atomic import Line, nitrogen + >>> from cherab.core.model import ExcitationLine, MultipletLineShape + >>> + >>> # multiplet specification in Nx2 array + >>> multiplet = [[403.509, 404.132, 404.354, 404.479, 405.692], [0.205, 0.562, 0.175, 0.029, 0.029]] + >>> + >>> # Adding the multiplet to the plasma model. + >>> nitrogen_II_404 = Line(nitrogen, 1, ("2s2 2p1 4f1 3G13.0", "2s2 2p1 3d1 3F10.0")) + >>> excit = ExcitationLine(nitrogen_II_404, lineshape=MultipletLineShape, lineshape_args=[multiplet]) + >>> plasma.models.add(excit) + """ + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, + object multiplet): + + super().__init__(line, wavelength, target_species, plasma) + + multiplet = np.array(multiplet, dtype=np.float64) + + if not (len(multiplet.shape) == 2 and multiplet.shape[0] == 2): + raise ValueError("The multiplet specification must be an array of shape (Nx2).") + + if not multiplet[1, :].sum() == 1.0: + raise ValueError("The multiplet line ratios should sum to one.") + + self._number_of_lines = multiplet.shape[1] + self._multiplet = multiplet + self._multiplet_mv = self._multiplet + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef double ts, sigma, shifted_wavelength, component_wavelength, component_radiance + cdef Vector3D ion_velocity + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + if ts <= 0.0: + return spectrum + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate the line width + sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) + + for i in range(self._number_of_lines): + + component_wavelength = self._multiplet_mv[MULTIPLET_WAVELENGTH, i] + component_radiance = radiance * self._multiplet_mv[MULTIPLET_RATIO, i] + + # calculate emission line central wavelength, doppler shifted along observation direction + shifted_wavelength = doppler_shift(component_wavelength, direction, ion_velocity) + + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + return spectrum diff --git a/cherab/core/model/lineshape/stark.pxd b/cherab/core/model/lineshape/stark.pxd new file mode 100644 index 00000000..8b4ac4b0 --- /dev/null +++ b/cherab/core/model/lineshape/stark.pxd @@ -0,0 +1,36 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 Spectrum +from cherab.core.math.integrators cimport Integrator1D +from cherab.core.model.lineshape.zeeman cimport ZeemanLineShapeModel + + +cpdef Spectrum add_lorentzian_line(double radiance, double wavelength, double lambda_1_2, Spectrum spectrum, + Integrator1D integrator) + + +cdef class StarkBroadenedLine(ZeemanLineShapeModel): + + cdef: + double _aij, _bij, _cij + double _fwhm_poly_coeff_gauss[7] + double _fwhm_poly_coeff_lorentz[7] + double _weight_poly_coeff[6] diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx new file mode 100644 index 00000000..ea26fd27 --- /dev/null +++ b/cherab/core/model/lineshape/stark.pyx @@ -0,0 +1,331 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 scipy.special import hyp2f1 + +from libc.math cimport sqrt, floor, ceil, fabs, log, exp +from raysect.core.math.function.float cimport Function1D +from raysect.optical cimport Point3D, Vector3D + +from cherab.core.atomic cimport Line +from cherab.core.species cimport Species +from cherab.core.plasma cimport Plasma +from cherab.core.atomic.elements import hydrogen, deuterium, tritium +from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d +from cherab.core.math.integrators cimport GaussianQuadrature +from cherab.core.utility.constants cimport BOHR_MAGNETON, HC_EV_NM +from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening +from cherab.core.model.lineshape.gaussian cimport add_gaussian_line + + +cimport cython + + +DEF PI_POLARISATION = 0 +DEF SIGMA_POLARISATION = 1 +DEF SIGMA_PLUS_POLARISATION = 1 +DEF SIGMA_MINUS_POLARISATION = -1 +DEF NO_POLARISATION = 2 + +DEF LORENTZIAN_CUTOFF_GAMMA = 50.0 + +cdef double _SIGMA2FWHM = 2 * sqrt(2 * log(2)) + + +cdef class StarkFunction(Function1D): + """ + Normalised Stark function for the StarkBroadenedLine line shape. + """ + + cdef double _a, _x0, _norm + + STARK_NORM_COEFFICIENT = 4 * LORENTZIAN_CUTOFF_GAMMA * hyp2f1(0.4, 1, 1.4, -(2 * LORENTZIAN_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 - LORENTZIAN_CUTOFF_GAMMA * lambda_1_2, _x0 + LORENTZIAN_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 self._norm / ((fabs(x - self._x0))**2.5 + self._a) + + +@cython.cdivision(True) +@cython.initializedcheck(False) +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef Spectrum add_lorentzian_line(double radiance, double wavelength, double lambda_1_2, Spectrum spectrum, Integrator1D integrator): + r""" + Adds a modified Lorentzian line to the given spectrum and returns the new spectrum. + + :param float radiance: Intensity of the line in radiance. + :param float wavelength: central wavelength of the line in nm. + :param float sigma: width of the line in nm. + :param Spectrum spectrum: the current spectrum to which the gaussian line is added. + :return: + """ + + cdef double cutoff_lower_wavelength, cutoff_upper_wavelength + cdef double lower_wavelength, upper_wavelength + cdef double bin_integral + cdef int start, end, i + + if lambda_1_2 <= 0: + return spectrum + + integrator.function = StarkFunction(wavelength, lambda_1_2) + + # calculate and check end of limits + cutoff_lower_wavelength = wavelength - LORENTZIAN_CUTOFF_GAMMA * lambda_1_2 + if spectrum.max_wavelength < cutoff_lower_wavelength: + return spectrum + + cutoff_upper_wavelength = wavelength + LORENTZIAN_CUTOFF_GAMMA * lambda_1_2 + if spectrum.min_wavelength > cutoff_upper_wavelength: + return spectrum + + # locate range of bins where there is significant contribution from the gaussian (plus a health margin) + 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)) + + # add line to spectrum + lower_wavelength = spectrum.min_wavelength + start * spectrum.delta_wavelength + + for i in range(start, end): + upper_wavelength = spectrum.min_wavelength + spectrum.delta_wavelength * (i + 1) + + bin_integral = integrator.evaluate(lower_wavelength, upper_wavelength) + spectrum.samples_mv[i] += radiance * bin_integral / spectrum.delta_wavelength + + lower_wavelength = upper_wavelength + + return spectrum + + +cdef class StarkBroadenedLine(ZeemanLineShapeModel): + """ + Parametrised Stark broadened line shape based on the Model Microfield Method (MMM). + Contains embedded atomic data in the form of fits to MMM. + Only Balmer and Paschen series are supported by default. + See B. Lomanowski, et al. "Inferring divertor plasma properties from hydrogen Balmer + and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) + `123028 `_. + + Call `show_supported_transitions()` to see the list of supported transitions and + default model coefficients. + + :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 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 = { + Line(hydrogen, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), + Line(hydrogen, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), + Line(hydrogen, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), + Line(hydrogen, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), + Line(hydrogen, 0, (7, 2)): (6.258e-16, 0.712, 0.029), + Line(hydrogen, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), + Line(hydrogen, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), + Line(hydrogen, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), + Line(hydrogen, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), + Line(hydrogen, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), + Line(hydrogen, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), + Line(hydrogen, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), + Line(hydrogen, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), + Line(deuterium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), + Line(deuterium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), + Line(deuterium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), + Line(deuterium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), + Line(deuterium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), + Line(deuterium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), + Line(deuterium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), + Line(deuterium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), + Line(deuterium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), + Line(deuterium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), + Line(deuterium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), + Line(deuterium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), + Line(deuterium, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), + Line(tritium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), + Line(tritium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), + Line(tritium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), + Line(tritium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), + Line(tritium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), + Line(tritium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), + Line(tritium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), + Line(tritium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), + Line(tritium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), + Line(tritium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), + Line(tritium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), + Line(tritium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), + 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, Integrator1D integrator=GaussianQuadrature(), polarisation='no'): + + stark_model_coefficients = stark_model_coefficients or self.STARK_MODEL_COEFFICIENTS_DEFAULT + + try: + # Fitted Stark Constants + cij, aij, bij = stark_model_coefficients[line] + if cij <= 0: + raise ValueError('Coefficient c_ij must be positive.') + if aij <= 0: + raise ValueError('Coefficient a_ij must be positive.') + if bij <= 0: + raise ValueError('Coefficient b_ij must be positive.') + self._aij = aij + self._bij = bij + self._cij = cij + except IndexError: + raise ValueError('Stark broadening coefficients for {} is not currently available.'.format(line)) + + # polynomial coefficients in increasing powers + self._fwhm_poly_coeff_gauss = [1., 0, 0.57575, 0.37902, -0.42519, -0.31525, 0.31718] + self._fwhm_poly_coeff_lorentz = [1., 0.15882, 1.04388, -1.38281, 0.46251, 0.82325, -0.58026] + + self._weight_poly_coeff = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04] + + super().__init__(line, wavelength, target_species, plasma, polarisation, integrator) + + def show_supported_transitions(self): + """ Prints all supported transitions.""" + for line, coeff in self.STARK_MODEL_COEFFICIENTS_DEFAULT.items(): + print('{}: c_ij={}, a_ij={}, b_ij={}'.format(line, coeff[0], coeff[1], coeff[2])) + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef: + double ne, te, ts, shifted_wavelength, photon_energy, b_magn, comp_radiance, cos_sqr, sin_sqr + double sigma, fwhm_lorentz, fwhm_gauss, fwhm_full, fwhm_ratio, fwhm_lorentz_to_total, lorentz_weight, gauss_weight + cdef Vector3D ion_velocity, b_field + int i + + ne = self.plasma.get_electron_distribution().density(point.x, point.y, point.z) + + te = self.plasma.get_electron_distribution().effective_temperature(point.x, point.y, point.z) + + fwhm_lorentz = self._cij * ne**self._aij / (te**self._bij) if ne > 0 and te > 0 else 0 + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + + fwhm_gauss = _SIGMA2FWHM * thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) if ts > 0 else 0 + + if fwhm_lorentz == 0 and fwhm_gauss == 0: + return spectrum + + # calculating total FWHM + if fwhm_gauss <= fwhm_lorentz: + fwhm_ratio = fwhm_gauss / fwhm_lorentz + fwhm_full = self._fwhm_poly_coeff_gauss[0] + for i in range(1, 7): + fwhm_full += self._fwhm_poly_coeff_gauss[i] * fwhm_ratio**i + else: + fwhm_ratio = fwhm_lorentz / fwhm_gauss + fwhm_full = self._fwhm_poly_coeff_lorentz[0] + for i in range(1, 7): + fwhm_full += self._fwhm_poly_coeff_lorentz[i] * fwhm_ratio**i + + sigma = fwhm_full / _SIGMA2FWHM + + fwhm_lorentz_to_total = fwhm_lorentz / fwhm_full + + # calculating Lorentzian weight + if fwhm_lorentz_to_total < 0.01: + lorentz_weight = 0 + fwhm_full = 0 # force add_lorentzian_line() to immediately return + elif fwhm_lorentz_to_total > 0.9999: + lorentz_weight = 1 + sigma = 0 # force add_gaussian_line() to immediately return + else: + lorentz_weight = self._weight_poly_coeff[i] + for i in range(1, 6): + lorentz_weight += self._weight_poly_coeff[i] * log(fwhm_lorentz_to_total)**i + lorentz_weight = exp(lorentz_weight) + + gauss_weight = 1 - lorentz_weight + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate emission line central wavelength, doppler shifted along observation direction + shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) + + # obtain magnetic field + b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) + b_magn = b_field.get_length() + + if b_magn == 0: + # no splitting if magnetic field strength is zero + if self._polarisation != NO_POLARISATION: + radiance *= 0.5 # pi or sigma polarisation, collecting only half of intensity + + spectrum = add_gaussian_line(gauss_weight * radiance, shifted_wavelength, sigma, spectrum) + spectrum = add_lorentzian_line(lorentz_weight * radiance, shifted_wavelength, fwhm_full, spectrum, self.integrator) + + return spectrum + + # coefficients for intensities parallel and perpendicular to magnetic field + cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 + sin_sqr = 1. - cos_sqr + + # adding pi component of the Zeeman triplet in case of NO_POLARISATION or PI_POLARISATION + if self._polarisation != SIGMA_POLARISATION: + comp_radiance = 0.5 * sin_sqr * radiance + spectrum = add_gaussian_line(gauss_weight * comp_radiance, shifted_wavelength, sigma, spectrum) + spectrum = add_lorentzian_line(lorentz_weight * comp_radiance, shifted_wavelength, fwhm_full, spectrum, self.integrator) + + # adding sigma +/- components of the Zeeman triplet in case of NO_POLARISATION or SIGMA_POLARISATION + if self._polarisation != PI_POLARISATION: + comp_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance + + photon_energy = HC_EV_NM / self.wavelength + + shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy - BOHR_MAGNETON * b_magn), direction, ion_velocity) + spectrum = add_gaussian_line(gauss_weight * comp_radiance, shifted_wavelength, sigma, spectrum) + spectrum = add_lorentzian_line(lorentz_weight * comp_radiance, shifted_wavelength, fwhm_full, spectrum, self.integrator) + + shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy + BOHR_MAGNETON * b_magn), direction, ion_velocity) + spectrum = add_gaussian_line(gauss_weight * comp_radiance, shifted_wavelength, sigma, spectrum) + spectrum = add_lorentzian_line(lorentz_weight * comp_radiance, shifted_wavelength, fwhm_full, spectrum, self.integrator) + + return spectrum diff --git a/cherab/core/model/lineshape/zeeman.pxd b/cherab/core/model/lineshape/zeeman.pxd new file mode 100644 index 00000000..b67d2042 --- /dev/null +++ b/cherab/core/model/lineshape/zeeman.pxd @@ -0,0 +1,48 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.atomic.zeeman cimport ZeemanStructure +from cherab.core.model.lineshape.base cimport LineShapeModel + + +cdef class ZeemanLineShapeModel(LineShapeModel): + + cdef double _polarisation + + pass + + +cdef class ZeemanTriplet(ZeemanLineShapeModel): + + pass + + +cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): + + cdef double _alpha, _beta, _gamma + + pass + + +cdef class ZeemanMultiplet(ZeemanLineShapeModel): + + cdef ZeemanStructure _zeeman_structure + + pass diff --git a/cherab/core/model/lineshape/zeeman.pyx b/cherab/core/model/lineshape/zeeman.pyx new file mode 100644 index 00000000..42c1e71f --- /dev/null +++ b/cherab/core/model/lineshape/zeeman.pyx @@ -0,0 +1,417 @@ +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 sqrt +from raysect.optical cimport Spectrum, Point3D, Vector3D + +from cherab.core.atomic cimport Line +from cherab.core.species cimport Species +from cherab.core.plasma cimport Plasma +from cherab.core.atomic.elements import hydrogen, deuterium, tritium, helium, helium3, beryllium, boron, carbon, nitrogen, oxygen, neon +from cherab.core.math.integrators cimport Integrator1D +from cherab.core.utility.constants cimport BOHR_MAGNETON, HC_EV_NM +from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening +from cherab.core.model.lineshape.gaussian cimport add_gaussian_line + +cimport cython + + +DEF MULTIPLET_WAVELENGTH = 0 +DEF MULTIPLET_RATIO = 1 + +DEF PI_POLARISATION = 0 +DEF SIGMA_POLARISATION = 1 +DEF SIGMA_PLUS_POLARISATION = 1 +DEF SIGMA_MINUS_POLARISATION = -1 +DEF NO_POLARISATION = 2 + + +cdef class ZeemanLineShapeModel(LineShapeModel): + r""" + A base class for building Zeeman line shapes. + + :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 str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: + "pi" - leave only :math:`\pi`-polarised components, + "sigma" - leave only :math:`\sigma`-polarised components, + "no" - leave all components (default). + :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, polarisation, + Integrator1D integrator=None): + super().__init__(line, wavelength, target_species, plasma, integrator) + + self.polarisation = polarisation + + @property + def polarisation(self): + if self._polarisation == PI_POLARISATION: + return 'pi' + if self._polarisation == SIGMA_POLARISATION: + return 'sigma' + if self._polarisation == NO_POLARISATION: + return 'no' + + @polarisation.setter + def polarisation(self, value): + if value.lower() == 'pi': + self._polarisation = PI_POLARISATION + elif value.lower() == 'sigma': + self._polarisation = SIGMA_POLARISATION + elif value.lower() == 'no': + self._polarisation = NO_POLARISATION + else: + raise ValueError('Select between "pi", "sigma" or "no", {} is unsupported.'.format(value)) + + +cdef class ZeemanTriplet(ZeemanLineShapeModel): + r""" + Simple Doppler-Zeeman triplet (Paschen-Back effect). + + :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 str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: + "pi" - leave central component, + "sigma" - leave side components, + "no" - all components (default). + """ + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, polarisation='no'): + + super().__init__(line, wavelength, target_species, plasma, polarisation) + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef double ts, sigma, shifted_wavelength, photon_energy, b_magn, component_radiance, cos_sqr, sin_sqr + cdef Vector3D ion_velocity, b_field + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + if ts <= 0.0: + return spectrum + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate emission line central wavelength, doppler shifted along observation direction + shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) + + # calculate the line width + sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) + + # obtain magnetic field + b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) + b_magn = b_field.get_length() + + if b_magn == 0: + # no splitting if magnetic field strength is zero + if self._polarisation == NO_POLARISATION: + return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) + + return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) + + # coefficients for intensities parallel and perpendicular to magnetic field + cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 + sin_sqr = 1. - cos_sqr + + # adding pi component of the Zeeman triplet in case of NO_POLARISATION or PI_POLARISATION + if self._polarisation != SIGMA_POLARISATION: + component_radiance = 0.5 * sin_sqr * radiance + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + # adding sigma +/- components of the Zeeman triplet in case of NO_POLARISATION or SIGMA_POLARISATION + if self._polarisation != PI_POLARISATION: + component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance + + photon_energy = HC_EV_NM / self.wavelength + + shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy - BOHR_MAGNETON * b_magn), direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + shifted_wavelength = doppler_shift(HC_EV_NM / (photon_energy + BOHR_MAGNETON * b_magn), direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + return spectrum + + +cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): + r""" + Parametrised Doppler-Zeeman triplet. It takes into account additional broadening due to + the line's fine structure without resolving the individual components of the fine + structure. The model is described with three parameters: :math:`\alpha`, + :math:`\beta` and :math:`\gamma`. + + The distance between :math:`\sigma^+` and :math:`\sigma^-` peaks: + :math:`\Delta \lambda_{\sigma} = \alpha B`, + where `B` is the magnetic field strength. + The ratio between Zeeman and thermal broadening line widths: + :math:`\frac{W_{Zeeman}}{W_{Doppler}} = \beta T^{\gamma}`, + where `T` is the species temperature in eV. + + Call `show_supported_transitions()` to see the list of supported transitions and + default parameters of the model. + + For details see A. Blom and C. Jupén, Parametrisation of the Zeeman effect + for hydrogen-like spectra in high-temperature plasmas, + Plasma Phys. Control. Fusion 44 (2002) `1229-1241 + `_. + + :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 dict line_parameters: Alternative parameters of the model in the form + {line_i: (alpha_i, beta_i, gamma_i), ...}. + If None, the default model parameters will be used. + :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: + "pi" - leave central component, + "sigma" - leave side components, + "no" - all components (default). + """ + + LINE_PARAMETERS_DEFAULT = { # alpha, beta, gamma parameters for selected lines + Line(hydrogen, 0, (3, 2)): (0.0402267, 0.3415, -0.5247), + Line(hydrogen, 0, (4, 2)): (0.0220724, 0.2837, -0.5346), + Line(deuterium, 0, (3, 2)): (0.0402068, 0.4384, -0.5015), + Line(deuterium, 0, (4, 2)): (0.0220610, 0.3702, -0.5132), + Line(helium3, 1, (4, 3)): (0.0205200, 1.4418, -0.4892), + Line(helium3, 1, (5, 3)): (0.0095879, 1.2576, -0.5001), + Line(helium3, 1, (6, 4)): (0.0401980, 0.8976, -0.4971), + Line(helium3, 1, (7, 4)): (0.0273538, 0.8529, -0.5039), + Line(helium, 1, (4, 3)): (0.0205206, 1.6118, -0.4838), + Line(helium, 1, (5, 3)): (0.0095879, 1.4294, -0.4975), + Line(helium, 1, (6, 4)): (0.0401955, 1.0058, -0.4918), + Line(helium, 1, (7, 4)): (0.0273521, 0.9563, -0.4981), + Line(beryllium, 3, (5, 4)): (0.0060354, 2.1245, -0.3190), + Line(beryllium, 3, (6, 5)): (0.0202754, 1.6538, -0.3192), + Line(beryllium, 3, (7, 5)): (0.0078966, 1.7017, -0.3348), + Line(beryllium, 3, (8, 6)): (0.0205025, 1.4581, -0.3450), + Line(boron, 4, (6, 5)): (0.0083423, 2.0519, -0.2960), + Line(boron, 4, (7, 6)): (0.0228379, 1.6546, -0.2941), + Line(boron, 4, (8, 6)): (0.0084065, 1.8041, -0.3177), + Line(boron, 4, (8, 7)): (0.0541883, 1.4128, -0.2966), + Line(boron, 4, (9, 7)): (0.0190781, 1.5440, -0.3211), + Line(boron, 4, (10, 8)): (0.0391914, 1.3569, -0.3252), + Line(carbon, 5, (6, 5)): (0.0040900, 2.4271, -0.2818), + Line(carbon, 5, (7, 6)): (0.0110398, 1.9785, -0.2816), + Line(carbon, 5, (8, 6)): (0.0040747, 2.1776, -0.3035), + Line(carbon, 5, (8, 7)): (0.0261405, 1.6689, -0.2815), + Line(carbon, 5, (9, 7)): (0.0092096, 1.8495, -0.3049), + Line(carbon, 5, (10, 8)): (0.0189020, 1.6191, -0.3078), + Line(carbon, 5, (11, 8)): (0.0110428, 1.6600, -0.3162), + Line(carbon, 5, (10, 9)): (0.0359009, 1.4464, -0.3104), + Line(nitrogen, 6, (7, 6)): (0.0060010, 2.4789, -0.2817), + Line(nitrogen, 6, (8, 7)): (0.0141271, 2.0249, -0.2762), + Line(nitrogen, 6, (9, 8)): (0.0300127, 1.7415, -0.2753), + Line(nitrogen, 6, (10, 8)): (0.0102089, 1.9464, -0.2975), + Line(nitrogen, 6, (11, 9)): (0.0193799, 1.7133, -0.2973), + Line(oxygen, 7, (8, 7)): (0.0083081, 2.4263, -0.2747), + Line(oxygen, 7, (9, 8)): (0.0176049, 2.0652, -0.2721), + Line(oxygen, 7, (10, 8)): (0.0059933, 2.3445, -0.2944), + Line(oxygen, 7, (10, 9)): (0.0343805, 1.8122, -0.2718), + Line(oxygen, 7, (11, 9)): (0.0113640, 2.0268, -0.2911), + Line(neon, 9, (9, 8)): (0.0072488, 2.8838, -0.2758), + Line(neon, 9, (10, 9)): (0.0141002, 2.4755, -0.2718), + Line(neon, 9, (11, 9)): (0.0046673, 2.8410, -0.2917), + Line(neon, 9, (11, 10)): (0.0257292, 2.1890, -0.2715) + } + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, dict line_parameters=None, polarisation='no'): + + super().__init__(line, wavelength, target_species, plasma, polarisation) + + line_parameters = line_parameters or self.LINE_PARAMETERS_DEFAULT + + try: + alpha, beta, gamma = line_parameters[self.line] + if alpha <= 0: + raise ValueError('Parameter alpha must be positive.') + if beta < 0: + raise ValueError('Parameter beta must be non-negative.') + self._alpha = alpha + self._beta = beta + self._gamma = gamma + + except KeyError: + raise ValueError('Data for {} is not available.'.format(self.line)) + + def show_supported_transitions(self): + """ Prints all supported transitions.""" + for line, param in self.LINE_PARAMETERS_DEFAULT.items(): + print('{}: alpha={}, beta={}, gamma={}'.format(line, param[0], param[1], param[2])) + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef double ts, sigma, shifted_wavelength, b_magn, component_radiance, cos_sqr, sin_sqr + cdef Vector3D ion_velocity, b_field + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + if ts <= 0.0: + return spectrum + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate emission line central wavelength, doppler shifted along observation direction + shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) + + # calculate the line width + sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) + + # fine structure broadening correction + sigma *= sqrt(1. + self._beta * self._beta * ts**(2. * self._gamma)) + + # obtain magnetic field + b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) + b_magn = b_field.get_length() + + if b_magn == 0: + # no splitting if magnetic filed strength is zero + if self._polarisation == NO_POLARISATION: + return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) + + return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) + + # coefficients for intensities parallel and perpendicular to magnetic field + cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 + sin_sqr = 1. - cos_sqr + + # adding pi component of the Zeeman triplet in case of NO_POLARISATION or PI_POLARISATION + if self._polarisation != SIGMA_POLARISATION: + component_radiance = 0.5 * sin_sqr * radiance + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + # adding sigma +/- components of the Zeeman triplet in case of NO_POLARISATION or SIGMA_POLARISATION + if self._polarisation != PI_POLARISATION: + component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance + + shifted_wavelength = doppler_shift(self.wavelength + 0.5 * self._alpha * b_magn, direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + shifted_wavelength = doppler_shift(self.wavelength - 0.5 * self._alpha * b_magn, direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance, shifted_wavelength, sigma, spectrum) + + return spectrum + + +cdef class ZeemanMultiplet(ZeemanLineShapeModel): + r""" + Doppler-Zeeman Multiplet. + + The lineshape radiance is calculated from a base PEC rate that is unresolved. This + radiance is then divided over a number of components as specified in the ``zeeman_structure`` + argument. The ``zeeman_structure`` specifies wavelengths and ratios of + :math:`\pi`-/:math:`\sigma`-polarised components as functions of the magnetic field strength. + These functions can be obtained using the output of the ADAS603 routines. + + :param Line line: The emission line object for the base rate radiance calculation. + :param float wavelength: The rest wavelength of the base emission line. + :param Species target_species: The target plasma species that is emitting. + :param Plasma plasma: The emitting plasma object. + :param zeeman_structure: A ``ZeemanStructure`` object that provides wavelengths and ratios + of :math:`\pi`-/:math:`\sigma^{+}`-/:math:`\sigma^{-}`-polarised + components for any given magnetic field strength. + :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: + "pi" - leave only :math:`\pi`-polarised components, + "sigma" - leave only :math:`\sigma`-polarised components, + "no" - leave all components (default). + + """ + + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, + ZeemanStructure zeeman_structure, polarisation='no'): + + super().__init__(line, wavelength, target_species, plasma, polarisation) + + self._zeeman_structure = zeeman_structure + + @cython.boundscheck(False) + @cython.wraparound(False) + @cython.initializedcheck(False) + @cython.cdivision(True) + cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): + + cdef int i + cdef double ts, sigma, shifted_wavelength, component_radiance + cdef Vector3D ion_velocity + cdef double[:, :] multiplet_pi_mv, multiplet_sigma_mv + + ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) + if ts <= 0.0: + return spectrum + + ion_velocity = self.target_species.distribution.bulk_velocity(point.x, point.y, point.z) + + # calculate the line width + sigma = thermal_broadening(self.wavelength, ts, self.line.element.atomic_weight) + + # obtain magnetic field + b_field = self.plasma.get_b_field().evaluate(point.x, point.y, point.z) + b_magn = b_field.get_length() + + if b_magn == 0: + # no splitting if magnetic filed strength is zero + shifted_wavelength = doppler_shift(self.wavelength, direction, ion_velocity) + if self._polarisation == NO_POLARISATION: + return add_gaussian_line(radiance, shifted_wavelength, sigma, spectrum) + + return add_gaussian_line(0.5 * radiance, shifted_wavelength, sigma, spectrum) + + # coefficients for intensities parallel and perpendicular to magnetic field + cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 + sin_sqr = 1. - cos_sqr + + # adding pi components of the Zeeman multiplet in case of NO_POLARISATION or PI_POLARISATION + if self._polarisation != SIGMA_POLARISATION: + component_radiance = 0.5 * sin_sqr * radiance + multiplet_mv = self._zeeman_structure.evaluate(b_magn, PI_POLARISATION) + + for i in range(multiplet_mv.shape[1]): + shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) + + # adding sigma components of the Zeeman multiplet in case of NO_POLARISATION or SIGMA_POLARISATION + if self._polarisation != PI_POLARISATION: + component_radiance = (0.25 * sin_sqr + 0.5 * cos_sqr) * radiance + + multiplet_mv = self._zeeman_structure.evaluate(b_magn, SIGMA_PLUS_POLARISATION) + + for i in range(multiplet_mv.shape[1]): + shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) + + multiplet_mv = self._zeeman_structure.evaluate(b_magn, SIGMA_MINUS_POLARISATION) + + for i in range(multiplet_mv.shape[1]): + shifted_wavelength = doppler_shift(multiplet_mv[MULTIPLET_WAVELENGTH, i], direction, ion_velocity) + spectrum = add_gaussian_line(component_radiance * multiplet_mv[MULTIPLET_RATIO, i], shifted_wavelength, sigma, spectrum) + + return spectrum diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 343059ad..2aa9691c 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -18,7 +18,7 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core cimport Plasma, AtomicData -from cherab.core.model.lineshape cimport GaussianLine, LineShapeModel +from cherab.core.model.lineshape cimport GaussianLine from cherab.core.utility.constants cimport RECIP_4_PI diff --git a/cherab/core/utility/constants.pxd b/cherab/core/utility/constants.pxd index 9cce304d..aa7db394 100644 --- a/cherab/core/utility/constants.pxd +++ b/cherab/core/utility/constants.pxd @@ -27,6 +27,8 @@ cdef: double ELEMENTARY_CHARGE double SPEED_OF_LIGHT double PLANCK_CONSTANT + double HC_EV_NM double ELECTRON_CLASSICAL_RADIUS double ELECTRON_REST_MASS double RYDBERG_CONSTANT_EV + double BOHR_MAGNETON diff --git a/cherab/core/utility/constants.pyx b/cherab/core/utility/constants.pyx index 03e70617..e881f910 100644 --- a/cherab/core/utility/constants.pyx +++ b/cherab/core/utility/constants.pyx @@ -29,6 +29,8 @@ cdef: double ELEMENTARY_CHARGE = 1.602176634e-19 double SPEED_OF_LIGHT = 299792458.0 double PLANCK_CONSTANT = 6.62607015e-34 + double HC_EV_NM = 1239.8419738620933 # (Planck constant in eV s) x (speed of light in nm/s) double ELECTRON_CLASSICAL_RADIUS = 2.8179403262e-15 double ELECTRON_REST_MASS = 9.1093837015e-31 double RYDBERG_CONSTANT_EV = 13.605693122994 + double BOHR_MAGNETON = 5.78838180123e-5 # in eV/T From 85cece3b6483ad82e6ea1da381c978f575cfc612 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 9 Jan 2023 17:12:33 +0300 Subject: [PATCH 010/134] Fixed errors in StarkBroadenedLine. --- cherab/core/model/lineshape/stark.pyx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx index ea26fd27..6c4a3b8a 100644 --- a/cherab/core/model/lineshape/stark.pyx +++ b/cherab/core/model/lineshape/stark.pyx @@ -254,17 +254,19 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): if fwhm_lorentz == 0 and fwhm_gauss == 0: return spectrum - # calculating total FWHM + # calculating full FWHM if fwhm_gauss <= fwhm_lorentz: fwhm_ratio = fwhm_gauss / fwhm_lorentz fwhm_full = self._fwhm_poly_coeff_gauss[0] for i in range(1, 7): fwhm_full += self._fwhm_poly_coeff_gauss[i] * fwhm_ratio**i + fwhm_full *= fwhm_lorentz else: fwhm_ratio = fwhm_lorentz / fwhm_gauss fwhm_full = self._fwhm_poly_coeff_lorentz[0] for i in range(1, 7): fwhm_full += self._fwhm_poly_coeff_lorentz[i] * fwhm_ratio**i + fwhm_full *= fwhm_gauss sigma = fwhm_full / _SIGMA2FWHM @@ -274,11 +276,11 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): if fwhm_lorentz_to_total < 0.01: lorentz_weight = 0 fwhm_full = 0 # force add_lorentzian_line() to immediately return - elif fwhm_lorentz_to_total > 0.9999: + elif fwhm_lorentz_to_total > 0.999: lorentz_weight = 1 sigma = 0 # force add_gaussian_line() to immediately return else: - lorentz_weight = self._weight_poly_coeff[i] + lorentz_weight = self._weight_poly_coeff[0] for i in range(1, 6): lorentz_weight += self._weight_poly_coeff[i] * log(fwhm_lorentz_to_total)**i lorentz_weight = exp(lorentz_weight) From 7a5041622817fac4b2118b59ff596ab70264ae41 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 10 Jan 2023 14:57:40 +0300 Subject: [PATCH 011/134] Plot Stark-broadened lines in logscale to show the power-law decay of the line wings. --- demos/emission_models/stark_broadening.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demos/emission_models/stark_broadening.py b/demos/emission_models/stark_broadening.py index 090e929a..9a3f747e 100755 --- a/demos/emission_models/stark_broadening.py +++ b/demos/emission_models/stark_broadening.py @@ -34,7 +34,7 @@ # tunables -ion_density = 1e20 +ion_density = 2e20 sigma = 0.25 # setup scenegraph @@ -99,4 +99,5 @@ plt.xlabel('Wavelength (nm)') plt.ylabel('Radiance (W/m^2/str/nm)') plt.title('Observed Spectrum') +plt.yscale('log') plt.show() From 86b4cd71b9ee957dc60badca4d2e7f9efa3714e9 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 12 Jan 2023 19:43:34 +0300 Subject: [PATCH 012/134] Fixed the unit test for the Stark-broadened line after changing the line shape model. --- cherab/core/tests/test_lineshapes.py | 66 +++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index da4dc41d..72b02973 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -300,23 +300,75 @@ def test_stark_broadened_line(self): spectrum = stark_line.add_line(radiance, point, direction, spectrum) # validating + velocity = target_species.distribution.bulk_velocity(point.x, point.y, point.z) + doppler_factor = (1 + velocity.dot(direction.normalise()) / SPEED_OF_LIGHT) + + b_field = self.plasma.b_field(point.x, point.y, point.z) + b_magn = b_field.length + photon_energy = HC_EV_NM / wavelength + wl_sigma_plus = HC_EV_NM / (photon_energy - BOHR_MAGNETON * b_magn) + wl_sigma_minus = HC_EV_NM / (photon_energy + BOHR_MAGNETON * b_magn) + cos_sqr = (b_field.dot(direction.normalise()) / b_magn)**2 + sin_sqr = 1. - cos_sqr + + # Gaussian parameters + temperature = target_species.distribution.effective_temperature(point.x, point.y, point.z) + sigma = np.sqrt(temperature * ELEMENTARY_CHARGE / (line.element.atomic_weight * ATOMIC_MASS)) * wavelength / SPEED_OF_LIGHT + fwhm_gauss = 2 * np.sqrt(2 * np.log(2)) * sigma + + # Lorentzian parameters cij, aij, bij = stark_line.STARK_MODEL_COEFFICIENTS_DEFAULT[line] ne = self.plasma.electron_distribution.density(point.x, point.y, point.z) te = self.plasma.electron_distribution.effective_temperature(point.x, point.y, point.z) - lambda_1_2 = cij * ne**aij / (te**bij) + fwhm_lorentz = cij * ne**aij / (te**bij) + + # Total FWHM + if fwhm_gauss <= fwhm_lorentz: + fwhm_poly_coeff = [1., 0, 0.57575, 0.37902, -0.42519, -0.31525, 0.31718] + fwhm_ratio = fwhm_gauss / fwhm_lorentz + fwhm_full = fwhm_lorentz * np.poly1d(fwhm_poly_coeff[::-1])(fwhm_ratio) + else: + fwhm_poly_coeff = [1., 0.15882, 1.04388, -1.38281, 0.46251, 0.82325, -0.58026] + fwhm_ratio = fwhm_lorentz / fwhm_gauss + fwhm_full = fwhm_gauss * np.poly1d(fwhm_poly_coeff[::-1])(fwhm_ratio) + + wavelengths, delta = np.linspace(min_wavelength, max_wavelength, bins + 1, retstep=True) + # Gaussian part + temp = 2 * np.sqrt(np.log(2)) / fwhm_full + erfs = erf((wavelengths - wavelength * doppler_factor) * temp) + gaussian = 0.25 * sin_sqr * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - wl_sigma_plus * doppler_factor) * temp) + gaussian += 0.5 * (0.25 * sin_sqr + 0.5 * cos_sqr) * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - wl_sigma_minus * doppler_factor) * temp) + gaussian += 0.5 * (0.25 * sin_sqr + 0.5 * cos_sqr) * (erfs[1:] - erfs[:-1]) / delta + + # Lorentzian part 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 + norm = (0.5 * fwhm_full)**1.5 / stark_norm_coeff - wavelengths, delta = np.linspace(min_wavelength, max_wavelength, bins + 1, retstep=True) + def stark_lineshape_pi(x): + return norm / ((np.abs(x - wavelength * doppler_factor))**2.5 + (0.5 * fwhm_full)**2.5) + + def stark_lineshape_sigma_plus(x): + return norm / ((np.abs(x - wl_sigma_plus * doppler_factor))**2.5 + (0.5 * fwhm_full)**2.5) + + def stark_lineshape_sigma_minus(x): + return norm / ((np.abs(x - wl_sigma_minus * doppler_factor))**2.5 + (0.5 * fwhm_full)**2.5) - def stark_lineshape(x): - return norm / ((np.abs(x - wavelength))**2.5 + (0.5 * lambda_1_2)**2.5) + weight_poly_coeff = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04] + lorentz_weight = np.exp(np.poly1d(weight_poly_coeff[::-1])(np.log(fwhm_lorentz / fwhm_full))) for i in range(bins): - 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, + lorentz_bin = 0.5 * sin_sqr * quadrature(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], + rtol=integrator.relative_tolerance)[0] / delta + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quadrature(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], + rtol=integrator.relative_tolerance)[0] / delta + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quadrature(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], + rtol=integrator.relative_tolerance)[0] / delta + ref_value = lorentz_bin * lorentz_weight + gaussian[i] * (1. - lorentz_weight) + self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-9, msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) From b252beee51ab5c17db10ee9b8e18329feea3567f Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 12 Jan 2023 23:21:22 +0300 Subject: [PATCH 013/134] Added demo that plots Balmer-alpha and Paschen-beta Stark-Zeeman lines as in B.A. Lomanowski 2015 NF paper. --- demos/emission_models/stark_broadening.py | 6 +- demos/emission_models/stark_zeeman.py | 94 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) create mode 100755 demos/emission_models/stark_zeeman.py diff --git a/demos/emission_models/stark_broadening.py b/demos/emission_models/stark_broadening.py index 9a3f747e..b3f2cf95 100755 --- a/demos/emission_models/stark_broadening.py +++ b/demos/emission_models/stark_broadening.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"); diff --git a/demos/emission_models/stark_zeeman.py b/demos/emission_models/stark_zeeman.py new file mode 100755 index 00000000..e212f27b --- /dev/null +++ b/demos/emission_models/stark_zeeman.py @@ -0,0 +1,94 @@ +# 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. + +""" +Plots Balmer-alpha and Paschen-beta Stark-Zeeman spectral lines +as in Figure 2 in B.A. Lomanowski at al, Nucl. Fusion 55 (2015) 123028, +https://iopscience.iop.org/article/10.1088/0029-5515/55/12/123028 +""" + + +# External imports +import matplotlib.pyplot as plt +from raysect.optical import World, translate, rotate, Vector3D, Point3D, Ray + +# Cherab imports +from cherab.core import Line +from cherab.core.atomic.elements import deuterium +from cherab.core.model import ExcitationLine, StarkBroadenedLine +from cherab.openadas import OpenADAS +from cherab.tools.plasmas.slab import build_constant_slab_plasma + + +# tunables +ion_density = 2e20 +sigma = 0.25 + +# setup scenegraph +world = World() + +# create atomic data source +adas = OpenADAS(permit_extrapolation=True) + +# setup the Balmer and Paschen lines +balmer_alpha = Line(deuterium, 0, (3, 2)) +paschen_beta = Line(deuterium, 0, (5, 3)) + +# setup plasma for Balmer-alpha line +plasma_species = [(deuterium, 0, 1.e20, 0.3, Vector3D(0, 0, 0))] +plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=5e20, electron_temperature=1., + plasma_species=plasma_species, b_field=Vector3D(0, 3., 0), parent=world) +plasma.atomic_data = adas + +# add Balmer-alpha line to the plasma +plasma.models = [ExcitationLine(balmer_alpha, lineshape=StarkBroadenedLine)] + +# Ray-trace and plot the results +r = Ray(origin=Point3D(-5, 0, 0), direction=Vector3D(1, 0, 0), + min_wavelength=655.9, max_wavelength=656.3, bins=200) +s = r.trace(world) + +plt.figure() +plt.plot(s.wavelengths, s.samples, ls='--', color='C3') +plt.xlabel('Wavelength (nm)') +plt.ylabel('Radiance (W/m^2/str/nm)') +plt.title('Balmer-alpha spectrum, Ne = 5e20 m-3, Te = 1 eV, B = 3 T') + +plasma.parent = None + +# setup plasma for Paschen-beta line +plasma_species = [(deuterium, 0, 1.e20, 0.3, Vector3D(0, 0, 0))] +plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e20, electron_temperature=1., + plasma_species=plasma_species, b_field=Vector3D(0, 3., 0), parent=world) +plasma.atomic_data = adas + +# add Paschen-beta line to the plasma +plasma.models = [ExcitationLine(paschen_beta, lineshape=StarkBroadenedLine)] + +# Ray-trace and plot the results +r = Ray(origin=Point3D(-5, 0, 0), direction=Vector3D(1, 0, 0), + min_wavelength=1280.3, max_wavelength=1282.6, bins=200) +s = r.trace(world) + +plt.figure() +plt.plot(s.wavelengths, s.samples, ls='--', color='C3') +plt.xlabel('Wavelength (nm)') +plt.ylabel('Radiance (W/m^2/str/nm)') +plt.title('Paschen-beta spectrum, Ne = 1e20 m-3, Te = 1 eV, B = 3 T') + +plt.show() From 55336c3fb8a322a1a6454ca00922f98e87b4a328 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 16 Jan 2023 15:56:18 +0300 Subject: [PATCH 014/134] Converted show_supported_transitions() to a class method. --- cherab/core/model/lineshape/stark.pyx | 5 +++-- cherab/core/model/lineshape/zeeman.pyx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx index 6c4a3b8a..8adc71af 100644 --- a/cherab/core/model/lineshape/stark.pyx +++ b/cherab/core/model/lineshape/stark.pyx @@ -224,9 +224,10 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): super().__init__(line, wavelength, target_species, plasma, polarisation, integrator) - def show_supported_transitions(self): + @classmethod + def show_supported_transitions(cls): """ Prints all supported transitions.""" - for line, coeff in self.STARK_MODEL_COEFFICIENTS_DEFAULT.items(): + for line, coeff in cls.STARK_MODEL_COEFFICIENTS_DEFAULT.items(): print('{}: c_ij={}, a_ij={}, b_ij={}'.format(line, coeff[0], coeff[1], coeff[2])) @cython.boundscheck(False) diff --git a/cherab/core/model/lineshape/zeeman.pyx b/cherab/core/model/lineshape/zeeman.pyx index 42c1e71f..81bbb991 100644 --- a/cherab/core/model/lineshape/zeeman.pyx +++ b/cherab/core/model/lineshape/zeeman.pyx @@ -261,9 +261,10 @@ cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): except KeyError: raise ValueError('Data for {} is not available.'.format(self.line)) - def show_supported_transitions(self): + @classmethod + def show_supported_transitions(cls): """ Prints all supported transitions.""" - for line, param in self.LINE_PARAMETERS_DEFAULT.items(): + for line, param in cls.LINE_PARAMETERS_DEFAULT.items(): print('{}: alpha={}, beta={}, gamma={}'.format(line, param[0], param[1], param[2])) @cython.boundscheck(False) From 035c95f9d01ed8a272742b4a959d8406ec5fd81d Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 17 Jan 2023 17:50:00 +0300 Subject: [PATCH 015/134] Updated documentation for the line shape models. --- cherab/core/model/lineshape/__init__.py | 2 +- cherab/core/model/lineshape/stark.pyx | 82 +++++++++++++++++-- demos/emission_models/stark_zeeman.py | 11 +-- .../line_shapes/spectral_line_shapes.rst | 34 +++++--- 4 files changed, 103 insertions(+), 26 deletions(-) diff --git a/cherab/core/model/lineshape/__init__.py b/cherab/core/model/lineshape/__init__.py index e0ad6a24..30100403 100644 --- a/cherab/core/model/lineshape/__init__.py +++ b/cherab/core/model/lineshape/__init__.py @@ -21,5 +21,5 @@ from .doppler import doppler_shift, thermal_broadening from .gaussian import add_gaussian_line, GaussianLine from .multiplet import MultipletLineShape -from .stark import StarkBroadenedLine +from .stark import add_lorentzian_line, StarkBroadenedLine from .zeeman import ZeemanLineShapeModel, ZeemanTriplet, ParametrisedZeemanTriplet, ZeemanMultiplet diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx index 8adc71af..93b165f7 100644 --- a/cherab/core/model/lineshape/stark.pyx +++ b/cherab/core/model/lineshape/stark.pyx @@ -52,6 +52,9 @@ cdef double _SIGMA2FWHM = 2 * sqrt(2 * log(2)) cdef class StarkFunction(Function1D): """ Normalised Stark function for the StarkBroadenedLine line shape. + + :param float wavelength: central wavelength of the line in nm. + :param float lambda_1_2: FWHM of the function in nm. """ cdef double _a, _x0, _norm @@ -86,10 +89,24 @@ cpdef Spectrum add_lorentzian_line(double radiance, double wavelength, double la r""" Adds a modified Lorentzian line to the given spectrum and returns the new spectrum. + The modified Lorentzian: + :math:`L(\lambda-\lambda_0, \Delta\lambda_{1/2}^{(L)})=\frac{C_0(\Delta\lambda_{1/2}^{(L)})^{3/2}}{(\lambda-\lambda_0)^{5/2}+(\frac{\Delta\lambda_{1/2}^{(L)}}{2})^{5/2}},` + + :math:`C_0=\frac{(1/2)^{3/2}}{4R{_2}F_1(\frac{2}{5},1,\frac{7}{5},-R^{5/2})},` + + where :math:`\Delta\lambda_{1/2}^{(L)}` is the line FWHM, :math:`{_2}F_1` is the hypergeometric function and :math:`R=50`. + The line shape is truncated at :math:`\lambdaR\Delta\lambda_{1/2}^{(L)}`. + + See B. Lomanowski, et al. "Inferring divertor plasma properties from hydrogen Balmer + and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) + `123028 `_ for details. + :param float radiance: Intensity of the line in radiance. :param float wavelength: central wavelength of the line in nm. - :param float sigma: width of the line in nm. - :param Spectrum spectrum: the current spectrum to which the gaussian line is added. + :param float lambda_1_2: FWHM of the line shape in nm. + :param Spectrum spectrum: the current spectrum to which the Lorentzian line is added. + :param Integrator1D integrator: Integrator1D instance to integrate the line shape + over the spectral bin. :return: """ @@ -131,14 +148,60 @@ cpdef Spectrum add_lorentzian_line(double radiance, double wavelength, double la cdef class StarkBroadenedLine(ZeemanLineShapeModel): - """ - Parametrised Stark broadened line shape based on the Model Microfield Method (MMM). - Contains embedded atomic data in the form of fits to MMM. - Only Balmer and Paschen series are supported by default. - See B. Lomanowski, et al. "Inferring divertor plasma properties from hydrogen Balmer + r""" + Parametrised Stark-Doppler-Zeeman line shape for Balmer and Paschen series based on + B. Lomanowski, et al. "Inferring divertor plasma properties from hydrogen Balmer and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) `123028 `_. + The following approximations are used: + + - The Zeeman and Stark effects are considered independently. + - Zeeman splitting is taken in the form of a simple triplet with a :math:`\pi`-component + centred at :math:`h\nu`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{h\nu-\mu B}` + and :math:`\sigma^{-}`-component at :math:`\frac{hc}{h\nu+\mu B}`. + - The model of Stark broadening is obtained by fitting the Model Microfield Method (MMM). + - The convolution of Stark-Zeeman and Doppler profiles is replaced with the weighted sum + to speed-up calculations (so-called pseudo-Voigt profile). + + The Stark-broadened line shape is modelled as modified Lorentzian: + :math:`L(\lambda-\lambda_0, \Delta\lambda_{1/2}^{(L)})=\frac{C_0(\Delta\lambda_{1/2}^{(L)})^{3/2}}{(\lambda-\lambda_0)^{5/2}+(\frac{\Delta\lambda_{1/2}^{(L)}}{2})^{5/2}},` + + :math:`C_0=\frac{(1/2)^{3/2}}{4R{_2}F_1(\frac{2}{5},1,\frac{7}{5},-R^{5/2})},` + + where :math:`\Delta\lambda_{1/2}^{(L)}=c_{ij}\frac{n_e^{a_{ij}}}{T_e^{b_{ij}}}` is the line FWHM, + :math:`{_2}F_1` is the hypergeometric function and :math:`R=50`. + The line shape is truncated at :math:`\lambdaR\Delta\lambda_{1/2}^{(L)}`. + The :math:`a_{ij}`, :math:`b_{ij}` and :math:`c_{ij}` are the fitting coefficients. + + Each Zeeman component is modelled as a weighted sum of Stark (Lorentzian), :math:`L(\lambda-\lambda_0, \Delta\lambda_{1/2}^{(L)})`, + and Doppler (Gauss), :math:`G(\lambda'-\lambda_0, \Delta\lambda_{1/2}^{(G)})` profiles: + + :math:`\eta L(\lambda-\lambda_0, \Delta\lambda_{1/2}^{(V)}) + (1-\eta)G(\lambda-\lambda_0, \Delta\lambda_{1/2}^{(V)})`, + + with :math:`\Delta\lambda_{1/2}^{(V)}\equiv \Delta\lambda_{1/2}^{(V)}(\Delta\lambda_{1/2}^{(G)}, \Delta\lambda_{1/2}^{(L)})` + and :math:`\eta\equiv \eta(\Delta\lambda_{1/2}^{(G)}, \Delta\lambda_{1/2}^{(L)})`. + + Both :math:`\Delta\lambda_{1/2}^{(V)}` and :math:`\eta` are obtained by fitting the convolution: + + :math:`\int_{-\infty}^{\infty}G(\lambda'-\lambda_0, \Delta\lambda_{1/2}^{(G)})L(\lambda-\lambda'-\lambda_0, \Delta\lambda_{1/2}^{(L)})d\lambda'`. + + The :math:`\Delta\lambda_{1/2}^{(V)}` function is fitted as: + :math:`\Delta\lambda_{1/2}^{(V)}=\sum_{n=0}^{6}a_n(\frac{\Delta\lambda_{1/2}^{(L)}}{\Delta\lambda_{1/2}^{(G)}})^n` + for :math:`\frac{\Delta\lambda_{1/2}^{(L)}}{\Delta\lambda_{1/2}^{(G)}} \le 1` and + :math:`\Delta\lambda_{1/2}^{(V)}=\sum_{n=0}^{6}b_n(\frac{\Delta\lambda_{1/2}^{(G)}}{\Delta\lambda_{1/2}^{(L)}})^n` + for :math:`\frac{\Delta\lambda_{1/2}^{(L)}}{\Delta\lambda_{1/2}^{(G)}} > 1` with + + `a` = [1., 0.15882, 1.04388, -1.38281, 0.46251, 0.82325, -0.58026] and + + `b` = [1., 0, 0.57575, 0.37902, -0.42519, -0.31525, 0.31718]. + + While the :math:`\eta` function is fitted as: + :math:`\eta=exp(\sum_{n=0}^{5}c_n(ln(\frac{\Delta\lambda_{1/2}^{(L)}}{\Delta\lambda_{1/2}^{(V)}}))^n` + for :math:`0.01<\frac{\Delta\lambda_{1/2}^{(L)}}{\Delta\lambda_{1/2}^{(V)}}<0.999` with + + `c` = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04]. + Call `show_supported_transitions()` to see the list of supported transitions and default model coefficients. @@ -151,7 +214,10 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): 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()`. - + :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: + "pi" - leave only :math:`\pi`-polarised components, + "sigma" - leave only :math:`\sigma`-polarised components, + "no" - leave all components (default). """ STARK_MODEL_COEFFICIENTS_DEFAULT = { diff --git a/demos/emission_models/stark_zeeman.py b/demos/emission_models/stark_zeeman.py index e212f27b..6dac025d 100755 --- a/demos/emission_models/stark_zeeman.py +++ b/demos/emission_models/stark_zeeman.py @@ -17,15 +17,16 @@ # under the Licence. """ -Plots Balmer-alpha and Paschen-beta Stark-Zeeman spectral lines -as in Figure 2 in B.A. Lomanowski at al, Nucl. Fusion 55 (2015) 123028, +Calculates Balmer-alpha and Paschen-beta Stark-Zeeman spectral lines. + +Compare the figures with Figure 2 in B.A. Lomanowski at al, Nucl. Fusion 55 (2015) 123028, https://iopscience.iop.org/article/10.1088/0029-5515/55/12/123028 """ # External imports import matplotlib.pyplot as plt -from raysect.optical import World, translate, rotate, Vector3D, Point3D, Ray +from raysect.optical import World, Vector3D, Point3D, Ray # Cherab imports from cherab.core import Line @@ -58,7 +59,7 @@ # add Balmer-alpha line to the plasma plasma.models = [ExcitationLine(balmer_alpha, lineshape=StarkBroadenedLine)] -# Ray-trace and plot the results +# Ray-trace perpendicular to magnetic field and plot the results r = Ray(origin=Point3D(-5, 0, 0), direction=Vector3D(1, 0, 0), min_wavelength=655.9, max_wavelength=656.3, bins=200) s = r.trace(world) @@ -80,7 +81,7 @@ # add Paschen-beta line to the plasma plasma.models = [ExcitationLine(paschen_beta, lineshape=StarkBroadenedLine)] -# Ray-trace and plot the results +# Ray-trace perpendicular to magnetic field and plot the results r = Ray(origin=Point3D(-5, 0, 0), direction=Vector3D(1, 0, 0), min_wavelength=1280.3, max_wavelength=1282.6, bins=200) s = r.trace(world) diff --git a/docs/source/models/line_shapes/spectral_line_shapes.rst b/docs/source/models/line_shapes/spectral_line_shapes.rst index 5dc6325e..0d7fac55 100644 --- a/docs/source/models/line_shapes/spectral_line_shapes.rst +++ b/docs/source/models/line_shapes/spectral_line_shapes.rst @@ -2,36 +2,46 @@ Spectral Line Shapes ==================== -Cherab contains Doppler-broadened, Stark-broadened and Doppler-Zeeman line shapes of -atomic spectra. Stark-Doppler and Stark-Doppler-Zeeman line shapes of hydrogen spectra -will be added in the future. +Cherab contains Doppler-broadened, Doppler-Zeeman and Stark-Doppler-Zeeman line shapes of +atomic spectra. **Assumption: Maxwellian distribution of emitting species is assumed.** **A general model of Doppler broadening will be implemented in the future.** -.. autoclass:: cherab.core.model.lineshape.add_gaussian_line +.. autoclass:: cherab.core.model.lineshape.doppler.doppler_shift -.. autoclass:: cherab.core.model.lineshape.LineShapeModel +.. autoclass:: cherab.core.model.lineshape.doppler.thermal_broadening + +.. autoclass:: cherab.core.model.lineshape.gaussian.add_gaussian_line + +.. autoclass:: cherab.core.model.lineshape.stark.add_lorentzian_line + +.. autoclass:: cherab.core.model.lineshape.base.LineShapeModel :members: -.. autoclass:: cherab.core.model.lineshape.GaussianLine +.. autoclass:: cherab.core.model.lineshape.gaussian.GaussianLine :members: -.. autoclass:: cherab.core.model.lineshape.MultipletLineShape +.. autoclass:: cherab.core.model.lineshape.multiplet.MultipletLineShape :members: -.. autoclass:: cherab.core.model.lineshape.StarkBroadenedLine +.. autoclass:: cherab.core.model.lineshape.zeeman.ZeemanLineShapeModel :members: -.. autoclass:: cherab.core.model.lineshape.ZeemanLineShapeModel +.. autoclass:: cherab.core.model.lineshape.zeeman.ZeemanTriplet :members: -.. autoclass:: cherab.core.model.lineshape.ZeemanTriplet +.. autoclass:: cherab.core.model.lineshape.zeeman.ParametrisedZeemanTriplet :members: -.. autoclass:: cherab.core.model.lineshape.ParametrisedZeemanTriplet +.. autoclass:: cherab.core.model.lineshape.zeeman.ZeemanMultiplet :members: -.. autoclass:: cherab.core.model.lineshape.ZeemanMultiplet +.. autoclass:: cherab.core.model.lineshape.stark.StarkBroadenedLine :members: +.. autoclass:: cherab.core.model.lineshape.beam.base.BeamLineShapeModel + :members: + +.. autoclass:: cherab.core.model.lineshape.beam.mse.BeamEmissionMultiplet + :members: From f07fd32510b9d7a9453b803f07319d71527f6631 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 17 Jan 2023 23:44:40 +0300 Subject: [PATCH 016/134] Updated documentation for demos. Changelog update. --- CHANGELOG.md | 9 ++++ docs/source/demonstrations/demonstrations.rst | 17 +++++--- .../passive_spectroscopy/stark_broadening.rst | 21 +++++----- .../stark_line_zoomed.png | Bin 26626 -> 0 bytes .../passive_spectroscopy/stark_spectrum.png | Bin 26687 -> 41296 bytes .../passive_spectroscopy/stark_zeeman.rst | 39 ++++++++++++++++++ .../stark_zeeman_balmer_alpha.png | Bin 0 -> 35119 bytes .../stark_zeeman_paschen_beta.png | Bin 0 -> 35725 bytes 8 files changed, 69 insertions(+), 17 deletions(-) delete mode 100644 docs/source/demonstrations/passive_spectroscopy/stark_line_zoomed.png create mode 100644 docs/source/demonstrations/passive_spectroscopy/stark_zeeman.rst create mode 100644 docs/source/demonstrations/passive_spectroscopy/stark_zeeman_balmer_alpha.png create mode 100644 docs/source/demonstrations/passive_spectroscopy/stark_zeeman_paschen_beta.png diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bf7c6d..f392324b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Project Changelog ================= +Release 1.5.0 (TBD) +------------------- + +API changes: +* The line shape models are moved to a dedicated submodule. The user code should not be affected though. (#396) + +New: +* StarkBroadenedLine now supports Doppler broadening and Zeeman splitting. (#393) + Release 1.4.0 (TBD) ------------------- diff --git a/docs/source/demonstrations/demonstrations.rst b/docs/source/demonstrations/demonstrations.rst index 6201aae1..e91f358a 100644 --- a/docs/source/demonstrations/demonstrations.rst +++ b/docs/source/demonstrations/demonstrations.rst @@ -151,16 +151,21 @@ Passive Spectroscopy - .. image:: ./passive_spectroscopy/multiplet_spectrum.png :height: 150px :width: 150px - * - :ref:`Stark Broadened Lines ` - - Specifying a Stark broadened lineshape instead of Doppler broadening. - - .. image:: ./passive_spectroscopy/stark_line_zoomed.png - :height: 150px - :width: 150px * - :ref:`Zeeman Spectroscopy ` - Specifying a Zeeman triplet or multiplet line shapes. - .. image:: ./passive_spectroscopy/zeeman_spectrum_45deg.png :height: 150px - :width: 150px + :width: 150px + * - :ref:`Stark Broadened Lines ` + - Specifying a Stark broadened lineshape. + - .. image:: ./passive_spectroscopy/stark_spectrum.png + :height: 150px + :width: 150px + * - :ref:`Stark-Zeeman Lines ` + - Modelling Stark-Zeeman lineshapes. + - .. image:: ./passive_spectroscopy/stark_zeeman_balmer_alpha.png + :height: 150px + :width: 150px Bolometry diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst b/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst index bd670a49..b7ebf3d1 100644 --- a/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst +++ b/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst @@ -12,9 +12,15 @@ electric field due to the presence of neighbouring ions. In normal tokamak operations this effect is negligible, except in the divertor region. It is possible to override the default doppler broadened line shape by -specifying the StarkBroadenedLine() lineshape class. In this example -we can see two stark broadened balmer series lines surrounding a -Nitrogen II multiplet feature. +specifying the StarkBroadenedLine() lineshape class. +This class suppors Balmer and Paschen series and is based on +the Stark-Doppler-Zeeman line shape model from B. Lomanowski, et al. +"Inferring divertor plasma properties from hydrogen Balmer +and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) +`123028 `_. +In this example we can see two stark broadened balmer series lines surrounding a +Nitrogen II multiplet feature. The logarithmic scale is chosen to illustrate +the power-law decay of the Stark-broadened line wings. .. literalinclude:: ../../../../demos/emission_models/stark_broadening.py @@ -23,11 +29,4 @@ Nitrogen II multiplet feature. :width: 450px **Caption:** The observed spectrum with two stark broadened balmer lines - (397nm and 410nm) surrounding a NII multiplet feature. - -.. figure:: stark_line_zoomed.png - :align: center - :width: 450px - - **Caption:** A zoomed in view of the 410nm Balmer line revealing the - characteristic stark broadened lineshape. + (397nm and 410nm) surrounding a NII multiplet feature in the logarithmic scale. diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_line_zoomed.png b/docs/source/demonstrations/passive_spectroscopy/stark_line_zoomed.png deleted file mode 100644 index 2a6ee04b9b1fc3b875e2a6a8ef924446b4a7de3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26626 zcmdqJWmr|~yEi%}-G~Z`lmaRtf)a|7(ik*IhlqrPwB$quR8VvZN(m_4DkV}=5D}$A zkXGrImVECA|9gLU_u1#m`F7T|*5zV4=Nw}^ao@jsc%XSjg`VaR4TfR#YO0Fb7)FA} zFx(Io1-v5O+BXb;khxt@)1`uc{HUz&!_U+%s)lYD#%PKD!)3^(+hN#YOifW<*E@c? z-^*L~`u6r8-(%PJ9;T*#a___`G7=J!>N{5BRI+>auv}MqgTKQ^^+7-&?7i6Ehra}# zryA~|6?pUh{TKD8Cn$=_@VB;c67*koM6H z+~^(D%{P+{4ha!Bch2a{fOoR;WA;qZ8%>eRzwLSo?P8^UJq=|kFyTE{VL+3t{njVa zC%f_!U%!4WX4`W3=&@syFU$j4&(D=72kvYPt`9%caPM_2iCVtq^Jw4kD;c*JN7Xen zx`;ufdT<#I*#MK?V#oHSiFR@KaZSA}-ONjgx8O2aMnze(WpkCM{TZ+kF5I4ZdEeF9 z&WhE6Y@;H3yP2M%EdAWrsiLZ_8E5$m;doI*(c2Psaz{SOdRod`RytJ=Av!MQosX9zk`5*~}U&}?| ziWIl8risQggYL9}1l=Ngy~cP+og2*wox}CdXg8X_wrJ^#y6*u zvZ8{kCl1N@y&r9eNuJ6pyCM;HIgCX}TUR%7wrXdar1?ccLf(@nd!+vkKU1284@~{3 z<1&CJ+|R<2mLTmrxjGmq+%Y&YP~pqGXHQa`(}zr=!_S{Toq~$|H&*Ps-@KA=>`8ep zWGo;iru*Q*gVnUCusN4&F$?HwT3e^|6gy^L|MpysVE;WQ^W}Lj z`^H%DcUMoqzxIQb6})yU(?8(h?wi$$H&)_aR)!G_jM57`jQ{Xv@oCnRSHWx0w+IGT4$_T)EG%B=x ziY^KFX|k|f?AV)S(->=1?tLq$Ykg@VIab`kzAZ(q=Z)oEv%oEnmr}R2=c;xpQ_61# zmkju1A3EcdL%r|V7n=gR<^*Y@s(^|qPKHngY2TF&19SgWN-jUm<*BZ&m6=}g>tFY8 z!Jr$xcX?%;C}@z^QGR<-%x}#_FV{%VW3r>wv7|puLv~9SmbrU&o{-6VO_(znL?Ksm zMyYwc%;vm8zeks;{f~F4`gtaqv;AcUCg;SC9FZ?AEnOeF%P3|+#Ag@F-d;%g;4zgZ zO9@qi|sk+*krPm&P>Y6*u!-a~uuw3JkD?3|rf#Ns2 zMbuz`p1DuumP}5hCw?!qYhRkLVVr`68Y^}C9R&k>MwFldOEwpFv;iB^x$^Sdb=A|` z3ooin-kQRg(W@!@_!{HNA9ld~wp$UiCDKkkPPbg>i|K za;{O)!@e$9gLG=2YZzsxzt85C&7S6Eh5b-5y>W`JM)mS#@hrV;8`aa3o!K|&SWkai z8&8f1*Ga#^Y}=b>dR~F>XW^T0S>BHs&t$b{$CTaO3!g?s>80G*`8$?KpWSKS=h^G1 z?BtYZY#dtibA1tJ=|-VLR~~V(9F}JYZ?ebKcjYTr9``zsQqYO)-Mg1&GkmcslveFD z?8Uc%I{{rqF`_pP5yHE>uXY7q46!_zto-OSZ)p*^W`GD8-CA^ey2eF9b#--Kchg@V z7_DB98`tY5o~m#@<%KEOdB&@kXJSZL`f($%{-=w{!ocEKv%%K-@@{cE@7b{IvDkR# zB0)dbXykjPX_=>eOHxp8QJiz7p;`8ziN%%Sw9$UdYO%;44Q|IoE)B=A@q>AeTsqE?GeIi`PmcqLPE^m znZme@etUD9H&;(&8JFZue$UN@`jHLe87q3@ zF{k%X5O;riVd9o{VDpRY!=t04W((^h0{35(1>Cy6U+%9QM&{R&TQZOZCGVoS|8kU3 ziIeHjGK}m5^x0c%uie*SC-e&_`L0Yqt@-J~wo+h}I|&ne&T~2gZczy)q#Wu=E<8^r zY^yBOvf^T={;bCw=UMFTFId6j>n%@qihIqP6gl-9L2+~!a&Zy3X--W|4NIq|%q!ox z(|L8azcxTd4R-2tZXO;qA0<bdiu~7?TDbx@U!fKt*Bc%lej#73$S8Mx z%6unFDU4+Ts+1<$@bk`E>#j08aM{`Kqn!rkLLCJ+``-T{G;cv?A7A_Pn>>Wz=ISa2 zbJn$LXlNKK>6Ql1mj*3}dE$rf^5juij?iEBM|<7249>NxefWaT%|N@%(=jzQwNx*h zk>gy-&gPh`(g#Lt?gBX=6x2z4=PJ?xwirJ;Brf2x_X}W!CFt4*q8X2?ZLH22K+*O1 zCRDF&4o9T`snj?Jz3SZ+Y`e6YSe zm2TIb#xEpv8MU2<%z}l0WG+b8L3>P+bQ>Kn?sd{BuxUiih$~=C9{yKbRaG^}f6wn< zzm|T52_rP8)&Ic*EfB{a@06S?|EAr!bLZjGw3?dQA!(n&1m9W7BD;3wlEHv7sHv4( zE5^08wRX^#2q`d@cF@UV&)#}XAQ1L*aLgEKx=NyA0VT=T+nAXDx_f7? zu|eD!=aU_S^q8>j=jutWZrwvkdZ`$jHfA-@bjTTotetKGR!rz;>!{nrv8L^BLB~qryTlgY#;i-&oe1+~9=G&cwy#ta|#` zm3Z%ZiQpMQ9-bO+DAA7u^s){DbQJSh8n@hB>2(h7p|F9H@>xei`ugVI=0FL6#Nkkz zlc5ZJ=J4vXyg5+*BiFIab0!?nUeg-D+wkFGYaU+SPcY&l+spZZ%o{)GYeZnwu8TSL zbaduH!}z=~^360qJuU4}f0@@AXhd{sc|w-)y+pr_nUWWN&QOy?4@um#C8MNdLy+^& z&rmKiPqyf=ovNOyz^Wtl-V>=W`ttIPioQOvZb+2fc^H_FeGRDLik*E&?o}<5$6U>B3yUY*CLmhsxd<__w)!IzSpf0r8cLCw2<*_BXt5>5_%6ITk zfWNH)MU=BlNlzD)kr`N?doRn4`4AEzlLcQ)h&1EqegZ|6v>G!OJ7(Irx0 z>I@1*?C^w(PZ}@(D|K?Uwx{hY@BG*?J}tP~lgQ^!h1Be$bUSB!v3aH4R{~__YgI<| zuy#>uJmz-*w+BDyc9!mC@t7*%SMr6US~#+yUMf7cIbi4en2u+zSv;PZ7#@Gxj~nAN zgf9qamfUopEzB~CZuDq$NxOi}pZfP|9~;x5l>04*)wK2V6js<(3}slb$b%$@v8u5g zEF_d7)5--;lEb3bKuR`LhAxocR_m;2`{V>mShd2!%9rC9$-Lg~SFtX|e-z%_6mdBR zHy6PTulqJJS2g_&Y}!dBp&}YEVYs!5LfTPGUEBN1Uz)>l@}%?VX#?h}CGiKgoqHtV zs)LFc+0g337?zNag^V_mUuw1%wZ$qQU{u7p)En;&u$mZBDq`L4cWp}I#|&_xZ)gtU zoq2XI_iDgJA4fXB4}EL?5W5`u!EyHzza;U0ndMl5u=&gAXCcw^1}p5LDYUCNsVbW2 zBNmvE>vWjMN?X*p$*&h~90e55k;zh2nK{9k`P!bN&u-1Frk9=G<+^#FGKa%>8l(|dVK zOUs9kAM59vRTTQJI+aL3k$O>Ub!!9lHRCu^N`?&Juvwa~?z~P;UhdG*;6GmHe|iC;4c$JitN=*|190AAA3ZZ9% zo~CuqYc_tWC^B-txP6D}sZ*!&985pBYXKhjob8*iZ_m^@UgGOH(Wb=D&;P=orK~@1 zY014r7#nER!#m ziaiX}I-t{|YMGgtPCqpWQyU!}9fHMbARH(whg?ulpp|L{;w1)*PG^t@|8As*B196V zOSO~%UlTmEWNFh{9r;QU8Q(?BQa%Fx2{;AQ){feOH2o)4s(GqjSIKJC+ZU#S6K z>*we90ATm9q@W+ufqW7h1~ep2q4-cnt~`KnCIGgz0T$$S7X!#4qyWoRC0Jd%=CL+! zSt8Li_i~X!xa*jd)Fz=*R_)!7n>37!UK=Ds6?8Q%tiEPgj4E70m}}Q1w>x5 z8v#S~%@+9mbv=6gc-*o+7U}k*G{lu7gtxS5+k-}O$KS@&DO2_U7Fh^;Bb1&k4H%^+ zm;cgJXkL-Au~{7*y4S6(RS7Gz{n=v)zE@yjwFy^kzTVkht(qLHszPeS=SOT82{7k5 z6!eG22Ylx`zvmhkxld?IZ%h|Jw~Gd#!D|PUsHem^F(F{xZfos#M685kEJEFNT!G~c z1LyH=Q8CZBW^c;Ra)llVYQo0(I&YBR+calMhJaE;J%64C%0M>s=Dd6WAnJrWK%Pck zm}i0Z15=ft9DeW>P!)&P*GhT5WMpLHi_NkI0RBPP@MxA^%?RAtjuo-IlYQCB>NSh7 zSq@@wfc3k+30LI+6p9nFz+`{r%o?5`2+hC7yk++EWJ1xBA8}&3Z+XzbPg+LC4EA;D zq(5-dr7EEF4;|rQ@{z!y+Du9G>TWhzy&0r$c!H&*%S3@FiAqs=pB{!wD(r3!Eqf9+ zx?}1+Q+@jmHYAAZ|8uv1M7PSp;FIioi*?A!hOYg4k-m=;kUr$}Ae=Ml8c%;%Kx&PD`L6-`Ks^=_eu-0?(E!A-K6dhD-aa2IR z6;TbJ#Y;Dnrn_N<;9Z!lGmiw_Gs4{ip$cO(gXmp-Fm)%J=R&ez?Ue5KoZc zhRo6Rzjq4a*(oWBXHd7ut!BU#K&Qw*0;6O6SLLJWdUDHW?mhnszq91s{*e`2Ln5gv z`5wIU?t5Y+gZn6-po#(F?NCXc=l=M`a0}Ezc5hHHkjEBn_1#N7-^x-T;>LPW8#dk@ zkSzfP;!RT0e{D~wOG3n;mnv52Fy;qX2S4a~a(0OCEVljcxmIxH4cvR!01^D>xwuFw z``G1*gK$G}{~kmD|JMye^D2bKrd}jG+nIFV_`lm5M1AN*{7(&mPkTgA>qU2e@j%Fj zY$wuZHE0w5d&A;7^l@@>6Gt9fDENM~@K*D$vXpBJTz3yJ;MhX61e(dQ)F2pX;SpyN z|B0XG{P2jON3M_Uh4EuR6X#xZZ75~)QPR;BlK(s>!GV$ZEeCtf+e=-) z?8I*dm=H^tAX(23xS44M zyIsBVE_$3&0lWbl34=A&LHykAjRc#KRSUqPxxf3cjJN{E!`Kd*i?jA<;r(s=Ze&w)t&7`;72rtf0w(=mx117IuBMN zo*{2E*u-8y+n(@`22*{&i0lEP(_k_C@v$T3qProHu=`ITu>cgXkWqk z4L?4N$HA{n;=If@O!wWGMXElL%yXoX6Kg^lryd+n(7bdhTxAu=*(2!IX>d8)zY8N@ zo0{}Nar9W3HZ+KXif;lUR@>&^#Uh)=LmX1xnuv`~E%)`Ac+3r$0=r*_+Pn;n1Zmkw z4$E%b-r7J|3A7A(+R^TFieuU&y z5c2F=lH_grnnuQ3lL7912bH>`*wGk7flNRY>!4mPbyaQa5te>O$Yup>t*fi4wO&5c zpCVkjb^&P?44hKqG$|mTwjd6?G_FNXN$D{BH0YFZT62(!bjCM66TI&ce?&42Ck!@Ztz`zTTIos&huoh z3Yq4ewbkBFlK7qVt|~N{wm{(#jtY{-K`U;x>OTXD0AW8f^HDXB3ZK0zDCh(XWe|rX zt5}D;Tja!lSnEsfp&zLi572X*(>?Dss|(^p6oK%|lkrAe9jEW~yJoAB-s>g3ri1C} z=?A8yaeT&2mtWo`%{8D4Bv{j?0KZs`X2E2)*RFy3a0&bvqY9s*6VEpp^}=ekbFguj zvrFCAA~HdnLVg7=zqq&_z{V_-(s#za(a{G})6zclm`LrP+1$2wY-?|idiwNLOG^t$ zv#4Dg?;|eR$}e-Ia(@X`)JvT=KAsK5&qy+`hWuRlL4gf?EtJG3t3-SULXUdAdB9&K zK)_!90Lt^PuCfD$fWdwR`|VRs-O?9;Vv*0EkFKu+HT}B&6x*+084>i&;n~h9yRoxp zF`4Q@EIKMG6s|!buo>2^hggR1*kD2-?!&~);xUl$fKy# zTkp-{Ms^W3BV(2IvIcU0faCU8V%~|>2_)R5`Fm{+Lgy0^!Wa&U$N}zHUXuy2v|XG3 zx!xSOoeavoz^PNMGs~a{@bmGh3hL*$nD)Z9g94ER!omLwL2#o&g0Z8jA@%1Ts2Z>m^Hex*E7sT7f0#&%<5dH}SN|>rz586nsyz@9^~;w# zLE_D}tO=>NDNrMX(6PP_+?q1?0k2TpWk@d9q*NOTTfb_<8rKDJnpY(6#^>%~mJ6%p zHnzZ@e&;61z<_Utad8@~G`$=r5ye}QtH;Z1*R}-6?Wh_|p;y^9$S0GP8?(=NFUOo# z($Cfd1^9WtlL0Ub&`!h*dhvM7fBWy)p>yuYjM95+r9=;-yzkg0mm;-Gmpb6u%K#du zKxsyD+Y$&k*|7JOZues4Po&G{ndSJMGtTmyf6Q?9w z;vn~ZD^^ogZ9%|Qa3CFIt@Z7VxvIYQ5BY>y?uKTe z4s0n2G?@ua_M{Hj~M<(yh3TPA^C|BM&=u+@9{wCMo!fC z9K&QJ3xY6yqd*rMVF6CM-BgIJLEEwgwc5TjE6T8h48PY>*oORxqXo7`(>{tRD=U9! znj*(9oG6H9mXo+cF(e4z2PiiFb2w`Ff9SVZk(jSqzF~?m!J)gh2jzXVS;LHR^b!lAqYxI)2qJM`k3hM>ztHC z7?w<^nupGN=CC%CVn>_wa3{7Szuft+EPy-au8$#%aJSA4V*^hQ7U5*fjfB=SgMQaO zi{j)SxawmHG?HY~CPEEbfH8yXaP#1B$BNkz9FHEH z;^u|Y$cWR^H^J`HM8&4eEd2_{ql2d|fFafS@h&AOoi{{3kV0R6d`ECvLL_Jxdh3u_ zXlg!TIdRpcRsuEBm*+eh#u+fFU*~A@V7-iKo?-(@hB=KM2U@{J+5;!WraUoCtJ7hI zxM2XRmDPohlBPN`lR>Mt1+5ee)ffT&obi?QKzS2b=0od9*#(MR@BxQKoUKXJjr9MA zmea`PYDBExUZpV^1fLNqO2Z@tCgu85eSq7U>@v(;#Jo3y1vt|nXTiVVnM|>eK+*CT zZD6GbkFXs;-2NuZKDlhAAYAZ&uKep3=C|BwaQ$EiYSe#z*9mfSS1%j6xV%9gGZajX zG90FJRuCVYyFF5bw679xc8rql;9)f&zsSwnf6%t4+6ohS3F!I4N$lxDjTNYeSMriu zq7*Q`{7W1AX=!QOfzA%CZ$o|40i|cDL9`j72H$evj(D1cENFb`;)g4ZqN9kIUyvPc z{#7Vv+pa@`nCfi#;uQpmz$5Hx*N}D8KLeA~uox$IAeH2xpZwKfXf>5Ym?QYhOi)t@ zDSsEovOz3Bs6xzlp~s?MpJkID zE4qQS#+u)l_jLEE%dui=fOECLNzntHf}T_ADhf^5*rc&GQ(GS7} zp-AXO3FzrU%_#Ulinn@2LWLb8Bf@=8fVrc4;ldpOgS;Got!3mNQIZD!BEgdsOH2Cq zKBOVMGs^9Ge=8Y~<7-$oneYze8D~GTJh=N#7Y=fR21g6vP<}yBM4`HO07=S&xy=TT z4kaj%NCi&k5Am^PtGC{pNtgvimzIl?4D~(KOBfqtOi02 z>PvOzJeWaF%Q7AzyFFjXhKVTsiMZqT@6uaRc^Cx+g#hSb5A%=n@_M7S#2rZWK3e4? z(gH{?^-`@v53%W|!PLXuc*cJHr(xlsfFq~~!0z;KeN6Lp2ISnRaV~X_IqdsC6j9I} zwD`P;$vyaLPNrR+Jk(7=fft_V!veOJyXI`EbS1Dq&S z&yCyrF#ymxsCgqt))2MAW5>H*nWzl3Mxs6-8bM-F`;Zwmsit-EA?DMjFAdchYh*VCf{d}-H{`~oak|LQ1N4x%%tW+{3t42-sfeKE9pI;3b z&L~9$NG@ji(mCIioV==SA835b?;M>jWTa+L(|oRgsz_+((A7XwgHm4$jp7llC5v5E1g4>II-KVpH0~5Ie zY)-q+xipS|2oS3SSeX4vA#@VxLtdA*wze*$x(wEyr2xXOw=c5){rORz1hUSdyJTMm z%!w3#2q?W<8lb{!>-1e4I=LN_V-gaC^F9+Hh`;jc)hnC+$t*Nv{Q(;@&~G|GTwI3c zkpbHtD54ffi`qIm%wUnOhz11(J$(33+ulBFmk~jB%gGU{vq`>^vNFqfms1H!k0I6g z%tkJbPrkOFZ2ec_+p)6`%O4(uCjzh<4E>iUv}Wtfu;9xBCc1#ZJhn=;l9lcI-oLf) z%F6}(m<<`6v}L!q&}Zy6*Zv&!48)FRzpo>zX}jR{Si-Ci-d?3w}>T`-Tn zKZy1PKdcLWsQ=`^@lks+vIGrpnczjnStRz@9<6 z`ufUD_WSo|)!IzG`cnanIDl$!NYXV0u}-+B%ix0iSp%IxBZl(?N&SXocH8N0P~=uc z9jVqr*qlThRhMA1>j2^IgeZ?%+dd7cS812y?7_=H+7xWz4p4cJXAZD}=lg8ZsW`@D|9Eb(2ucTiO6Hkhn`Rv;}+w zy!&2cR&B5SW`uBAi+Ijt$NO8qFAQeG0y2;?bDC&NHKw=X@H_bM40XHBjWtfBs=7!e(&*uz?EZF6sHq+IQh@*lMf6 z$091;xZ zs4}ujP{4{)+6NVUl;r^clZKE#AQc;_g!y&V}o7a0{-VxF1)Pm%XbYry3II6(^#V#B`cfQt*g z=FBQqdK^%JN8=9rJGPY`+us}fD);^IpnC|yMTqz_eSx4^R*^#&%E6*^4pPTJ;wvb_ z;kj>70O@!~&f>*0UT)VOPfkbdyE| zM0S1hq|Vpup|8THO_cb)b zBP*<)uz#GZIMJ1t6ylfj-t|3M{QYAM+``h#LbhiyVZBXU8?N zAk}3fMTRl$vBA5h%)#~A_-zfFt*oaTKBa2iCp z&Afk-!@Z2Za|DYOGCG|Iy>72hCAd>!wa;$_bUKl12;$=@>4=oxVr1n$%l6WX4Wf`) zjz`U)_sVGLuqT6yY?>gz})a~I+SIGCf12u2nY_jO0nZldo&f%bhp!33=pZq z+cA8zm^p$FIa^u!`D@LoiYs7RQtiu=m2-i#DJ%;JHQVcy>-$Xc*uq zA?MH zm#yslN^Mv>h!8)VuC{*(2U!pfw}GC3Hnel#Mx2CGA75DQYsfy?1KRRgs6UR5FznvJ zd9Q%x$|1RIVCWIEwJ@lDLHsvx7uyDRw!HwVpi&7)SqI`2u#T7Dh{PlWicjs{)TL(< zK}}^U19dj~j}=N8YJSU6W7j_xVzzIuLek7u8iFG|1vet#ipk)OXK1IY+j!uxTC49g zLt%4IpT5jimuQz!#sOwqsYk*$SRjz6$OP=4HwB}nX~-3a!2)8e%k{?-_rI5cjN$T3Zx`qXNne^`3aJYt&-6SEi7ACV2D^ zCJ80!f?k>rh(5LxN~xjE^V@D9@5el6R^y`VK<@H!(R4&!|)}i99I@Vu|O7uWB&&|lvd+~fZv6| zfQ?)z;x-_pXj;b{Yr;h?+_gRieA4V|A&mvB320WYK?ecFLj$P!2q7(xJ49Y4`7D+cmNQVMCZ0q@`R5OltbVz$f&4y41>pkqBKqYAtWP7<6$Nv8brg@?wPWJ6o0o1D9>zDtNO zH*@WLmJq?4|(~I9#sOf#WPq| zZL%Zt860t#(j|cXRpi*K4{lVR1VoKmA+zlP*2cvZ$CBQtgoN1@@}$j+%?-5nmPcR; zrlfXO!(W1JeLhBi*FZ%j7Tl{W;9N8`G_JlCa5O7z=~-;7!0x$@GR}Sbe)|Z4-39ij zQ$8hV6#@cZb0JJ(736!?JS;r?M9b=b1&jtL@ojEB%-G$$ws$V^@@=dqaKv6Hf{-Q? zQag0kC20yY+^3cYf%$)JT;47{jhYDXg`ev(($c2D@UO2aVl=khZ5(|Kqh9w_ffBK|Y`}wmy=o)!M zbesvwggpQULJG*SpmVDOpmlwzQ2sv|oF~13FswjeN;5}81EJv5h?ux7H*k`qfyQVY z^MR?57t-bzDH+%`!FTIj1{Z*3ofA3=r$Ip~r=mh3tAES70E3E=@qNx*ZCDsoM@5Si%z? zic!u3p>yk%>XDptBI0q7^eaZurI%m=A)N%2g;XF@4j^s-EV#AR089>@Q9{k)pS|@R zW!ZgD3j+Yu4vD~y979pm_OE>ox1>EttUIeiZbJv+{xi#us>Lqe%SB83PDT5>kK!O%8p$OITnD zogS#@MXf^BBu=ik!4TtClgH@?la2WMou;EBvc;?j-hz-_k;jxi%4R?w4oO%jzXhZQ z?!m~$gA`M8?HSgQi?6jfY863 z0;U;@0l7#=-w_MM#2ck(@Sw2d{p<(VRfm%bYtVHKlS2g7jMJYREYGqRo1$3cfup5V zii9h@V4amS_I8oZ+f5vOPah6pBW!NC^|%!%2ecK{Uf{DPxRYbG1X(hZ)67J72$gsr z0SF7;g-KvjLqiAXC>=@iqyWw?5+rVRCjlFrgbX>LAY4+kx;?W?0QnQB7aQJ756%M3 z>q_K;)TlJ@f0XrvmOWQ&+Pm2wvMwM z0x3lR^7_4hKO&@{QUauDy-s%967_%Erb!csw6!E*I&;WEIs*+6bs+Y3H$e3=&3$g_LHR}C7nN+wq+C_7njYi zlV&?me*2uu5dPh}cYS8Ew|?D2GQ+FL4oTt_%Omn%8;S*A1|P=C!=X!oKr{ssWh)$Q z%7o(w(tkefLo4KZkr8+(XA!2nAeafu`6>jKjiGXn!I4pPAPAHkgy_&I59pf4PzYMV zK1J|}rcsjY=E|{_EAbTnKBA+e)0v^gqXvjE3O4#NwGYeRjf=ehSpECnq8}X_kpatcAcXmdcNl|>wl z=8$$4LwL53&_|*C)!g(F%t*||g*Bt=VsQ@$*LGKm=9PVOw zbkX5$3LIxuLvjKyKM#)rtdgTpLQJ$D7+}qm-`tV-qVt+fzIVp6Vsqge$f+mlovX3BxvG5VM)t@u0Da8W)wK``6-2F&E&cF>VBOEyxC44`t&3l0X`i}EaQ}7l zyPgq=+}~e+_RNF9oCj$SH3DQ@`9Sgpq8>j_nRH1~!4Q`A*H=eH$xMp0&E80P_Ly<6 zQsN2Vc{VgQA{BZH9RmdWxW2s?oUF$$>uIlRG2gvj^t?ehMNeSFf8}N4S3PBYrw2CZQh~a`UV$GZ*J%sCCj4%B zE9zORqR02P5+hy$_KYK?c<&zw83Jz~0L$HfL~kn)^#sYyQ?LJlXVuHKZs$rr?Y?;v z5sNM$=BRGjNKs6rS4Epzb0TL`ZLB`X9nI)AolpM{% zp7-sU4_=i;6jKS=Rs_)}di+1J@f?wJq#$;Y!a_)N&)JU-FLa6^62}Oa(>P~FlZ{W^ zq#3$}UZO%TedQuK`IF`_1TnmipbuR;_?f(y7}@lX`k_!`Gr$z-)) zLGrntRSfY=mVYn(Kriv{i0$%IQgkiwH!ogefSB%WBI+uX=vmy#BL(rD)$wui`~Lf- zJ`Xw*ej$>dQWlumXF<{-ka59p@2dpVGv*^LDyjnwR0p|@nc7E}XZwvooAE5G`+tn> z>GU|0%Tc>D)e06V9Y%u2H?rr;r2D&&wF0^Awq<_{i*4iEn}6z1H9@KbbT1@!pihyhN{fj}wF_{3T7Z%jS=aBqoG4?8 z&StsfcQ_Rr2|+~@^Qx_txcWIVeEQedB;J;RXr+=SQsDufw}S13e0yX= z$ji$cy|oIST1I*_2&iD}Jm&=*;sBcOIoIL)*^Qv9F+{DO(?%p48ViIhGvMr{z~RGkutanqKndTGay-HN z0+0eY8_~rRA@v$K2wFIVl%|*Gvq2#F05UO(#laLz!g=maK-ks=QBB26nEGeu<6nkd z%pr1NQ}+0n`p}>uHC<9X57Z~tCh~*VwQvnnFF-fkEo!0+k*zEuSDK>g;8XwVqKi|t zi6=90(HHlQnGWyz7*OQ#lEjnCx+7R99sV#K9D43mJ?BrC{BV^p=*qCT!Xt*uVD^6C!y+ESg zz4ze#L-!wwnAljHOq}Bq;i*w#3KFd;Pl%w5`03XqnYg$eFUmFAIA(H;zLMRN9!Q(}VK$6K?dK-%E5WdX(W@_;oKSlhzFp9Peux`PuV$++n%&kgA9*NzMW4 zONz5$)Q6dtsg;@4}TYWc7y#~j%)?lX08 zV^Xm{tQ0I4MOXVA$)2R+AAB+0z52@Um_yyuBwI`NiezhFI2iC@55^xXE{ntpomNp$ z>|og^$Gu?tSLn))I)T?s?DG0wdyynb)~%pqyy9?3G`9Dt%Eoq~uN3w{ypHtM;+4g7 za@?-6=$@0WbI3Ac?@whVHavZQNc!v^Y<@;#?E2$-_cMf!!H3^oG0mvc7@JJeRXyl+ zIk2?m)~SH2RQNQqPOE}%?DR}e1$W=Iav*^e`=mm_EtmdW@{j=!&nDAww!CRFy=1DM zYp8&8@qAzuDxB+<4PeH1u9&Rceje-oJlxccl3j z>(bh@N=)|AMYT!qLGC?u(j zt6G2Bh1>gd!vam#SVyoAyVNZl_C%^rI)BGa^$|Te=J(W`p`i8{y3@<(PV4^8x7bnP z!r6Mb?ygUaFpLpCo3?W?;bbx-4Bw1(vzFEz;U*!X`3&zrIsftJGf0u1p(jYje$x@) zZD8-;KL#zx`fWT%da(l)=&>46DVR768i zGkvy0Y~9P)gbWr~L#un!6ASZjj=f|@@DU7&&(E`SbECPC%$gGFerdpfMT;NP9<|MD zaNZZIj2o2o-YZe|MDh^#f+zZ}Yd0G z?$wQt3r_v1POrnTZ^!Gbyrbnu?|-Pv?F#uh8^?Ww?9I(OrjBSK*HA@8vk+LerK$GM z$g6t&_INy!fZ@JJ8NejurP@Ck)2nQyDa&!iAZC8B*VWrVglSm-jhJTx3tPZWll=bp zO!*A?Fxf#4#?HZgdtny@>o-cogB|JkzRNvnAN;POV8 z&*SD)F_8kOz% z#;We)v+lyGgXt%uG3=@Qxtw^3z0dM#Ur3%?J$#Q?Jg5FAJ&<|mf`7XxiHhRd?~mrn z*LvugTCyTftEdkDSo_;ZHlH<5uh=1J6Gm@X*Llzj1q`W1Z=!TQYC?_F;XV3S`QRvy?>ygM~e&`~d-?xN-qMQl=se zs-WLDrsg{%Zan?-+t7N)os<51Sl2sx`aiX#C5qQZ$K_k>qSs8X==t*$+q|&4_?M}#Z+yg}ct_HRo%O=G&CQhsvUvy$ z)WCmXK>ZJ&I>iLOx&Y*Mg6`ZQLBH$8)4ww4%f9Xz5-4UK^!gp*5>*mcwYqGTOz+Z* z+X!Y|dX$k>l9V;@$6h`vlZjcJKEq!8YX|2`qX*HNvSVM`GlDKBYaC+U7dBvJU-}~^ovxH*_DJp}#g<)ub zv!fpBe-qabJj%yQO+($7zn3?YU#70`0!fN~{^OB68;&}u1@_HN3SC#q!!JI6R%Sov zPK8d=Jm=p}b1D8T6Pyj3$!l+E!H0xUj1{)pl3^#-#&8)Pm9{bJXHsbztI8si`yV`h z?p$%@n_quXd9tHPz~76Xd*v%Qwgy`!e9mWNFK@r?o_TYX{Dj2$Lyn%^)324PJx{%I zi`1&yzjr8G@1sGoy?AW9*w=Qko@9Cal^J$w8VTDyvvYT`m$6(foT`~m_OPa?SU=&c zuTkh}DCs2^m}1j1b5y==%9C};Q6(sQQYgdm&Fjk6hwBu^i*phk;zlpG-V}d)*c0MO zGJm>hSf@2|zW@jI>uW|wwZwWhjy9ytL09w4IL9ZL+XynNg9*3tb&Zs#4I9KZEfgX_RfjaMZ}V=`%8;Jwt* z+mG#{7Jg;id-pe@O#O$CdvYgVNSnugMSe9+fuk?%>N3>go-!A`tIc@$JKe6ZRweab zzCN{iE{##t9X0k+O+H?-4Lhdk^om~|GoHAD&X5lwY2RpixPpr%Rg#ULO9qr&HIDEr zi7ocfbUN^+r-#3wxA$?+dF83NbmNHil6X&!iK>OIiHz+2uv7K9-E5O14Dz04ahNBl zkAiRDWH_2TMQdx`?Xy!9<6CqX{yA5^kgP*0i--BrgCrF_%1>s zQ|2i`PKJ;p8R9r1l(7kIL?Wq7+my{dCk~OZOcf3#BvWO~P?^F>gJG8;NzN7_98<)1 zJ-zR@-tSxM`&!jn)*d|1e*VvW-~apmUDxjZC8&_16_t0{QJY-Z?V(g~`&&(Eoawvk zukw@G^KbGgv@K`(+UT+uoRvLLXn24Atf;Xy!>5K-@S2wNxK4z@?+#slN={M3?R*CV zcAiXnCSc;yVP^bA@S2fv(pg`{p^%E4&-a^~M?Dwr28+4v5u2#cc|5FE(xV|ZH#Xi) z@BNT@d^FC$0Y}Zk%KB@;k&l3Jl}E8D{v|MQWyt8y9*^vvitoQJPFV3cT;kloW92Bb z&uG1W)&3Cmw-H|Di+`6_j-6E(xhlMe1zbd@+T+1E^op;-YN6$M6#fLK(BnuOpS;O6 z^G0V6!{QCVubM$JMAy`4!TQB<>eY{@T|K^&7BEN=go8v3ly}*+wPF|&F2N3`Z$QYv z(9p0uHE`aSR3ebI13!EUT3ViLrjvNl)d^B4*}o~IT-maG-_`S5Z3g@Fb>M?Oz=5ec2|?T+NzmeFc-;{3F!4 zc0B!I88x$TcO|FO%JV%la@;sj>D`SsPky|ZsJn<4pF+ujvvx&|N1U;+98JO#qs9c8 z&1gO4Km5y6!v#XCv7rXNRk*6aJqmNW7S}pl2^m@0r%o!{b#%laE0no^>|A7j!TtM9 zNwQ|#qM}a2ck%r7qXw>3|?qoGGkBKj-2NB|T1n(DoV4N_fA&CFDxUmKeb zMyhd!(lxb+F5Y+C+WO5@S)$BlZT~M`%5>4zX%Hx)c-e>PX)co_%cDno&eXNHoBoxd zEx5L}#>T&>k|bd4CU`7JNp=Czfb3;Iv7}6^Zpa8 zyOD`WyIe_%8MCtpbQs3`w206c5fP!>;WtYCMfXsG@i%nCn$RY0f(&@OM9?Q5x8-LM zTMmE$K4@mf2QRQ~L|5*hsi{xr%NH;BMPiL~)>r3pN=sodc{=J`N!>9Wu#Msu6n5{n z0K@w07ff#f*IvQrrV1ZJReX@CqogQeig7(Td0*HexoiEE7Hm?`?lr^GYmE02g;MTY z-{Zj#-l;APj#AIt+IR>;fohle8gx2E%0sV89IZiNga4Ux?q1%ydl#)OMAW{Q!8}p z&3bSSHM4gepN?#AiR11RW%4e}RJF?MFdvD=NMHs4d<@u-DCkVchQ`K^n}(7XH_aEz$NYs3MCG@5=f6H5Q6(F>n0}5^77V(GE}}4>@RWF{SKLKg=d&v0`^2PnS4*+#or%wF9;bn0>N` zg@wI-_3FdIcOrJb0N@ImpW zrnmGq_Dw)J`^kBL&iBSSy7}c&Clhb4Q(Af+Sxl|bg$KP?2ddX82fV#i+dDeQ6@uK{ zG!P7_YjpAHOXCh9_bVfVFhcSMDj^2!;zjN3tdG0;4GsGF@Q4T^jOuquzQAyaA4F6u zuDF$hLmX&OCQ^HQyB2>VF5Xv&i9Regtk;<>qZ989-%f)P`UtSFz2H>B`}*vN_yE=G zrKxIGfMqST4NP8;i0i-tR$gBIiLoU0(|~^mmt?EI_}oApRS6`{X3PNf0XCThSx7c? z64f(!N3(R(Tmwr>%gB}L|D0=4ThF@I?ZQdl$p4&c#O=XJ@vM~Z(;kbW6g@21PoRqe z;Q&wyn>dZz`WZw+^uB%NR_G3!;P8kfRR&t)^qDgT*;T8w(%0@y7$B>_^5@Z-z*BUs zwZa-^T12gmACCmNL;1#)c5Cw7@+mEkZh1siC+G(?LsvD6tN*^C-2CQ-wwT_vws4Sn z)nW>)0h*yxe~I@oroNXH>g2AIl@|1TcIs^U@(bn#;nC6TS0!{sb|tgpFBV=SA__~g zDX|9BH8d)!A5I{dvcdGmd^fpf+tH&(<&~70@qw$rSdR1r9oOB}-w_QCWoYrUT;2dB zPPkoB{ij(-D>NzNP&kX|F5{_<<8=R z4*$iOhwnU7i*_K&^Yim7yB;g#N5m1IVgQ`zn5ulQ8LHc}pS60$(W0`1@qfdBtW#Q7eyD`tr4e1R9Q3E9`h-58|nBv5loA z?ado&B=^o-Ws@Worza#tWzV5{@x;VLA>ClV$bJox*fgCWHFXhAj6He^)>R;oBpmMC z%k3&JI$kPc(l<0@CON?Y`guo(@A8fcx0e!2D=RMW8j$F!2P!%YD8v{cCvV}G013TZ z>!5d9Ai5&&-P@HqA&daABAcH$r7L8s*Uk}=F> zt!6N1jlrF=rQB}PXYn1>9fqKd@LmE2Y*u8l60n_S3|b7WtP(q1*a2v9=X^Hr1_2OV zTdQMr;)Fawie=L$ngNjv)KdXGIzz|v5jaUsRn>_Qpqb8yHqMZ_bW+6Pv+=P#c#b6H z7%~&Vwt4gCSr_@>ht#decZi8)z^XL7|LL7kP*C6lyy%SS(MH-5a8*O-Sv14eYJJmj zfYAiBc-qY^=KIQG4CGPOI?6e93*-w8^S%F_YyX{b7xTC?yVfCuk-{waR{TAv=6UeG z%JKKF#K!Uy#z?-wC@`c!{}M;~5vaX0;_~EvmcOl6VuK~IROmIAZyu5>bAK&OTwvlv z0Sf$tsB*H`XLUypM9K7QOF zuj>k&jAx*kf2{LIBe|bZ>fFrEBP{%GO7(Nm$l{lWFDq8|6Ef?Jj$3+R4!9kSk%BiP zC8>t3g`{$2E%g06(V+S;|51bB|Gf8E27%`5Afs0L$ah^Y*_B|x)!d$}Z~)>1BFP|P zw?9jzbr9GZeCO*@EA)&KMG zx2qV^O9l*hM-H4KkxWfZx0@u*oqJJiXlEw^->26uLqp?U(3*0HsIyTh6x|x}f3>dj zy8y_LE8Bw&ABxl$KU`m@?&5R0yDQ@=I|&b4D5g1=1mvn=d+NA(%5fa9|id9Em%Q z#FqnSgQQvvY{Hl1Nz7Lz2@g&)YYRgv)`s->BTVHGTepL+wM|J({OxmB-VQA-5u&a! z3xbBsHy9%@p`Sgr;j-g;T>&qZjbBVc+RX0yTngb@>Le`S`4AW9=jTZ@aPiI<_MwI} zw6x^DYmpTP@6N3w<^7W<9-iTZcO72EEhrcXXzhutkx5cQN(w~w%&1sV0B7S`5SaN`DhokM(8)j;q+ zT_h4KjrfiLO_W7sAIS!A18M4KdEt+C%N4kR7!1_QTss6{70rxwUxkOQY-~nO%E`)Z z0zCQ@1L9~XvZNFhIbbGWNG6N_{W6Q|@>5z`TAId6AfU8-)Z`E-D+>$q%qwB>j78tM zdskXlcW_@uW@a3A3q+mPaafKIwE~$}Kf>TQP>7t%%gb+TgZ3bX#bAuMk%YoKzW>5w zPB7fQ?|MoLvF z&CLK!9Bx>fXy}vChDux=j>7z6I)U8CafI8083!m^UWd?SSE6fy$z>qSq%-DHmF)k3 z#g7YMRp79{+S&@^0-YRpsPGhq0ibHgk{511M$+x`>FMbR@9s9Ct4rP?n9nc4(ryNy zw=*E`6VSDEsNrH5d3mV-&d^r0eZb76`ww?0z5zh`r=K>12xvhqpo+<{n*k?DB$fr2 z5a;woMNWGBGBeL>{2h?Zh=ahbmyt1+YsfmE`-E2cc>q*-~mxd#^E}B7u8OX>8 zIN6>FN)W##;+79?cXfj8Qu|TCS9gcurO^m1 z4UK70`P0K}+j+=|SWVqMJ;NAo6RQDaEe|j6@0dF?C^W=eLk^4BkH6g^nt-(#S()% z?o&^V%1gIRtgLv(Cni+kki(b_4GksEs82Dhk#nzR7!0vVq$5X;kVt@%RZwZCj@nx| zw&#vth;KXXv+w(a%IW9aLL4lqHFoqaAA8eWUSzjcWZqk@U^FK$IOcU#UuEaD8p=!3 zCP~jKV$X!#IX5zm_wch#xNn$9*uB$WrW=BlifhsefyVgBygPT8D+m2WC4$GKYN0w} zV`qN`0Hh3p4-{{xfPcOH+6iqatQ}F}3n@JND>#V;#gTh=^=!(^%Z2uw;ecm#pWNMq zgai%OxQQ)#D_{0-h-toZ%{*2}NrxqQ=a8()##esdodT-f)W8QF#}i@JJCa{W(VbN4 zN}oJ_?AYuXaqRYy9g0tQcc z%SB4{0?WcE@Ry$5`Xj{Rgm6bn$;q_@k%eDfC-ruBYf1`^5P1X?#R$ZCEnx2A;dJi| z(cSs+WSPyrfB*FqAEpZYMpA=<>m;B>=f~Zm;?lx#RY-N?76C=tJI^UE!>>RJpP*tW zLu@4;fR#bBTNd??v9U4pN(j%Zv9sPpoG46QZdX+aL3Pm3K?z3s+77Y^n?o85Cqkg$ z&nRxFn;c~@DucEiws==h)t0l!yb86?V*5hWbKQf8R{`9HhMA|4L$K3m#ajm7S%dFm zqr3V?Vx1>`6BiK`CAt~J0)__K4a=OUAhC7k5fzPr z%>WAu^8{^m=^SNZOh)vq0{PhfpB@$-{?};n|88;puZNc%tFAqM5HJyq;)p~tIbd#B JY~b|!e*xUA+iU;; diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_spectrum.png b/docs/source/demonstrations/passive_spectroscopy/stark_spectrum.png index 53230968c5f25157865e0a57cd7e419d3c0e5e3b..04d90589b54ce2560d0a999e166cf00bd7e41b54 100644 GIT binary patch literal 41296 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�pkL}MELO}7BFP=~Z$}&rB*YNG{55X$t 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 Y$5s7~frXkhACIUn{Db_2zLDAg1IjjNW&i*H literal 26687 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 fuSdgEKLw_pf(_fQfBp`iE0Xa6bJ`O`_ Stark and Zeeman features +are treated independently. This is a simplification and for better accuracy these effects +must be taken into account jointly as in J. Rosato, Y. Marandet, R. Stamm, +Journal of Quantitative Spectroscopy & Radiative Transfer 187 (2017) +`333 `_. + +The StarkBroadenedLine() follows the Lomanowski's paper, but introduces a couple of additional +approximations: + +* Zeeman splitting is taken in the form of a simple triplet with a :math:`\pi`-component + centred at :math:`h\nu`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{h\nu-\mu B}` + and :math:`\sigma^{-}`-component at :math:`\frac{hc}{h\nu+\mu B}`. +* The convolution of Stark-Zeeman and Doppler profiles is replaced with the weighted sum + to speed-up calculations (`pseudo-Voigt `_ approximation). + +This example calculates Balmer-alpha and Paschen-beta Stark-Zeeman spectral lines +for the same parameters of plasma as in Figure 2 in Lomanowski's paper. + +.. literalinclude:: ../../../../demos/emission_models/stark_zeeman.py + +.. figure:: stark_zeeman_balmer_alpha.png + :align: center + :width: 450px + + **Caption:** The Stark-Zeeman structure of the Balmer-alpha line. + +.. figure:: stark_zeeman_paschen_beta.png + :align: center + :width: 450px + + **Caption:** The Stark-Zeeman structure of the Paschen-beta line. diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_zeeman_balmer_alpha.png b/docs/source/demonstrations/passive_spectroscopy/stark_zeeman_balmer_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..532a8eabf61dbab380df567f75a701d442a2829b GIT binary patch literal 35119 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 Mdr0k>%89H01-E-E%m4rY literal 0 HcmV?d00001 diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_zeeman_paschen_beta.png b/docs/source/demonstrations/passive_spectroscopy/stark_zeeman_paschen_beta.png new file mode 100644 index 0000000000000000000000000000000000000000..afa5044abfab78d6232d9d506e4319bf8fb39114 GIT binary patch literal 35725 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 Nqot?$O5ODO{{qr==`a8Q literal 0 HcmV?d00001 From d9441758a2e903759d8258837c939704d5204475 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 18 Jan 2023 16:56:59 +0300 Subject: [PATCH 017/134] A small fix in documentation for StarkBroadenedLine and Stark-Zeeman demo. --- cherab/core/model/lineshape/stark.pyx | 12 ++++++------ .../passive_spectroscopy/stark_zeeman.rst | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx index 93b165f7..e290ca98 100644 --- a/cherab/core/model/lineshape/stark.pyx +++ b/cherab/core/model/lineshape/stark.pyx @@ -156,12 +156,12 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): The following approximations are used: - - The Zeeman and Stark effects are considered independently. - - Zeeman splitting is taken in the form of a simple triplet with a :math:`\pi`-component - centred at :math:`h\nu`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{h\nu-\mu B}` - and :math:`\sigma^{-}`-component at :math:`\frac{hc}{h\nu+\mu B}`. - - The model of Stark broadening is obtained by fitting the Model Microfield Method (MMM). - - The convolution of Stark-Zeeman and Doppler profiles is replaced with the weighted sum + * The Zeeman and Stark effects are considered independently. + * Zeeman splitting is taken in the form of a simple triplet with a :math:`\pi`-component + centred at :math:`\lambda`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{hc/\lambda -\mu B}` + and :math:`\sigma^{-}`-component at :math:`\frac{hc}{hc/\lambda +\mu B}`. + * The model of Stark broadening is obtained by fitting the Model Microfield Method (MMM). + * The convolution of Stark-Zeeman and Doppler profiles is replaced with the weighted sum to speed-up calculations (so-called pseudo-Voigt profile). The Stark-broadened line shape is modelled as modified Lorentzian: diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_zeeman.rst b/docs/source/demonstrations/passive_spectroscopy/stark_zeeman.rst index 46ec7161..eb07750e 100644 --- a/docs/source/demonstrations/passive_spectroscopy/stark_zeeman.rst +++ b/docs/source/demonstrations/passive_spectroscopy/stark_zeeman.rst @@ -16,8 +16,8 @@ The StarkBroadenedLine() follows the Lomanowski's paper, but introduces a couple approximations: * Zeeman splitting is taken in the form of a simple triplet with a :math:`\pi`-component - centred at :math:`h\nu`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{h\nu-\mu B}` - and :math:`\sigma^{-}`-component at :math:`\frac{hc}{h\nu+\mu B}`. + centred at :math:`\lambda`, :math:`\sigma^{+}`-component at :math:`\frac{hc}{hc/\lambda -\mu B}` + and :math:`\sigma^{-}`-component at :math:`\frac{hc}{hc/\lambda +\mu B}`. * The convolution of Stark-Zeeman and Doppler profiles is replaced with the weighted sum to speed-up calculations (`pseudo-Voigt `_ approximation). From 0b290cd97c269a16b3acf60addaea9aedd582329 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 19 Jan 2023 18:31:48 +0300 Subject: [PATCH 018/134] Update changelog.md. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f392324b..3a677175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Release 1.5.0 (TBD) API changes: * The line shape models are moved to a dedicated submodule. The user code should not be affected though. (#396) +* The method show_supported_transitions() of StarkBroadenedLine and ParametrisedZeemanTriplet is made a class method. New: * StarkBroadenedLine now supports Doppler broadening and Zeeman splitting. (#393) From 63e964be60e56e11d1f1b500ff1eee8befd18692 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Fri, 20 Jan 2023 15:52:46 +0300 Subject: [PATCH 019/134] Allow custom line shapes in BeamCXLine model. (#394) --- CHANGELOG.md | 6 ++ cherab/core/model/beam/charge_exchange.pxd | 3 + cherab/core/model/beam/charge_exchange.pyx | 84 +++++++++++++++------- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bf7c6d..e6483556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Project Changelog ================= +Release 1.5.0 (TBD) +------------------- + +New: +* Add custom line shape support to BeamCXLine model. (#394) + Release 1.4.0 (TBD) ------------------- diff --git a/cherab/core/model/beam/charge_exchange.pxd b/cherab/core/model/beam/charge_exchange.pxd index 4d9220e5..cd0b48db 100644 --- a/cherab/core/model/beam/charge_exchange.pxd +++ b/cherab/core/model/beam/charge_exchange.pxd @@ -21,6 +21,7 @@ from raysect.optical cimport Node, World, Primitive, Ray, Spectrum, SpectralFunc from cherab.core cimport Species, Plasma, Beam, Line, AtomicData, BeamCXPEC from cherab.core.beam cimport BeamModel +from cherab.core.model.lineshape cimport LineShapeModel cdef class BeamCXLine(BeamModel): @@ -31,6 +32,8 @@ cdef class BeamCXLine(BeamModel): double _wavelength BeamCXPEC _ground_beam_rate list _excited_beam_data + LineShapeModel _lineshape + object _lineshape_class, _lineshape_args, _lineshape_kwargs cdef double _composite_cx_rate(self, double x, double y, double z, double interaction_energy, Vector3D donor_velocity, double receiver_temperature, double receiver_density) except? -1e999 diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 5cefa3d6..295d5f5f 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -1,8 +1,8 @@ # cython: language_level=3 -# 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-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -28,7 +28,7 @@ cimport cython from raysect.optical.material.emitter.inhomogeneous import NumericalIntegrator from cherab.core cimport Species, Plasma, Beam, Element, BeamPopulationRate -from cherab.core.model.lineshape cimport doppler_shift, thermal_broadening, add_gaussian_line +from cherab.core.model.lineshape cimport GaussianLine from cherab.core.utility.constants cimport RECIP_4_PI, ELEMENTARY_CHARGE, ATOMIC_MASS cdef double RECIP_ELEMENTARY_CHARGE = 1 / ELEMENTARY_CHARGE @@ -44,24 +44,58 @@ cdef double ms_to_evamu(double x): cdef class BeamCXLine(BeamModel): - """Calculates CX emission for a beam. - - :param line: - :param step: integration step in meters - :return: + """ + Calculates emission produced by charge-exchange of plasma ions + with beam species. + + :param Line line: The emission line object. + :param Beam beam: The beam object. + :param Plasma plasma: The emitting plasma object. + :param AtomicData atomic_data: The atomic data provider. + :param object lineshape: The spectral line shape class. Must be a subclass of `LineShapeModel`. + Defaults to `GaussianLine`. + :param object lineshape_args: The arguments of spectral line shape class. Defaults is None. + :param object lineshape_kwargs: The keyword arguments of spectral line shape class. + Defaults is None. + + :ivar Line line: The emission line object. + + .. code-block:: pycon + >>> from cherab.core.model import BeamCXLine + >>> from cherab.core.atomic import carbon + >>> from cherab.core.model import ParametrisedZeemanTriplet + >>> + >>> cVI_8_7 = Line(carbon, 5, (8, 7)) # emission line + >>> # define plasma, beam and atomic data, plasma mast contain C6+ ions. + >>> ... + >>> # here we override default line shape class, GaussianLine, + >>> # with ParametrisedZeemanTriplet to take into account Zeeman splitting. + >>> beam_cx_line = BeamCXLine(cVI_8_7, lineshape=ParametrisedZeemanTriplet) + >>> beam.models = [beam_cx_line] """ - def __init__(self, Line line not None, Beam beam=None, Plasma plasma=None, AtomicData atomic_data=None): + def __init__(self, Line line not None, Beam beam=None, Plasma plasma=None, AtomicData atomic_data=None, + object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): super().__init__(beam, plasma, atomic_data) self._line = line - # initialise cache to empty - self._target_species = None - self._wavelength = 0.0 - self._ground_beam_rate = None - self._excited_beam_data = None + self._lineshape_class = lineshape or GaussianLine + if not issubclass(self._lineshape_class, LineShapeModel): + raise TypeError("The attribute lineshape must be a subclass of LineShapeModel.") + + if lineshape_args: + self._lineshape_args = lineshape_args + else: + self._lineshape_args = [] + if lineshape_kwargs: + self._lineshape_kwargs = lineshape_kwargs + else: + self._lineshape_kwargs = {} + + # ensure that cache is initialised + self._change() @property def line(self): @@ -85,9 +119,9 @@ cdef class BeamCXLine(BeamModel): cdef: double x, y, z double donor_density - double receiver_temperature, receiver_density, receiver_ion_mass, interaction_speed, interaction_energy, emission_rate + double receiver_temperature, receiver_density, interaction_speed, interaction_energy, emission_rate Vector3D receiver_velocity, donor_velocity, interaction_velocity - double natural_wavelength, central_wavelength, radiance, sigma + double radiance # cache data on first run if self._target_species is None: @@ -116,7 +150,6 @@ cdef class BeamCXLine(BeamModel): return spectrum receiver_velocity = self._target_species.distribution.bulk_velocity(x, y, z) - receiver_ion_mass = self._target_species.element.atomic_weight donor_velocity = beam_direction.normalise().mul(evamu_to_ms(self._beam.get_energy())) @@ -127,20 +160,16 @@ cdef class BeamCXLine(BeamModel): # calculate the composite charge-exchange emission coefficient emission_rate = self._composite_cx_rate(x, y, z, interaction_energy, donor_velocity, receiver_temperature, receiver_density) - # calculate emission line central wavelength, doppler shifted along observation direction - natural_wavelength = self._wavelength - central_wavelength = doppler_shift(natural_wavelength, observation_direction, receiver_velocity) - # spectral line emission in W/m^3/str radiance = RECIP_4_PI * donor_density * receiver_density * emission_rate - sigma = thermal_broadening(natural_wavelength, receiver_temperature, receiver_ion_mass) - return add_gaussian_line(radiance, central_wavelength, sigma, spectrum) + + return self._lineshape.add_line(radiance, plasma_point, observation_direction, spectrum) @cython.boundscheck(False) @cython.wraparound(False) @cython.cdivision(True) cdef double _composite_cx_rate(self, double x, double y, double z, double interaction_energy, - Vector3D donor_velocity, double receiver_temperature, double receiver_density) except? -1e999: + Vector3D donor_velocity, double receiver_temperature, double receiver_density) except? -1e999: """ Performs a beam population weighted average of the effective cx rates. @@ -328,6 +357,10 @@ cdef class BeamCXLine(BeamModel): # link each rate with its population data self._excited_beam_data.append((rate, population_data)) + # instance line shape renderer + self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, + *self._lineshape_args, **self._lineshape_kwargs) + def _change(self): # clear cache to force regeneration on first use @@ -335,4 +368,3 @@ cdef class BeamCXLine(BeamModel): self._wavelength = 0.0 self._ground_beam_rate = None self._excited_beam_data = None - From 4a6807fbde06bf39c93c7d0c760d8fcee634ee22 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 23 Jan 2023 17:11:43 +0300 Subject: [PATCH 020/134] Update install_adf15() so it could install thermal CX PECs from starndard ADF15 files. --- cherab/openadas/install.py | 35 +++++++++++++++++++++++++++++++--- cherab/openadas/parse/adf15.py | 12 ++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/cherab/openadas/install.py b/cherab/openadas/install.py index 27e0c5f4..bc0268cd 100644 --- a/cherab/openadas/install.py +++ b/cherab/openadas/install.py @@ -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-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -21,9 +21,11 @@ import os import urllib.parse import urllib.request +import numpy as np from cherab.openadas import repository from cherab.openadas.parse import * +from cherab.core.atomic import hydrogen from cherab.core.utility import RecursiveDict, PerCm3ToPerM3, Cm3ToM3 @@ -264,6 +266,13 @@ def install_adf15(element, ionisation, file_path, download=False, repository_pat # decode file and write out rates rates, wavelengths = parse_adf15(element, ionisation, path, header_format=header_format) + + if 'thermalcx' in rates: + cx_rates = rates.pop('thermalcx') + # CX rates for Tdon = Trec (2D function of Ne, Te) + # converting to 3D function of Ne, Te, Tdon + repository.update_pec_thermal_cx_rates(_thermalcx_adf15_2dto3d_converter(cx_rates)) + repository.update_pec_rates(rates, repository_path) repository.update_wavelengths(wavelengths, repository_path) @@ -393,3 +402,23 @@ def _notation_adf11_adas2cherab(rate_adas, filetype): return rate_cherab +def _thermalcx_adf15_2dto3d_converter(rates): + """ + Converts thermal CX PEC rates parsed from a standard ADF 15 file + to the format supported by the repository. + + In the standard ADF 15 file, it is assumed that the donor is H0 and Tdon = Trec. + """ + new_rates = RecursiveDict() + for element, charge_states in rates.items(): + for charge, transitions in charge_states.items(): + for transition, rate in transitions.items(): + data = np.empty((len(rate['ne']), len(rate['te']), 2)) + data[:, :, :] = rate['rate'][:, :, None] + new_rate = {'ne': rate['ne'], + 'te': rate['te'], + 'td': np.array([0.01, 10000]), + 'rate': data} + new_rates[hydrogen][0][element][charge][transition] = new_rate + + return new_rates diff --git a/cherab/openadas/parse/adf15.py b/cherab/openadas/parse/adf15.py index b9fe42be..ec93fbf2 100644 --- a/cherab/openadas/parse/adf15.py +++ b/cherab/openadas/parse/adf15.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-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -118,7 +118,7 @@ def _scrape_metadata_hydrogen(file, element, charge): elif rate_type_adas == 'RECOM': rate_type = 'recombination' elif rate_type_adas == 'CHEXC': - rate_type = 'cx_thermal' + rate_type = 'thermalcx' else: raise ValueError("Unrecognised rate type - {}".format(rate_type_adas)) @@ -161,7 +161,7 @@ def _scrape_metadata_hydrogen_like(file, element, charge): elif rate_type_adas == 'RECOM': rate_type = 'recombination' elif rate_type_adas == 'CHEXC': - rate_type = 'cx_thermal' + rate_type = 'thermalcx' else: raise ValueError("Unrecognised rate type - {}".format(rate_type_adas)) @@ -229,7 +229,7 @@ def _scrape_metadata_full(file, element, charge): elif rate_type_adas == 'RECOM': rate_type = 'recombination' elif rate_type_adas == 'CHEXC': - rate_type = 'cx_thermal' + rate_type = 'thermalcx' else: raise ValueError("Unrecognised rate type - {}".format(rate_type_adas)) From 394fe4cb205c23a5958b58d61164489c2f85f76a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 24 Jan 2023 22:13:12 +0300 Subject: [PATCH 021/134] Add numerical integration to Bremsstrahlung plasma model. --- cherab/core/model/plasma/bremsstrahlung.pxd | 21 ++- cherab/core/model/plasma/bremsstrahlung.pyx | 149 +++++++++++++------- 2 files changed, 109 insertions(+), 61 deletions(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pxd b/cherab/core/model/plasma/bremsstrahlung.pxd index f8251b28..44025c8c 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pxd +++ b/cherab/core/model/plasma/bremsstrahlung.pxd @@ -19,20 +19,27 @@ # cython: language_level=3 from numpy cimport ndarray +from cherab.core.math cimport Function1D +from cherab.core.math.integrators cimport Integrator1D from cherab.core.atomic cimport FreeFreeGauntFactor from cherab.core.plasma cimport PlasmaModel -cdef class Bremsstrahlung(PlasmaModel): +cdef class BremsFunction(Function1D): cdef: - FreeFreeGauntFactor _gaunt_factor - bint _user_provided_gaunt_factor - ndarray _species_charge, _species_density - double[::1] _species_density_mv, _species_charge_mv + double ne, te + FreeFreeGauntFactor gaunt_factor + ndarray species_density, species_charge + double[::1] species_density_mv + double[::1] species_charge_mv - cdef double _bremsstrahlung(self, double wvl, double te, double ne) - cdef int _populate_cache(self) except -1 +cdef class Bremsstrahlung(PlasmaModel): + cdef: + BremsFunction _brems_func + bint _user_provided_gaunt_factor + Integrator1D _integrator + cdef int _populate_cache(self) except -1 diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 2ba7245b..e076478c 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -21,7 +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.math.integrators cimport GaussianQuadrature 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 @@ -33,6 +33,61 @@ cdef double PH_TO_J_FACTOR = PLANCK_CONSTANT * SPEED_OF_LIGHT * 1e9 cdef double EXP_FACTOR = PH_TO_J_FACTOR / ELEMENTARY_CHARGE +cdef class BremsFunction(Function1D): + """ + Calculates bremsstrahlung spectrum. + + :param FreeFreeGauntFactor gaunt_factor: Free-free Gaunt factor as a function of Z, Te and + wavelength. + :param object species_density: Array-like object wiyh ions' density in m-3. + :param object species_charge: Array-like object wiyh ions' charge. + :param double ne: Electron density in m-3. + :param double te: Electron temperature in eV. + """ + + def __init__(self, FreeFreeGauntFactor gaunt_factor, object species_density, object species_charge, double ne, double te): + + if ne <= 0: + raise ValueError("Argument ne must be positive.") + self.ne = ne + + if te <= 0: + raise ValueError("Argument te must be positive.") + self.te = te + + self.gaunt_factor = gaunt_factor + self.species_density = np.asarray(species_density, dtype=np.float64) # copied if type does not match + self.species_density_mv = self.species_density + self.species_charge = np.asarray(species_charge, dtype=np.float64) # copied if type does not match + self.species_charge_mv = self.species_charge + + @cython.cdivision(True) + @cython.boundscheck(False) + @cython.wraparound(False) + cdef double evaluate(self, double wvl) except? -1e999: + """ + :param double wvl: Wavelength in nm. + :return: + """ + + cdef double ni_gff_z2, radiance, pre_factor, ni, z + cdef int i + + 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, self.te, wvl) * z * z + + # bremsstrahlung equation W/m^3/str/nm + pre_factor = 0.95e-19 * RECIP_4_PI * ni_gff_z2 * self.ne / (sqrt(self.te) * wvl) + radiance = pre_factor * exp(- EXP_FACTOR / (self.te * wvl)) * PH_TO_J_FACTOR + + # convert to W/m^3/str/nm + return radiance / wvl + + # todo: doppler shift? cdef class Bremsstrahlung(PlasmaModel): """ @@ -51,13 +106,17 @@ cdef class Bremsstrahlung(PlasmaModel): :ivar FreeFreeGauntFactor gaunt_factor: Free-free Gaunt factor as a function of Z, Te and wavelength. If not provided, the `atomic_data` is used. + :ivar Integrator1D integrator: Integrator1D instance to integrate Bremsstrahlung radiation + over the spectral bin. Default is `GaussianQuadrature`. """ - def __init__(self, Plasma plasma=None, AtomicData atomic_data=None, FreeFreeGauntFactor gaunt_factor=None): + def __init__(self, Plasma plasma=None, AtomicData atomic_data=None, FreeFreeGauntFactor gaunt_factor=None, Integrator1D integrator=GaussianQuadrature()): super().__init__(plasma, atomic_data) + self._brems_func = BremsFunction.__new__(BremsFunction) self.gaunt_factor = gaunt_factor + self.integrator = integrator # ensure that cache is initialised self._change() @@ -65,14 +124,24 @@ cdef class Bremsstrahlung(PlasmaModel): @property def gaunt_factor(self): - return self._gaunt_factor + return self._brems_func.gaunt_factor @gaunt_factor.setter def gaunt_factor(self, value): - self._gaunt_factor = value + self._brems_func.gaunt_factor = value self._user_provided_gaunt_factor = True if value else False + @property + def integrator(self): + + return self._integrator + + @integrator.setter + def integrator(self, Integrator1D value not None): + + self._integrator = value + def __repr__(self): return '' @@ -84,13 +153,12 @@ cdef class Bremsstrahlung(PlasmaModel): cdef: double ne, te - double lower_wavelength, upper_wavelength - double lower_sample, upper_sample + double lower_wavelength, upper_wavelength, bin_integral Species species int i # cache data on first run - if self._species_charge is None: + if self._brems_func.species_charge is None: self._populate_cache() ne = self._plasma.get_electron_distribution().density(point.x, point.y, point.z) @@ -100,57 +168,30 @@ cdef class Bremsstrahlung(PlasmaModel): if te <= 0: return spectrum + self._brems_func.ne = ne + self._brems_func.te = te + # 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) + self._brems_func.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 + self._integrator.function = self._brems_func + + # add bremsstrahlung to spectrum lower_wavelength = spectrum.min_wavelength - 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) - spectrum.samples_mv[i] += 0.5 * (lower_sample + upper_sample) + bin_integral = self._integrator.evaluate(lower_wavelength, upper_wavelength) + spectrum.samples_mv[i] += bin_integral / spectrum.delta_wavelength lower_wavelength = upper_wavelength - lower_sample = upper_sample return spectrum - @cython.cdivision(True) - @cython.boundscheck(False) - @cython.wraparound(False) - cdef double _bremsstrahlung(self, double wvl, double te, double ne): - """ - :param double wvl: Wavelength in nm. - :param double te: Electron temperature in eV - :param double ne: Electron dencity in m^-3 - :return: - """ - - cdef double ni_gff_z2, radiance, pre_factor, ni, z - cdef int i - - 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 * 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 @@ -159,12 +200,12 @@ cdef class Bremsstrahlung(PlasmaModel): if self._plasma is None: raise RuntimeError("The emission model is not connected to a plasma object.") - if self._gaunt_factor is None: + if self._brems_func.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 using the atomic data - self._gaunt_factor = self._atomic_data.free_free_gaunt_factor() + self._brems_func.gaunt_factor = self._atomic_data.free_free_gaunt_factor() species_charge = [] for species in self._plasma.get_composition(): @@ -172,18 +213,18 @@ cdef class Bremsstrahlung(PlasmaModel): 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._brems_func.species_charge = np.array(species_charge, dtype=np.float64) + self._brems_func.species_charge_mv = self._brems_func.species_charge - self._species_density = np.zeros_like(self._species_charge) - self._species_density_mv = self._species_density + self._brems_func.species_density = np.zeros_like(self._brems_func.species_charge) + self._brems_func.species_density_mv = self._brems_func.species_density def _change(self): # clear cache to force regeneration on first use if not self._user_provided_gaunt_factor: - self._gaunt_factor = None - self._species_charge = None - self._species_charge_mv = None - self._species_density = None - self._species_density_mv = None + self._brems_func.gaunt_factor = None + self._brems_func.species_charge = None + self._brems_func.species_charge_mv = None + self._brems_func.species_density = None + self._brems_func.species_density_mv = None From 0578c09dfd90b02a88a97b47baac58e088a6362d Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 24 Jan 2023 23:08:04 +0300 Subject: [PATCH 022/134] Update changelog. --- CHANGELOG.md | 7 +++++++ cherab/core/model/plasma/bremsstrahlung.pyx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bf7c6d..276040eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Project Changelog ================= +Release 1.5.0 (TBD) +------------------- + +New: +* Add numerical integration of Bremsstrahlung spectrum over a spectral bin. (#395) + + Release 1.4.0 (TBD) ------------------- diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index e076478c..01e896a9 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -110,13 +110,13 @@ cdef class Bremsstrahlung(PlasmaModel): over the spectral bin. Default is `GaussianQuadrature`. """ - def __init__(self, Plasma plasma=None, AtomicData atomic_data=None, FreeFreeGauntFactor gaunt_factor=None, Integrator1D integrator=GaussianQuadrature()): + def __init__(self, Plasma plasma=None, AtomicData atomic_data=None, FreeFreeGauntFactor gaunt_factor=None, Integrator1D integrator=None): super().__init__(plasma, atomic_data) self._brems_func = BremsFunction.__new__(BremsFunction) self.gaunt_factor = gaunt_factor - self.integrator = integrator + self.integrator = integrator or GaussianQuadrature() # ensure that cache is initialised self._change() From bbcc03092350d3a3a316dfc7e30e70fe9ca61c09 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 26 Jan 2023 12:54:53 +0300 Subject: [PATCH 023/134] Set the integrand in Bremsstrahlung in integrator setter and not in emission(). --- cherab/core/model/plasma/bremsstrahlung.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 01e896a9..fa761eae 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -141,6 +141,7 @@ cdef class Bremsstrahlung(PlasmaModel): def integrator(self, Integrator1D value not None): self._integrator = value + self._integrator.function = self._brems_func def __repr__(self): return '' @@ -178,8 +179,6 @@ cdef class Bremsstrahlung(PlasmaModel): self._brems_func.species_density_mv[i] = species.distribution.density(point.x, point.y, point.z) i += 1 - self._integrator.function = self._brems_func - # add bremsstrahlung to spectrum lower_wavelength = spectrum.min_wavelength for i in range(spectrum.bins): From 520abd06e81359ea7dca14399f73c33a94591c18 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 26 Jan 2023 14:57:23 +0300 Subject: [PATCH 024/134] Fixed an error in _thermalcx_adf15_2dto3d_converter. --- cherab/openadas/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/openadas/install.py b/cherab/openadas/install.py index bc0268cd..dc1ef6e7 100644 --- a/cherab/openadas/install.py +++ b/cherab/openadas/install.py @@ -419,6 +419,6 @@ def _thermalcx_adf15_2dto3d_converter(rates): 'te': rate['te'], 'td': np.array([0.01, 10000]), 'rate': data} - new_rates[hydrogen][0][element][charge][transition] = new_rate + new_rates[hydrogen][0][element][charge + 1][transition] = new_rate return new_rates From 56e4e7a3d746990f76ac66589d18eafc6930d9be Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Mon, 13 Feb 2023 10:02:22 +0000 Subject: [PATCH 025/134] Require raysect 0.8.1 --- .github/workflows/ci.yml | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d488326..f4c15ee4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Install Python dependencies 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.8.0 + run: pip install raysect==0.8.1 - name: Build cherab run: dev/build.sh - name: Run tests diff --git a/pyproject.toml b/pyproject.toml index 8a37ce38..ce56ed60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "wheel", "oldest-supported-numpy", "cython>=0.28", "raysect==0.8.0"] +requires = ["setuptools", "wheel", "oldest-supported-numpy", "cython>=0.28", "raysect==0.8.1"] build-backend="setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index d9462771..24a9b6e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ cython>=0.28 numpy>=1.14 scipy matplotlib -raysect==0.8.0 +raysect==0.8.1 diff --git a/setup.py b/setup.py index 062a5ed8..b90af19e 100644 --- a/setup.py +++ b/setup.py @@ -103,7 +103,7 @@ "numpy>=1.14", "scipy", "matplotlib", - "raysect==0.8.0", + "raysect==0.8.1", ], packages=find_packages(), include_package_data=True, From 7e201798c035d83dadcf66567c079c4aa375c8f9 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 14 Feb 2023 16:01:16 +0000 Subject: [PATCH 026/134] Force -O3 compilation Recent Anaconda and other Python distributions have changed to -O2 which results in a performance degradation. Manylinux and older Anacondas use -O3, so specify this to maintain behaviour. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b90af19e..476697e8 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ source_paths = ["cherab", "demos"] compilation_includes = [".", numpy.get_include()] -compilation_args = [] +compilation_args = ["-O3"] cython_directives = {"language_level": 3} setup_path = path.dirname(path.abspath(__file__)) From e851cee6f9870e074f59dea38ac4af7c8a60f6c7 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 14 Feb 2023 16:07:46 +0000 Subject: [PATCH 027/134] Make development version number PEP440-compliant --- cherab/core/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/VERSION b/cherab/core/VERSION index af5728b4..c831dd27 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.5.0dev1 +1.5.0.dev1 From 0ab324ab02d2eb3e48c7982f03a0895f9cf7ee09 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 14 Feb 2023 16:08:53 +0000 Subject: [PATCH 028/134] Update Changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae0a7d3..0a17fd37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Project Changelog ================= +Release 1.5.0 (TBD) +------------------- + +New: +* Support Raysect 0.8 + + Release 1.4.0 (3 Feb 2023) ------------------- From e12fd5b79c85c2cc5bd89c92595210a1e2e5e369 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 23 Feb 2023 12:11:17 +0000 Subject: [PATCH 029/134] Fix coverage configuration to properly omit test files Omit wildcard was incorrect: should specify tests directories at all levels. --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index a67a87f0..a27c0920 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,4 @@ [run] plugins = Cython.Coverage source = cherab -omit = *tests* +omit = */tests/* From a029b09cd2a91a5ce752115fc4b85d989bfd50dc Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 23 Feb 2023 17:49:59 +0000 Subject: [PATCH 030/134] Package demo scripts and data in sdists and wheels This enables users who haven't cloned the git repository to still run the demos. A few unnecessary entries were removed from MANIFEST.in too. TODO: see whether we can get rid of MANIFEST.in completely and use setuptools' find_packages and package_data instead. --- MANIFEST.in | 7 ++----- setup.py | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2f977070..2d850775 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,3 @@ -include README.md CHANGELOG.md LICENSE.txt CITE.md AUTHORS.md MANIFEST.in setup.py requirements.txt .gitignore +include CHANGELOG.md CITE.md AUTHORS.md include cherab/core/VERSION -recursive-include cherab *.py *.pyx *.pxd *.json *.cl *.npy *.obj -prune demos* - - +recursive-include cherab *.pyx *.pxd *.json *.cl *.npy *.obj diff --git a/setup.py b/setup.py index 476697e8..ba84e56e 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,10 @@ from setuptools import setup, find_packages, Extension +from collections import defaultdict import sys import numpy import os import os.path as path +from pathlib import Path import multiprocessing from Cython.Build import cythonize @@ -68,6 +70,16 @@ compiler_directives=cython_directives, ) +# Include demos in a separate directory in the distribution as data_files. +demo_parent_path = Path("share/cherab/demos/core") +data_files = defaultdict(list) +demos_source = Path("demos") +for item in demos_source.rglob("*"): + if item.is_file(): + install_dir = demo_parent_path / item.parent.relative_to(demos_source) + data_files[str(install_dir)].append(str(item)) +data_files = list(data_files.items()) + # parse the package version number with open(path.join(path.dirname(__file__), "cherab/core/VERSION")) as version_file: version = version_file.read().strip() @@ -105,8 +117,9 @@ "matplotlib", "raysect==0.8.1", ], - packages=find_packages(), + packages=find_packages(include=["cherab*"]), include_package_data=True, + data_files=data_files, zip_safe=False, ext_modules=extensions, ) From ba14ce9eee5d51ff0f46c8d9e9c1bc066f76e8b4 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 23 Feb 2023 18:00:51 +0000 Subject: [PATCH 031/134] Build extensions in parallel by default This speeds up installation from source using pip, where it is otherwise cumbersome to pass the `--parallel` option to `setup.py build_ext`. --- README.md | 7 +++++++ docs/source/installation_and_structure.rst | 6 ++++++ setup.py | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/README.md b/README.md index 963b0834..177feb9b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,13 @@ 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. +By default, pip will install from wheel archives on PyPI. If a binary wheel is not +available for your version of Python, or if you are installing in editable mode +for development, the package will be compiled locally on your machine. Compilation +is done in parallel by default, using all available processors, but can be +overridden by setting the environment variable `CHERAB_NCPU` to the number of +processors to use. + Governance ---------- diff --git a/docs/source/installation_and_structure.rst b/docs/source/installation_and_structure.rst index 3af9d3ad..e9c051bf 100644 --- a/docs/source/installation_and_structure.rst +++ b/docs/source/installation_and_structure.rst @@ -82,6 +82,12 @@ line to install the packages under your own user account. Alternatively, conside `virtual environment `_ to avoid the risk of conflicting versions of packages in your Python environment. +By default, pip will install from wheel archives on PyPI. If a binary wheel is not available for +your version of Python, or if you are installing from source in editable mode for development (see +below), the package will be compiled locally on your machine. Compilation is done in parallel by +default, using all available processors, but can be overridden by setting the environment variable +``CHERAB_NCPU`` to the number of processors to use. + Installing from source ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/setup.py b/setup.py index 476697e8..f0610a6c 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,9 @@ compilation_args = ["-O3"] cython_directives = {"language_level": 3} setup_path = path.dirname(path.abspath(__file__)) +num_processes = int(os.getenv("CHERAB_NCPU", "-1")) +if num_processes == -1: + num_processes = multiprocessing.cpu_count() if line_profile: compilation_args.append("-DCYTHON_TRACE=1") @@ -109,6 +112,9 @@ include_package_data=True, zip_safe=False, ext_modules=extensions, + options=dict( + build_ext={"parallel": num_processes}, + ), ) # setup a rate repository with common rates From 3007d558c5af83caf8146af734deaddb39c3a278 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 24 Feb 2023 16:40:56 +0000 Subject: [PATCH 032/134] Remove MANIFEST.in and include_package_data, specify package_data manually This allows finer control over what gets packaged, and fixes a deprecation warning in recent setuptools versions (see https://github.com/pypa/setuptools/issues/3308 for detais). It also prevents Cython-transpiled C source files being included in the wheel distribution, which roughly halves the size of the installed distribution. --- MANIFEST.in | 3 --- pyproject.toml | 2 +- setup.py | 12 ++++++++---- 3 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 2d850775..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include CHANGELOG.md CITE.md AUTHORS.md -include cherab/core/VERSION -recursive-include cherab *.pyx *.pxd *.json *.cl *.npy *.obj diff --git a/pyproject.toml b/pyproject.toml index ce56ed60..a4eabed0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "wheel", "oldest-supported-numpy", "cython>=0.28", "raysect==0.8.1"] +requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython>=0.28", "raysect==0.8.1"] build-backend="setuptools.build_meta" diff --git a/setup.py b/setup.py index ba84e56e..58d28075 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,11 @@ -from setuptools import setup, find_packages, Extension from collections import defaultdict import sys -import numpy import os import os.path as path from pathlib import Path import multiprocessing +import numpy +from setuptools import setup, find_packages, Extension from Cython.Build import cythonize multiprocessing.set_start_method('fork') @@ -117,8 +117,12 @@ "matplotlib", "raysect==0.8.1", ], - packages=find_packages(include=["cherab*"]), - include_package_data=True, + packages=find_packages(include=["cherab"]), + package_data={"": [ + "**/*.pyx", "**/*.pxd", # Needed to build Cython extensions. + "**/*.json", "**/*.cl", "**/*.npy", "**/*.obj", # Supplementary data + "cherab/core/VERSION", # Used by cherab.core to determine version at run time + ]}, data_files=data_files, zip_safe=False, ext_modules=extensions, From 6b8b71c2e469c746990ca9123aa4c8364c1e8829 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Mon, 27 Feb 2023 04:58:48 -0500 Subject: [PATCH 033/134] Fix find_packages and package_data --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 58d28075..32588343 100644 --- a/setup.py +++ b/setup.py @@ -117,12 +117,13 @@ "matplotlib", "raysect==0.8.1", ], - packages=find_packages(include=["cherab"]), + packages=find_packages(include=["cherab*"]), package_data={"": [ "**/*.pyx", "**/*.pxd", # Needed to build Cython extensions. "**/*.json", "**/*.cl", "**/*.npy", "**/*.obj", # Supplementary data - "cherab/core/VERSION", # Used by cherab.core to determine version at run time - ]}, + ], + "cherab.core": ["VERSION"], # Used to determine version at run time + }, data_files=data_files, zip_safe=False, ext_modules=extensions, From 751f324960afdb7be2402a40f702c1721427e83a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 14 Mar 2023 10:57:47 +0300 Subject: [PATCH 034/134] Add tests for VectorPeriodicTransform#D functions. --- cherab/core/math/tests/test_transform.py | 115 +++++++++++++++++++++-- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/cherab/core/math/tests/test_transform.py b/cherab/core/math/tests/test_transform.py index 2ee4eefc..6fad98b0 100644 --- a/cherab/core/math/tests/test_transform.py +++ b/cherab/core/math/tests/test_transform.py @@ -28,10 +28,12 @@ class TestCylindricalTransform(unittest.TestCase): def setUp(self): """Initialisation with functions to map.""" - def f3d(r, phi, z): return r * np.cos(phi) + z + def f3d(r, phi, z): + return r * np.cos(phi) + z self.function3d = f3d - def vecf3d(r, phi, z): return Vector3D(np.sin(phi), r * z, np.cos(phi)) + def vecf3d(r, phi, z): + return Vector3D(np.sin(phi), r * z, np.cos(phi)) self.vectorfunction3d = vecf3d def test_cylindrical_transform(self): @@ -63,13 +65,30 @@ class TestPeriodicTransform(unittest.TestCase): def setUp(self): """Initialisation with functions to map.""" - def f1d(x): return x*np.cos(x-3) + def f1d(x): + return x * np.cos(x - 3) self.function1d = f1d - def f2d(x, y): return x*np.sin(y) + + def f2d(x, y): + return x * np.sin(y) self.function2d = f2d - def f3d(x, y, z): return x*x*np.exp(y)-2*z*y + + def f3d(x, y, z): + return x * x * np.exp(y) - 2 * z * y self.function3d = f3d + def vecf1d(x): + return Vector3D(x, x**2, x**3) + self.vectorfunction1d = vecf1d + + def vecf2d(x, y): + return Vector3D(x, y, x * y) + self.vectorfunction2d = vecf2d + + def vecf3d(x, y, z): + return Vector3D(x + y + z, (x + y) * z, x * y * z) + self.vectorfunction3d = vecf3d + def test_periodic_transform_1d(self): """1D periodic transform""" period_func = transform.PeriodicTransform1D(self.function1d, np.pi) @@ -140,7 +159,7 @@ def test_periodic_transform_3d(self): places=10) def test_periodic_transform_3d_invalid_arg(self): - """2D periodic transform. Invalid arguments.""" + """3D periodic transform. Invalid arguments.""" # 1st argument is not callable self.assertRaises(TypeError, transform.PeriodicTransform3D, "blah", np.pi, np.pi, np.pi) # period is not a number @@ -152,6 +171,90 @@ def test_periodic_transform_3d_invalid_arg(self): self.assertRaises(ValueError, transform.PeriodicTransform3D, self.function3d, np.pi, -1, np.pi) self.assertRaises(ValueError, transform.PeriodicTransform3D, self.function3d, np.pi, np.pi, -1) + def test_vector_periodic_transform_1d(self): + """1D vector periodic transform""" + period_func = transform.VectorPeriodicTransform1D(self.vectorfunction1d, 1) + vec1 = period_func(1.4) + vec2 = Vector3D(0.4, 0.16, 0.064) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + def test_vector_periodic_transform_1d_invalid_arg(self): + """1D vector periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.VectorPeriodicTransform1D, "blah", 1.) + # period is not a number + self.assertRaises(TypeError, transform.VectorPeriodicTransform1D, self.vectorfunction1d, "blah") + # period is negative + self.assertRaises(ValueError, transform.VectorPeriodicTransform1D, self.vectorfunction1d, -1) + + def test_vector_periodic_transform_2d(self): + """2D vector periodic transform""" + period_func = transform.VectorPeriodicTransform2D(self.vectorfunction2d, 1, 1) + vec1 = period_func(-0.4, 1.6) + vec2 = Vector3D(0.6, 0.6, 0.36) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + # Periodic only along x + period_func = transform.VectorPeriodicTransform2D(self.vectorfunction2d, 1, 0) + vec1 = period_func(-0.4, 1.6) + vec2 = Vector3D(0.6, 1.6, 0.96) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + # Periodic only along y + period_func = transform.VectorPeriodicTransform2D(self.vectorfunction2d, 0, 1) + vec1 = period_func(-0.4, 1.6) + vec2 = Vector3D(-0.4, 0.6, -0.24) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + def test_vector_periodic_transform_2d_invalid_arg(self): + """2D vector periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, "blah", 1, 1) + # period is not a number + self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.function2d, "blah", 1) + self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.function2d, 1, "blah") + # period is negative + self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.function2d, -1, 1) + self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.function2d, 1, -1) + + def test_vector_periodic_transform_3d(self): + """3D vector periodic transform""" + period_func = transform.VectorPeriodicTransform3D(self.vectorfunction3d, 1, 1, 1) + vec1 = period_func(-0.4, 1.6, 1.2) + vec2 = Vector3D(1.4, 0.24, 0.072) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + # Periodic along y and z + period_func = transform.VectorPeriodicTransform3D(self.vectorfunction3d, 0, 1, 1) + vec1 = period_func(-0.4, 1.6, 1.2) + vec2 = Vector3D(0.4, 0.04, -0.048) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + # Periodic along x and z + period_func = transform.VectorPeriodicTransform3D(self.vectorfunction3d, 1, 0, 1) + vec1 = period_func(-0.4, 1.6, 1.2) + vec2 = Vector3D(2.4, 0.44, 0.192) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + # Periodic along x and y + period_func = transform.VectorPeriodicTransform3D(self.vectorfunction3d, 1, 1, 0) + vec1 = period_func(-0.4, 1.6, 1.2) + vec2 = Vector3D(2.4, 1.44, 0.432) + np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) + + def test_vector_periodic_transform_3d_invalid_arg(self): + """2D vector periodic transform. Invalid arguments.""" + # 1st argument is not callable + self.assertRaises(TypeError, transform.VectorPeriodicTransform3D, "blah", 1, 1, 1) + # period is not a number + self.assertRaises(TypeError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, "blah", 1, 1) + self.assertRaises(TypeError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, 1, "blah", 1) + self.assertRaises(TypeError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, 1, 1, "blah") + # period is negative + self.assertRaises(ValueError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, -1, 1, 1) + self.assertRaises(ValueError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, 1, -1, 1) + self.assertRaises(ValueError, transform.VectorPeriodicTransform3D, self.vectorfunction3d, 1, 1, -1) + if __name__ == '__main__': unittest.main() From ab14fb8b74a61e0745553b37f6de14071bdebb5e Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 Mar 2023 12:57:22 +0300 Subject: [PATCH 035/134] Fixed copy-paste errors in TestPeriodicTransform. --- cherab/core/math/tests/test_transform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cherab/core/math/tests/test_transform.py b/cherab/core/math/tests/test_transform.py index 6fad98b0..fc3a2445 100644 --- a/cherab/core/math/tests/test_transform.py +++ b/cherab/core/math/tests/test_transform.py @@ -211,11 +211,11 @@ def test_vector_periodic_transform_2d_invalid_arg(self): # 1st argument is not callable self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, "blah", 1, 1) # period is not a number - self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.function2d, "blah", 1) - self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.function2d, 1, "blah") + self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.vectorfunction2d, "blah", 1) + self.assertRaises(TypeError, transform.VectorPeriodicTransform2D, self.vectorfunction2d, 1, "blah") # period is negative - self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.function2d, -1, 1) - self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.function2d, 1, -1) + self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.vectorfunction2d, -1, 1) + self.assertRaises(ValueError, transform.VectorPeriodicTransform2D, self.vectorfunction2d, 1, -1) def test_vector_periodic_transform_3d(self): """3D vector periodic transform""" @@ -243,7 +243,7 @@ def test_vector_periodic_transform_3d(self): np.testing.assert_almost_equal([vec1.x, vec1.y, vec1.z], [vec2.x, vec2.y, vec2.z], decimal=10) def test_vector_periodic_transform_3d_invalid_arg(self): - """2D vector periodic transform. Invalid arguments.""" + """3D vector periodic transform. Invalid arguments.""" # 1st argument is not callable self.assertRaises(TypeError, transform.VectorPeriodicTransform3D, "blah", 1, 1, 1) # period is not a number From e2ed3cc327fb8bfac37656be38d1d352347b4de6 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 5 Apr 2023 16:20:56 +0300 Subject: [PATCH 036/134] Added a test for the BeamEmissionMultiplet line shape model. --- cherab/core/tests/test_lineshapes.py | 91 +++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index 72b02973..0f7ab7ef 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -26,11 +26,12 @@ from raysect.core.math.function.float import Arg1D, Constant1D from raysect.optical import Spectrum -from cherab.core import Line +from cherab.core import Beam, Line from cherab.core.math.integrators import GaussianQuadrature from cherab.core.atomic import deuterium, nitrogen, ZeemanStructure from cherab.tools.plasmas.slab import build_constant_slab_plasma from cherab.core.model import GaussianLine, MultipletLineShape, StarkBroadenedLine, ZeemanTriplet, ParametrisedZeemanTriplet, ZeemanMultiplet +from cherab.core.model import BeamEmissionMultiplet ATOMIC_MASS = 1.66053906660e-27 @@ -46,6 +47,11 @@ class TestLineShapes(unittest.TestCase): (nitrogen, 1, 1.e17, 10., Vector3D(1.e4, 5.e4, 0))] plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=20., plasma_species=plasma_species, b_field=Vector3D(0, 5., 0)) + beam = Beam() + beam.plasma = plasma + beam.energy = 60000 + beam.temperature = 10 + beam.element = deuterium def test_gaussian_line(self): # setting up a line shape model @@ -371,6 +377,89 @@ def stark_lineshape_sigma_minus(x): self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-9, msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + def test_beam_emission_multiplet(self): + # Test MSE line shape + # setting up a line shape model + line = Line(deuterium, 0, (3, 2)) # D-alpha line + wavelength = 656.104 + sigma_to_pi = 0.56 + sigma1_to_sigma0 = 0.7060001671878492 + pi2_to_pi3 = 0.3140003593919741 + pi4_to_pi3 = 0.7279994935840365 + mse_line = BeamEmissionMultiplet(line, wavelength, self.beam, sigma_to_pi, + sigma1_to_sigma0, pi2_to_pi3, pi4_to_pi3) + + # spectrum parameters + min_wavelength = wavelength - 3 + max_wavelength = wavelength + 3 + bins = 512 + point = Point3D(0.5, 0.5, 0.5) + direction = Vector3D(-1, 1, 0) / np.sqrt(2) + beam_direction = self.beam.direction(point.x, point.y, point.z) + + # obtaining spectrum + radiance = 1.0 + spectrum = Spectrum(min_wavelength, max_wavelength, bins) + spectrum = mse_line.add_line(radiance, point, point, beam_direction, direction, spectrum) + + # validating + + # calculate Stark splitting + b_field = self.plasma.b_field(point.x, point.y, point.z) + beam_velocity = beam_direction.normalise() * np.sqrt(2 * self.beam.energy * ELEMENTARY_CHARGE / ATOMIC_MASS) + e_field = beam_velocity.cross(b_field).length + STARK_SPLITTING_FACTOR = 2.77e-8 + stark_split = np.abs(STARK_SPLITTING_FACTOR * e_field) + + # calculate emission line central wavelength, doppler shifted along observation direction + central_wavelength = wavelength * (1 + beam_velocity.dot(direction.normalise()) / SPEED_OF_LIGHT) + + # calculate doppler broadening + beam_ion_mass = self.beam.element.atomic_weight + beam_temperature = self.beam.temperature + sigma = np.sqrt(beam_temperature * ELEMENTARY_CHARGE / (beam_ion_mass * ATOMIC_MASS)) * wavelength / SPEED_OF_LIGHT + temp = 1. / (np.sqrt(2.) * sigma) + + # calculate relative intensities of sigma and pi lines + d = 1 / (1 + sigma_to_pi) + intensity_sig = sigma_to_pi * d * radiance + intensity_pi = 0.5 * d * radiance + + wavelengths, delta = np.linspace(min_wavelength, max_wavelength, bins + 1, retstep=True) + + # add Sigma lines to output + intensity_s0 = 1 / (sigma1_to_sigma0 + 1) + intensity_s1 = 0.5 * sigma1_to_sigma0 * intensity_s0 + + erfs = erf((wavelengths - central_wavelength) * temp) + test_spectrum = 0.5 * intensity_sig * intensity_s0 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength - stark_split) * temp) + test_spectrum += 0.5 * intensity_sig * intensity_s1 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength + stark_split) * temp) + test_spectrum += 0.5 * intensity_sig * intensity_s1 * (erfs[1:] - erfs[:-1]) / delta + + # add Pi lines to output + intensity_pi3 = 1 / (1 + pi2_to_pi3 + pi4_to_pi3) + intensity_pi2 = pi2_to_pi3 * intensity_pi3 + intensity_pi4 = pi4_to_pi3 * intensity_pi3 + + erfs = erf((wavelengths - central_wavelength - 2 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi2 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength + 2 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi2 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength - 3 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi3 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength + 3 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi3 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength - 4 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi4 * (erfs[1:] - erfs[:-1]) / delta + erfs = erf((wavelengths - central_wavelength + 4 * stark_split) * temp) + test_spectrum += 0.5 * intensity_pi * intensity_pi4 * (erfs[1:] - erfs[:-1]) / delta + + for i in range(bins): + self.assertAlmostEqual(test_spectrum[i], spectrum.samples[i], delta=1e-10, + msg='BeamEmissionMultiplet.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + if __name__ == '__main__': unittest.main() From 101456789d8ac1dcf47a8bfb17c0a6e8479358d5 Mon Sep 17 00:00:00 2001 From: "Lovell, Jack J" Date: Fri, 17 Jan 2020 13:37:52 +0000 Subject: [PATCH 037/134] Add analytic plasma demo using raysect's function framework This is roughly 25x faster than the pure python, class-based demo. --- .../analytic_plasma_function_framework.py | 165 ++++++++++++++++++ docs/source/conf.py | 2 + .../plasmas/analytic_function_plasma.rst | 22 ++- 3 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 demos/plasmas/analytic_plasma_function_framework.py diff --git a/demos/plasmas/analytic_plasma_function_framework.py b/demos/plasmas/analytic_plasma_function_framework.py new file mode 100644 index 00000000..1459f28e --- /dev/null +++ b/demos/plasmas/analytic_plasma_function_framework.py @@ -0,0 +1,165 @@ + +# Copyright 2016-2018 Euratom +# Copyright 2016-2018 United Kingdom Atomic Energy Authority +# Copyright 2016-2018 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 scipy.constants import electron_mass, atomic_mass + +from raysect.core.math.function.float import Arg2D, Exp2D, Sqrt2D +from raysect.primitive import Cylinder +from raysect.optical import World, translate, Point3D, Vector3D, rotate_basis, Spectrum +from raysect.optical.observer import PinholeCamera, PowerPipeline2D + +from cherab.core import Species, Maxwellian, Plasma, Line +from cherab.core.math import sample3d, AxisymmetricMapper +from cherab.core.atomic import deuterium +from cherab.core.model import ExcitationLine +from cherab.openadas import OpenADAS + + +def NeutralFunction(peak_value, sigma, magnetic_axis, lcfs_radius=1): + """A neutral profile that is constant outside the plasma, + then exponentially decays inside the LCFS.""" + raxis = magnetic_axis[0] + zaxis = magnetic_axis[1] + radius_from_axis = Sqrt2D((Arg2D('x') - raxis)**2 + (Arg2D('y') - zaxis)**2) + scale = Exp2D(-((radius_from_axis - lcfs_radius)**2) / (2 * sigma**2)) + inside_lcfs = (radius_from_axis <= lcfs_radius) + # density = peak * scale * inside_lcfs + peak * (inside_lcfs - 1). + # Rearrange so inside_lcfs and scale are only called once each. + density = peak_value * (inside_lcfs * (scale - 1) + 1) + return AxisymmetricMapper(density) + + +def IonFunction(v_core, v_lcfs, magnetic_axis, p=4, q=3, lcfs_radius=1): + """An approximate toroidal plasma profile that follows a double + quadratic between the LCFS and magnetic axis.""" + r_axis = magnetic_axis[0] + z_axis = magnetic_axis[1] + radius_from_axis = Sqrt2D((Arg2D('x') - r_axis)**2 + (Arg2D('y') - z_axis)**2) + density = (v_core - v_lcfs) * (1 - (radius_from_axis / lcfs_radius)**p)**q + v_lcfs + inside_lcfs = (radius_from_axis <= lcfs_radius) + return AxisymmetricMapper(density * inside_lcfs) + + +# tunables +peak_density = 1e19 +peak_temperature = 2500 +magnetic_axis = (2.5, 0) + + +# setup scenegraph +world = World() + + +################### +# plasma creation # + +plasma = Plasma(parent=world) +plasma.atomic_data = OpenADAS(permit_extrapolation=True) +plasma.geometry = Cylinder(3.5, 2.2, transform=translate(0, 0, -1.1)) +plasma.geometry_transform = translate(0, 0, -1.1) + +# No net velocity for any species +zero_velocity = Vector3D(0, 0, 0) + +# define neutral species distribution +d0_density = NeutralFunction(peak_density, 0.1, magnetic_axis) +d0_temperature = 0.5 # constant 0.5eV temperature for all neutrals +d0_distribution = Maxwellian(d0_density, d0_temperature, zero_velocity, + deuterium.atomic_weight * atomic_mass) +d0_species = Species(deuterium, 0, d0_distribution) + +# define deuterium ion species distribution +d1_density = IonFunction(peak_density, 0, magnetic_axis) +d1_temperature = IonFunction(peak_temperature, 0, magnetic_axis) +d1_distribution = Maxwellian(d1_density, d1_temperature, zero_velocity, + deuterium.atomic_weight * atomic_mass) +d1_species = Species(deuterium, 1, d1_distribution) + +# define the electron distribution +e_density = IonFunction(peak_density, 0, magnetic_axis) +e_temperature = IonFunction(peak_temperature, 0, magnetic_axis) +e_distribution = Maxwellian(e_density, e_temperature, zero_velocity, electron_mass) + +# define species +plasma.b_field = Vector3D(1.0, 1.0, 1.0) +plasma.electron_distribution = e_distribution +plasma.composition = [d0_species, d1_species] + +# Add a balmer alpha line for visualisation purposes +d_alpha_excit = ExcitationLine(Line(deuterium, 0, (3, 2))) +plasma.models = [d_alpha_excit] + + +#################### +# Visualise Plasma # + +# Run some plots to check the distribution functions and emission profile are as expected +r, _, z, t_samples = sample3d(d1_temperature, (0, 4, 200), (0, 0, 1), (-2, 2, 200)) +plt.imshow(np.transpose(np.squeeze(t_samples)), extent=[0, 4, -2, 2]) +plt.colorbar() +plt.axis('equal') +plt.xlabel('r axis') +plt.ylabel('z axis') +plt.title("Ion temperature profile in r-z plane") + +plt.figure() +r, _, z, t_samples = sample3d(d1_temperature, (-4, 4, 400), (-4, 4, 400), (0, 0, 1)) +plt.imshow(np.transpose(np.squeeze(t_samples)), extent=[-4, 4, -4, 4]) +plt.colorbar() +plt.axis('equal') +plt.xlabel('x axis') +plt.ylabel('y axis') +plt.title("Ion temperature profile in x-y plane") + +plt.figure() +r, _, z, t_samples = sample3d(d0_density, (0, 4, 200), (0, 0, 1), (-2, 2, 200)) +plt.imshow(np.transpose(np.squeeze(t_samples)), extent=[0, 4, -2, 2]) +plt.colorbar() +plt.axis('equal') +plt.xlabel('r axis') +plt.ylabel('z axis') +plt.title("Neutral Density profile in r-z plane") + +plt.figure() +xrange = np.linspace(0, 4, 200) +yrange = np.linspace(-2, 2, 200) +d_alpha_rz_intensity = np.zeros((200, 200)) +direction = Vector3D(0, 1, 0) +for i, x in enumerate(xrange): + for j, y in enumerate(yrange): + emission = d_alpha_excit.emission(Point3D(x, 0.0, y), direction, Spectrum(650, 660, 1)) + d_alpha_rz_intensity[j, i] = emission.total() +plt.imshow(d_alpha_rz_intensity, extent=[0, 4, -2, 2], origin='lower') +plt.colorbar() +plt.xlabel('r axis') +plt.ylabel('z axis') +plt.title("D-alpha emission in R-Z") + + +camera = PinholeCamera((256, 256), pipelines=[PowerPipeline2D()], parent=world) +camera.transform = translate(2.5, -4.5, 0)*rotate_basis(Vector3D(0, 1, 0), Vector3D(0, 0, 1)) +camera.pixel_samples = 1 + +plt.ion() +camera.observe() +plt.ioff() +plt.show() diff --git a/docs/source/conf.py b/docs/source/conf.py index 60589037..d17ec793 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -39,6 +39,7 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.mathjax', + 'sphinx_tabs.tabs', ] # Add any paths that contain templates here, relative to this directory. @@ -142,6 +143,7 @@ html_context = {'css_files': [ '_static/theme_overrides.css', # override wide tables in RTD theme + '_static/tabs.css', ] } diff --git a/docs/source/demonstrations/plasmas/analytic_function_plasma.rst b/docs/source/demonstrations/plasmas/analytic_function_plasma.rst index e74ba874..627f0c66 100644 --- a/docs/source/demonstrations/plasmas/analytic_function_plasma.rst +++ b/docs/source/demonstrations/plasmas/analytic_function_plasma.rst @@ -10,11 +10,25 @@ shows how to use these functions in a plasma and visualises the results. Note that while it is possible to use pure python functions for development, they are typically ~100 times slower than their cython counterparts. Therefore, for use cases where speed is important -we recommend moving these functions to cython classes. For an example of a cython function, -see the `Gaussian `_ -cython class in the demos folder. +we recommend moving these functions to cython classes. An alternative solution which may not require +writing and compiling any additional cython code is to use Raysect's +`function framework `_ to build up +expressions which will be evaluated like Python functions. These will typically run slightly slower +than a hand-coded cython implementation but still significantly faster than a pure python +implementation. -.. literalinclude:: ../../../../demos/plasmas/analytic_plasma.py +Two examples are provided, one using a pure python implementation of analytic forms for neutral and +ion plasma species distributions, and one using objects from Raysect's function framework. + +.. tabs:: + + .. tab:: Pure python + + .. literalinclude:: ../../../../demos/plasmas/analytic_plasma.py + + .. tab:: Function framework + + .. literalinclude:: ../../../../demos/plasmas/analytic_plasma_function_framework.py .. figure:: analytic_plasma_slices.png :align: center From ac6863a5e548f6b06e4b60c5a66939e57e068472 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 6 Apr 2023 15:48:16 +0100 Subject: [PATCH 038/134] Add a docs extras requirement for building documentation When building the docs, run `pip install cherab[docs]` to get the dependencies required for successfully running `./dev/build_docs.sh`. --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index f0610a6c..5d9d7a8e 100644 --- a/setup.py +++ b/setup.py @@ -108,6 +108,10 @@ "matplotlib", "raysect==0.8.1", ], + extras_require={ + # Running ./dev/build_docs.sh runs setup.py, which requires cython. + "docs": ["cython", "sphinx", "sphinx-rtd-theme", "sphinx-tabs"], + }, packages=find_packages(), include_package_data=True, zip_safe=False, From 04d5d19eeed384a72dd85e6b6a8119baf97fd7a2 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 11 Apr 2023 23:54:35 +0300 Subject: [PATCH 039/134] Moved the parameters of StarkBroadenedLine and ParametrisedZeemanTriplet to the default atomic data repository. --- CHANGELOG.md | 5 +- .../core/atomic/data/lineshape/stark/d.json | 70 +++++++++++++ .../core/atomic/data/lineshape/stark/h.json | 70 +++++++++++++ .../core/atomic/data/lineshape/stark/t.json | 70 +++++++++++++ .../data/lineshape/zeeman/parametrised/b.json | 35 +++++++ .../lineshape/zeeman/parametrised/be.json | 25 +++++ .../data/lineshape/zeeman/parametrised/c.json | 45 +++++++++ .../data/lineshape/zeeman/parametrised/d.json | 15 +++ .../data/lineshape/zeeman/parametrised/h.json | 15 +++ .../lineshape/zeeman/parametrised/he.json | 25 +++++ .../lineshape/zeeman/parametrised/he3.json | 25 +++++ .../data/lineshape/zeeman/parametrised/n.json | 30 ++++++ .../lineshape/zeeman/parametrised/ne.json | 25 +++++ .../data/lineshape/zeeman/parametrised/o.json | 30 ++++++ cherab/core/atomic/interface.pxd | 5 +- cherab/core/atomic/interface.pyx | 52 +++++++++- cherab/core/model/beam/beam_emission.pyx | 10 +- cherab/core/model/lineshape/base.pxd | 2 + cherab/core/model/lineshape/base.pyx | 4 +- cherab/core/model/lineshape/beam/base.pxd | 2 + cherab/core/model/lineshape/beam/base.pyx | 4 +- cherab/core/model/lineshape/beam/mse.pyx | 7 +- cherab/core/model/lineshape/gaussian.pyx | 7 +- cherab/core/model/lineshape/multiplet.pyx | 7 +- cherab/core/model/lineshape/stark.pyx | 72 ++------------ cherab/core/model/lineshape/zeeman.pyx | 97 +++++-------------- .../core/model/plasma/impact_excitation.pyx | 10 +- cherab/core/model/plasma/recombination.pyx | 10 +- cherab/core/tests/test_lineshapes.py | 29 +++--- 29 files changed, 623 insertions(+), 180 deletions(-) create mode 100644 cherab/core/atomic/data/lineshape/stark/d.json create mode 100644 cherab/core/atomic/data/lineshape/stark/h.json create mode 100644 cherab/core/atomic/data/lineshape/stark/t.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/b.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/be.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/c.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/d.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/h.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/he.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/he3.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/n.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/ne.json create mode 100644 cherab/core/atomic/data/lineshape/zeeman/parametrised/o.json diff --git a/CHANGELOG.md b/CHANGELOG.md index de83a946..f486aabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,10 @@ Release 1.5.0 (TBD) API changes: * The line shape models are moved to a dedicated submodule. The user code should not be affected though. (#396) -* The method show_supported_transitions() of StarkBroadenedLine and ParametrisedZeemanTriplet is made a class method. +* The line shape models now have AtomicData as a required parameter. +* The method show_supported_transitions() of StarkBroadenedLine and ParametrisedZeemanTriplet is removed. +* The argument stark_model_coefficients of StarkBroadenedLine is now a tuple instead of a dict. +* The argument line_parameters of ParametrisedZeemanTriplet is now a tuple instead of a dict. New: * Support Raysect 0.8 diff --git a/cherab/core/atomic/data/lineshape/stark/d.json b/cherab/core/atomic/data/lineshape/stark/d.json new file mode 100644 index 00000000..c0092760 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/stark/d.json @@ -0,0 +1,70 @@ +{ + "0": { + "3 -> 2": [ + 3.71e-18, + 0.7665, + 0.064 + ], + "4 -> 2": [ + 8.425e-18, + 0.7803, + 0.050 + ], + "5 -> 2": [ + 1.31e-15, + 0.6796, + 0.030 + ], + "6 -> 2": [ + 3.954e-16, + 0.7149, + 0.028 + ], + "7 -> 2": [ + 6.258e-16, + 0.712, + 0.029 + ], + "8 -> 2": [ + 7.378e-16, + 0.7159, + 0.032 + ], + "9 -> 2": [ + 8.947e-16, + 0.7177, + 0.033 + ], + "4 -> 3": [ + 1.330e-16, + 0.7449, + 0.045 + ], + "5 -> 3": [ + 6.64e-16, + 0.7356, + 0.044 + ], + "6 -> 3": [ + 2.481e-15, + 0.7118, + 0.016 + ], + "7 -> 3": [ + 3.270e-15, + 0.7137, + 0.029 + ], + "8 -> 3": [ + 4.343e-15, + 0.7133, + 0.032 + ], + "9 -> 3": [ + 5.588e-15, + 0.7165, + 0.033 + ] + }, + "reference": "B. Lomanowski, et al. Inferring divertor plasma properties from hydrogen Balmer and Paschen series spectroscopy in JET-ILW. Nuclear Fusion 55.12 (2015) 123028" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/stark/h.json b/cherab/core/atomic/data/lineshape/stark/h.json new file mode 100644 index 00000000..c0092760 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/stark/h.json @@ -0,0 +1,70 @@ +{ + "0": { + "3 -> 2": [ + 3.71e-18, + 0.7665, + 0.064 + ], + "4 -> 2": [ + 8.425e-18, + 0.7803, + 0.050 + ], + "5 -> 2": [ + 1.31e-15, + 0.6796, + 0.030 + ], + "6 -> 2": [ + 3.954e-16, + 0.7149, + 0.028 + ], + "7 -> 2": [ + 6.258e-16, + 0.712, + 0.029 + ], + "8 -> 2": [ + 7.378e-16, + 0.7159, + 0.032 + ], + "9 -> 2": [ + 8.947e-16, + 0.7177, + 0.033 + ], + "4 -> 3": [ + 1.330e-16, + 0.7449, + 0.045 + ], + "5 -> 3": [ + 6.64e-16, + 0.7356, + 0.044 + ], + "6 -> 3": [ + 2.481e-15, + 0.7118, + 0.016 + ], + "7 -> 3": [ + 3.270e-15, + 0.7137, + 0.029 + ], + "8 -> 3": [ + 4.343e-15, + 0.7133, + 0.032 + ], + "9 -> 3": [ + 5.588e-15, + 0.7165, + 0.033 + ] + }, + "reference": "B. Lomanowski, et al. Inferring divertor plasma properties from hydrogen Balmer and Paschen series spectroscopy in JET-ILW. Nuclear Fusion 55.12 (2015) 123028" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/stark/t.json b/cherab/core/atomic/data/lineshape/stark/t.json new file mode 100644 index 00000000..c0092760 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/stark/t.json @@ -0,0 +1,70 @@ +{ + "0": { + "3 -> 2": [ + 3.71e-18, + 0.7665, + 0.064 + ], + "4 -> 2": [ + 8.425e-18, + 0.7803, + 0.050 + ], + "5 -> 2": [ + 1.31e-15, + 0.6796, + 0.030 + ], + "6 -> 2": [ + 3.954e-16, + 0.7149, + 0.028 + ], + "7 -> 2": [ + 6.258e-16, + 0.712, + 0.029 + ], + "8 -> 2": [ + 7.378e-16, + 0.7159, + 0.032 + ], + "9 -> 2": [ + 8.947e-16, + 0.7177, + 0.033 + ], + "4 -> 3": [ + 1.330e-16, + 0.7449, + 0.045 + ], + "5 -> 3": [ + 6.64e-16, + 0.7356, + 0.044 + ], + "6 -> 3": [ + 2.481e-15, + 0.7118, + 0.016 + ], + "7 -> 3": [ + 3.270e-15, + 0.7137, + 0.029 + ], + "8 -> 3": [ + 4.343e-15, + 0.7133, + 0.032 + ], + "9 -> 3": [ + 5.588e-15, + 0.7165, + 0.033 + ] + }, + "reference": "B. Lomanowski, et al. Inferring divertor plasma properties from hydrogen Balmer and Paschen series spectroscopy in JET-ILW. Nuclear Fusion 55.12 (2015) 123028" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/b.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/b.json new file mode 100644 index 00000000..b63c7c85 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/b.json @@ -0,0 +1,35 @@ +{ + "4": { + "6 -> 5": [ + 0.0083423, + 2.0519, + -0.2960 + ], + "7 -> 6": [ + 0.0228379, + 1.6546, + -0.2941 + ], + "8 -> 6": [ + 0.0084065, + 1.8041, + -0.3177 + ], + "8 -> 7": [ + 0.0541883, + 1.4128, + -0.2966 + ], + "9 -> 7": [ + 0.0190781, + 1.5440, + -0.3211 + ], + "10 -> 8": [ + 0.0391914, + 1.3569, + -0.3252 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/be.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/be.json new file mode 100644 index 00000000..a5e37caa --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/be.json @@ -0,0 +1,25 @@ +{ + "3": { + "5 -> 4": [ + 0.0060354, + 2.1245, + -0.3190 + ], + "6 -> 5": [ + 0.0202754, + 1.6538, + -0.3192 + ], + "7 -> 5": [ + 0.0078966, + 1.7017, + -0.3348 + ], + "8 -> 6": [ + 0.0205025, + 1.4581, + -0.3450 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/c.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/c.json new file mode 100644 index 00000000..8e6ad067 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/c.json @@ -0,0 +1,45 @@ +{ + "5": { + "6 -> 5": [ + 0.0040900, + 2.4271, + -0.2818 + ], + "7 -> 6": [ + 0.0110398, + 1.9785, + -0.2816 + ], + "8 -> 6": [ + 0.0040747, + 2.1776, + -0.3035 + ], + "8 -> 7": [ + 0.0261405, + 1.6689, + -0.2815 + ], + "9 -> 7": [ + 0.0092096, + 1.8495, + -0.3049 + ], + "10 -> 8": [ + 0.0189020, + 1.6191, + -0.3078 + ], + "11 -> 8": [ + 0.0110428, + 1.6600, + -0.3162 + ], + "10 -> 9": [ + 0.0359009, + 1.4464, + -0.3104 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/d.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/d.json new file mode 100644 index 00000000..3b7e8150 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/d.json @@ -0,0 +1,15 @@ +{ + "0": { + "3 -> 2": [ + 0.0402068, + 0.4384, + -0.5015 + ], + "4 -> 2": [ + 0.0220610, + 0.3702, + -0.5132 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/h.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/h.json new file mode 100644 index 00000000..13745495 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/h.json @@ -0,0 +1,15 @@ +{ + "0": { + "3 -> 2": [ + 0.0402267, + 0.3415, + -0.5247 + ], + "4 -> 2": [ + 0.0220724, + 0.2837, + -0.5346 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/he.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/he.json new file mode 100644 index 00000000..2ebd0625 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/he.json @@ -0,0 +1,25 @@ +{ + "1": { + "4 -> 3": [ + 0.0205206, + 1.6118, + -0.4838 + ], + "5 -> 3": [ + 0.0095879, + 1.4294, + -0.4975 + ], + "6 -> 4": [ + 0.0401955, + 1.0058, + -0.4918 + ], + "7 -> 4": [ + 0.0273521, + 0.9563, + -0.4981 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/he3.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/he3.json new file mode 100644 index 00000000..04729f74 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/he3.json @@ -0,0 +1,25 @@ +{ + "1": { + "4 -> 3": [ + 0.0205200, + 1.4418, + -0.4892 + ], + "5 -> 3": [ + 0.0095879, + 1.2576, + -0.5001 + ], + "6 -> 4": [ + 0.0401980, + 0.8976, + -0.4971 + ], + "7 -> 4": [ + 0.0273538, + 0.8529, + -0.5039 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/n.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/n.json new file mode 100644 index 00000000..d086f4a0 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/n.json @@ -0,0 +1,30 @@ +{ + "6": { + "7 -> 6": [ + 0.0060010, + 2.4789, + -0.2817 + ], + "8 -> 7": [ + 0.0141271, + 2.0249, + -0.2762 + ], + "9 -> 8": [ + 0.0300127, + 1.7415, + -0.2753 + ], + "10 -> 8": [ + 0.0102089, + 1.9464, + -0.2975 + ], + "11 -> 9": [ + 0.0193799, + 1.7133, + -0.2973 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/ne.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/ne.json new file mode 100644 index 00000000..07e2c41b --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/ne.json @@ -0,0 +1,25 @@ +{ + "9": { + "9 -> 8": [ + 0.0072488, + 2.8838, + -0.2758 + ], + "10 -> 9": [ + 0.0141002, + 2.4755, + -0.2718 + ], + "11 -> 9": [ + 0.0046673, + 2.8410, + -0.2917 + ], + "11 -> 10": [ + 0.0257292, + 2.1890, + -0.2715 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/data/lineshape/zeeman/parametrised/o.json b/cherab/core/atomic/data/lineshape/zeeman/parametrised/o.json new file mode 100644 index 00000000..3f3663d7 --- /dev/null +++ b/cherab/core/atomic/data/lineshape/zeeman/parametrised/o.json @@ -0,0 +1,30 @@ +{ + "7": { + "8 -> 7": [ + 0.0083081, + 2.4263, + -0.2747 + ], + "9 -> 8": [ + 0.0176049, + 2.0652, + -0.2721 + ], + "10 -> 8": [ + 0.0059933, + 2.3445, + -0.2944 + ], + "10 -> 9": [ + 0.0343805, + 1.8122, + -0.2718 + ], + "11 -> 9": [ + 0.0113640, + 2.0268, + -0.2911 + ] + }, + "reference": "A. Blom and C. Jupén. Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas. Plasma Phys. Control. Fusion 44 (2002) 1229-1241" +} \ No newline at end of file diff --git a/cherab/core/atomic/interface.pxd b/cherab/core/atomic/interface.pxd index 66d8faf8..2b671176 100644 --- a/cherab/core/atomic/interface.pxd +++ b/cherab/core/atomic/interface.pxd @@ -57,5 +57,8 @@ cdef class AtomicData: cpdef ZeemanStructure zeeman_structure(self, Line line, object b_field=*) - cpdef FreeFreeGauntFactor free_free_gaunt_factor(self) + cpdef tuple zeeman_triplet_parameters(self, Line line) + + cpdef tuple stark_model_coefficients(self, Line line) + cpdef FreeFreeGauntFactor free_free_gaunt_factor(self) diff --git a/cherab/core/atomic/interface.pyx b/cherab/core/atomic/interface.pyx index 286ebfb4..9e4e1113 100644 --- a/cherab/core/atomic/interface.pyx +++ b/cherab/core/atomic/interface.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2022 Euratom -# Copyright 2016-2022 United Kingdom Atomic Energy Authority -# Copyright 2016-2022 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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,6 +17,8 @@ # under the Licence. from .gaunt import MaxwellianFreeFreeGauntFactor +import json +from os import path cdef class AtomicData: @@ -95,6 +97,50 @@ 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 tuple zeeman_triplet_parameters(self, Line line): + """ + Returns Zeeman truplet parameters. See Table 1 in A. Blom and C. Jupén. + "Parametrisation of the Zeeman effect for hydrogen-like spectra in + high-temperature plasmas", Plasma Phys. Control. Fusion 44 (2002) `1229-1241 + `_. + """ + + symbol = line.element.symbol.lower() + upper, lower = line.transition + encoded_transition = '{} -> {}'.format(str(upper).lower(), str(lower).lower()) + + try: + with open(path.join(path.dirname(__file__), "data/lineshape/zeeman/parametrised/{}.json".format(symbol))) as f: + data = json.load(f) + coefficients = data[str(line.charge)][encoded_transition] + except (FileNotFoundError, KeyError): + raise RuntimeError('Requested Zeeman triplet parameters (element={}, charge={}, transition={})' + ' are not available.'.format(line.element.symbol, line.charge, line.transition)) + + return tuple(coefficients) + + cpdef tuple stark_model_coefficients(self, Line line): + """ + Returns Stark model coefficients. See Table 1 in B. Lomanowski, et al. + "Inferring divertor plasma properties from hydrogen Balmer + and Paschen series spectroscopy in JET-ILW." Nuclear Fusion 55.12 (2015) + `123028 `_. + """ + + symbol = line.element.symbol.lower() + upper, lower = line.transition + encoded_transition = '{} -> {}'.format(str(upper).lower(), str(lower).lower()) + + try: + with open(path.join(path.dirname(__file__), "data/lineshape/stark/{}.json".format(symbol))) as f: + data = json.load(f) + coefficients = data[str(line.charge)][encoded_transition] + except (FileNotFoundError, KeyError): + raise RuntimeError('Requested Stark model coefficients (element={}, charge={}, transition={})' + ' are not available.'.format(line.element.symbol, line.charge, line.transition)) + + return tuple(coefficients) + cpdef FreeFreeGauntFactor free_free_gaunt_factor(self): """ Returns the Maxwellian-averaged free-free Gaunt factor interpolated over the data diff --git a/cherab/core/model/beam/beam_emission.pyx b/cherab/core/model/beam/beam_emission.pyx index 67c41ac1..e00aaf5d 100644 --- a/cherab/core/model/beam/beam_emission.pyx +++ b/cherab/core/model/beam/beam_emission.pyx @@ -1,8 +1,8 @@ # cython: language_level=3 -# 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-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -212,8 +212,8 @@ cdef class BeamEmissionLine(BeamModel): self._rates_list.append((species, rate)) # instance line shape renderer - self._lineshape = BeamEmissionMultiplet(self._line, self._wavelength, self._beam, self._sigma_to_pi, - self._sigma1_to_sigma0, self._pi2_to_pi3, self._pi4_to_pi3) + self._lineshape = BeamEmissionMultiplet(self._line, self._wavelength, self._beam, self._atomic_data, + self._sigma_to_pi, self._sigma1_to_sigma0, self._pi2_to_pi3, self._pi4_to_pi3) def _change(self): diff --git a/cherab/core/model/lineshape/base.pxd b/cherab/core/model/lineshape/base.pxd index 4ec2d2e6..b6267d45 100644 --- a/cherab/core/model/lineshape/base.pxd +++ b/cherab/core/model/lineshape/base.pxd @@ -22,6 +22,7 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core.atomic cimport Line from cherab.core.species cimport Species from cherab.core.plasma cimport Plasma +from cherab.core.atomic cimport AtomicData from cherab.core.math.integrators cimport Integrator1D @@ -32,6 +33,7 @@ cdef class LineShapeModel: double wavelength Species target_species Plasma plasma + AtomicData atomic_data Integrator1D integrator cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum) diff --git a/cherab/core/model/lineshape/base.pyx b/cherab/core/model/lineshape/base.pyx index 7577508e..0b0cf7a2 100644 --- a/cherab/core/model/lineshape/base.pyx +++ b/cherab/core/model/lineshape/base.pyx @@ -27,16 +27,18 @@ 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 AtomicData atomic_data: The atomic data provider. :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, Integrator1D integrator=None): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, Integrator1D integrator=None): self.line = line self.wavelength = wavelength self.target_species = target_species self.plasma = plasma + self.atomic_data = atomic_data self.integrator = integrator cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): diff --git a/cherab/core/model/lineshape/beam/base.pxd b/cherab/core/model/lineshape/beam/base.pxd index b3da2323..e5633446 100644 --- a/cherab/core/model/lineshape/beam/base.pxd +++ b/cherab/core/model/lineshape/beam/base.pxd @@ -21,6 +21,7 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core.atomic cimport Line from cherab.core.beam cimport Beam +from cherab.core.atomic cimport AtomicData cdef class BeamLineShapeModel: @@ -30,6 +31,7 @@ cdef class BeamLineShapeModel: Line line double wavelength Beam beam + AtomicData atomic_data cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum) diff --git a/cherab/core/model/lineshape/beam/base.pyx b/cherab/core/model/lineshape/beam/base.pyx index 793e17d4..d333dd6e 100644 --- a/cherab/core/model/lineshape/beam/base.pyx +++ b/cherab/core/model/lineshape/beam/base.pyx @@ -26,13 +26,15 @@ cdef class BeamLineShapeModel: :param Line line: The emission line object for this line shape. :param float wavelength: The rest wavelength for this emission line. :param Beam beam: The beam class that is emitting. + :param AtomicData atomic_data: The atomic data provider. """ - def __init__(self, Line line, double wavelength, Beam beam): + def __init__(self, Line line, double wavelength, Beam beam, AtomicData atomic_data): self.line = line self.wavelength = wavelength self.beam = beam + self.atomic_data = atomic_data cpdef Spectrum add_line(self, double radiance, Point3D beam_point, Point3D plasma_point, Vector3D beam_direction, Vector3D observation_direction, Spectrum spectrum): diff --git a/cherab/core/model/lineshape/beam/mse.pyx b/cherab/core/model/lineshape/beam/mse.pyx index 3075cff2..9c64d435 100644 --- a/cherab/core/model/lineshape/beam/mse.pyx +++ b/cherab/core/model/lineshape/beam/mse.pyx @@ -23,6 +23,7 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core.plasma cimport Plasma from cherab.core.beam cimport Beam +from cherab.core.atomic cimport AtomicData from cherab.core.atomic cimport Line from cherab.core.math.function cimport autowrap_function1d, autowrap_function2d from cherab.core.utility.constants cimport ATOMIC_MASS, ELEMENTARY_CHARGE @@ -47,10 +48,10 @@ cdef class BeamEmissionMultiplet(BeamLineShapeModel): Produces Beam Emission Multiplet line shape, also known as the Motional Stark Effect spectrum. """ - def __init__(self, Line line, double wavelength, Beam beam, object sigma_to_pi, - object sigma1_to_sigma0, object pi2_to_pi3, object pi4_to_pi3): + def __init__(self, Line line, double wavelength, Beam beam, AtomicData atomic_data, + object sigma_to_pi, object sigma1_to_sigma0, object pi2_to_pi3, object pi4_to_pi3): - super().__init__(line, wavelength, beam) + super().__init__(line, wavelength, beam, atomic_data) self._sigma_to_pi = autowrap_function2d(sigma_to_pi) self._sigma1_to_sigma0 = autowrap_function1d(sigma1_to_sigma0) diff --git a/cherab/core/model/lineshape/gaussian.pyx b/cherab/core/model/lineshape/gaussian.pyx index 936d70df..ef854ea9 100644 --- a/cherab/core/model/lineshape/gaussian.pyx +++ b/cherab/core/model/lineshape/gaussian.pyx @@ -21,7 +21,7 @@ from libc.math cimport erf, M_SQRT2, floor, ceil from raysect.optical cimport Point3D, Vector3D -from cherab.core.atomic cimport Line +from cherab.core.atomic cimport Line, AtomicData from cherab.core.species cimport Species from cherab.core.plasma cimport Plasma from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening @@ -98,6 +98,7 @@ cdef class GaussianLine(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 AtomicData atomic_data: The atomic data provider. .. code-block:: pycon @@ -110,9 +111,9 @@ cdef class GaussianLine(LineShapeModel): >>> plasma.models.add(excit) """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data): - super().__init__(line, wavelength, target_species, plasma) + super().__init__(line, wavelength, target_species, plasma, atomic_data) @cython.boundscheck(False) @cython.wraparound(False) diff --git a/cherab/core/model/lineshape/multiplet.pyx b/cherab/core/model/lineshape/multiplet.pyx index e30093eb..6f9e39ab 100644 --- a/cherab/core/model/lineshape/multiplet.pyx +++ b/cherab/core/model/lineshape/multiplet.pyx @@ -22,7 +22,7 @@ import numpy as np from raysect.optical cimport Spectrum, Point3D, Vector3D -from cherab.core.atomic cimport Line +from cherab.core.atomic cimport Line, AtomicData from cherab.core.species cimport Species from cherab.core.plasma cimport Plasma from cherab.core.model.lineshape.doppler cimport doppler_shift, thermal_broadening @@ -53,6 +53,7 @@ cdef class MultipletLineShape(LineShapeModel): :param float wavelength: The rest wavelength of the base emission line. :param Species target_species: The target plasma species that is emitting. :param Plasma plasma: The emitting plasma object. + :param AtomicData atomic_data: The atomic data provider. :param multiplet: An Nx2 array that specifies the multiplet wavelengths and line ratios. .. code-block:: pycon @@ -69,10 +70,10 @@ cdef class MultipletLineShape(LineShapeModel): >>> plasma.models.add(excit) """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, object multiplet): - super().__init__(line, wavelength, target_species, plasma) + super().__init__(line, wavelength, target_species, plasma, atomic_data) multiplet = np.array(multiplet, dtype=np.float64) diff --git a/cherab/core/model/lineshape/stark.pyx b/cherab/core/model/lineshape/stark.pyx index e290ca98..9e333a5f 100644 --- a/cherab/core/model/lineshape/stark.pyx +++ b/cherab/core/model/lineshape/stark.pyx @@ -24,7 +24,7 @@ from libc.math cimport sqrt, floor, ceil, fabs, log, exp from raysect.core.math.function.float cimport Function1D from raysect.optical cimport Point3D, Vector3D -from cherab.core.atomic cimport Line +from cherab.core.atomic cimport Line, AtomicData from cherab.core.species cimport Species from cherab.core.plasma cimport Plasma from cherab.core.atomic.elements import hydrogen, deuterium, tritium @@ -202,16 +202,14 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): `c` = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04]. - Call `show_supported_transitions()` to see the list of supported transitions and - default model coefficients. - :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 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 AtomicData atomic_data: The atomic data provider. + :param tuple stark_model_coefficients: Stark model coefficients in the form (c_ij, a_ij, b_ij). + Default is None (will use + `atomic_data.stark_model_coefficients`). :param Integrator1D integrator: Integrator1D instance to integrate the line shape over the spectral bin. Default is `GaussianQuadrature()`. :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: @@ -220,56 +218,14 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): "no" - leave all components (default). """ - STARK_MODEL_COEFFICIENTS_DEFAULT = { - Line(hydrogen, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(hydrogen, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(hydrogen, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(hydrogen, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(hydrogen, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(hydrogen, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(hydrogen, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(hydrogen, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(hydrogen, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(hydrogen, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(hydrogen, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(hydrogen, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - Line(hydrogen, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), - Line(deuterium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(deuterium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(deuterium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(deuterium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(deuterium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(deuterium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(deuterium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(deuterium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(deuterium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(deuterium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(deuterium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(deuterium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - Line(deuterium, 0, (9, 3)): (5.588e-15, 0.7165, 0.033), - Line(tritium, 0, (3, 2)): (3.71e-18, 0.7665, 0.064), - Line(tritium, 0, (4, 2)): (8.425e-18, 0.7803, 0.050), - Line(tritium, 0, (5, 2)): (1.31e-15, 0.6796, 0.030), - Line(tritium, 0, (6, 2)): (3.954e-16, 0.7149, 0.028), - Line(tritium, 0, (7, 2)): (6.258e-16, 0.712, 0.029), - Line(tritium, 0, (8, 2)): (7.378e-16, 0.7159, 0.032), - Line(tritium, 0, (9, 2)): (8.947e-16, 0.7177, 0.033), - Line(tritium, 0, (4, 3)): (1.330e-16, 0.7449, 0.045), - Line(tritium, 0, (5, 3)): (6.64e-16, 0.7356, 0.044), - Line(tritium, 0, (6, 3)): (2.481e-15, 0.7118, 0.016), - Line(tritium, 0, (7, 3)): (3.270e-15, 0.7137, 0.029), - Line(tritium, 0, (8, 3)): (4.343e-15, 0.7133, 0.032), - 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, Integrator1D integrator=GaussianQuadrature(), polarisation='no'): - - stark_model_coefficients = stark_model_coefficients or self.STARK_MODEL_COEFFICIENTS_DEFAULT + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, + tuple stark_model_coefficients=None, Integrator1D integrator=GaussianQuadrature(), polarisation='no'): + + super().__init__(line, wavelength, target_species, plasma, atomic_data, polarisation, integrator) try: # Fitted Stark Constants - cij, aij, bij = stark_model_coefficients[line] + cij, aij, bij = stark_model_coefficients or self.atomic_data.stark_model_coefficients(line) if cij <= 0: raise ValueError('Coefficient c_ij must be positive.') if aij <= 0: @@ -288,14 +244,6 @@ cdef class StarkBroadenedLine(ZeemanLineShapeModel): self._weight_poly_coeff = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04] - super().__init__(line, wavelength, target_species, plasma, polarisation, integrator) - - @classmethod - def show_supported_transitions(cls): - """ Prints all supported transitions.""" - for line, coeff in cls.STARK_MODEL_COEFFICIENTS_DEFAULT.items(): - print('{}: c_ij={}, a_ij={}, b_ij={}'.format(line, coeff[0], coeff[1], coeff[2])) - @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) diff --git a/cherab/core/model/lineshape/zeeman.pyx b/cherab/core/model/lineshape/zeeman.pyx index 81bbb991..eeea9f7b 100644 --- a/cherab/core/model/lineshape/zeeman.pyx +++ b/cherab/core/model/lineshape/zeeman.pyx @@ -21,7 +21,7 @@ from libc.math cimport sqrt from raysect.optical cimport Spectrum, Point3D, Vector3D -from cherab.core.atomic cimport Line +from cherab.core.atomic cimport Line, AtomicData from cherab.core.species cimport Species from cherab.core.plasma cimport Plasma from cherab.core.atomic.elements import hydrogen, deuterium, tritium, helium, helium3, beryllium, boron, carbon, nitrogen, oxygen, neon @@ -51,6 +51,7 @@ cdef class ZeemanLineShapeModel(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 AtomicData atomic_data: The atomic data provider. :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: "pi" - leave only :math:`\pi`-polarised components, "sigma" - leave only :math:`\sigma`-polarised components, @@ -59,9 +60,9 @@ cdef class ZeemanLineShapeModel(LineShapeModel): over the spectral bin. Default is None. """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, polarisation, - Integrator1D integrator=None): - super().__init__(line, wavelength, target_species, plasma, integrator) + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, + polarisation='no', Integrator1D integrator=None): + super().__init__(line, wavelength, target_species, plasma, atomic_data, integrator) self.polarisation = polarisation @@ -94,15 +95,16 @@ cdef class ZeemanTriplet(ZeemanLineShapeModel): :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 AtomicData atomic_data: The atomic data provider. :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: "pi" - leave central component, "sigma" - leave side components, "no" - all components (default). """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, polarisation='no'): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, polarisation='no'): - super().__init__(line, wavelength, target_species, plasma, polarisation) + super().__init__(line, wavelength, target_species, plasma, atomic_data, polarisation) @cython.boundscheck(False) @cython.wraparound(False) @@ -174,9 +176,6 @@ cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): :math:`\frac{W_{Zeeman}}{W_{Doppler}} = \beta T^{\gamma}`, where `T` is the species temperature in eV. - Call `show_supported_transitions()` to see the list of supported transitions and - default parameters of the model. - For details see A. Blom and C. Jupén, Parametrisation of the Zeeman effect for hydrogen-like spectra in high-temperature plasmas, Plasma Phys. Control. Fusion 44 (2002) `1229-1241 @@ -186,70 +185,22 @@ cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): :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 dict line_parameters: Alternative parameters of the model in the form - {line_i: (alpha_i, beta_i, gamma_i), ...}. - If None, the default model parameters will be used. + :param AtomicData atomic_data: The atomic data provider. + :param tuple line_parameters: Parameters of the model in the form (alpha, beta, gamma). + Default is None (will use `atomic_data.zeeman_triplet_parameters`). :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: "pi" - leave central component, "sigma" - leave side components, "no" - all components (default). """ - LINE_PARAMETERS_DEFAULT = { # alpha, beta, gamma parameters for selected lines - Line(hydrogen, 0, (3, 2)): (0.0402267, 0.3415, -0.5247), - Line(hydrogen, 0, (4, 2)): (0.0220724, 0.2837, -0.5346), - Line(deuterium, 0, (3, 2)): (0.0402068, 0.4384, -0.5015), - Line(deuterium, 0, (4, 2)): (0.0220610, 0.3702, -0.5132), - Line(helium3, 1, (4, 3)): (0.0205200, 1.4418, -0.4892), - Line(helium3, 1, (5, 3)): (0.0095879, 1.2576, -0.5001), - Line(helium3, 1, (6, 4)): (0.0401980, 0.8976, -0.4971), - Line(helium3, 1, (7, 4)): (0.0273538, 0.8529, -0.5039), - Line(helium, 1, (4, 3)): (0.0205206, 1.6118, -0.4838), - Line(helium, 1, (5, 3)): (0.0095879, 1.4294, -0.4975), - Line(helium, 1, (6, 4)): (0.0401955, 1.0058, -0.4918), - Line(helium, 1, (7, 4)): (0.0273521, 0.9563, -0.4981), - Line(beryllium, 3, (5, 4)): (0.0060354, 2.1245, -0.3190), - Line(beryllium, 3, (6, 5)): (0.0202754, 1.6538, -0.3192), - Line(beryllium, 3, (7, 5)): (0.0078966, 1.7017, -0.3348), - Line(beryllium, 3, (8, 6)): (0.0205025, 1.4581, -0.3450), - Line(boron, 4, (6, 5)): (0.0083423, 2.0519, -0.2960), - Line(boron, 4, (7, 6)): (0.0228379, 1.6546, -0.2941), - Line(boron, 4, (8, 6)): (0.0084065, 1.8041, -0.3177), - Line(boron, 4, (8, 7)): (0.0541883, 1.4128, -0.2966), - Line(boron, 4, (9, 7)): (0.0190781, 1.5440, -0.3211), - Line(boron, 4, (10, 8)): (0.0391914, 1.3569, -0.3252), - Line(carbon, 5, (6, 5)): (0.0040900, 2.4271, -0.2818), - Line(carbon, 5, (7, 6)): (0.0110398, 1.9785, -0.2816), - Line(carbon, 5, (8, 6)): (0.0040747, 2.1776, -0.3035), - Line(carbon, 5, (8, 7)): (0.0261405, 1.6689, -0.2815), - Line(carbon, 5, (9, 7)): (0.0092096, 1.8495, -0.3049), - Line(carbon, 5, (10, 8)): (0.0189020, 1.6191, -0.3078), - Line(carbon, 5, (11, 8)): (0.0110428, 1.6600, -0.3162), - Line(carbon, 5, (10, 9)): (0.0359009, 1.4464, -0.3104), - Line(nitrogen, 6, (7, 6)): (0.0060010, 2.4789, -0.2817), - Line(nitrogen, 6, (8, 7)): (0.0141271, 2.0249, -0.2762), - Line(nitrogen, 6, (9, 8)): (0.0300127, 1.7415, -0.2753), - Line(nitrogen, 6, (10, 8)): (0.0102089, 1.9464, -0.2975), - Line(nitrogen, 6, (11, 9)): (0.0193799, 1.7133, -0.2973), - Line(oxygen, 7, (8, 7)): (0.0083081, 2.4263, -0.2747), - Line(oxygen, 7, (9, 8)): (0.0176049, 2.0652, -0.2721), - Line(oxygen, 7, (10, 8)): (0.0059933, 2.3445, -0.2944), - Line(oxygen, 7, (10, 9)): (0.0343805, 1.8122, -0.2718), - Line(oxygen, 7, (11, 9)): (0.0113640, 2.0268, -0.2911), - Line(neon, 9, (9, 8)): (0.0072488, 2.8838, -0.2758), - Line(neon, 9, (10, 9)): (0.0141002, 2.4755, -0.2718), - Line(neon, 9, (11, 9)): (0.0046673, 2.8410, -0.2917), - Line(neon, 9, (11, 10)): (0.0257292, 2.1890, -0.2715) - } - - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, dict line_parameters=None, polarisation='no'): - - super().__init__(line, wavelength, target_species, plasma, polarisation) - - line_parameters = line_parameters or self.LINE_PARAMETERS_DEFAULT + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, + tuple line_parameters=None, polarisation='no'): + + super().__init__(line, wavelength, target_species, plasma, atomic_data, polarisation) try: - alpha, beta, gamma = line_parameters[self.line] + alpha, beta, gamma = line_parameters or self.atomic_data.zeeman_triplet_parameters(line) if alpha <= 0: raise ValueError('Parameter alpha must be positive.') if beta < 0: @@ -261,12 +212,6 @@ cdef class ParametrisedZeemanTriplet(ZeemanLineShapeModel): except KeyError: raise ValueError('Data for {} is not available.'.format(self.line)) - @classmethod - def show_supported_transitions(cls): - """ Prints all supported transitions.""" - for line, param in cls.LINE_PARAMETERS_DEFAULT.items(): - print('{}: alpha={}, beta={}, gamma={}'.format(line, param[0], param[1], param[2])) - @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) @@ -337,9 +282,11 @@ cdef class ZeemanMultiplet(ZeemanLineShapeModel): :param float wavelength: The rest wavelength of the base emission line. :param Species target_species: The target plasma species that is emitting. :param Plasma plasma: The emitting plasma object. + :param AtomicData atomic_data: The atomic data provider. :param zeeman_structure: A ``ZeemanStructure`` object that provides wavelengths and ratios of :math:`\pi`-/:math:`\sigma^{+}`-/:math:`\sigma^{-}`-polarised components for any given magnetic field strength. + Default is None (will use atomic_data.zeeman_structure). :param str polarisation: Leaves only :math:`\pi`-/:math:`\sigma`-polarised components: "pi" - leave only :math:`\pi`-polarised components, "sigma" - leave only :math:`\sigma`-polarised components, @@ -347,12 +294,12 @@ cdef class ZeemanMultiplet(ZeemanLineShapeModel): """ - def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, - ZeemanStructure zeeman_structure, polarisation='no'): + def __init__(self, Line line, double wavelength, Species target_species, Plasma plasma, AtomicData atomic_data, + ZeemanStructure zeeman_structure=None, polarisation='no'): - super().__init__(line, wavelength, target_species, plasma, polarisation) + super().__init__(line, wavelength, target_species, plasma, atomic_data, polarisation) - self._zeeman_structure = zeeman_structure + self._zeeman_structure = zeeman_structure or self.atomic_data.zeeman_structure(line) @cython.boundscheck(False) @cython.wraparound(False) diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 2aa9691c..e0305889 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -1,6 +1,8 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -100,7 +102,7 @@ cdef class ExcitationLine(PlasmaModel): # instance line shape renderer self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, - *self._lineshape_args, **self._lineshape_kwargs) + self._atomic_data, *self._lineshape_args, **self._lineshape_kwargs) def _change(self): diff --git a/cherab/core/model/plasma/recombination.pyx b/cherab/core/model/plasma/recombination.pyx index 6edac138..7193d5e8 100644 --- a/cherab/core/model/plasma/recombination.pyx +++ b/cherab/core/model/plasma/recombination.pyx @@ -1,6 +1,8 @@ -# Copyright 2016-2018 Euratom -# Copyright 2016-2018 United Kingdom Atomic Energy Authority -# Copyright 2016-2018 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# cython: language_level=3 + +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -103,7 +105,7 @@ cdef class RecombinationLine(PlasmaModel): # instance line shape renderer self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, - *self._lineshape_args, **self._lineshape_kwargs) + self._atomic_data, *self._lineshape_args, **self._lineshape_kwargs) def _change(self): diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index 0f7ab7ef..6f300e59 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.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-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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"); @@ -26,7 +26,7 @@ from raysect.core.math.function.float import Arg1D, Constant1D from raysect.optical import Spectrum -from cherab.core import Beam, Line +from cherab.core import Beam, Line, AtomicData from cherab.core.math.integrators import GaussianQuadrature from cherab.core.atomic import deuterium, nitrogen, ZeemanStructure from cherab.tools.plasmas.slab import build_constant_slab_plasma @@ -47,6 +47,7 @@ class TestLineShapes(unittest.TestCase): (nitrogen, 1, 1.e17, 10., Vector3D(1.e4, 5.e4, 0))] plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=20., plasma_species=plasma_species, b_field=Vector3D(0, 5., 0)) + atomic_data = AtomicData() beam = Beam() beam.plasma = plasma beam.energy = 60000 @@ -58,7 +59,7 @@ def test_gaussian_line(self): line = Line(deuterium, 0, (3, 2)) # D-alpha line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma, self.atomic_data) # spectrum parameters min_wavelength = wavelength - 0.5 @@ -93,7 +94,7 @@ def test_multiplet_line_shape(self): target_species = self.plasma.composition.get(line.element, line.charge) multiplet = [[403.509, 404.132, 404.354, 404.479, 405.692], [0.205, 0.562, 0.175, 0.029, 0.029]] wavelength = 404.21 - multiplet_line = MultipletLineShape(line, wavelength, target_species, self.plasma, multiplet) + multiplet_line = MultipletLineShape(line, wavelength, target_species, self.plasma, self.atomic_data, multiplet) # spectrum parameters min_wavelength = min(multiplet[0]) - 0.5 @@ -129,7 +130,7 @@ def test_zeeman_triplet(self): line = Line(deuterium, 0, (3, 2)) # D-alpha line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - triplet = ZeemanTriplet(line, wavelength, target_species, self.plasma) + triplet = ZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) # spectrum parameters min_wavelength = wavelength - 0.5 @@ -180,7 +181,7 @@ def test_parametrised_zeeman_triplet(self): line = Line(deuterium, 0, (3, 2)) # D-alpha line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - triplet = ParametrisedZeemanTriplet(line, wavelength, target_species, self.plasma) + triplet = ParametrisedZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) # spectrum parameters min_wavelength = wavelength - 0.5 @@ -198,7 +199,7 @@ def test_parametrised_zeeman_triplet(self): spectrum[pol] = triplet.add_line(radiance, point, direction, spectrum[pol]) # validating - alpha, beta, gamma = triplet.LINE_PARAMETERS_DEFAULT[line] + alpha, beta, gamma = self.atomic_data.zeeman_triplet_parameters(line) temperature = target_species.distribution.effective_temperature(point.x, point.y, point.z) velocity = target_species.distribution.bulk_velocity(point.x, point.y, point.z) sigma = np.sqrt(temperature * ELEMENTARY_CHARGE / (line.element.atomic_weight * ATOMIC_MASS)) * wavelength / SPEED_OF_LIGHT @@ -239,7 +240,7 @@ def test_zeeman_multiplet(self): sigma_minus_components = [(HC_EV_NM / (photon_energy + BOHR_MAGNETON * Arg1D()), Constant1D(0.5))] zeeman_structure = ZeemanStructure(pi_components, sigma_plus_components, sigma_minus_components) - multiplet = ZeemanMultiplet(line, wavelength, target_species, self.plasma, zeeman_structure) + multiplet = ZeemanMultiplet(line, wavelength, target_species, self.plasma, self.atomic_data, zeeman_structure) # spectrum parameters min_wavelength = wavelength - 0.5 @@ -291,7 +292,7 @@ def test_stark_broadened_line(self): target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 integrator = GaussianQuadrature(relative_tolerance=1.e-5) - stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma, integrator=integrator) + stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma, self.atomic_data, integrator=integrator) # spectrum parameters min_wavelength = wavelength - 0.2 @@ -323,7 +324,7 @@ def test_stark_broadened_line(self): fwhm_gauss = 2 * np.sqrt(2 * np.log(2)) * sigma # Lorentzian parameters - cij, aij, bij = stark_line.STARK_MODEL_COEFFICIENTS_DEFAULT[line] + cij, aij, bij = self.atomic_data.stark_model_coefficients(line) ne = self.plasma.electron_distribution.density(point.x, point.y, point.z) te = self.plasma.electron_distribution.effective_temperature(point.x, point.y, point.z) fwhm_lorentz = cij * ne**aij / (te**bij) @@ -386,8 +387,8 @@ def test_beam_emission_multiplet(self): sigma1_to_sigma0 = 0.7060001671878492 pi2_to_pi3 = 0.3140003593919741 pi4_to_pi3 = 0.7279994935840365 - mse_line = BeamEmissionMultiplet(line, wavelength, self.beam, sigma_to_pi, - sigma1_to_sigma0, pi2_to_pi3, pi4_to_pi3) + mse_line = BeamEmissionMultiplet(line, wavelength, self.beam, self.atomic_data, + sigma_to_pi, sigma1_to_sigma0, pi2_to_pi3, pi4_to_pi3) # spectrum parameters min_wavelength = wavelength - 3 From af0581e5d90079a080359ea4e7c61a8e8cafeeae Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 27 Apr 2023 17:43:15 +0300 Subject: [PATCH 040/134] Added tests for BeamCXLine model. --- cherab/core/tests/test_beamcxline.py | 167 +++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 cherab/core/tests/test_beamcxline.py diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py new file mode 100644 index 00000000..d3aa2976 --- /dev/null +++ b/cherab/core/tests/test_beamcxline.py @@ -0,0 +1,167 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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 scipy.special import erf + +from raysect.core import Point3D, Vector3D, translate +from raysect.optical import World, Spectrum, Ray + +from cherab.core import Beam +from cherab.core.atomic import Line, AtomicData, BeamCXPEC, BeamStoppingRate +from cherab.core.math.integrators import GaussianQuadrature +from cherab.core.atomic import deuterium, nitrogen +from cherab.tools.plasmas.slab import build_constant_slab_plasma +from cherab.core.model import SingleRayAttenuator, BeamCXLine, GaussianLine, ZeemanTriplet + + +class ConstantBeamCXPEC(BeamCXPEC): + """ + Constant beam CX PEC for test purpose. + """ + + def __init__(self, donor_metastable, value): + self.donor_metastable = donor_metastable + self.value = value + + def evaluate(self, energy, temperature, density, z_effective, b_field): + + return self.value + + +class ConstantBeamStoppingRate(BeamStoppingRate): + """ + Constant beam CX PEC for test purpose. + """ + + def __init__(self, donor_metastable, value): + self.donor_metastable = donor_metastable + self.value = value + + def evaluate(self, energy, density, temperature): + + return self.value + + +class TestAtomicData(AtomicData): + """Fake atomic data for test purpose.""" + + def beam_cx_pec(self, donor_ion, receiver_ion, receiver_charge, transition): + + return [ConstantBeamCXPEC(1, 3.4e-34)] + + def beam_stopping_rate(self, beam_ion, plasma_ion, charge): + + return ConstantBeamStoppingRate(1, 0) + + def wavelength(self, ion, charge, transition): + + return 656.104 + + +class TestBeamCXLine(unittest.TestCase): + + world = World() + + atomic_data = TestAtomicData() + + plasma_species = [(deuterium, 1, 1.e19, 200., Vector3D(0, 0, 0))] + plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=200., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + plasma.atomic_data = atomic_data + plasma.parent = world + + beam = Beam(transform=translate(0.5, 0, 0)) + beam.atomic_data = atomic_data + beam.plasma = plasma + beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) + beam.energy = 50000 + beam.power = 1e6 + beam.temperature = 10 + beam.element = deuterium + beam.parent = world + + def test_default_lineshape(self): + # setting up the model + line = Line(deuterium, 0, (3, 2)) # D-alpha line + self.beam.models = [BeamCXLine(line)] + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=655.1, max_wavelength=657.1, bins=512) + cx_spectrum = ray.trace(self.world) + + # validating + dx = self.beam.integrator.step + rate = self.atomic_data.beam_cx_pec(deuterium, deuterium, 1, (3, 2))[0].value + ni = self.plasma.ion_density(0.5, 0, 0) # constant slab + nd_beam = 0 # beam density + for i in range(-int(0.5 / dx), int(0.5 / dx)): + x = dx * (i + 0.5) + nd_beam += self.beam.density(x, 0, 0) # in internal beam coordinates + radiance = 0.25 * rate * ni * nd_beam * dx / np.pi + + target_species = self.plasma.composition.get(line.element, line.charge + 1) + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(cx_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='BeamCXLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + def test_custom_lineshape(self): + # setting up the model + line = Line(deuterium, 0, (3, 2)) # D-alpha line + self.beam.models = [BeamCXLine(line, lineshape=ZeemanTriplet)] + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=655.1, max_wavelength=657.1, bins=512) + cx_spectrum = ray.trace(self.world) + + # validating + dx = self.beam.integrator.step + rate = self.atomic_data.beam_cx_pec(deuterium, deuterium, 1, (3, 2))[0].value + ni = self.plasma.ion_density(0.5, 0, 0) # constant slab + nd_beam = 0 # beam density + for i in range(-int(0.5 / dx), int(0.5 / dx)): + x = dx * (i + 0.5) + nd_beam += self.beam.density(x, 0, 0) # in internal beam coordinates + radiance = 0.25 * rate * ni * nd_beam * dx / np.pi + + target_species = self.plasma.composition.get(line.element, line.charge + 1) + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(cx_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='BeamCXLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + +if __name__ == '__main__': + unittest.main() From e4305474e8310d199f5e41f9ac592d2a7a56fd1f Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 1 May 2023 16:41:38 +0300 Subject: [PATCH 041/134] The numerical constant in the bremsstrahlung model is expressed in terms of physical constants as suggested by @skuba31. --- cherab/core/model/plasma/bremsstrahlung.pyx | 35 ++++++++++++++------- cherab/core/utility/constants.pxd | 1 + cherab/core/utility/constants.pyx | 1 + 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index fa761eae..16b10aef 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -23,14 +23,17 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core cimport Plasma, AtomicData from cherab.core.math.integrators cimport GaussianQuadrature 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 +from cherab.core.utility.constants cimport RECIP_4_PI, ELEMENTARY_CHARGE, SPEED_OF_LIGHT, PLANCK_CONSTANT, ELECTRON_REST_MASS, VACUUM_PERMITTIVITY +from libc.math cimport sqrt, log, exp, M_PI cimport cython -cdef double PH_TO_J_FACTOR = PLANCK_CONSTANT * SPEED_OF_LIGHT * 1e9 +cdef double EXP_FACTOR = PLANCK_CONSTANT * SPEED_OF_LIGHT * 1e9 / ELEMENTARY_CHARGE -cdef double EXP_FACTOR = PH_TO_J_FACTOR / ELEMENTARY_CHARGE +cdef double BREMS_CONST = (ELEMENTARY_CHARGE**2 * RECIP_4_PI / VACUUM_PERMITTIVITY)**3 +BREMS_CONST *= 32 * M_PI**2 / (3 * sqrt(3) * ELECTRON_REST_MASS**2 * SPEED_OF_LIGHT**3) +BREMS_CONST *= sqrt(2 * ELECTRON_REST_MASS / (M_PI * ELEMENTARY_CHARGE)) +BREMS_CONST *= SPEED_OF_LIGHT * 1e9 * RECIP_4_PI cdef class BremsFunction(Function1D): @@ -80,9 +83,9 @@ cdef class BremsFunction(Function1D): if ni > 0: ni_gff_z2 += ni * self.gaunt_factor.evaluate(z, self.te, wvl) * z * z - # bremsstrahlung equation W/m^3/str/nm - pre_factor = 0.95e-19 * RECIP_4_PI * ni_gff_z2 * self.ne / (sqrt(self.te) * wvl) - radiance = pre_factor * exp(- EXP_FACTOR / (self.te * wvl)) * PH_TO_J_FACTOR + # bremsstrahlung equation W/m^3/str + pre_factor = BREMS_CONST * ni_gff_z2 * self.ne / (sqrt(self.te) * wvl) + radiance = pre_factor * exp(- EXP_FACTOR / (self.te * wvl)) # convert to W/m^3/str/nm return radiance / wvl @@ -93,13 +96,21 @@ cdef class Bremsstrahlung(PlasmaModel): """ Emitter that calculates bremsstrahlung emission from a plasma object. - The bremmstrahlung formula implemented is equation 2 from M. Beurskens, - et. al., 'ITER LIDAR performance analysis', Rev. Sci. Instrum. 79, 10E727 (2008), + The bremmstrahlung formula implemented is equation 5.3.40 + from I. H. Hutchinson, 'Principles of Plasma Diagnostics', second edition, + Cambridge University Press, 2002, ISBN: 9780511613630, + https://doi.org/10.1017/CBO9780511613630 .. 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}}, - - where the emission :math:`\\epsilon (\\lambda)` is in units of radiance (ph/s/sr/m^3/nm). + \\epsilon_{\\mathrm{ff}}(\\lambda) = \\left( \\frac{e^2}{4 \\pi \\varepsilon_0} \\right)^3 + \\frac{32 \\pi^2}{3 \\sqrt{3} m_\\mathrm{e}^2 c^3} + \\sqrt{\\frac{2 m_\\mathrm{e}^3}{\\pi e T_\\mathrm{e}}} + \\frac{10^{9} c}{4 \\pi \\lambda^2} + n_\\mathrm{e} \\sum_i \\left( n_\\mathrm{i} g_\\mathrm{ff} (Z_\\mathrm{i}, T_\\mathrm{e}, \\lambda) Z_\\mathrm{i}^2 \\right) + \\mathrm{e}^{-\\frac{10^9 hc}{e T_\\mathrm{e}}}\\,, + + where the emission :math:`\\epsilon_{\\mathrm{ff}}(\\lambda)` is in units of radiance (W/sr/m^3/nm), + :math:`T_\\mathrm{e}` is in eV and :math:`\\lambda` is in 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. diff --git a/cherab/core/utility/constants.pxd b/cherab/core/utility/constants.pxd index 9cce304d..4c59b4a8 100644 --- a/cherab/core/utility/constants.pxd +++ b/cherab/core/utility/constants.pxd @@ -30,3 +30,4 @@ cdef: double ELECTRON_CLASSICAL_RADIUS double ELECTRON_REST_MASS double RYDBERG_CONSTANT_EV + double VACUUM_PERMITTIVITY diff --git a/cherab/core/utility/constants.pyx b/cherab/core/utility/constants.pyx index 03e70617..d36b99f9 100644 --- a/cherab/core/utility/constants.pyx +++ b/cherab/core/utility/constants.pyx @@ -32,3 +32,4 @@ cdef: double ELECTRON_CLASSICAL_RADIUS = 2.8179403262e-15 double ELECTRON_REST_MASS = 9.1093837015e-31 double RYDBERG_CONSTANT_EV = 13.605693122994 + double VACUUM_PERMITTIVITY = 8.8541878128e-12 From 5f61aaec157a5f88b4b41c78636e7242527b45dc Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 1 May 2023 18:49:20 +0300 Subject: [PATCH 042/134] Updated bremsstrahlung model docstring. --- cherab/core/model/plasma/bremsstrahlung.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index 16b10aef..cb8a88a6 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -112,6 +112,8 @@ cdef class Bremsstrahlung(PlasmaModel): where the emission :math:`\\epsilon_{\\mathrm{ff}}(\\lambda)` is in units of radiance (W/sr/m^3/nm), :math:`T_\\mathrm{e}` is in eV and :math:`\\lambda` is in nm. + :math:`g_\\mathrm{ff} (Z_\\mathrm{i}, T_\\mathrm{e}, \\lambda)` is the free-free Gaunt factor. + :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 From 807fc555f00520257c8dfa58f200169390fe01c0 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 1 May 2023 18:56:30 +0300 Subject: [PATCH 043/134] Removed unused imports in test_beamcxline.py --- cherab/core/tests/test_beamcxline.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py index d3aa2976..6de8ad58 100644 --- a/cherab/core/tests/test_beamcxline.py +++ b/cherab/core/tests/test_beamcxline.py @@ -19,15 +19,13 @@ import unittest import numpy as np -from scipy.special import erf from raysect.core import Point3D, Vector3D, translate from raysect.optical import World, Spectrum, Ray from cherab.core import Beam from cherab.core.atomic import Line, AtomicData, BeamCXPEC, BeamStoppingRate -from cherab.core.math.integrators import GaussianQuadrature -from cherab.core.atomic import deuterium, nitrogen +from cherab.core.atomic import deuterium from cherab.tools.plasmas.slab import build_constant_slab_plasma from cherab.core.model import SingleRayAttenuator, BeamCXLine, GaussianLine, ZeemanTriplet From 00c70f30303eff0a3d42f05dcd6a36b3d45c4509 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 2 May 2023 01:52:47 +0300 Subject: [PATCH 044/134] Added a test for Bremsstrahlung model. --- cherab/core/tests/test_bremsstrahlung.py | 94 ++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 cherab/core/tests/test_bremsstrahlung.py diff --git a/cherab/core/tests/test_bremsstrahlung.py b/cherab/core/tests/test_bremsstrahlung.py new file mode 100644 index 00000000..5373776b --- /dev/null +++ b/cherab/core/tests/test_bremsstrahlung.py @@ -0,0 +1,94 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.core import Point3D, Vector3D +from raysect.optical import World, Ray + +from cherab.core.atomic import AtomicData, MaxwellianFreeFreeGauntFactor +from cherab.core.math.integrators import GaussianQuadrature +from cherab.core.atomic import deuterium, nitrogen +from cherab.tools.plasmas.slab import build_constant_slab_plasma +from cherab.core.model import Bremsstrahlung + +import scipy.constants as const + + +class TestBremsstrahlung(unittest.TestCase): + + world = World() + + plasma_species = [(deuterium, 1, 1.e19, 2000., Vector3D(0, 0, 0)), (nitrogen, 7, 1.e18, 2000., Vector3D(0, 0, 0))] + plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=2000., + plasma_species=plasma_species) + plasma.parent = world + plasma.atomic_data = AtomicData() + + def test_bremsstrahlung_model(self): + # setting up the model + gaunt_factor = MaxwellianFreeFreeGauntFactor() + bremsstrahlung = Bremsstrahlung(gaunt_factor=gaunt_factor) + self.plasma.models = [bremsstrahlung] + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=400., max_wavelength=800., bins=128) + brems_spectrum = ray.trace(self.world) + + # validating + brems_const = (const.e**2 * 0.25 / np.pi / const.epsilon_0)**3 + brems_const *= 32 * np.pi**2 / (3 * np.sqrt(3) * const.m_e**2 * const.c**3) + brems_const *= np.sqrt(2 * const.m_e / (np.pi * const.e)) + brems_const *= const.c * 1e9 * 0.25 / np.pi + exp_factor = const.h * const.c * 1.e9 / const. e + + ne = self.plasma.electron_distribution.density(0.5, 0, 0) + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + + def brems_func(wvl): + ni_gff_z2 = 0 + for species in self.plasma.composition: + z = species.charge + ni = self.plasma.composition[(species.element, species.charge)].distribution.density(0.5, 0, 0) + ni_gff_z2 += ni * gaunt_factor(z, te, wvl) * z * z + + return brems_const * ni_gff_z2 * ne / (np.sqrt(te) * wvl * wvl) * np.exp(- exp_factor / (te * wvl)) + + integrator = GaussianQuadrature(brems_func) + + test_samples = np.zeros(brems_spectrum.bins) + delta_wavelength = (brems_spectrum.max_wavelength - brems_spectrum.min_wavelength) / brems_spectrum.bins + lower_wavelength = brems_spectrum.min_wavelength + for i in range(brems_spectrum.bins): + upper_wavelength = brems_spectrum.min_wavelength + delta_wavelength * (i + 1) + bin_integral = integrator(lower_wavelength, upper_wavelength) + test_samples[i] = bin_integral / delta_wavelength + lower_wavelength = upper_wavelength + + for i in range(brems_spectrum.bins): + self.assertAlmostEqual(brems_spectrum.samples[i], test_samples[i], delta=1e-10, + msg='BeamCXLine model gives a wrong value at {} nm.'.format(brems_spectrum.wavelengths[i])) + + +if __name__ == '__main__': + unittest.main() From d748239380890a6bbad28093512cdd3740c0ba0b Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 3 May 2023 00:57:36 +0300 Subject: [PATCH 045/134] Added 'units' attribute to RayTransferPipelineXD. Added tests for RayTransferPipelineXD. --- CHANGELOG.md | 3 + cherab/tools/raytransfer/pipelines.py | 149 ++++++++++++----- cherab/tools/tests/test_raytransfer.py | 158 +++++++++++++++++- .../geometry_matrix_with_raytransfer.py | 2 +- demos/ray_transfer/1_ray_transfer_box.py | 7 +- 5 files changed, 274 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9856d282..7f334f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Project Changelog Release 1.5.0 (TBD) ------------------- +Bug fixes: +* Fix improper multiplication by sensitivity factor in RayTransferPixelProcessor resulting in wrong units (m^3 sr instead of m) of ray transfer matrices. (#412) + New: * Support Raysect 0.8 * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) diff --git a/cherab/tools/raytransfer/pipelines.py b/cherab/tools/raytransfer/pipelines.py index b94223ea..1ac1366f 100644 --- a/cherab/tools/raytransfer/pipelines.py +++ b/cherab/tools/raytransfer/pipelines.py @@ -34,24 +34,63 @@ from raysect.optical.observer.base import Pipeline0D, Pipeline1D, Pipeline2D, PixelProcessor -class RayTransferPipeline0D(Pipeline0D): +class RayTransferPipelineBase(): + + def __init__(self, name=None, units='power'): + + self.name = name + self._matrix = None + self._samples = 0 + self._bins = 0 + self.units = units + + @property + def units(self): + """ + The units in which the matrix is calculated. Can be 'power' or 'radiance'. + The 'power' stands for [m^3 sr] and the 'radiance' stands for [m]. + """ + return self._units + + @units.setter + def units(self, value): + _units = value.lower() + if _units in ('power', 'radiance'): + self._units = _units + else: + raise ValueError("The units property must be 'power' or 'radiance'.") + + @property + def matrix(self): + return self._matrix + + +class RayTransferPipeline0D(Pipeline0D, RayTransferPipelineBase): """ Simple 0D pipeline for ray transfer matrix (geometry matrix) calculation. + :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. + :param str units: The units in which the matrix is calculated. Can + be 'power' (default) or 'radiance'. + The 'power' stands for [m^3 sr] and when the matrix is collapsed with + the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. + The 'radiance' stands for [m] and when the matrix is collapsed with + the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. + If the 'power' is selected, the matrix is multiplied by the detector sensitivity. + Note that if the detector sensitivity is 1, the 'power' and 'radiance' + give the same results. + :ivar np.ndarray matrix: Ray transfer matrix, a 1D array of size :math:`N_{bin}`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline0D - >>> pipeline = RayTransferPipeline0D() + >>> pipeline = RayTransferPipeline0D(units='radiance') """ - def __init__(self, name=None): + def __init__(self, name='RayTransferPipeline0D', units='power'): - self.name = name or 'RayTransferPipeline0D' - self._matrix = None - self._samples = 0 - self._bins = 0 + RayTransferPipelineBase.__init__(self, name, units) def initialise(self, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): self._samples = 0 @@ -59,7 +98,10 @@ def initialise(self, min_wavelength, max_wavelength, spectral_bins, spectral_sli self._matrix = np.zeros(spectral_bins) def pixel_processor(self, slice_id): - return RayTransferPixelProcessor(self._bins) + if self._units == 'power': + return PowerRayTransferPixelProcessor(self._bins) + else: + return RadianceRayTransferPixelProcessor(self._bins) def update(self, slice_id, packed_result, pixel_samples): self._samples += pixel_samples @@ -68,30 +110,34 @@ def update(self, slice_id, packed_result, pixel_samples): def finalise(self): self._matrix /= self._samples - @property - def matrix(self): - return self._matrix - -class RayTransferPipeline1D(Pipeline1D): +class RayTransferPipeline1D(Pipeline1D, RayTransferPipelineBase): """ Simple 1D pipeline for ray transfer matrix (geometry matrix) calculation. + :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. + :param str units: The units in which the matrix is calculated. Can + be 'power' (default) or 'radiance'. + The 'power' stands for [m^3 sr] and when the matrix is collapsed with + the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. + The 'radiance' stands for [m] and when the matrix is collapsed with + the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. + If the 'power' is selected, the matrix is multiplied by the detector sensitivity. + Note that if the detector sensitivity is 1, the 'power' and 'radiance' + give the same results. + :ivar np.ndarray matrix: Ray transfer matrix, a 2D array of shape :math:`(N_{pixel}, N_{bin})`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline1D - >>> pipeline = RayTransferPipeline1D() + >>> pipeline = RayTransferPipeline1D(units='radiance') """ - def __init__(self, name=None): + def __init__(self, name='RayTransferPipeline1D', units='power'): - self.name = name or 'RayTransferPipeline1D' - self._matrix = None + RayTransferPipelineBase.__init__(self, name, units) self._pixels = None - self._samples = 0 - self._bins = 0 def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): self._pixels = pixels @@ -100,7 +146,10 @@ def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spec self._matrix = np.zeros((pixels, spectral_bins)) def pixel_processor(self, pixel, slice_id): - return RayTransferPixelProcessor(self._bins) + if self._units == 'power': + return PowerRayTransferPixelProcessor(self._bins) + else: + return RadianceRayTransferPixelProcessor(self._bins) def update(self, pixel, slice_id, packed_result): self._matrix[pixel] = packed_result[0] / self._samples @@ -108,30 +157,34 @@ def update(self, pixel, slice_id, packed_result): def finalise(self): pass - @property - def matrix(self): - return self._matrix - -class RayTransferPipeline2D(Pipeline2D): +class RayTransferPipeline2D(Pipeline2D, RayTransferPipelineBase): """ Simple 2D pipeline for ray transfer matrix (geometry matrix) calculation. + :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. + :param str units: The units in which the matrix is calculated. Can + be 'power' (default) or 'radiance'. + The 'power' stands for [m^3 sr] and when the matrix is collapsed with + the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. + The 'radiance' stands for [m] and when the matrix is collapsed with + the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. + If the 'power' is selected, the matrix is multiplied by the detector sensitivity. + Note that if the detector sensitivity is 1, the 'power' and 'radiance' + give the same results. + :ivar np.ndarray matrix: Ray transfer matrix, a 3D array of shape :math:`(N_x, N_y, N_{bin})`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline2D - >>> pipeline = RayTransferPipeline2D() + >>> pipeline = RayTransferPipeline2D(units='radiance') """ - def __init__(self, name=None): + def __init__(self, name='RayTransferPipeline2D', units='power'): - self.name = name or 'RayTransferPipeline2D' - self._matrix = None + RayTransferPipelineBase.__init__(self, name, units) self._pixels = None - self._samples = 0 - self._bins = 0 def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): self._pixels = pixels @@ -140,7 +193,10 @@ def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spec self._matrix = np.zeros((pixels[0], pixels[1], spectral_bins)) def pixel_processor(self, x, y, slice_id): - return RayTransferPixelProcessor(self._bins) + if self._units == 'power': + return PowerRayTransferPixelProcessor(self._bins) + else: + return RadianceRayTransferPixelProcessor(self._bins) def update(self, x, y, slice_id, packed_result): self._matrix[x, y] = packed_result[0] / self._samples @@ -148,21 +204,32 @@ def update(self, x, y, slice_id, packed_result): def finalise(self): pass - @property - def matrix(self): - return self._matrix - -class RayTransferPixelProcessor(PixelProcessor): +class RayTransferPixelProcessorBase(PixelProcessor): """ - PixelProcessor that stores ray transfer matrix for each pixel. + Base class for PixelProcessor that stores ray transfer matrix for each pixel. """ def __init__(self, bins): self._matrix = np.zeros(bins) - def add_sample(self, spectrum, sensitivity): - self._matrix += spectrum.samples * sensitivity - def pack_results(self): return (self._matrix, 0) + + +class RadianceRayTransferPixelProcessor(RayTransferPixelProcessorBase): + """ + PixelProcessor that stores ray transfer matrix in the units of [m] for each pixel. + """ + + def add_sample(self, spectrum, sensitivity): + self._matrix += spectrum.samples + + +class PowerRayTransferPixelProcessor(RayTransferPixelProcessorBase): + """ + PixelProcessor that stores ray transfer matrix in the units of [m^3 sr] for each pixel. + """ + + def add_sample(self, spectrum, sensitivity): + self._matrix += spectrum.samples * sensitivity diff --git a/cherab/tools/tests/test_raytransfer.py b/cherab/tools/tests/test_raytransfer.py index 38a5cdcf..29737419 100644 --- a/cherab/tools/tests/test_raytransfer.py +++ b/cherab/tools/tests/test_raytransfer.py @@ -18,9 +18,10 @@ import unittest import numpy as np -from raysect.optical import World, Ray, Point3D, Point2D, Vector3D, NumericalIntegrator +from raysect.optical import World, Ray, Point3D, Point2D, Vector3D, NumericalIntegrator, Spectrum from raysect.primitive import Box, Cylinder, Subtract from cherab.tools.raytransfer import RayTransferBox, RayTransferCylinder, CartesianRayTransferEmitter, CylindricalRayTransferEmitter +from cherab.tools.raytransfer import RayTransferPipeline0D, RayTransferPipeline1D, RayTransferPipeline2D from cherab.tools.inversions import ToroidalVoxelGrid @@ -239,3 +240,158 @@ def test_evaluate_function_3d(self): spectrum_test = np.zeros(12) spectrum_test[2] = spectrum_test[9] = np.sqrt(2.) self.assertTrue(np.allclose(spectrum_test, spectrum.samples, atol=0.001)) + + +class TestRayTransferPipeline0D(unittest.TestCase): + """ + Test cases for RayTransferPipeline0D class. + """ + + def test_initialise(self): + """ + Test initialise method. + """ + nbins = 10 + pipeline = RayTransferPipeline0D('test_pipeline_0D', units='power') + pipeline.initialise(0, 0, nbins, 0, 0) + + self.assertTrue(pipeline.matrix.shape == (nbins,)) + self.assertTrue(pipeline.name == 'test_pipeline_0D') + self.assertTrue(pipeline.units == 'power') + + self.assertRaises(ValueError, RayTransferPipeline0D, 'test_pipeline_0D', 'blah') + + def test_units(self): + """ + Test if the 'units' attribute works properly. + """ + nbins = 10 + sensitivity = 2. + spectral_value = 1. + spectrum = Spectrum(1., 2., nbins) + spectrum.samples[:] = spectral_value + + pipeline = RayTransferPipeline0D('test_pipeline_0D', units='power') + pipeline.initialise(0, 0, nbins, 0, 0) + + pixel_processor = pipeline.pixel_processor(0) + + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity + self.assertTrue(np.all(matrix == sensitivity * spectral_value)) + + pipeline.units = 'radiance' + pixel_processor = pipeline.pixel_processor(0) + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # not multiplied by sensitivity + self.assertTrue(np.all(matrix == spectral_value)) + + +class TestRayTransferPipeline1D(unittest.TestCase): + """ + Test cases for RayTransferPipeline1D class. + """ + + def test_initialise(self): + """ + Test initialise method. + """ + nbins = 10 + pixels = 20 + samples = 1 + pipeline = RayTransferPipeline1D('test_pipeline_1D', units='radiance') + pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) + + self.assertTrue(pipeline.matrix.shape == (pixels, nbins)) + self.assertTrue(pipeline.name == 'test_pipeline_1D') + self.assertTrue(pipeline.units == 'radiance') + self.assertTrue(pipeline._samples == samples) + + self.assertRaises(ValueError, RayTransferPipeline1D, 'test_pipeline_1D', 'blah') + + def test_units(self): + """ + Test if the 'units' attribute works properly. + """ + nbins = 10 + pixels = 20 + samples = 1 + sensitivity = 2. + spectral_value = 1. + spectrum = Spectrum(1., 2., nbins) + spectrum.samples[:] = spectral_value + + pipeline = RayTransferPipeline1D('test_pipeline_1D', units='power') + pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) + + pixel_processor = pipeline.pixel_processor(0, 0) + + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity + self.assertTrue(np.all(matrix == sensitivity * spectral_value)) + + pipeline.units = 'radiance' + pixel_processor = pipeline.pixel_processor(0, 0) + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # not multiplied by sensitivity + self.assertTrue(np.all(matrix == spectral_value)) + + +class TestRayTransferPipeline2D(unittest.TestCase): + """ + Test cases for RayTransferPipeline2D class. + """ + + def test_initialise(self): + """ + Test initialise method. + """ + nbins = 10 + pixels = (20, 5) + samples = 1 + pipeline = RayTransferPipeline2D('test_pipeline_2D', units='radiance') + pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) + + self.assertTrue(pipeline.matrix.shape == (pixels[0], pixels[1], nbins)) + self.assertTrue(pipeline.name == 'test_pipeline_2D') + self.assertTrue(pipeline.units == 'radiance') + self.assertTrue(pipeline._samples == samples) + + self.assertRaises(ValueError, RayTransferPipeline2D, 'test_pipeline_2D', 'blah') + + def test_units(self): + """ + Test if the 'units' attribute works properly. + """ + nbins = 10 + pixels = (20, 5) + samples = 1 + sensitivity = 2. + spectral_value = 1. + spectrum = Spectrum(1., 2., nbins) + spectrum.samples[:] = spectral_value + + pipeline = RayTransferPipeline2D('test_pipeline_2D', units='power') + pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) + + pixel_processor = pipeline.pixel_processor(0, 0, 0) + + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity + self.assertTrue(np.all(matrix == sensitivity * spectral_value)) + + pipeline.units = 'radiance' + pixel_processor = pipeline.pixel_processor(0, 0, 0) + pixel_processor.add_sample(spectrum, sensitivity) + + matrix, _ = pixel_processor.pack_results() # not multiplied by sensitivity + self.assertTrue(np.all(matrix == spectral_value)) + + +if __name__ == '__main__': + unittest.main() diff --git a/demos/observers/bolometry/geometry_matrix_with_raytransfer.py b/demos/observers/bolometry/geometry_matrix_with_raytransfer.py index a9300636..a6bee82d 100644 --- a/demos/observers/bolometry/geometry_matrix_with_raytransfer.py +++ b/demos/observers/bolometry/geometry_matrix_with_raytransfer.py @@ -240,7 +240,7 @@ def _point3d_to_rz(point): for camera in cameras: for foil in camera: print("Calculating sensitivity for {}...".format(foil.name)) - foil.pipelines = [RayTransferPipeline0D()] + foil.pipelines = [RayTransferPipeline0D(units=foil.units)] # All objects in world have wavelength-independent material properties, # so it doesn't matter which wavelength range we use (as long as # max_wavelength - min_wavelength = 1) diff --git a/demos/ray_transfer/1_ray_transfer_box.py b/demos/ray_transfer/1_ray_transfer_box.py index e4d5e8bb..a4029431 100644 --- a/demos/ray_transfer/1_ray_transfer_box.py +++ b/demos/ray_transfer/1_ray_transfer_box.py @@ -36,8 +36,8 @@ from raysect.optical.observer import PinholeCamera, FullFrameSampler2D # RayTransferPipeline2D is optimised for calculation of ray transfer matrices. -# It's also possible to use SpectralRadiancePipeline2D but for the matrices with >1000 elements -# the performance will be lower. +# It's also possible to use SpectralRadiancePipeline2D or SpectralPowerPipeline2D but +# for the matrices with >1000 elements the performance will be lower. from cherab.tools.raytransfer import RayTransferPipeline2D, RayTransferBox # Here we use special materials optimised for calculation of ray transfer matrices. @@ -66,6 +66,9 @@ rtb.step = 0.2 # creating ray transfer pipeline +# Be careful when choosing pipeline units ('power' or 'radiance'). +# In case of 'power', the matrix [m] is multiplied by the detector's sensitivity [m^2 sr]. +# For the PinholeCamera this does not matter, because its pixel sensitivity is 1. pipeline = RayTransferPipeline2D() # setting up the camera From df3c33e6a9f2cd3ae4c4c8700dc2f98d5c5ad4da Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 3 May 2023 09:10:59 +0300 Subject: [PATCH 046/134] Fix incorrect description of the new feature in CHANGELOG.md --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f334f8d..cab23889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,11 @@ Project Changelog Release 1.5.0 (TBD) ------------------- -Bug fixes: -* Fix improper multiplication by sensitivity factor in RayTransferPixelProcessor resulting in wrong units (m^3 sr instead of m) of ray transfer matrices. (#412) - New: * Support Raysect 0.8 * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) +* Add the units attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity or not. (#412) Release 1.4.0 (3 Feb 2023) From 8e719ba31aed225e19b963b191a722d56452391a Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 3 May 2023 13:28:36 +0300 Subject: [PATCH 047/134] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f982618..19f1ae56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ New: * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) * Add numerical integration of Bremsstrahlung spectrum over a spectral bin. (#395) +* Replace the coarse numerical constant in the Bremsstrahlung model with an exact expression. (#409) Release 1.4.0 (3 Feb 2023) From b3b0d5713a73eaa8e53a6be4d0a6fb605f135d2e Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 3 May 2023 23:51:47 +0300 Subject: [PATCH 048/134] Made expression for bremsstrahlung look clearer and updated docstring in response to @skuba31 comments. --- cherab/core/model/plasma/bremsstrahlung.pyx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cherab/core/model/plasma/bremsstrahlung.pyx b/cherab/core/model/plasma/bremsstrahlung.pyx index cb8a88a6..404750a2 100644 --- a/cherab/core/model/plasma/bremsstrahlung.pyx +++ b/cherab/core/model/plasma/bremsstrahlung.pyx @@ -83,12 +83,11 @@ cdef class BremsFunction(Function1D): if ni > 0: ni_gff_z2 += ni * self.gaunt_factor.evaluate(z, self.te, wvl) * z * z - # bremsstrahlung equation W/m^3/str - pre_factor = BREMS_CONST * ni_gff_z2 * self.ne / (sqrt(self.te) * wvl) + # bremsstrahlung equation W/m^3/str/nm + pre_factor = BREMS_CONST / (sqrt(self.te) * wvl * wvl) * self.ne * ni_gff_z2 radiance = pre_factor * exp(- EXP_FACTOR / (self.te * wvl)) - # convert to W/m^3/str/nm - return radiance / wvl + return radiance # todo: doppler shift? @@ -101,16 +100,20 @@ cdef class Bremsstrahlung(PlasmaModel): Cambridge University Press, 2002, ISBN: 9780511613630, https://doi.org/10.1017/CBO9780511613630 + Note that in eq. 5.3.40, the emissivity :math:`j(\\nu)` is given in (W/m^3/sr/Hz) with respect + to frequency, :math:`\\nu`. Here, the emissivity :math:`\\epsilon_{\\mathrm{ff}}(\\lambda)` + is given in (W/m^3/nm/sr) with respect to wavelength, :math:`\\lambda = \\frac{10^{9} c}{\\nu}`, + and taking into account that :math:`d\\nu=-\\frac{10^{9} c}{\\lambda^2}d\\lambda`. + .. math:: \\epsilon_{\\mathrm{ff}}(\\lambda) = \\left( \\frac{e^2}{4 \\pi \\varepsilon_0} \\right)^3 \\frac{32 \\pi^2}{3 \\sqrt{3} m_\\mathrm{e}^2 c^3} \\sqrt{\\frac{2 m_\\mathrm{e}^3}{\\pi e T_\\mathrm{e}}} \\frac{10^{9} c}{4 \\pi \\lambda^2} n_\\mathrm{e} \\sum_i \\left( n_\\mathrm{i} g_\\mathrm{ff} (Z_\\mathrm{i}, T_\\mathrm{e}, \\lambda) Z_\\mathrm{i}^2 \\right) - \\mathrm{e}^{-\\frac{10^9 hc}{e T_\\mathrm{e}}}\\,, + \\mathrm{e}^{-\\frac{10^9 hc}{e T_\\mathrm{e} \\lambda}}\\,, - where the emission :math:`\\epsilon_{\\mathrm{ff}}(\\lambda)` is in units of radiance (W/sr/m^3/nm), - :math:`T_\\mathrm{e}` is in eV and :math:`\\lambda` is in nm. + where :math:`T_\\mathrm{e}` is in eV and :math:`\\lambda` is in nm. :math:`g_\\mathrm{ff} (Z_\\mathrm{i}, T_\\mathrm{e}, \\lambda)` is the free-free Gaunt factor. From 09d519cdddd4c0bc50cba8e33a1a7e6ce4e51a0e Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Fri, 5 May 2023 12:21:21 +0300 Subject: [PATCH 049/134] Updated BeamCXLine and related tests to support new LineShapeModel interface. --- cherab/core/model/beam/charge_exchange.pyx | 2 +- cherab/core/tests/test_beamcxline.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 295d5f5f..50f34249 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -358,7 +358,7 @@ cdef class BeamCXLine(BeamModel): self._excited_beam_data.append((rate, population_data)) # instance line shape renderer - self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, + self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, self._atomic_data, *self._lineshape_args, **self._lineshape_kwargs) def _change(self): diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py index 6de8ad58..6fdd80b4 100644 --- a/cherab/core/tests/test_beamcxline.py +++ b/cherab/core/tests/test_beamcxline.py @@ -120,7 +120,7 @@ def test_default_lineshape(self): target_species = self.plasma.composition.get(line.element, line.charge + 1) wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) - gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -152,7 +152,7 @@ def test_custom_lineshape(self): target_species = self.plasma.composition.get(line.element, line.charge + 1) wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) - zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) From fb924d420f3dbe8c9c15d1641f40138f3744b9e7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 10 May 2023 16:21:27 +0300 Subject: [PATCH 050/134] Renamed 'units' attribute of the RayTransferPipelineBase as 'kind' and updated the docs. --- CHANGELOG.md | 2 +- cherab/tools/raytransfer/pipelines.py | 107 +++++++++--------- cherab/tools/tests/test_raytransfer.py | 34 +++--- .../geometry_matrix_with_raytransfer.py | 2 +- demos/ray_transfer/1_ray_transfer_box.py | 4 +- 5 files changed, 77 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cab23889..357f8fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ New: * Support Raysect 0.8 * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) -* Add the units attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity or not. (#412) +* Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) Release 1.4.0 (3 Feb 2023) diff --git a/cherab/tools/raytransfer/pipelines.py b/cherab/tools/raytransfer/pipelines.py index 1ac1366f..2339915a 100644 --- a/cherab/tools/raytransfer/pipelines.py +++ b/cherab/tools/raytransfer/pipelines.py @@ -36,29 +36,34 @@ class RayTransferPipelineBase(): - def __init__(self, name=None, units='power'): + def __init__(self, name=None, kind='power'): self.name = name self._matrix = None self._samples = 0 self._bins = 0 - self.units = units + self.kind = kind @property - def units(self): + def kind(self): """ - The units in which the matrix is calculated. Can be 'power' or 'radiance'. - The 'power' stands for [m^3 sr] and the 'radiance' stands for [m]. + The kind of the pipeline. Can be 'power' or 'radiance'. + In the case of 'power', the resulting matrix is multiplied by the sensitivity + of the detector, and the units of the matrix are [m^3 sr], which gives the units + of power [W] for the product of the ray transfer matrix and the emission profile. + In case of 'radiance', the sensitivity is not taken into account and + the matrix is calculated in [m], which gives the units of radiance [W m^-2 sr^-1] + for the product of the ray transfer matrix and the emission profile. """ - return self._units + return self._kind - @units.setter - def units(self, value): - _units = value.lower() - if _units in ('power', 'radiance'): - self._units = _units + @kind.setter + def kind(self, value): + _kind = value.lower() + if _kind in ('power', 'radiance'): + self._kind = _kind else: - raise ValueError("The units property must be 'power' or 'radiance'.") + raise ValueError("The kind property must be 'power' or 'radiance'.") @property def matrix(self): @@ -70,27 +75,27 @@ class RayTransferPipeline0D(Pipeline0D, RayTransferPipelineBase): Simple 0D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str units: The units in which the matrix is calculated. Can - be 'power' (default) or 'radiance'. - The 'power' stands for [m^3 sr] and when the matrix is collapsed with - the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. - The 'radiance' stands for [m] and when the matrix is collapsed with - the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. - If the 'power' is selected, the matrix is multiplied by the detector sensitivity. - Note that if the detector sensitivity is 1, the 'power' and 'radiance' - give the same results. + :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + In the case of 'power', the resulting matrix is multiplied by the sensitivity + of the detector, and the units of the matrix are [m^3 sr], which gives the units + of power [W] for the product of the ray transfer matrix and the emission profile. + In case of 'radiance', the sensitivity is not taken into account and + the matrix is calculated in [m], which gives the units of radiance [W m^-2 sr^-1] + for the product of the ray transfer matrix and the emission profile. + Note that if the sensitivity of the detector is 1 (e.g. `PinholeCamera`, `VectorCamera`), + the 'power' and 'radiance' give the same results. :ivar np.ndarray matrix: Ray transfer matrix, a 1D array of size :math:`N_{bin}`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline0D - >>> pipeline = RayTransferPipeline0D(units='radiance') + >>> pipeline = RayTransferPipeline0D(kind='radiance') """ - def __init__(self, name='RayTransferPipeline0D', units='power'): + def __init__(self, name='RayTransferPipeline0D', kind='power'): - RayTransferPipelineBase.__init__(self, name, units) + RayTransferPipelineBase.__init__(self, name, kind) def initialise(self, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): self._samples = 0 @@ -98,7 +103,7 @@ def initialise(self, min_wavelength, max_wavelength, spectral_bins, spectral_sli self._matrix = np.zeros(spectral_bins) def pixel_processor(self, slice_id): - if self._units == 'power': + if self._kind == 'power': return PowerRayTransferPixelProcessor(self._bins) else: return RadianceRayTransferPixelProcessor(self._bins) @@ -116,27 +121,27 @@ class RayTransferPipeline1D(Pipeline1D, RayTransferPipelineBase): Simple 1D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str units: The units in which the matrix is calculated. Can - be 'power' (default) or 'radiance'. - The 'power' stands for [m^3 sr] and when the matrix is collapsed with - the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. - The 'radiance' stands for [m] and when the matrix is collapsed with - the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. - If the 'power' is selected, the matrix is multiplied by the detector sensitivity. - Note that if the detector sensitivity is 1, the 'power' and 'radiance' - give the same results. + :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + In the case of 'power', the resulting matrix is multiplied by the sensitivity + of the detector, and the units of the matrix are [m^3 sr], which gives the units + of power [W] for the product of the ray transfer matrix and the emission profile. + In case of 'radiance', the sensitivity is not taken into account and + the matrix is calculated in [m], which gives the units of radiance [W m^-2 sr^-1] + for the product of the ray transfer matrix and the emission profile. + Note that if the sensitivity of the detector is 1 (e.g. `PinholeCamera`, `VectorCamera`), + the 'power' and 'radiance' give the same results. :ivar np.ndarray matrix: Ray transfer matrix, a 2D array of shape :math:`(N_{pixel}, N_{bin})`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline1D - >>> pipeline = RayTransferPipeline1D(units='radiance') + >>> pipeline = RayTransferPipeline1D(kind='radiance') """ - def __init__(self, name='RayTransferPipeline1D', units='power'): + def __init__(self, name='RayTransferPipeline1D', kind='power'): - RayTransferPipelineBase.__init__(self, name, units) + RayTransferPipelineBase.__init__(self, name, kind) self._pixels = None def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): @@ -146,7 +151,7 @@ def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spec self._matrix = np.zeros((pixels, spectral_bins)) def pixel_processor(self, pixel, slice_id): - if self._units == 'power': + if self._kind == 'power': return PowerRayTransferPixelProcessor(self._bins) else: return RadianceRayTransferPixelProcessor(self._bins) @@ -163,27 +168,27 @@ class RayTransferPipeline2D(Pipeline2D, RayTransferPipelineBase): Simple 2D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str units: The units in which the matrix is calculated. Can - be 'power' (default) or 'radiance'. - The 'power' stands for [m^3 sr] and when the matrix is collapsed with - the emission profile [W m^-3 sr-1 nm-1] it gives the power [W nm-1]. - The 'radiance' stands for [m] and when the matrix is collapsed with - the emission profile it gives the radiance [W m^-2 sr-1 nm-1]. - If the 'power' is selected, the matrix is multiplied by the detector sensitivity. - Note that if the detector sensitivity is 1, the 'power' and 'radiance' - give the same results. + :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + In the case of 'power', the resulting matrix is multiplied by the sensitivity + of the detector, and the units of the matrix are [m^3 sr], which gives the units + of power [W] for the product of the ray transfer matrix and the emission profile. + In case of 'radiance', the sensitivity is not taken into account and + the matrix is calculated in [m], which gives the units of radiance [W m^-2 sr^-1] + for the product of the ray transfer matrix and the emission profile. + Note that if the sensitivity of the detector is 1 (e.g. `PinholeCamera`, `VectorCamera`), + the 'power' and 'radiance' give the same results. :ivar np.ndarray matrix: Ray transfer matrix, a 3D array of shape :math:`(N_x, N_y, N_{bin})`. .. code-block:: pycon >>> from cherab.tools.raytransfer import RayTransferPipeline2D - >>> pipeline = RayTransferPipeline2D(units='radiance') + >>> pipeline = RayTransferPipeline2D(kind='radiance') """ - def __init__(self, name='RayTransferPipeline2D', units='power'): + def __init__(self, name='RayTransferPipeline2D', kind='power'): - RayTransferPipelineBase.__init__(self, name, units) + RayTransferPipelineBase.__init__(self, name, kind) self._pixels = None def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spectral_bins, spectral_slices, quiet): @@ -193,7 +198,7 @@ def initialise(self, pixels, pixel_samples, min_wavelength, max_wavelength, spec self._matrix = np.zeros((pixels[0], pixels[1], spectral_bins)) def pixel_processor(self, x, y, slice_id): - if self._units == 'power': + if self._kind == 'power': return PowerRayTransferPixelProcessor(self._bins) else: return RadianceRayTransferPixelProcessor(self._bins) diff --git a/cherab/tools/tests/test_raytransfer.py b/cherab/tools/tests/test_raytransfer.py index 29737419..76ecd4d4 100644 --- a/cherab/tools/tests/test_raytransfer.py +++ b/cherab/tools/tests/test_raytransfer.py @@ -252,18 +252,18 @@ def test_initialise(self): Test initialise method. """ nbins = 10 - pipeline = RayTransferPipeline0D('test_pipeline_0D', units='power') + pipeline = RayTransferPipeline0D('test_pipeline_0D', kind='power') pipeline.initialise(0, 0, nbins, 0, 0) self.assertTrue(pipeline.matrix.shape == (nbins,)) self.assertTrue(pipeline.name == 'test_pipeline_0D') - self.assertTrue(pipeline.units == 'power') + self.assertTrue(pipeline.kind == 'power') self.assertRaises(ValueError, RayTransferPipeline0D, 'test_pipeline_0D', 'blah') - def test_units(self): + def test_kind(self): """ - Test if the 'units' attribute works properly. + Test if the 'kind' attribute works properly. """ nbins = 10 sensitivity = 2. @@ -271,7 +271,7 @@ def test_units(self): spectrum = Spectrum(1., 2., nbins) spectrum.samples[:] = spectral_value - pipeline = RayTransferPipeline0D('test_pipeline_0D', units='power') + pipeline = RayTransferPipeline0D('test_pipeline_0D', kind='power') pipeline.initialise(0, 0, nbins, 0, 0) pixel_processor = pipeline.pixel_processor(0) @@ -281,7 +281,7 @@ def test_units(self): matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity self.assertTrue(np.all(matrix == sensitivity * spectral_value)) - pipeline.units = 'radiance' + pipeline.kind = 'radiance' pixel_processor = pipeline.pixel_processor(0) pixel_processor.add_sample(spectrum, sensitivity) @@ -301,19 +301,19 @@ def test_initialise(self): nbins = 10 pixels = 20 samples = 1 - pipeline = RayTransferPipeline1D('test_pipeline_1D', units='radiance') + pipeline = RayTransferPipeline1D('test_pipeline_1D', kind='radiance') pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) self.assertTrue(pipeline.matrix.shape == (pixels, nbins)) self.assertTrue(pipeline.name == 'test_pipeline_1D') - self.assertTrue(pipeline.units == 'radiance') + self.assertTrue(pipeline.kind == 'radiance') self.assertTrue(pipeline._samples == samples) self.assertRaises(ValueError, RayTransferPipeline1D, 'test_pipeline_1D', 'blah') - def test_units(self): + def test_kind(self): """ - Test if the 'units' attribute works properly. + Test if the 'kind' attribute works properly. """ nbins = 10 pixels = 20 @@ -323,7 +323,7 @@ def test_units(self): spectrum = Spectrum(1., 2., nbins) spectrum.samples[:] = spectral_value - pipeline = RayTransferPipeline1D('test_pipeline_1D', units='power') + pipeline = RayTransferPipeline1D('test_pipeline_1D', kind='power') pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) pixel_processor = pipeline.pixel_processor(0, 0) @@ -333,7 +333,7 @@ def test_units(self): matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity self.assertTrue(np.all(matrix == sensitivity * spectral_value)) - pipeline.units = 'radiance' + pipeline.kind = 'radiance' pixel_processor = pipeline.pixel_processor(0, 0) pixel_processor.add_sample(spectrum, sensitivity) @@ -353,19 +353,19 @@ def test_initialise(self): nbins = 10 pixels = (20, 5) samples = 1 - pipeline = RayTransferPipeline2D('test_pipeline_2D', units='radiance') + pipeline = RayTransferPipeline2D('test_pipeline_2D', kind='radiance') pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) self.assertTrue(pipeline.matrix.shape == (pixels[0], pixels[1], nbins)) self.assertTrue(pipeline.name == 'test_pipeline_2D') - self.assertTrue(pipeline.units == 'radiance') + self.assertTrue(pipeline.kind == 'radiance') self.assertTrue(pipeline._samples == samples) self.assertRaises(ValueError, RayTransferPipeline2D, 'test_pipeline_2D', 'blah') def test_units(self): """ - Test if the 'units' attribute works properly. + Test if the 'kind' attribute works properly. """ nbins = 10 pixels = (20, 5) @@ -375,7 +375,7 @@ def test_units(self): spectrum = Spectrum(1., 2., nbins) spectrum.samples[:] = spectral_value - pipeline = RayTransferPipeline2D('test_pipeline_2D', units='power') + pipeline = RayTransferPipeline2D('test_pipeline_2D', kind='power') pipeline.initialise(pixels, samples, 0, 0, nbins, 1, 0) pixel_processor = pipeline.pixel_processor(0, 0, 0) @@ -385,7 +385,7 @@ def test_units(self): matrix, _ = pixel_processor.pack_results() # multiplied by sensitivity self.assertTrue(np.all(matrix == sensitivity * spectral_value)) - pipeline.units = 'radiance' + pipeline.kind = 'radiance' pixel_processor = pipeline.pixel_processor(0, 0, 0) pixel_processor.add_sample(spectrum, sensitivity) diff --git a/demos/observers/bolometry/geometry_matrix_with_raytransfer.py b/demos/observers/bolometry/geometry_matrix_with_raytransfer.py index a6bee82d..35c525e1 100644 --- a/demos/observers/bolometry/geometry_matrix_with_raytransfer.py +++ b/demos/observers/bolometry/geometry_matrix_with_raytransfer.py @@ -240,7 +240,7 @@ def _point3d_to_rz(point): for camera in cameras: for foil in camera: print("Calculating sensitivity for {}...".format(foil.name)) - foil.pipelines = [RayTransferPipeline0D(units=foil.units)] + foil.pipelines = [RayTransferPipeline0D(kind=foil.units)] # All objects in world have wavelength-independent material properties, # so it doesn't matter which wavelength range we use (as long as # max_wavelength - min_wavelength = 1) diff --git a/demos/ray_transfer/1_ray_transfer_box.py b/demos/ray_transfer/1_ray_transfer_box.py index a4029431..43461709 100644 --- a/demos/ray_transfer/1_ray_transfer_box.py +++ b/demos/ray_transfer/1_ray_transfer_box.py @@ -66,8 +66,8 @@ rtb.step = 0.2 # creating ray transfer pipeline -# Be careful when choosing pipeline units ('power' or 'radiance'). -# In case of 'power', the matrix [m] is multiplied by the detector's sensitivity [m^2 sr]. +# Be careful when setting the 'kind' attribute of the pipeline to 'power' or 'radiance'. +# In the case of 'power', the matrix [m] is multiplied by the detector's sensitivity [m^2 sr]. # For the PinholeCamera this does not matter, because its pixel sensitivity is 1. pipeline = RayTransferPipeline2D() From 41e7f70b3d3a8c6f874f1bf0df577d3c7b4fcc53 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 10 May 2023 16:26:20 +0300 Subject: [PATCH 051/134] Updated the docstrings of RayTransferPipelineXD. --- cherab/tools/raytransfer/pipelines.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cherab/tools/raytransfer/pipelines.py b/cherab/tools/raytransfer/pipelines.py index 2339915a..e051e3f7 100644 --- a/cherab/tools/raytransfer/pipelines.py +++ b/cherab/tools/raytransfer/pipelines.py @@ -75,7 +75,7 @@ class RayTransferPipeline0D(Pipeline0D, RayTransferPipelineBase): Simple 0D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + :param str kind: The kind of the pipeline. Can be 'power' (default) or 'radiance'. In the case of 'power', the resulting matrix is multiplied by the sensitivity of the detector, and the units of the matrix are [m^3 sr], which gives the units of power [W] for the product of the ray transfer matrix and the emission profile. @@ -121,7 +121,7 @@ class RayTransferPipeline1D(Pipeline1D, RayTransferPipelineBase): Simple 1D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + :param str kind: The kind of the pipeline. Can be 'power' (default) or 'radiance'. In the case of 'power', the resulting matrix is multiplied by the sensitivity of the detector, and the units of the matrix are [m^3 sr], which gives the units of power [W] for the product of the ray transfer matrix and the emission profile. @@ -168,7 +168,7 @@ class RayTransferPipeline2D(Pipeline2D, RayTransferPipelineBase): Simple 2D pipeline for ray transfer matrix (geometry matrix) calculation. :param str name: The name of the pipeline. Default is 'RayTransferPipeline0D'. - :param str kind: The kind of the pipeline. Can be 'power' or 'radiance'. + :param str kind: The kind of the pipeline. Can be 'power' (default) or 'radiance'. In the case of 'power', the resulting matrix is multiplied by the sensitivity of the detector, and the units of the matrix are [m^3 sr], which gives the units of power [W] for the product of the ray transfer matrix and the emission profile. From 8a5ace6ad5fc9a9365826cefe677d84c15b6afd4 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Wed, 9 Aug 2023 12:43:15 +0300 Subject: [PATCH 052/134] Fix NotImplementedError in TestLaserSpectrum. --- cherab/core/laser/tests/test_laserspectrum.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cherab/core/laser/tests/test_laserspectrum.py b/cherab/core/laser/tests/test_laserspectrum.py index 9473e43d..6545f5c7 100644 --- a/cherab/core/laser/tests/test_laserspectrum.py +++ b/cherab/core/laser/tests/test_laserspectrum.py @@ -1,9 +1,11 @@ import unittest import numpy as np -from cherab.core.laser.laserspectrum import LaserSpectrum +# The evaluate() method is not implemented in the base LaserSpectrum, so importing the simplest subclass. +from cherab.core.model.laser.laserspectrum import ConstantSpectrum as LaserSpectrum from raysect.optical.spectrum import Spectrum + class TestLaserSpectrum(unittest.TestCase): def test_laserspectrum_init(self): @@ -24,12 +26,12 @@ def test_laserspectrum_init(self): 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) + msg="LaserSpectrum did not raise a ValueError with bins <= 0."): + LaserSpectrum(30, 40, 0) + LaserSpectrum(30, 40, -1) def test_laserspectrum_changes(self): laser_spectrum = LaserSpectrum(100, 200, 100) @@ -59,4 +61,4 @@ def test_laserspectrum_changes(self): # 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 + "with same boundaries and number of bins") From 1aafe6276105a77aa1e284459461750a5267301f Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Thu, 26 Oct 2023 20:16:43 +0200 Subject: [PATCH 053/134] Fix update bug of LaserMaterial Laser primitives have to be rebuild with the materials. Possible source of the problem: Laser node is the parent of the laser primitives After laser.transform is changed the _modified method is called before the transforms of the laser primitives is changed because the Laser node is above the laser primitives in the scenegraph. --- cherab/core/laser/node.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cherab/core/laser/node.pyx b/cherab/core/laser/node.pyx index b0b821b5..16b564cf 100644 --- a/cherab/core/laser/node.pyx +++ b/cherab/core/laser/node.pyx @@ -255,7 +255,7 @@ cdef class Laser(Node): def _plasma_changed(self): """React to change of plasma and propagate the information.""" - self._configure_materials() + self.configure_geometry() def _modified(self): - self._configure_materials() + self.configure_geometry() From f0ff4f25b09afe8e61fe213425944d16ac415d09 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Thu, 26 Oct 2023 21:41:28 +0200 Subject: [PATCH 054/134] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9af7646d..01daa26c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ New: * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) +Bug fixes: +* Fix deprecated cached transforms in LaserMaterial after laser.transform update Release 1.4.0 (3 Feb 2023) ------------------- From 09abc73540fe6fd9a08a8c083747349a69e6e4b2 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Thu, 26 Oct 2023 21:47:39 +0200 Subject: [PATCH 055/134] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01daa26c..5fde4c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ New: * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) Bug fixes: -* Fix deprecated cached transforms in LaserMaterial after laser.transform update +* Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) Release 1.4.0 (3 Feb 2023) ------------------- From 23d1a7b17889d7ec7a2f30c8f694d9c9be0fefb2 Mon Sep 17 00:00:00 2001 From: MatejTomes Date: Fri, 27 Oct 2023 11:38:20 +0200 Subject: [PATCH 056/134] Change LaserMaterial transforms caching The transforms are cached first time the emission method is called This should be a safer option The LaserMaterial now can be assigned only to a single primitive --- cherab/core/laser/material.pxd | 6 +++++- cherab/core/laser/material.pyx | 26 +++++++++++++++++++++----- cherab/core/laser/node.pxd | 4 +++- cherab/core/laser/node.pyx | 10 ++++++++-- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/cherab/core/laser/material.pxd b/cherab/core/laser/material.pxd index c91fa416..24695146 100644 --- a/cherab/core/laser/material.pxd +++ b/cherab/core/laser/material.pxd @@ -17,7 +17,7 @@ # under the Licence. -from raysect.core.scenegraph._nodebase cimport _NodeBase +from raysect.core cimport Primitive from raysect.core.math cimport AffineMatrix3D from raysect.optical.material.emitter cimport InhomogeneousVolumeEmitter @@ -28,4 +28,8 @@ cdef class LaserMaterial(InhomogeneousVolumeEmitter): cdef: AffineMatrix3D _laser_to_plasma, _laser_segment_to_laser_node + Primitive _primitive + Laser _laser list _models + + cdef void _cache_transforms(self) \ No newline at end of file diff --git a/cherab/core/laser/material.pyx b/cherab/core/laser/material.pyx index 4732e01a..75cc46a6 100644 --- a/cherab/core/laser/material.pyx +++ b/cherab/core/laser/material.pyx @@ -17,7 +17,7 @@ # under the Licence. -from raysect.core.scenegraph._nodebase cimport _NodeBase +from raysect.core cimport Primitive 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 @@ -28,12 +28,12 @@ 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): + def __init__(self, Laser laser not None, Primitive 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._laser = laser + self._primitive = laser_segment self.importance = laser.importance #validate and set models @@ -54,6 +54,10 @@ cdef class LaserMaterial(InhomogeneousVolumeEmitter): Point3D point_plasma, point_laser Vector3D direction_plasma, direction_laser LaserModel model + + # cache the important transforms + if self._laser_segment_to_laser_node is None or self._laser_to_plasma is None: + self._cache_transforms() 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 @@ -63,4 +67,16 @@ cdef class LaserMaterial(InhomogeneousVolumeEmitter): for model in self._models: spectrum = model.emission(point_plasma, direction_plasma, point_laser, direction_laser, spectrum) - return spectrum + return spectrum + + cdef void _cache_transforms(self): + """ + cache transforms from laser primitive to laser and plasma + """ + + # if transforms are cached, the material should be used only for one primitive for safety + if not len(self.primitives) == 1: + raise ValueError("LaserMaterial must be attached to exactly one primitive.") + + self._laser_segment_to_laser_node = self._primitive.to(self._laser) + self._laser_to_plasma = self._primitive.to(self._laser.get_plasma()) diff --git a/cherab/core/laser/node.pxd b/cherab/core/laser/node.pxd index 6045fb4a..8fec6821 100644 --- a/cherab/core/laser/node.pxd +++ b/cherab/core/laser/node.pxd @@ -52,4 +52,6 @@ cdef class Laser(Node): list _geometry VolumeIntegrator _integrator - cdef object __weakref__ \ No newline at end of file + cdef object __weakref__ + + cdef Plasma get_plasma(self) \ No newline at end of file diff --git a/cherab/core/laser/node.pyx b/cherab/core/laser/node.pyx index 16b564cf..9475fb69 100644 --- a/cherab/core/laser/node.pyx +++ b/cherab/core/laser/node.pyx @@ -153,6 +153,12 @@ cdef class Laser(Node): self._plasma.notifier.add(self._plasma_changed) self._configure_materials() + + cdef Plasma get_plasma(self): + """ + Fast method to obtain laser's plasma reference. + """ + return self._plasma @property def importance(self): @@ -255,7 +261,7 @@ cdef class Laser(Node): def _plasma_changed(self): """React to change of plasma and propagate the information.""" - self.configure_geometry() + self._configure_materials() def _modified(self): - self.configure_geometry() + self._configure_materials() From 91ae41a8a8660e02604a853385a27a57bad4b016 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 27 Oct 2023 13:10:26 +0300 Subject: [PATCH 057/134] Fix deprecated Polygon initialisation. --- cherab/tools/inversions/voxels.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/tools/inversions/voxels.pyx b/cherab/tools/inversions/voxels.pyx index 6baaa821..c682e01a 100644 --- a/cherab/tools/inversions/voxels.pyx +++ b/cherab/tools/inversions/voxels.pyx @@ -682,7 +682,7 @@ class ToroidalVoxelGrid(VoxelCollection): patches = [] for voxel in self: - polygon = Polygon([(v.x, v.y) for v in voxel.vertices], True) + polygon = Polygon([(v.x, v.y) for v in voxel.vertices], closed=True) patches.append(polygon) p = PatchCollection(patches, cmap=cmap) From bba72296cc776c802be0842cc7d0b1bf59465660 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Thu, 23 Nov 2023 13:54:31 +0300 Subject: [PATCH 058/134] Fix parsing of the ADF15 metadata for H-like ions. --- CHANGELOG.md | 1 + cherab/openadas/parse/adf15.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9af7646d..fdb1a75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ New: * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) +* Improved parsing of the ADAS ADF15 metadata for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) Release 1.4.0 (3 Feb 2023) diff --git a/cherab/openadas/parse/adf15.py b/cherab/openadas/parse/adf15.py index b9fe42be..14103c04 100644 --- a/cherab/openadas/parse/adf15.py +++ b/cherab/openadas/parse/adf15.py @@ -67,11 +67,18 @@ def parse_adf15(element, charge, adf_file_path, header_format=None): # use simple electron configuration structure for hydrogen-like ions if header_format == 'hydrogen' or element == hydrogen: config = _scrape_metadata_hydrogen(file, element, charge) - elif header_format == 'hydrogen-like' or element.atomic_number - charge == 1: + elif header_format == 'hydrogen-like': config = _scrape_metadata_hydrogen_like(file, element, charge) + elif element.atomic_number - charge == 1: + config = _scrape_metadata_hydrogen_like(file, element, charge) + if not config: # try hydrogen header (works for 'bnd' files) + config = _scrape_metadata_hydrogen(file, element, charge) else: config = _scrape_metadata_full(file, element, charge) + if not config: + raise RuntimeError("Unable to parse ADF15 metadata.") + # process rate data rates = RecursiveDict() for cls in ('excitation', 'recombination', 'thermalcx'): From 53ef1d32cf274595c8f9a80dff87ad324e1d3a3c Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 1 Dec 2023 17:13:46 +0300 Subject: [PATCH 059/134] Explicitly handle the 'bnd' ADF15 files when parsing the metadata. --- CHANGELOG.md | 2 +- cherab/openadas/parse/adf15.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb1a75e..dcc88e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ New: * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) -* Improved parsing of the ADAS ADF15 metadata for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) +* Improved parsing of metadata from the ADAS ADF15 'bnd' files for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) Release 1.4.0 (3 Feb 2023) diff --git a/cherab/openadas/parse/adf15.py b/cherab/openadas/parse/adf15.py index 14103c04..51bbcf6b 100644 --- a/cherab/openadas/parse/adf15.py +++ b/cherab/openadas/parse/adf15.py @@ -71,7 +71,8 @@ def parse_adf15(element, charge, adf_file_path, header_format=None): config = _scrape_metadata_hydrogen_like(file, element, charge) elif element.atomic_number - charge == 1: config = _scrape_metadata_hydrogen_like(file, element, charge) - if not config: # try hydrogen header (works for 'bnd' files) + if not config and 'bnd#' in adf_file_path: + # ADF15 files with the "bnd" suffix may have metadata in the "hydrogen" format config = _scrape_metadata_hydrogen(file, element, charge) else: config = _scrape_metadata_full(file, element, charge) From dc6afcea24ebb27e3eda1e042118325af6812d2a Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Mon, 4 Dec 2023 10:27:59 +0300 Subject: [PATCH 060/134] Added a test for the ExcitationLine model. --- cherab/core/tests/test_line_emission.py | 164 ++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 cherab/core/tests/test_line_emission.py diff --git a/cherab/core/tests/test_line_emission.py b/cherab/core/tests/test_line_emission.py new file mode 100644 index 00000000..56b0ff8e --- /dev/null +++ b/cherab/core/tests/test_line_emission.py @@ -0,0 +1,164 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.core import Point3D, Vector3D, translate +from raysect.optical import World, Spectrum, Ray + +from cherab.core.atomic import Line, AtomicData, ImpactExcitationPEC, RecombinationPEC, ThermalCXPEC +from cherab.core.atomic import deuterium, carbon +from cherab.tools.plasmas.slab import build_constant_slab_plasma +from cherab.core.model import ExcitationLine, RecombinationLine, ThermalCXLine, GaussianLine, ZeemanTriplet + + +class ConstantImpactExcitationPEC(ImpactExcitationPEC): + """ + Constant electron impact excitation PEC for test purpose. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, density, temperature): + + return self.value + + +class ConstantRecombinationPEC(RecombinationPEC): + """ + Constant recombination PEC for test purpose. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, density, temperature): + + return self.value + + +class ConstantThermalCXPEC(ThermalCXPEC): + """ + Constant recombination PEC for test purpose. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, electron_density, electron_temperature, donor_temperature): + + return self.value + + +class TestAtomicData(AtomicData): + """Fake atomic data for test purpose.""" + + def impact_excitation_pec(self, ion, charge, transition): + + return ConstantImpactExcitationPEC(1.4e-39) + + def recombination_pec(self, ion, charge, transition): + + return ConstantRecombinationPEC(8.e-41) + + def thermal_cx_pec(self, donor_ion, donor_charge, receiver_ion, receiver_charge, transition): + + return ConstantThermalCXPEC(1.2e-46) + + def wavelength(self, ion, charge, transition): + + return 529.27 + + +class TestExcitationLine(unittest.TestCase): + + world = World() + + atomic_data = TestAtomicData() + + plasma_species = [(carbon, 5, 2.e18, 200., Vector3D(0, 0, 0))] + slab_length = 1. + plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + plasma.atomic_data = atomic_data + plasma.parent = world + + def test_default_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [ExcitationLine(line)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + excit_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.impact_excitation_pec(line.element, line.charge, line.transition)(ne, te) + target_species = self.plasma.composition.get(line.element, line.charge) + ni = target_species.distribution.density(0.5, 0, 0) # constant slab + radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length + + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(excit_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='ExcitationLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + def test_custom_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [ExcitationLine(line, lineshape=ZeemanTriplet)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + excit_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.impact_excitation_pec(line.element, line.charge, line.transition)(ne, te) + target_species = self.plasma.composition.get(line.element, line.charge) + ni = target_species.distribution.density(0.5, 0, 0) # constant slab + radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length + + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(excit_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='ExcitationLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + +if __name__ == '__main__': + unittest.main() From 270241b8c88407b87c46362a3548a9f5de00d27f Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 4 Dec 2023 22:12:05 +0300 Subject: [PATCH 061/134] Added tests for ExcitationLine, RecombinationLine and ThermalCXLine. --- cherab/core/tests/test_line_emission.py | 163 +++++++++++++++++++++++- 1 file changed, 157 insertions(+), 6 deletions(-) diff --git a/cherab/core/tests/test_line_emission.py b/cherab/core/tests/test_line_emission.py index 56b0ff8e..eb5a8599 100644 --- a/cherab/core/tests/test_line_emission.py +++ b/cherab/core/tests/test_line_emission.py @@ -94,8 +94,8 @@ class TestExcitationLine(unittest.TestCase): atomic_data = TestAtomicData() - plasma_species = [(carbon, 5, 2.e18, 200., Vector3D(0, 0, 0))] - slab_length = 1. + plasma_species = [(carbon, 5, 2.e18, 800., Vector3D(0, 0, 0))] + slab_length = 1.2 plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) plasma.atomic_data = atomic_data @@ -115,11 +115,11 @@ def test_default_lineshape(self): excit_spectrum = ray.trace(self.world) # validating - ne = self.plasma.electron_distribution.density(0.5, 0, 0) + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) rate = self.atomic_data.impact_excitation_pec(line.element, line.charge, line.transition)(ne, te) target_species = self.plasma.composition.get(line.element, line.charge) - ni = target_species.distribution.density(0.5, 0, 0) # constant slab + ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) @@ -144,11 +144,11 @@ def test_custom_lineshape(self): excit_spectrum = ray.trace(self.world) # validating - ne = self.plasma.electron_distribution.density(0.5, 0, 0) + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) rate = self.atomic_data.impact_excitation_pec(line.element, line.charge, line.transition)(ne, te) target_species = self.plasma.composition.get(line.element, line.charge) - ni = target_species.distribution.density(0.5, 0, 0) # constant slab + ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) @@ -160,5 +160,156 @@ def test_custom_lineshape(self): msg='ExcitationLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) +class TestRecombinationLine(unittest.TestCase): + + world = World() + + atomic_data = TestAtomicData() + + plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0))] + slab_length = 1.2 + plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + plasma.atomic_data = atomic_data + plasma.parent = world + + def test_default_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [RecombinationLine(line)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + recomb_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.recombination_pec(line.element, line.charge, line.transition)(ne, te) + target_species = self.plasma.composition.get(line.element, line.charge + 1) + ni = target_species.distribution.density(0.5, 0, 0) + radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length + + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(recomb_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='RecombinationLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + def test_custom_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [RecombinationLine(line, lineshape=ZeemanTriplet)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + recomb_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.recombination_pec(line.element, line.charge, line.transition)(ne, te) + target_species = self.plasma.composition.get(line.element, line.charge + 1) + ni = target_species.distribution.density(0.5, 0, 0) + radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length + + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(recomb_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='RecombinationLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + +class TestThermalCXLine(unittest.TestCase): + + world = World() + + atomic_data = TestAtomicData() + + plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0)), + (deuterium, 0, 1.e19, 100., Vector3D(0, 0, 0))] + slab_length = 1.2 + plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + plasma.atomic_data = atomic_data + plasma.parent = world + + def test_default_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [ThermalCXLine(line)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + thermalcx_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + donor_species = self.plasma.composition.get(deuterium, 0) + donor_density = donor_species.distribution.density(0.5, 0, 0) + donor_temperature = donor_species.distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.thermal_cx_pec(deuterium, 0, line.element, line.charge, line.transition)(ne, te, donor_temperature) + target_species = self.plasma.composition.get(line.element, line.charge + 1) + receiver_density = target_species.distribution.density(0.5, 0, 0) + radiance = 0.25 / np.pi * rate * receiver_density * donor_density * self.slab_length + + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(thermalcx_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='ThermalCXLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + def test_custom_lineshape(self): + # setting up the model + line = Line(carbon, 5, (8, 7)) + self.plasma.models = [ThermalCXLine(line, lineshape=ZeemanTriplet)] + wavelength = self.atomic_data.wavelength(line.element, line.charge, line.transition) + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=wavelength - 1.5, max_wavelength=wavelength + 1.5, bins=512) + thermalcx_spectrum = ray.trace(self.world) + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0, 0) # constant slab + te = self.plasma.electron_distribution.effective_temperature(0.5, 0, 0) + donor_species = self.plasma.composition.get(deuterium, 0) + donor_density = donor_species.distribution.density(0.5, 0, 0) + donor_temperature = donor_species.distribution.effective_temperature(0.5, 0, 0) + rate = self.atomic_data.thermal_cx_pec(deuterium, 0, line.element, line.charge, line.transition)(ne, te, donor_temperature) + target_species = self.plasma.composition.get(line.element, line.charge + 1) + receiver_density = target_species.distribution.density(0.5, 0, 0) + radiance = 0.25 / np.pi * rate * receiver_density * donor_density * self.slab_length + + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) + spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) + + for i in range(ray.bins): + self.assertAlmostEqual(thermalcx_spectrum.samples[i], spectrum.samples[i], delta=1e-8, + msg='ThermalCXLine model gives a wrong value at {} nm.'.format(spectrum.wavelengths[i])) + + if __name__ == '__main__': unittest.main() From fb6e5497d33161a4b7bd98a978b90498016c15f7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 4 Dec 2023 22:13:04 +0300 Subject: [PATCH 062/134] Fix error in BeamCXLine docstring. --- cherab/core/model/beam/charge_exchange.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 295d5f5f..314cfd43 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -61,6 +61,7 @@ cdef class BeamCXLine(BeamModel): :ivar Line line: The emission line object. .. code-block:: pycon + >>> from cherab.core.model import BeamCXLine >>> from cherab.core.atomic import carbon >>> from cherab.core.model import ParametrisedZeemanTriplet From 7b068c7a2486ea7320b7a72fcbd238806883fce3 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 4 Dec 2023 22:15:30 +0300 Subject: [PATCH 063/134] Added documentation for line emission models. --- .../core/model/plasma/impact_excitation.pyx | 23 ++++++++++++++++ cherab/core/model/plasma/recombination.pyx | 23 ++++++++++++++++ cherab/core/model/plasma/thermal_cx.pyx | 26 +++++++++++++++++++ .../models/basic_line/basic_line_emission.rst | 6 ++++- 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 343059ad..70abdfe0 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -23,6 +23,29 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ExcitationLine(PlasmaModel): + """ + Emitter that calculates spectral line emission from a plasma object + as a result of excitation of the target species by electron impact. + + .. math:: + \\epsilon_{\\mathrm{excit}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i}} n_\\mathrm{e} + \\mathrm{PEC}_{\\mathrm{excit}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + + where :math:`n_{Z_\\mathrm{i}}` is the target species density, + :math:`\\mathrm{PEC}_{\\mathrm{excit}}` is the electron impact excitation photon emission coefficient + for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, + :math:`f(\\lambda)` is the normalised spectral line shape, + + :param Line line: Spectroscopic emission line object. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + :param object lineshape: Line shape model class. Default is None (GaussianLine). + :param object lineshape_args: A list of line shape model arguments. Default is None. + :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. + + :ivar Plasma plasma: The plasma to which this emission model is attached. + :ivar AtomicData atomic_data: The atomic data provider for this model. + """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/cherab/core/model/plasma/recombination.pyx b/cherab/core/model/plasma/recombination.pyx index 6edac138..c025cb26 100644 --- a/cherab/core/model/plasma/recombination.pyx +++ b/cherab/core/model/plasma/recombination.pyx @@ -23,6 +23,29 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class RecombinationLine(PlasmaModel): + """ + Emitter that calculates spectral line emission from a plasma object + as a result of dielectronic recombination of the target species. + + .. math:: + \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} n_\\mathrm{e} + \\mathrm{PEC}_{\\mathrm{recomb}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + + where :math:`n_{Z_\\mathrm{i} + 1}` is the recombining species density, + :math:`\\mathrm{PEC}_{\\mathrm{recomb}}` is the dielectronic recombination photon emission coefficient + for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, + :math:`f(\\lambda)` is the normalised spectral line shape, + + :param Line line: Spectroscopic emission line object. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + :param object lineshape: Line shape model class. Default is None (GaussianLine). + :param object lineshape_args: A list of line shape model arguments. Default is None. + :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. + + :ivar Plasma plasma: The plasma to which this emission model is attached. + :ivar AtomicData atomic_data: The atomic data provider for this model. + """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx index f290649a..a5f08a4b 100644 --- a/cherab/core/model/plasma/thermal_cx.pyx +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -24,6 +24,32 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ThermalCXLine(PlasmaModel): + """ + Emitter that calculates spectral line emission from a plasma object + as a result of thermal charge exchange of the target species with the donor species. + + .. math:: + \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} + \\sum_j{n_{Z_\\mathrm{j}} \\mathrm{PEC}_{\\mathrm{cx}}(n_\\mathrm{e}, T_\\mathrm{e}, T_{Z_\\mathrm{j}})} + f(\\lambda), + + where :math:`n_{Z_\\mathrm{i} + 1}` is the receiver species density, + :math:`n_{Z_\\mathrm{j}}` is the donor species density, + :math:`\\mathrm{PEC}_{\\mathrm{cx}}` is the thermal CX photon emission coefficient + for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, + :math:`T_{Z_\\mathrm{j}}` is the donor species temperature, + :math:`f(\\lambda)` is the normalised spectral line shape, + + :param Line line: Spectroscopic emission line object. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + :param object lineshape: Line shape model class. Default is None (GaussianLine). + :param object lineshape_args: A list of line shape model arguments. Default is None. + :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. + + :ivar Plasma plasma: The plasma to which this emission model is attached. + :ivar AtomicData atomic_data: The atomic data provider for this model. + """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/docs/source/models/basic_line/basic_line_emission.rst b/docs/source/models/basic_line/basic_line_emission.rst index c07995db..b71fc03c 100644 --- a/docs/source/models/basic_line/basic_line_emission.rst +++ b/docs/source/models/basic_line/basic_line_emission.rst @@ -2,4 +2,8 @@ Basic Line Emission =================== -Documentation for this model will go here soon... +.. autoclass:: cherab.core.model.plasma.impact_excitation.ExcitationLine + +.. autoclass:: cherab.core.model.plasma.recombination.RecombinationLine + +.. autoclass:: cherab.core.model.plasma.thermal_cx.ThermalCXLine From 259971723854bf3d3227de950960b68282fe6559 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 4 Dec 2023 22:15:56 +0300 Subject: [PATCH 064/134] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13202802..ea77c327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ New: * Replace the coarse numerical constant in the Bremsstrahlung model with an exact expression. (#409) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) * Improved parsing of metadata from the ADAS ADF15 'bnd' files for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) +* Add thermal charge-exchange emission model. (#57) Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) From 0518d727eb5a83f89a2b71d9212507a00adc2a93 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 5 Dec 2023 17:46:40 +0300 Subject: [PATCH 065/134] Remove target species as a donor from cached CX PECs. --- cherab/core/model/plasma/thermal_cx.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx index a5f08a4b..5f157bb7 100644 --- a/cherab/core/model/plasma/thermal_cx.pyx +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -137,7 +137,7 @@ cdef class ThermalCXLine(PlasmaModel): # obtain rate functions self._rates = [] for species in self._plasma.composition: - if species.charge < species.element.atomic_number: + if species != self._target_species and species.charge < species.element.atomic_number: rate = self._atomic_data.thermal_cx_pec(species.element, species.charge, # donor self._line.element, receiver_charge, # receiver self._line.transition) From 25428af6adfd2df07eee73fbe92add4a1d2e0fd8 Mon Sep 17 00:00:00 2001 From: Vladislav Neverov Date: Tue, 5 Dec 2023 17:47:39 +0300 Subject: [PATCH 066/134] Add demo for ThermalCXLine model. --- demos/emission_models/charge_exchange.py | 2 +- .../thermal_charge_exchange.py | 130 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 demos/emission_models/thermal_charge_exchange.py diff --git a/demos/emission_models/charge_exchange.py b/demos/emission_models/charge_exchange.py index 11d95386..02d61235 100644 --- a/demos/emission_models/charge_exchange.py +++ b/demos/emission_models/charge_exchange.py @@ -122,7 +122,7 @@ integration_step = 0.0025 beam_transform = translate(-0.5, 0.0, 0) * rotate_basis(Vector3D(1, 0, 0), Vector3D(0, 0, 1)) -beam_energy = 50000 # keV +beam_energy = 50000 # eV beam_full = Beam(parent=world, transform=beam_transform) beam_full.plasma = plasma diff --git a/demos/emission_models/thermal_charge_exchange.py b/demos/emission_models/thermal_charge_exchange.py new file mode 100644 index 00000000..f4cead84 --- /dev/null +++ b/demos/emission_models/thermal_charge_exchange.py @@ -0,0 +1,130 @@ + +import numpy as np +import matplotlib.pyplot as plt +from scipy.constants import electron_mass, atomic_mass + +from raysect.core import translate, rotate_basis, Point3D, Vector3D +from raysect.primitive import Box +from raysect.optical import World, Ray + +from cherab.core import Plasma, Beam, Maxwellian, Species +from cherab.core.math import ScalarToVectorFunction3D +from cherab.core.atomic import hydrogen, deuterium, carbon, Line +from cherab.core.model import SingleRayAttenuator, BeamCXLine, ThermalCXLine +from cherab.tools.plasmas.slab import NeutralFunction, IonFunction +from cherab.openadas import OpenADAS +from cherab.openadas.install import install_adf15 + + +# Install PECs for CVI spectral lines +install_adf15(carbon, 5, 'adf15/pec96#c/pec96#c_pju#c5.dat', download=True) + +############### +# Make Plasma # + +width = 1.0 +length = 1.0 +height = 3.0 +peak_density = 5e19 +edge_density = 1e18 +pedestal_top = 1 +neutral_temperature = 0.5 +peak_temperature = 2500 +edge_temperature = 25 +impurities = [(carbon, 6, 0.005)] + +world = World() +adas = OpenADAS(permit_extrapolation=True, missing_rates_return_null=True) + +plasma = Plasma(parent=world) +plasma.atomic_data = adas + +# plasma slab along x direction +plasma.geometry = Box(Point3D(0, -width / 2, -height / 2), Point3D(length, width / 2, height / 2)) + +species = [] + +# make a non-zero velocity profile for the plasma +vy_profile = IonFunction(1E5, 0, pedestal_top=pedestal_top) +velocity_profile = ScalarToVectorFunction3D(0, vy_profile, 0) + +# define neutral species distribution +h0_density = NeutralFunction(peak_density, 0.1, pedestal_top=pedestal_top) +h0_temperature = neutral_temperature +h0_distribution = Maxwellian(h0_density, h0_temperature, velocity_profile, + hydrogen.atomic_weight * atomic_mass) +species.append(Species(hydrogen, 0, h0_distribution)) + +# define hydrogen ion species distribution +h1_density = IonFunction(peak_density, edge_density, pedestal_top=pedestal_top) +h1_temperature = IonFunction(peak_temperature, edge_temperature, pedestal_top=pedestal_top) +h1_distribution = Maxwellian(h1_density, h1_temperature, velocity_profile, + hydrogen.atomic_weight * atomic_mass) +species.append(Species(hydrogen, 1, h1_distribution)) + +# add impurities +if impurities: + for impurity, ionisation, concentration in impurities: + imp_density = IonFunction(peak_density * concentration, edge_density * concentration, pedestal_top=pedestal_top) + imp_temperature = IonFunction(peak_temperature, edge_temperature, pedestal_top=pedestal_top) + imp_distribution = Maxwellian(imp_density, imp_temperature, velocity_profile, + impurity.atomic_weight * atomic_mass) + species.append(Species(impurity, ionisation, imp_distribution)) + +# define the electron distribution +e_density = IonFunction(peak_density, edge_density, pedestal_top=pedestal_top) +e_temperature = IonFunction(peak_temperature, edge_temperature, pedestal_top=pedestal_top) +e_distribution = Maxwellian(e_density, e_temperature, velocity_profile, electron_mass) + +# define species +plasma.electron_distribution = e_distribution +plasma.composition = species + +# add thermal CX emission model +cVI_5_4 = Line(carbon, 5, (5, 4)) +plasma.models = [ThermalCXLine(cVI_5_4)] + +# trace thermal CX spectrum along y direction +ray = Ray(origin=Point3D(0.4, -3.5, 0), direction=Vector3D(0, 1, 0), + min_wavelength=112.3, max_wavelength=112.7, bins=512) +thermal_cx_spectrum = ray.trace(world) + +########################### +# Inject beam into plasma # + +integration_step = 0.0025 +# injected along x direction +beam_transform = translate(-0.5, 0.0, 0) * rotate_basis(Vector3D(1, 0, 0), Vector3D(0, 0, 1)) +beam_energy = 50000 # eV + +beam = Beam(parent=world, transform=beam_transform) +beam.plasma = plasma +beam.atomic_data = adas +beam.energy = beam_energy +beam.power = 3e6 +beam.element = deuterium +beam.sigma = 0.05 +beam.divergence_x = 0.5 +beam.divergence_y = 0.5 +beam.length = 3.0 +beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) +beam.integrator.step = integration_step +beam.integrator.min_samples = 10 + +# remove thermal CX model and add beam CX model +plasma.models = [] +beam.models = [BeamCXLine(cVI_5_4)] + +# trace the spectrum again +beam_cx_spectrum = ray.trace(world) + +# plot the spectra +plt.figure() +plt.plot(thermal_cx_spectrum.wavelengths, thermal_cx_spectrum.samples, label='thermal CX') +plt.plot(beam_cx_spectrum.wavelengths, beam_cx_spectrum.samples, label='beam CX') +plt.legend() +plt.xlabel('Wavelength (nm)') +plt.ylabel('Radiance (W/m^2/str/nm)') +plt.title('Sampled CX spectra') + +plt.show() From b84f9ad459c74336d2bedc10ff7b2350c261f125 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 19 Dec 2023 14:15:14 +0300 Subject: [PATCH 067/134] Add missing declarations for internal variables in ZeemanMultiplet. --- cherab/core/model/lineshape/zeeman.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cherab/core/model/lineshape/zeeman.pyx b/cherab/core/model/lineshape/zeeman.pyx index eeea9f7b..2be33020 100644 --- a/cherab/core/model/lineshape/zeeman.pyx +++ b/cherab/core/model/lineshape/zeeman.pyx @@ -308,9 +308,9 @@ cdef class ZeemanMultiplet(ZeemanLineShapeModel): cpdef Spectrum add_line(self, double radiance, Point3D point, Vector3D direction, Spectrum spectrum): cdef int i - cdef double ts, sigma, shifted_wavelength, component_radiance - cdef Vector3D ion_velocity - cdef double[:, :] multiplet_pi_mv, multiplet_sigma_mv + cdef double ts, sigma, shifted_wavelength, component_radiance, b_magn, cos_sqr, sin_sqr + cdef Vector3D ion_velocity, b_field + cdef double[:, :] multiplet_mv ts = self.target_species.distribution.effective_temperature(point.x, point.y, point.z) if ts <= 0.0: From 745a3ea1030b7536510e588d8f7da50171e7b761 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Mon, 4 Mar 2024 16:37:58 +0000 Subject: [PATCH 068/134] Fix IRVB calculate sensitivity method Spectral pipelines have no display_progress option. --- CHANGELOG.md | 1 + cherab/tools/observers/bolometry.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13202802..8156571a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ New: Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) +* Fix IRVB calculate sensitivity method. Release 1.4.0 (3 Feb 2023) ------------------- diff --git a/cherab/tools/observers/bolometry.py b/cherab/tools/observers/bolometry.py index dc149b29..20179796 100644 --- a/cherab/tools/observers/bolometry.py +++ b/cherab/tools/observers/bolometry.py @@ -943,7 +943,7 @@ def calculate_sensitivity(self, voxel_collection, ray_count=None): raise TypeError("voxel_collection must be of type VoxelCollection") pipeline_class = self._SPECTRAL_PIPELINES[self._units] - pipeline = pipeline_class(display_progress=False) + pipeline = pipeline_class() voxel_collection.set_active("all") From 3e1208149c7108907c6829c019512e612f19f1d4 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 2 Apr 2024 13:26:16 +0300 Subject: [PATCH 069/134] Add support for Apple GPUs to get_flops() function. --- cherab/tools/inversions/opencl/opencl_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cherab/tools/inversions/opencl/opencl_utils.py b/cherab/tools/inversions/opencl/opencl_utils.py index 51747a93..cefb78ba 100644 --- a/cherab/tools/inversions/opencl/opencl_utils.py +++ b/cherab/tools/inversions/opencl/opencl_utils.py @@ -75,7 +75,7 @@ def get_flops(device, verbose=False): elif "amd" in vendor or "advanced" in vendor: try: ww = device.get_info(cl.device_info.WAVEFRONT_WIDTH_AMD) - except: + except AttributeError: ww = 64 gflops = comp_units * ww * 2 * gpu_clock / 1000. @@ -85,6 +85,10 @@ def get_flops(device, verbose=False): elif "arm" in vendor: gflops = comp_units * 2 * 16 * gpu_clock / 1000. + elif "apple" in vendor: + alu_lanes = 128 + gflops = comp_units * 2 * alu_lanes * gpu_clock / 1000. + else: warnings.warn('Unsupported device vendor: {}. Unable to estimate theoretical peak performance.'.format(vendor)) return 0 From e4f8071e2ee92d3d2a5e79bd5aed4d26f78e4c9a Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 25 Apr 2024 00:28:09 +0300 Subject: [PATCH 070/134] Improve beam direction and beam density calculations. --- CHANGELOG.md | 2 ++ cherab/core/beam/node.pxd | 2 +- cherab/core/beam/node.pyx | 22 ++++++++++++++++------ cherab/core/model/attenuator/singleray.pyx | 7 ++++--- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8156571a..caeae178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ New: * Replace the coarse numerical constant in the Bremsstrahlung model with an exact expression. (#409) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) * Improved parsing of metadata from the ADAS ADF15 'bnd' files for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) +* Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414) +* Improved beam direction calculation to allow for natural broadening of the BES line shape due to beam divergence. (#414) Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) diff --git a/cherab/core/beam/node.pxd b/cherab/core/beam/node.pxd index d8159bbb..8f58e0c0 100644 --- a/cherab/core/beam/node.pxd +++ b/cherab/core/beam/node.pxd @@ -47,7 +47,7 @@ cdef class Beam(Node): Vector3D BEAM_AXIS double _energy, _power, _temperature Element _element - double _divergence_x, _divergence_y + double _divergence_x, _divergence_y, _tanxdiv, _tanydiv double _length, _sigma Plasma _plasma AtomicData _atomic_data diff --git a/cherab/core/beam/node.pyx b/cherab/core/beam/node.pyx index 36877421..9fede133 100644 --- a/cherab/core/beam/node.pyx +++ b/cherab/core/beam/node.pyx @@ -30,7 +30,7 @@ from cherab.core.beam.material cimport BeamMaterial from cherab.core.beam.model cimport BeamModel from cherab.core.atomic cimport AtomicData, Element from cherab.core.utility import Notifier -from libc.math cimport tan, M_PI +from libc.math cimport tan, M_PI, sqrt cdef double DEGREES_TO_RADIANS = M_PI / 180 @@ -188,6 +188,8 @@ cdef class Beam(Node): self._element = element = None # beam species, an Element object self._divergence_x = 0.0 # beam divergence x (degrees) self._divergence_y = 0.0 # beam divergence y (degrees) + self._tanxdiv = 0.0 # tan(DEGREES_TO_RADIANS * divergence_x) + self._tanydiv = 0.0 # tan(DEGREES_TO_RADIANS * divergence_y) self._length = 1.0 # m self._sigma = 0.1 # m (gaussian beam width at origin) @@ -248,9 +250,15 @@ cdef class Beam(Node): return self.BEAM_AXIS # calculate direction from divergence - cdef double dx = tan(DEGREES_TO_RADIANS * self._divergence_x) - cdef double dy = tan(DEGREES_TO_RADIANS * self._divergence_y) - return new_vector3d(dx, dy, 1.0).normalise() + cdef double z_tanx_sqr = z * z * self._tanxdiv * self._tanxdiv + cdef double z_tany_sqr = z * z * self._tanydiv * self._tanydiv + cdef double sigma_sqr = self._sigma * self._sigma + cdef double sigma_x_sqr = sigma_sqr + z_tanx_sqr + cdef double sigma_y_sqr = sigma_sqr + z_tany_sqr + cdef double ex = x * z_tanx_sqr / sigma_x_sqr + cdef double ey = y * z_tany_sqr / sigma_y_sqr + + return new_vector3d(ex, ey, z).normalise() @property def energy(self): @@ -315,6 +323,7 @@ cdef class Beam(Node): if value < 0: raise ValueError('Beam x divergence cannot be less than zero.') self._divergence_x = value + self._tanxdiv = tan(DEGREES_TO_RADIANS * value) self.notifier.notify() cdef double get_divergence_x(self): @@ -329,6 +338,7 @@ cdef class Beam(Node): if value < 0: raise ValueError('Beam y divergence cannot be less than zero.') self._divergence_y = value + self._tanydiv = tan(DEGREES_TO_RADIANS * value) self.notifier.notify() cdef double get_divergence_y(self): @@ -501,10 +511,10 @@ cdef class Beam(Node): # radii of bounds at the beam origin (z=0) and the beam end (z=length) radius_start = num_sigma * self.sigma - radius_end = radius_start + self.length * num_sigma * drdz + radius_end = num_sigma * sqrt(self.sigma**2 + self.length**2 * drdz**2) # distance of the cone apex to the beam origin - distance_apex = radius_start / (num_sigma * drdz) + distance_apex = radius_start * self.length / (radius_end - radius_start) cone_height = self.length + distance_apex # calculate volumes diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index a2cfbde1..ca611d09 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -97,7 +97,7 @@ cdef class SingleRayAttenuator(BeamAttenuator): :return: Density in m^-3. """ - cdef double sigma_x, sigma_y, norm_radius_sqr, gaussian_sample + cdef double sigma0_sqr, sigma_x, sigma_y, norm_radius_sqr, gaussian_sample # use cached data if available if self._stopping_data is None: @@ -107,8 +107,9 @@ cdef class SingleRayAttenuator(BeamAttenuator): self._calc_attenuation() # calculate beam width - sigma_x = self._beam.get_sigma() + z * self._tanxdiv - sigma_y = self._beam.get_sigma() + z * self._tanydiv + sigma0_sqr = self._beam.get_sigma()**2 + sigma_x = sqrt(sigma0_sqr + (z * self._tanxdiv)**2) + sigma_y = sqrt(sigma0_sqr + (z * self._tanydiv)**2) # normalised radius squared norm_radius_sqr = ((x / sigma_x)**2 + (y / sigma_y)**2) From 8a2eb6382c3cb1a261c20235a74b5049499615b9 Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 27 Apr 2024 12:22:36 +0300 Subject: [PATCH 071/134] Fix incorrect docstrings for atomic rate interface classes. --- cherab/core/atomic/rates.pxd | 6 +-- cherab/core/atomic/rates.pyx | 79 ++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/cherab/core/atomic/rates.pxd b/cherab/core/atomic/rates.pxd index 1f844122..006dbf8a 100644 --- a/cherab/core/atomic/rates.pxd +++ b/cherab/core/atomic/rates.pxd @@ -75,7 +75,7 @@ cdef class TotalRadiatedPower: cdef: readonly Element element - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 + cdef double evaluate(self, double density, double temperature) except? -1e999 cdef class _RadiatedPower: @@ -84,7 +84,7 @@ cdef class _RadiatedPower: readonly Element element readonly int charge - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 + cdef double evaluate(self, double density, double temperature) except? -1e999 cdef class LineRadiationPower(_RadiatedPower): @@ -106,4 +106,4 @@ cdef class FractionalAbundance: readonly int charge public str name - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 + cdef double evaluate(self, double density, double temperature) except? -1e999 diff --git a/cherab/core/atomic/rates.pyx b/cherab/core/atomic/rates.pyx index 25b94e7e..a8f10141 100644 --- a/cherab/core/atomic/rates.pyx +++ b/cherab/core/atomic/rates.pyx @@ -35,9 +35,9 @@ cdef class IonisationRate: cpdef double evaluate(self, double density, double temperature) except? -1e999: """Returns an effective ionisation rate coefficient at the specified plasma conditions. + :param density: Electron density in m^-3. :param temperature: Electron temperature in eV. - :param density: Electron density in m^-3 - :return: The effective ionisation rate in m^-3. + :return: The effective ionisation rate in m^3.s^-1. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -57,9 +57,9 @@ cdef class RecombinationRate: cpdef double evaluate(self, double density, double temperature) except? -1e999: """Returns an effective recombination rate coefficient at the specified plasma conditions. + :param density: Electron density in m^-3. :param temperature: Electron temperature in eV. - :param density: Electron density in m^-3 - :return: The effective ionisation rate in m^-3. + :return: The effective ionisation rate in m^3.s^-1. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -79,9 +79,9 @@ cdef class ThermalCXRate: cpdef double evaluate(self, double density, double temperature) except? -1e999: """Returns an effective charge exchange rate coefficient at the specified plasma conditions. + :param density: Electron density in m^-3. :param temperature: Electron temperature in eV. - :param density: Electron density in m^-3 - :return: The effective charge exchange rate in m^-3. + :return: The effective charge exchange rate in m^3.s^-1. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -101,9 +101,9 @@ cdef class _PECRate: cpdef double evaluate(self, double density, double temperature) except? -1e999: """Returns a photon emissivity coefficient at given conditions. - :param temperature: Receiver ion temperature in eV. - :param density: Receiver ion density in m^-3 - :return: The effective PEC rate in W/m^3. + :param density: Electron density in m^-3. + :param temperature: Electron temperature in eV. + :return: The effective PEC rate in W.m^3. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -221,29 +221,28 @@ cdef class BeamEmissionPEC(_BeamRate): cdef class TotalRadiatedPower(): - """The total radiated power in equilibrium conditions.""" + """The total radiated power rate in equilibrium conditions.""" def __init__(self, Element element): self.element = element - def __call__(self, double electron_density, double electron_temperature): + def __call__(self, double density, double temperature): """ - Evaluate the radiated power rate at the given plasma conditions. + Evaluate the total radiated power rate at the given plasma conditions. - Calls the cython evaluate() method under the hood. - - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + This function just wraps the cython evaluate() method. """ - return self.evaluate(electron_density, electron_temperature) + return self.evaluate(density, temperature) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cdef double evaluate(self, double density, double temperature) except? -1e999: """ - Evaluate the radiated power at the given plasma conditions. + Evaluate the total radiated power rate at the given plasma conditions. - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + :param float density: Electron density in m^-3. + :param float temperature: Electron temperature in eV. + + :return: The total radiated power rate in W.m^3. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -256,23 +255,22 @@ cdef class _RadiatedPower: self.element = element self.charge = charge - def __call__(self, double electron_density, double electron_temperature): + def __call__(self, double density, double temperature): """ Evaluate the radiated power rate at the given plasma conditions. - Calls the cython evaluate() method under the hood. - - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + This function just wraps the cython evaluate() method. """ - return self.evaluate(electron_density, electron_temperature) + return self.evaluate(density, temperature) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cdef double evaluate(self, double density, double temperature) except? -1e999: """ Evaluate the radiated power at the given plasma conditions. - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + :param float density: Electron density in m^-3. + :param float temperature: Electron temperature in eV. + + :return: The radiated power rate in W.m^3. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") @@ -308,9 +306,9 @@ cdef class FractionalAbundance: """ Rate provider for fractional abundances in thermodynamic equilibrium. - :param Element element: the radiating element - :param int charge: the integer charge state for this ionisation stage - :param str name: optional label identifying this rate + :param Element element: the radiating element. + :param int charge: the integer charge state for this ionisation stage. + :param str name: optional label identifying this rate. """ def __init__(self, element, charge, name=''): @@ -322,23 +320,24 @@ cdef class FractionalAbundance: raise ValueError("Charge state must be neutral or positive.") self.charge = charge - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cdef double evaluate(self, double density, double temperature) except? -1e999: """ Evaluate the fractional abundance of this ionisation stage at the given plasma conditions. - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + :param float density: Electron density in m^-3. + :param float temperature: Electron temperature in eV. + + :return: Fractional abundance. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") - def __call__(self, double electron_density, double electron_temperature): + def __call__(self, double density, double temperature): """ Evaluate the fractional abundance of this ionisation stage at the given plasma conditions. - :param float electron_density: electron density in m^-3 - :param float electron_temperature: electron temperature in eV + This function just wraps the cython evaluate() method. """ - return self.evaluate(electron_density, electron_temperature) + return self.evaluate(density, temperature) def plot_temperature(self, temp_low=1, temp_high=1000, num_points=100, dens=1E19): From 68189f19fd1ad0e58b0180258066755d393519e8 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 2 May 2024 01:49:45 +0300 Subject: [PATCH 072/134] Update BES demo and beam-related pictures in the docs. --- .../emission_models/beam_emission_spectrum.py | 4 ++-- .../active_spectroscopy/BES_camera.png | Bin 4602 -> 7702 bytes .../active_spectroscopy/BES_sightline.png | Bin 24390 -> 25377 bytes .../active_spectroscopy/BES_spectrum_full.png | Bin 23734 -> 24021 bytes .../BES_spectrum_zoomed.png | Bin 36765 -> 37615 bytes .../active_spectroscopy/CXS_camera.png | Bin 5427 -> 5067 bytes .../CXS_multi_sightlines.png | Bin 58844 -> 59708 bytes .../active_spectroscopy/CXS_spectrum.png | Bin 29530 -> 24577 bytes .../plasmas/beam_attenuation.png | Bin 43674 -> 42157 bytes .../plasmas/beam_density_xz.png | Bin 31858 -> 31660 bytes .../plasmas/beam_into_plasma.png | Bin 5541 -> 4390 bytes 11 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/emission_models/beam_emission_spectrum.py b/demos/emission_models/beam_emission_spectrum.py index f7675449..cbf5505b 100644 --- a/demos/emission_models/beam_emission_spectrum.py +++ b/demos/emission_models/beam_emission_spectrum.py @@ -69,9 +69,9 @@ beam_energy = 110000 # keV beam_current = 10 # A beam_sigma = 0.05 -beam_divergence = 0.5 +beam_divergence = 1.3 # degree beam_length = 3.0 -beam_temperature = 20 +beam_temperature = 1.0 bes_full_model = BeamEmissionLine(Line(deuterium, 0, (3, 2)), sigma_to_pi=SIGMA_TO_PI, sigma1_to_sigma0=SIGMA1_TO_SIGMA0, diff --git a/docs/source/demonstrations/active_spectroscopy/BES_camera.png b/docs/source/demonstrations/active_spectroscopy/BES_camera.png index 860e9c2da4d6416480ec04c913c811ea7108fe4b..7ad3b0624608da48743524270000060309a14d55 100644 GIT binary patch literal 7702 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 literal 4602 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 Xp*VS-`YQeh3dntT^Loj;oge=P&Fz4! diff --git a/docs/source/demonstrations/active_spectroscopy/BES_sightline.png b/docs/source/demonstrations/active_spectroscopy/BES_sightline.png index c15a207f18558d9d722860048a1b5f0503900e2d..c71d2f87f01367c98abbee463d791f54d9670fc9 100644 GIT binary patch literal 25377 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 O#tPGwCO3^7gZ~W+5jlJS literal 24390 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 YwN7i@)>Q7t`Gi6h7`pbtho2t$7xk1obN~PV literal 23734 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 diff --git a/docs/source/demonstrations/active_spectroscopy/BES_spectrum_zoomed.png b/docs/source/demonstrations/active_spectroscopy/BES_spectrum_zoomed.png index b3b461bb449560edb25e34dbbaf2abbea2fe948e..3c288ee4e34f6cdec1a7e56f577a3b8aa75f41ba 100644 GIT binary patch literal 37615 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 OIjMPCqfpH{?0*3Vw-d$y literal 36765 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 z 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 Vp47Vn&~pU-sHH diff --git a/docs/source/demonstrations/active_spectroscopy/CXS_camera.png b/docs/source/demonstrations/active_spectroscopy/CXS_camera.png index c390e0cde15783db7cfb850a1f39813366c795d4..7f8fa5cf7c983bc95ead589a319e84aa6586444d 100644 GIT binary patch literal 5067 zcmeHLTU1k58a{|3wj%A&o0JBo?Xv=|_@7AT1^NVw!ij#WqO z1*1#bv?4jJ7E=p>N`Q!j94ysPAVeEUlnB9w5HU?sa)2b{awfja$m-KP^~Hz1_W!Wg z-v8SF_kI86lO37cA|8o;1OR}DwC$VU27m=Ya{&+@7Tl)UH|By{QrVXG%CeY+WtF>1 z3xJGWWhHxWfE#Q+esE^Tw_JM7c85kre%>z506q%lT@_ma0^7%6k$P5CNH3#fTx z^NO{5H)6%ip{;WjM-R-1>ir~vSXZ3zIZ-VOl!Q-j_w{Tl$h ze=PXq!G!?u%!>ff9Tx%sVGoZy{FjGd_=`z0Jrd;W?)vE3aee(Q$mgQRt^ZoyTx@DnVfnL?^qRo6`iZBvAPIt$l*M~Y7Qd(KbCH)!|FV)U z=J-9_HW&3MtddmlJ246Pl%!`w2^V0K9x|v16R^AGO&3V6Q-Lg_u&4r>=k_-iV`#}v zEk^OZzV)=@jff5QdeQZuw zQodc|>XEFAgPEPN$dxH+(Vbs=9?Rq{&5Sh;>aR9~Vdg3IZDk^uu2a5E_pBe~ENen3#I$tb8Ehd&P!J(X4ol6@td;Eg;;<1y&E7_)1V2HF3 z_R1z;FqDH($fb#V7eKb~L-Pr2Y zUnScDw{35&v!#8cB3;_{Z65Vmf*chHj@X2ecFwx-XCD-45DP|d3i)innL@KWetV^B zm6zo$=kRp{MpdA(!|XABIki)mdKA`Ier>_-^?M26qPGt9nMhYQ>m0v%3L>O4ajl%W zwn8x&zR#!XImOeB;l1fVU$d>CCwn%)S2bE->WrAVSLg*G%Ol|l?u8(h1 z!AdC8NMJnoiMBip4=zg7fANvV(rPj7jZEbsxkm}6x_mNa+qjeSDFTZ$#ycLSGvdL7 zBc?`nTUXEuYB2A`ag6({mkKHU_;y(#wmSwU&bapRb*<15e~yU>F3RcY7k``#>Ec+v zWTihL)viGcA{u5^e39XbvLNf4-e}(J6N=Bg$AwJmBjbvILYaM5gZnhWA)kriA34R& zXV$8Ww07ya5!zKwm2{%+K9-2uY!j1c<_kFdqelC-KJN)VYxv+XQ$Y<;U*2mJ^!jt& zVQ8AACw9;k)!-8%fBj3hxp+L`-A<@}@i!onePU2gB4vui2YaHqU;F{J226pEOZnUB1E^K0Z@W z1G?^$V}no=&BkG`+V!8^R<2ySqdjYb@)HxnMDJzkekMeT`lm!^thZ)krG-@5gveij z2^t}mV3y-Rp`^3np4_;648Q>C4GP{V~H&Feg6d5~;!6xQ7|%+xhS zl_W=EKNgv$`8GFS>2b|T+`QA6vC4J^`eQ!0l#Ao?bc>Eay=`V$n3y<+Y!hxAL^W+b z)+jXl%=o1R9(N4ZlE(#FCa|^CGq(q$Av=>8Xs;vw&{9z~LVenS_fph>!Wx8u3~&@k z6~|lU0sqL>GKu$ci^f_HL&L0+OCu8_FpkPwmPvPeC{LQ*37SBuvXe2QR~0}MRS&6GTM&!R;x_1tEU`rV`_@x>Q}JH>FL)P^5mpg zhHtII8}uaz2^%)19T6ptl{_cZT9M>Hu|*vVr;y-VStTAHpYo@ew;CyX(UYW~haDtU z*4!)Yth|r)Ya%b^k!vfx={a7%MKSu73rlE9)Gz5ZZtb=8Uji8UP)bT&)1xIjLz$@C zP|=V&!CX9E@!D_ZWZ!K(dJ}bZvv8itofsqEqq%_Zo*bU$baf!o*GeuWK%G#fKA;gL zIx1^jRm+iq7nsdtueV}-B;tSS@<^M<^yFGGh{RX=g*Rd1+-(6&41C{V8OCmf9`nYx zm`$jw9+gKqUFT6jmvE{j>`OgNY>YvM26jH0s#^%v=t-7ag*B^SYC{LC`#DQVrDP;k zcTOKWi5l~G*z8VMH5ef(kLdF_1>+I@;zK9BsQ+|uiM-ZfcOv~NLs7;NnE8yn!xbcS z4)0~^{xGm5Dq|Ih8wtI}J)m4O9|u7t`L%v=9PK5{)o(FK&xwdX!)J=(^dW9I5oW@= z-&q^z_i|*)0aR#fOq=jz>BeM>$6EGoytp#zvE^%2dNf75+*j3iFq^1iZj@vfVwKA#{o!MT=3(Mp!z(Y(Y`n3u2IC1LvF`2{d zZ(r7}$%+4zB#=K9oz*Y^3PV_($2+$G6+Kl#Et& z11Xp~n=!M$@)jsrZh_r z2ikbyznbOmFVjEi>6>4%hzLs0K+p5^P0QcPM^+kgLJ3~kxsi5RkfV+m5`DkTo!|(p zB?qkwq-Ke344tE%w7IKekijTCxKMgdtU>BX^kJKPp>v6x)5nnd(=(8P+Py?wW2*d= zebTC>wRar-$?Rv#&JELAMbT9qh-fO(GDYRSx{SB1uhnO2vuAZuZ9b{x+o9$H`Y-iJQI^gEes=Q%u^AawIFyw9s4u@0MT1Euipj$)55pYfLSxbgS+H*AUsUxtljedtnk5~ zwdb?&b5y}*ItKt^{`TmM_?9Zk1%P?OTh_06zof>l*L`_!W6@KC7dHRn_K#ve!FHcw zFJCp(hiE^0Xn*GY;!_4z*AZaOYwv@*H`Sh^JH=vgh(8UufQ_sF3IOty2mqK71pseu z27tqB!dRY}0{|aZhFAS$Ism-*G5}nSe=_AsT%M5Oe=3rPJ()(p*j~&oi@bWAx;u#= zZs7fT6y|hCJSNk@PD%TBir0D8Sig0$*lB4%*pVrvSO$l0cM-?u8oGD_V#DjD2EDK( zkX6wbMb(~T5}B~Lv)dP>4l|tdsWHYW0*_f;e}hsgRDW!9WhX7()qRG#fYG$!2bG3X z+V#S+Eljb3s*IADAd;|yxaH$?N=62)1x;BdI2k&K7lrimng^d-Y zItnBdNBj0o#qA?90R=dDXP~;kA!POnGSnrHCajYVAt zuc^;ff&uN~FTk-o!kwxC8{~cV-x+iJT@LU18iUd5l4!^60R`wfxK{n4>Oeodi`ahx zbEDW0=u>IXAs??;z6sGGh$b%ksxl-p&wt$6%7hdkpX3j2I|Ywb*kiH@tGB%ENNU9z zG0}tS>{!M*Vry?IeLG?^hqRFeJGQea(M@R7 zdP(q;tN9q@%gh^xK9WlpWr>;As6M>mnBgR1W9C)T&z<n7bczDQ$mHXvmOARqZhQN$OoF*J5Py%KCGW z4fbnobQ7#=DbOz7#`>k4^0LI#8XGd3t282zC^i$KN$0yU05D$gt7{I*G?9a&?ZN^3 zw_IYLj{bINfsU?1aISy^fgva*RG8!>EO#J_tZ;hs%y{P#)@}Own?Ysl4g*a+*phRa zGLw!fUCkQ}YAxF~uSV@|$4go=9gq{Av$as|#JE=$g4k$FIK9|R#y z4Kq!bei#1{#bmkEMa%*1OaW%|!qO_a#ZceXzIFUA3nl#-3Mn_7uJ_+aE3~eoOC0M$Bq;8z-NT`U#uFA|t%31}emd zlT#G%Z7da8BZfftJqUeBwoR`h9aW-#hO-7wBC#JZ$gG_r()QhT4in;g`!U#RKsF6v6H zSa-6xgnve4bT}_BF*Q5ZU0lSx!WIj6u4CJlFYvvV+*V%KxO?1R&2$! zPQJ1^_iX;&QC8ZE5-^l;JugU;k>@HQv4|4c{(SM>(2fDb)pATXdcz_|JKzOI^;>Np z8^@wn>h~iO^_UWuk^ZTDyMUuVrXn57_=Ihn_Ml^dO!G?H-MIycG`NxX2(r@KY4O2q z+l(rd6D*%7rB)SgTSkd6dyEgJ+9Qtwh`u~{@#_c9p0MxoKeI0&SFnREImA+n$083? zNm10`m*HQ#(s|R;Wi(31%sM-a{cH8zfoj66h!h)%6faZYOrlZmL8h!LIuEB4^R5l% zAJgf~f+4fXXF-E47qkqc*=af?WDxF2th@)^9^|wq3BD69qpu?qP;w~)Guz*wM2KMc zjxd+|XCd4AsRds$C3`*8*&;UQ=UZ1GZ3`i^hLeDjFRj2dC3~2=3HE!Y8{GDWoHMVS zg?0x^5^$+s7CV|tTg~Zc^XRJyYhr}x$7@PgdpMe-?G{S|o^jQT^4f?)wTD-PtQ~;A z^dP1xjL^!l($FUsKQv&XmQji=_cdICb_pLT1QlcFu&v2OOrK8m2(~eD@SV%_b$CMFFLCF3 zAyxu2@58qk>Tx0%C)9_)JTDP4H|gN(A;|PaDD(EcmQ8ixU`Zx2r{A4hX`B7PwTj_g zze`XNu=;UsmlxA5pXLu;{_3domc`bOOTe4t1A<_3Yc=nri2N-sF6)HH*7W%nBZ!65 z(YP_Oj3GgLS!vzFSCt-}%j%Hh8QTYJ%n(~=6InTy)FmxG;2F~pBeAN1A}RCzw_ zF#GW>i`z>9ma=v*&*ppy29!vrsQR7EB_q_c8;zs-z0p(_7IuURgd1olIAe$j^;kmE zTjBRIG(eA3x>6Q|R$M(ViSK2BJyr1eO3U7~L?=~~yKh3!yu>_&KI;>Kfh*7R)epDV zzXvtWuS~bJOXP$yaeg`4t;1^ul<5+dPg6b7&vn~Fq^_cn6(60i6=c>mo{m$*X~tt9 zX1(8NkLs%?&RbrEe5(41=*M?m-CbdaOLk6{6F0f~1AO(a=H4?PF8x|Kzzt*@_ya*= ztJ=Y4;g)1?YBYR%gd>*|Rp4DYBhLoxInnBg-*e(EG6Y^p?T6E<5gHa-FXCkvOg2i}rg#=6DomQK*;4Z6RG#Zq^~5ySH^*P2uM6$C(lTnu8>hdG zRCBz~T&^w=L+Dt`sIgI#=U&B77ohGH3b3lC4|BJ)Xw26hmu@dEeCND_323^{vj#)dB- z2b|d=v{v&{1~*Y5wW%OcAV@)-sBDbR(-%hkF0IFBr!{zH?aDKp-y6@mEQ%Fg^vk%O zd~9q>Ul2h#m~>5+Lv|dRD9vZ^Y)khw_v-S#OXC>|6I{}}o8EH_g)39j(7LU>IgAqV zxK-}RSjVQkRLmMlJ5wy|Xj~mkPU*<0S%xGjsM2pN7UzCsZBppa!Xp>nNVWb9Z&20gZDU-IA(PW2zIzv`yG@Kpl5mc2e?-3u# zIQJ7?xbTR|72|BW5oA%G5*0>Ic)lupRvY9b(gORVSG9_dsEkEzSuI`k7V1LSv;N=B z(jQzNU9hlJSPUWN$)33<6~gFKHvwj1t0|$@V@I^YyIYL&N0UrbG>||D02~y3J--sU6g$wrGi4C`=d~3 zQ#e@g6W-pjKk$!$v$U48s-2m$o3W!QO2OFK{;8ewQ_IKKTumLFEbVN$+4$HvS*}?) zJKH-6va{R##|zl(9L?E(;o*e9L2&IKXgi@$gvQ7hTBcZrB?`6hMONybntQ^^xSPA$ z;NHgOx`|nzmlDo1QaKCsujmgnw;2JeZds1HNc>$J zi8Pf$%l>p7eZxtYL;)wPD7j{@^;%8SQT1$%C=bHyRJ+N#oz-9E9fjHFtbrD7H&LX+ zX+iiDmuYA^yFLneET%UOwq7RDXUZbm`KiIVa>l>@^$c*_!!x>8g*{sy+U^ zlR4=2J8SCsB>6Hl^!nYqAKWH=qRkUMf_+bqV%xi7xkCyHIEHex1MWK=)^}$rr)H6r zbR>$zPt?DRT{yd8f2{5d)`4UO^_HJ7UHuL0OEU%Z%_4xuZPt0tBHR8T;~ zz`&3_mR~0&CB@@RV)bo&{G+aIW!k5BWt(9Y-Jk;AMKsdkkJ`m%pH#jaz@2Ho;CFj( zZ*R0AD@uWp{gVz{onx(d_rJQ5U0

C<<|o5?-=~vx<9g->2DFAgyJSpJcu{huwD)yu+&SSB zO09>t4rboW%+H4u7jwhA6ZjpT7d~n2%Ti}f`1sLsDCbI3@Wq*iXBbg+bm_(oyUW8Q zbaZxk{XCyz5)->V%SKqhOOTTY3E4hc&R11aGuf=wDKJ8V_<&8aF{o*%u4UK$fwgkG zOGvx$F{XZ%^Vc6gUcwn_G2~F7O~5$7uh8SB`aQ0u+KfirbQ*g z1?dmYB?>4=<8lIYZ1CD0A*q1g~wb!pCHXD4^wqbCPjE|4+ zXPHgAb$+dxs9`VBMq?0ud&%zcdVT&*ZLE+R8*KH8s;a(PFSoP9dAjXN&L=G!a9`sm z@%!6V2(8rfXwmQbD4p*P8_Fl}6B`;DdV70U>`!=A+D}VVE=RLyOYEhT}lfyXYKRTwLt9-N`q6@H2{8 zlga-wG4ZF7(a}48=cjI!Tr;h2G*PBoe}1%you8zVQcw(MzW!xftI9w_2j)di6 z<~HwAgG?3)R~geD6&cBRdbF#`LlwUD`xQG=wnnbX2Ugwmn!TUaZG>Vc$&8^X-&$Ij z6crWQU^_*PjA-4udsjoidEspeW9VfPl2nvju~}y%Lz1`3;dZxJRFdDBHe6lXTxWa~ zg@OCM!^8QcsLpskX2?SEj{D7OZfR_kKn@1$_3AP$t!*ARGxNhcjEs*2 zU6wvqR8%mo*X}14xvuKWhVxB4f-~fDUg&XLS4Tb%?(UsQzw_E6hnf4cQ(ptfhJK|; z3zlKO0&a;e(Ju*t9)rL%R_SF;{NZG zlI-g0l#)c{l0<#l#`gb_(PwW|Zutb3g;cz9z3w=Yj9G(m(r5o;UzLk>n)EAj)5O)C#eTfxlBc68 zSA^XQwjg4eA?;Q76+V$^Zf}41qoYF&j;)x$|7i@u2G7X19uj0!V`HP-FT2Ibg*HEY z>BrVj?DdS0ZY5&(AG~N0M;CX{msM2MK$#9? zDwkIIPBlEkOBUYl;3n_RNKa=fpYY5^XY@}Qw2Gn?@nkGE@78d1bj+$be@Vg+d5K0a zCg07`Q3Z>DGTD2lS7!FPZ2#e(KPKCp9~Co7Y<_9H3=89-^IH0nK?)^%c4f5KG|vx$ zg0$hJeQ`Oz9zJ!tF(|66x4BswNug~te5Xa!BA(7s-Cz0_BY*#XoCcM$E%J_P7RzLY z5?z|l@t)~xR;>bYw8*rF(H{iPx`fsl{gDvc<`m!D*m%^}-=CfHP&o`OIU*v$acxXs zyAXC7<|8Ntku^2K+nW$``e?{Di?Xz{K^K=+j!g1B`l8pPHn=-@C|W=oRtl$$1T@5r z%3Pw`gh$3@Qqs>0yGM>T93FGA=A=qW-FV%)S*rQN%+H=Zn>|AbdF|!E4PwRerx9{x z*$(M}CC*08iOIpzCi)&(rlp^f@#3Nw6yns<$Dodqip#>bhF@O3e3>--ekv?&rF>;V z0`)Bpjz?EG9|xu|wj>eybJXxgbeF=_)%EE_P3-UAza32> zi;YydFblb@{eePkcCax`E#w-eY3LL6nnvh-T%3+24KXqC%CGV_uz$IsqS#ii-gR9Y z`@!8UED0I!4hsv5s&9io+Jy@jV(K-r2g9fZ-jgsW+QWTAK{CGwF@|&L(*2D7SVPHR z0yLxTxlYsx`IQJn=~0MGS#cFe+D_V$ZOx0oK;6H{i%_WQRT8iYhPaz2kB0lSZ)(&dvQ@a+Y{Mn zg-@_UQzE}`-@1kQQ85G{Uesz4nT(7~QBhGFO}-+#wv8GRtLgjmfRJVO}Ff+@y`;=F^~C=$WDcoj%2p8dUeMrYw60;66A9BZsF~)Lu=Kl z+t%E*(SRI^o9F9PUXimNK-xy*cU_@;7|k||ys){snMJFh;r1#?%H7Y{kM!IOe0J4I zrKevqBo^?8>8hwSZq2rL8m|8=eFgYB9@etA-p{WgW(`VgARvdxN*NWE_sxO|O$q5n z+1Gh%eUV-18@sgY`2p~dlvJQVMJ*t_PD?ddX^l10s`v>YwE1;NN%OeMg|+F27<#l^cg z&Gn^g|LY*!2T7>#Bjt8U&Fimh$E(zZj2s;tjK9C7uQ@wf2E^pF(DMmznSg+RrM!)w zp8k{0x`H&5XSYt-Q|&pAEIbQq>z?F$5Y38E8mGN1WtWF?520(2gby4oHh-h=^R|?Y zEMaoRn)CX^MKfetRN1vS@Z?{~voBI8`5l~knZ2Pv) zs94S?Sa1Old$hvGE5!g2g8^N2B?vTSsb^~e{HCX;N7CH4Z{IkK8!tfmLl6K5CBX`` zw*a~Chf`sSBUfHxNdhT=fsql1kFT7`uW$l_jm4lQrp)G7A0$85^@+~v^*Y^ETCIoo z-_Ulp()o2fc_Y#S?cTyj;f-HqHcue-kt7RfHFsz~FI$GrClcvdWN1AC84}%}mz40# zb;Jf#S9?VbyDs#g`NJKg280g%3AE?g1q_5)`#wfL%#(atAYv$aEQlcKe**0CIU@rq zLU*Ew*TG^&5>w3{#A*X{4)6mgu~LqXT##Z~nwkQx+ST;_=7mZppCtM*BhfPya8nYT zXhKJaJT#w*j~+eB^-6>ri_;*qUeg4h@+f=Id1>H>u&|vqG~XsIVONkCg0vV4ZQso7 zY;*G~ibJSC0O5^~b{1DA>ywaH44c;7Un5rsidJ_^_|?&RKe7CpExFnf^%qvw3+jv# zBXW9r30osi-rV{0=~E~a11P&G&WnB09)SBhE%^*Q7jHFpbt#Tkxnwtc$;a`?BJdvI z7a8CR=(QOMMRwjIomcbVpn9)WshovH<`%TvOd*7Hc|{6IK6|l_0Ed{Sr>Bu_;<24w zJ~X3@B)8=s0Wxoo#EgWxp>y~rLMiga3mgOo+x#k1uY(rZalh7i@%J5M8S&b$_CqbYZ$>$e(&dQ{%AnO}D#2W`9x1V|J> zggXyEuzVR}GJ;#=u|0POIend6wutv>DF`e#vLq`=VQj1OIgTX0ai0@Rt7#LBo zZ4lUlzIgi&%zzykV3x%fg4C}(2w8M)>X+i@N35{q|S z-Us#o7}?b8@;^)zr=(mrJvrD6O-f2iQTakDE&$LdZ8iS~(^PhS2D4`#f)N*o^OJtB zt>r<>`}Zo>>d$>mq0xeS&<4R21u2vfSqx}(5p0W;l{RcT??;wx4D?jbfdovONL8el zic(`DgVe)A=d+_M$tTT|ygYq;jhj0H2mmMOrM3Y~@i{vM5`8Bs0HTmtPEm2r@BEk! zKB%d+H3as<7Vs}`BO+91NpXQj{D(apgp;zL>$q}TAx_HEQ?RS6Ys|5o6)6V^aSw2M zH%xG}{v{iEIDmZ&BvR<$B|-Ua{a^rQVR~WVfwMD@ZnOXn_F7A?57&ITCpxteS_o4%F5#EBPynWeOP(@W07Y~ogX$>L=!G3cWYnZvb_MSqrGI*8omjQkf z5fh`8!OEO(8ZL_BsjLc7P>z)I8fS?rccu=j^`jAVo=T6m%__MFyM+mYiP!IARFpK} z*OxC}4*taWdrlYGg@oc^$!6x}hI$k%Ip9b;ySvi48qv$E4`pTV47ZzKrlhnetht|h zr+nJe*H={R?BE8sSx06OD==*kHIYJVYin$1$#6;z;g)Uv?-1CezFggM$L;eEZ2EG_%EWJkw_dnhW&6u-Kj1w=5g9EFeDU6F zra=@WOAct;H6-*XvHU9vZ-#9QV%v z)>J||Yu_cngPSg)mrr#{#L)m?U-sccC>vL9-n!NMNfH~9H1yM~s0VU#Oc5j;EG%Kr z7jl}mk&2!jJb4}v5G&><3fsvr_LvrM73|AGNHm^4w8$}E1UoDIL3^45G#zw^*MOp! zL&1k-5=$%hEILDD7i`<+B9p6;+H1j-FvCz^pZ|_l9NzfbQ$m^FFt2gs$00IAUL+C2?r&=&k>JRX&k`GmDSbw9V<0!Rnj2nUD@=jzAg4= zK>k&aP}tME^6xv;^z_2M8YM&S;};Tw&7b!W2o;M`ovuWYhd^Dwe*OC5_~-jF5|7~= zQqUgcN80>2))nw@`@3o4DE}S>hyH(jqs$7o>0X)&aFjh3Jb}T%Tn@jbxVX5U8o0r7 zMKCC}K$`-8)~c4-fKoQ!_RQD3j9foa%1Na-hl6^`R)CIZuvELFW>1s)_oj@Ri>_xq}Im5!RR-u1_a7YQxieS zE>;EUxXL7bT@?6D30JAI#QF9kl9fCBVXfOvR#KMGDj(l)tVBB~jT& zkBDyE4?{j3VLG9s+30)7j+WfNcmP+9XaKB3#n6pGV+RUB@}ZW!JqK)3Bgl}@yJTvt z+97PcgkC%;=2?sh!P&qi`x*B~I|bIn-`GP8lLCni18$Cx4f{X8EV4SG*MH50E|C+* z{CFt@gIOa7;WTRvlmH!~p!wxZ8n!A0s55@z0{7}yxt%%i4^Yq0{T*gnh)GDETtaE) z>B;)~)*YSWr`<~?;UJHPbn{*V!7(L?`TpA;|JGZyg9+M z@_J#2-i=mLs4b>PBt4eAaW9aqj%z^hiNF;~|P#E_=otc-M2>wuRAWAWW ze^h&EIpgb+CjHi3d;F&{u6W7{iaNX@<~$u7=@Ax;EfG?NCnN%STW^rb8x(c zT?Vn&)6N?8w$8N zxBTMu>-*V*MYiLD+Z+zCd!XSo0hm7Imw8(}*guG9_(P5p(SGgb_Pye-eXdHbRtx8J zGxOEtSG)*!La$M<9DAR60 z>~xZ7%2^)@t_+?!Tt~0dX&{qZ8LG{0!iB z*cEQ9T7&L|-qh@znE*o3DUiC%zP%s@<%k5Bjt}gHuXXf`>7N>ZRw0#*QP&+E9d&ni ze;XAQ1kHdXpUjJ8LkHT9@aK&z?i(@|+@H6$LZZL+$$V|5OQ*#uE!*0X7zlu-1{f0s z2=u>+E&*%z8;O$r=5J{rt<845H1|Z?*X?34U6p2p(L_P3SBXrbB64DNcP`K=^3slo|>N2M9i8LlDqg`DDyF@ zxhqDKP;1jKN-tHa{$WdcfDrT`3B9YZ$wJX2Y|+;@mJH{w;-b-fP3B&^!UE|T9gf7? zNLo``pD{OFcGdR8U2BdCm;S;7BqGWGWHgPgwsdFf99qjBSBKLnonm6f4p~J()hk9) z36F2<@D-=?!*Y!Ef;hwVUwGcSS3{mDY~utGrQk+@Y}|W5qi=dLVrH?rPOM$kw{dV( zRW5@=pXY!iWh%Y^T16W!--#-!%5$c^Ea&P>lI43_llb&;{7A_N{}sf~*l_`y5NA#2 z__$;NSI7LjLhv7qA*+vY0ug^tz&7J9m00jDx(kg;v=#NW9JfUgQ`%&rQcj@40mp*U z6naad8($wRir$=w ztzY(Kr;p2oZY&})@*6ap2Ow5x)p-j<+*T;^sD%mNq&i%1|C76_cb384qo`7VK4EbW%PrtlDqXF#p<6c z{nj^8-=k}Fil0RwRJv)1!R?*fZrnraM6Y{n zu%BeL?s_7tT*$M(%iEW4Cr+I7R}NXi1fqFlG! z3M?*^bhRW_1ZP!a0aAzTcfsiFT7q`t~z5_=qF!m(vmU4 zULMzd@l)lC6%WC$82R|S;XurA073%H2u(Y)i02MtG1mcM+erR^k9u|Jcbk4ZjIMjV@T@k-;(5B_$2OpjW##2fpbr`Y0|7$Q z$J^D#0u%Gf1ATR6g%V5$p0$Zkxiqy~pJU=L^rhh|32$CNLA0ksWo6MV>sadUC(R_4 zzT2FWyR!0BSXlUbTU+D!c-f^U-)AA8=YzlUR5?Lb6(PNhSdecj=10ge=HyPLKJR$5jvd zlo>m)TGJ2l#a{%!kBVvr{CNl^1+ujmRh583A20WXlOGI8F{uYYT$DkpZg~ho1qlre zA_T)KLr0se7Yn2hqM`tiZSU-SlZh!5p#SI+aUjuP6sh^i!Y?dpy<9&(O}`BsOwx*i z;iG*D2{kLwLJ*x^cy~Z`4CKO#;|aGH;}+2LicD~Ut`bi2-ogc8;7@V4=o26usYShG zdU|>sXF*5o1eMg|_e%x@1|9x_+P|C3w4bTR?1D$?v z^;f6vNC{AtzO)Z;lm%4t_3Wbs$M2i>Y|gx+(=PBs)0Wnrn-P628s=uxDd|6cIPQ`q z3B^J>+!qI=*_iQaH}1c!G5AKl!_im`NUmPh%{u{x6J&b|AV*OUHDSl+4WUC!i3y(j+P3C@X4Q|C90jBNn<<^ykV4hTj5RNM2NvhD5Zy zq^Pd_?vuMFX_Jxu?NOs$tKUqm{yOpU1rqfjmIq=%5pESxbW%9)i zf^E~OYQh-W81GjvJC?>jwlc28V?li5XPCH629~&WhN)CMvA4j+!!gp4nT7AVh5hfs zv?N}nf9}QhudA!e99(j&^|^EB&aCI5JP=)QLo9fJtV0n2DGKbHa`xaH7ce6?85mUc z^qy|kAq;`Gw)UJRh|~JC3OL5HG$DTH{D;`tiun?$eH-^C2^XfUD=7+74Q1vkT^kjZ zwLVmZh$+f3;n7R=_by;8{7X8M=R?Nzs@wuDCJnfuIZQfae?S93 zNr2@P3;2|nlr#{yw&r6&e*USoHG{cZfdxf^IjFiAw|4^PU1(E_Y)Vc}J)Kb>>hFA} z=r*CG2d>jm!3lq69W?rsUE7h5o9(0Li4pY8qUu|2-erg46SSah{Nsqc-00JkKIH9 zkRwHaK*8+r4f064Q@2PfbAFA=dN)HLW0cD!JnnFO@sm_5=fzJ0k~#dP=>EfY9yH1z_V@AOO@2Sy-%gvi%i+2Q+cuw)6*HGFoT|bn?BGFW#vj@K!_fcCMA` zA%SCE;ofu5TYk}k8qO!gOZ0+~)&a-QDOyhWcQE5`U!TITe2QjAK0&luvFxp zblsD{Nd>${LP8Rtm@%_4-6RY++r-Qa@n($Vbm3F5*-tlOLRJz1Q$#DWY(Vr-u<1Sr`)?kxhB!{wSL=g-Z~ z(@uG-Lx>bWTEYNXJuonk!+uH}X;I+lo%6e%B9JR@u&@w-X$yL`rVu(`3(?mj@q1@|GU@zu-H*kvj{2jbc|3M)7t$+P2(!_0_~sa& z)yD*Iq6jWsmFFHdklz4 zKUWd+;_kji{jw0!-BfYShR#XNI;(LFSK$&2^IWIW_I#~EbReL2JVk)AtaSqgVO<`DPk6DK8*)2s)56{2(sA)zq9DY;tHAQlHY{bk(?eFUH6Vw& zUw&0zdJ(Sz^LcT;!*OL~Q`l4P_pI|}1uK8J5|@vtFD=Ef-X~7Eo4}*E;%*@mFsM)R zUnCwXPQ0NE5L~?*7866+*woY+E0hK0stM>_2N#!FSXE~39Tuzme?i+L0By_Nc?=mh zL(K9zv*eHT=%et^e|}wLeS2A{`zr0to?CDK)R+t<>DwhzFb1-IQOlyb*mULH+(AMnl6A;rW^8i6(08Wev8FI*o6_VL7+2{7V^|jJb*LUT6oi z2kg+E5-CW5(-l!c!Hb1{jf)F$;T4*7Qh{*0M**{`7C;_LAi5Z&MnWzb)ZGLs8f+GDN1z?K>yfwo@d5(=r+?GWv|3^Ln-_HzEdOR(QFA4DLv2uA zr68N38YY{(ZfQ!u=yrSkkNi+<6(IB52z|_=SMm1SHz_3f*X~cW1NmEdI2R8DaB)oQ za97uBkXn+5m)lq7On*R18-+FnFbi_dAVO_3b8_}-ym8Twpf{qIYEmV2+Av)U&u*k+ zI7cO$+bf~#qc3ZmlZp7Fp0PdKAz1q{w5_Y{V1?Q3?2&G}Pw`vO*m4HbKZMrv1$+Ur zfc66Rh-n)lL5#V8UVtc}$;rub%V5*B``9=5VhhxE&{&Gt#g)q(@cEOzGreLCZA{8I zzc7{ey?V)@-cWa@H39#T(O93A#la(#Mv^vby*t#(oE(;+e8Xt-L^E@cwSxY-kO`X)MF@b~RJo=$e1db+3m`ZL`Z zFB%D)VsIThfo*bgKvl^BFewnpaW|1h|6)GRLSwy6X=uJ^F_ph4y-!ZLV;>J;0S~21 zZ&i=bH%h%Tk2$5~0s26c5rp5L`T1>-n0TC3<5>Q_y?X#-LDL15$%tp&m4jyVsQuoM zb(1>&Jrq_sH9hz)yXHlZMjhx64+YhGc^|kEh{r|_^wBFMj)T<$$Qsfo>TS`+?MvNCeRWwnFm~)Wt_fbFdTRhI&#ol0 z?VGZ`;u)!;%&0z;3gU00(2jWcx-iC^Z6j=!$UqwSON1E5dZMy%P{pA|Bk@HP4K*B1 zOY$}qYIirCCg0YfRw^q(a#C1+4X}RWH3FY+rWz9vDOBOXU{=4anD77u+0JEO@Xx(? z@dBiBT;OPf3F*4t-FW}Gw(M`?n?RL=Iy*j@Tp$y9m-sHQB4{LCWDd2FS!k0MSIx)J zcmusQg<(%3$7YHAHx(zizCn%ScbX$G^xniotU`UCW$)(m2@1wRmis+3qg~t*6Ym5j zz=d7U_+cLrWNx9?zNlN$L9tW=b?!55*ANrmZRUL)MMi2SVPF-jDiXhY!RR<>wJh84 zg|+{Gy?V1kKWw^o+=$QKQ|;EQDXqc}rL_Il*@t5jzBLgJ^l9^@)-o@FN;s=b+__4YZiKC9c{3r$H@`SNu5 zQtuze(B~?Tdak)-CnQ*%o_haN%)>RZdFi+*2>@{vK>7Ce@fof25mFL8ya{3tu<*|T z7L(A@c7f*D^pL&wfvQ5FlFqaS5bOg>6A1$H~6)@ODxHSnK`!Ef0bPUoYmsnfx; zo|o;XNj-20&hZQDm)3S_*8%>x{;%IT3M|@yrk4Sh0{j+6;AcjhTR$QgqVE(NeMJXr zP;gQb9paOLBpw*p?!nUS@i7BfucIZ*=w2d0foT_ntXpUueIIjQAQ0`57L>%ls0t zIxc&0TXOb~RJ=&_)tt3Bdj|)4F0RY|fXn*So38uX+L825l4 zeG{06q9?7Fwna`(Pp<$vggg(*PcZ;~K-Oxe zK4oTRDz4gP{Uz!+@QUg8Qy@=#$9uI}Uy_8bk2djoqGT`2J9C6dew&z^B>D5o=aUEB z4^9$J7zZed*iDmGe_j8^n+#JWC3cgEV7Wmfx^m@5o_;mrZUl1>jBHF8fu#<}E>M5q zf@^k%bi-(b%K8Kmw>x+)M{B+KK>h*8IM?HEI0*>}l!DHg3W>{}41Y6fjOV!?Mni9E z1I(YU$dHQyqOM$Q;yKTMKW&&ff9Qu|igKXjcL;&M3M6Cu`LHIw zXZ2@yH1$dF+=6L?GWU~DA+*2Yi_4FOghWza9^988;jp>sgKQ%ia_JT$V`iUcKtKRY ztf+ThWnucKYQ$)wG=)#%bragZ4g{daE;~Q;DQWv&u5O8PuNk|eN;q})@c7OE+3!q; zoO)MWhmXLcJ%)Sg4f-?UK}D!0*?&&8a+h$Fc;X2S{Dz;G=>-K?mlIe$q@j05UVB?EBj=Sa-TQWUH14&DnK{F4* ziCtg)?kE#|U;!0US9jT&iJgOz%dor2$zPE!;PJKkH{IBe(9LkCZ?0igh^%_(l8bX1kn>Pa2zgvX>Bon8lkjkP#@h! zh|w;LXe)nH;wg8A{}=az{kB6trl>4Y9rX{EwvT_lbj*Ny$A=X)hY{(_0oC^{Qhkri z718=}DEwpGqxVqh(UXmMQ!OauY=_z6bI>(NgMcM022&!)ZjxYf0Co;MSwY}e3kP22 zSBegN=JnDo5&wrfw^pi6#;kI6#mkSZ)V0$mWbpwB>Z4bo?4cRGb#%GJ%EqTu9*2IZ zBTK{|6B840F~c#qO}-NW!ys#Bb{m9!V7obNe)52_0Gojkv~i{IwNpr~koAKhUco!@ zbiAsxZ!4b9?zW&H6?j6SUC=Htx)-|*VR+ZJ&Sx;YprD|on>8@=Q%Rd@1GSux)TcTk zc5L=W;l`yxV@-}uH2LB&49Yp$;EVtU>7(nv`)8I5jU{lD-83$uU_*2Q_(x2D&>|s% z>7aidnt2TSQ z^_e*Ez;r^n>DMHjrQMr5|2kV{dh2;!=9p!aoV0m;7ivXeRQJtAl9XxSFIvWoQ+h3M zE?zVRw#LN7WI*>SsWjr~1YGlXlrDSU^T{9zw1e|Mz!J=XdtWNMRG7LCMn4e$>T5bN zMd(YiT_!7?<`E|vbU;er3WwU73VsR1Bt+iMu2tX<@CcSk27U!L^Lf~H^Uygkt170c z5K~fWA=DJ7>UvT!A-|U{%VWIEDlF&Re2u92-`IP$$&}Y-!kVvtJQczqioUFA*LgYR z(?!spfbv1ymOwUr1?McVzACkYVqn1!K5p6&d_0IMfQ+F|!DMP@rv$@$cIbqU{&5>! z0jp8$45CDVHGn+-Wq3HNrY~qHE8x6+|NioJA-E@CAmtvA#V}03;I-doxG6D`V0o5w zXLJj4K4*po=WYQnPSjGUu~GXrN&#(ZBC?h-R7DAHs`3A})>X0wj#N&Niok4aG9F^} z{Am@L9vBaf_tzP~%G{(ry0c!joM$zDgO?ZaB#4WP$IgJSZnn-t{{PO-N`W_28-~na z$N_2ofTVp3QCS4V1`nejEOnuwY=oGm=H{BqR|1JNIoMH!!Y5vLk6B`+V*%<|ERUDb zUNw8Bbm3;RIzM4St&$gyD^(v}KYz5>>p9z^28DswDU1uD?dyW7bUb^mrEAJUE=!~^ z6UfHBWX?f}*lxiV0i%yaAaf;pZ^?onfM5%dIBp9IC%|}1aI}FZF6v*#1t6Nfu|5c2 zh-ns?P2qZMj(Ha!rI~Ng2?DpQf&!}>gv+PlWozg9?0Nw*=`TB1hg1%k-%Ns+4)N2? zZ36@P8TinX`t$ws7%{)PW-svQ#DiEhoH;J>B3rEx^{wQn{G(EJu%WhYvz(Dk_>EQX zpM~drzQ0v|13E8FXTG~}=oEkT^BRNLO2E)P{nf4iaDUht@C*E_;OWk}nf`w{KWc6P zocRRqU;;YPn)}@hEXCTi4gbT#3b9LoB^j%!@LUWWE(2CBl@oQ5k&%wI-nW5EBX76n zp#lp#i@_6f^HzWW-#2GknV+_2=hw+;XnZ(~0%zLlJds4CoS5l}MO(2OnJUU1i2U8b zl+aiww8X4}2NYsY1Sz#~uE&Lf?UI;+0AJQuqHjovIa(QKxS&!d3(e@`Inz2V^dTn4FWd{xFEEQb-{jQZ*QO3 ztcpY5(Sc@E(cB--gvv?5CQ5$gM^Ptu5_Q2pMG>M(_|P~BE4%vkU;UBiY!X&g6ug#C z08j(DOX3e6Q7&+mP57PrRJpCcj*q_z^DS(Ed^skYCMT1On}WfQBMIyt0NBUSJ(CSp z!u%TK-I_@auaP%py6XAxf%(48EUPH>t?WR_SBhJuDS z*PWyUxSY=K#Np#F=D<#&^ZE(gk`W%Y7AeZmDLE|}m-Bhb#cmoAxsEU*Lnq@!&rf}y=T zmFE8;jvCW|R~iq}FS}C?$OGO0lz}Yk@FeUH9AQD7fXmq(%$~@65;7GvyuAlrWsPHl z8#1qNva*I>;Wm3487T#1EyAyZjTm(e3XlA-BkTj@-hvG54QLANm)u|!0_ZO4^j8Rk zeGgd)e0<5!_dFdf3W~R_`tn+dEim)Zk>!j`xlw@|o7<825*k71GoxsulZ4ZyN(XX& zk1PFye=@Fku>}Z5G5=ov^DtAF5mx|r<>9DI{#%2KJ&)+AkizpXA@)1U;~*{Q)P2i_aWC-~-tmI)#lPT|1zr!sM1S8P#wzl0Cp&i?HorX9q3#GJtMy>wJgf9{@OL z*F&~>4CfO>tpiV#ZSn^`eaL+0+ezmfPg)2JJZ8xtddh~Ra2!}UvHd@sKj!N`-?o!N zOIEs>a9rN!;ZJiaSadSrxR9j{2cd#%xGrtKh+0H7i*AL*#pFiW=PttwMNn!N!4$jLm zt(*WmY}6$^!AD8CKy2x}BclR*to&oc&R4ch0L@vZKGbc-l4CFid*sJ(%=O!%B8iWE z9DGXTaOkZ%%|!fxpQt(A`}K6B;29)Yq)2RMD5}?_D8dYL)=iY~d*2HIj~ga`(D|94 z!#(9akZKsUiNf-yx1~pPT6mOY)Kp zdBM1^Z7seY;Pt$sXbW-whSlnb>iI2%f-X6lNGwYtuSUQLjCWzS<7QIL+%eg zjA$=#LmL)nmB9MO)}7y-Xo*}-LJ=KXm9VckR{k0BaMJ&)Kv%Pd%<4dzjj4A9 zsomq_FmpVnKQfS!Ys(r~F*NY_V82uB()$mZ^3~Veu*wLHCXQyj%VtRfgf8^m8gS$? zi{HA$hvmPR@hNsz4_Oh#@3KaIOR$X1ErI8=z?h;ajE$(Y6H?5Ol!m; z%Fpo-YLwVq|&*fZA)s-bQ z@|tW%o6`K%jk0%(Hj_H2MBg~E$vv=H7bv4unmuTy*1;UuCiLkaYu;(2cwVy2nO-E2 z03>pzsJ=4sHeGf7y(Qnh?4({nL5=E@P%>t4Wl84`t7>WCG>la`b zD{ypZ0|f>SXB6_t5a1|dFdAUfA>;C*c0WMEm4`|T;E-C_{Vg&v3|&GY05~du>)V@{-iip@OkCLWpIAUIfJ{wq6J@kK)JblUn=JS~-O@nEWJpRDDo*K13hlaebap~p)q32y>{$FB?Pd828GZ&XC42r=4xfbM z0m&I4J!YmFiVaW4j9i8TVfy*qyLYILB(Ws8XynlzW?c!m4KTwS%Vmm(%nyPar?s;) z9JXgPprx2~WXQI_xCzQEJ@htUctRc{Lsgmz=oh^6NSh4CVQc5yf=z=4c<2X==5K&q z$q63L0!F@t@oGNUlIcjt3>c~`#{j37GLlr>)$+}_I@;9NlDOA}XCOp{iqjZL4nq0fRzsUAVWyWg|({2l

npWdp%+a4*>b82!^VTaD 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 IbxsLQ0K{d>jQ{`u literal 58844 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 MihIHK{OgDQ1N8Q7Jpcdz literal 29530 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 diff --git a/docs/source/demonstrations/plasmas/beam_attenuation.png b/docs/source/demonstrations/plasmas/beam_attenuation.png index 8f6c682dcb5c1fc06df64c2300776a8501a67493..e729570b53f1e90755e7a7010ad4dc75c314a786 100644 GIT binary patch literal 42157 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 HiTRNoc z%;o$2opZ*$Vlvl%nKb|;GL*V~$?d7zcP$*(!DL#3{LfAfx`jm&Z*Xx*YiUuve*Ky?e*JePv9Yo7XZkmvY{sid2L}gt zh+f6?xBvL4OiD=^G(Z2;Em%^F{4MIWknwP#wpN3;=x~v4@V7VYEW*Mxq8|ILsiGe9 zbV)szDJe}BdlKW=eEBT;3eVEBv&%6D#>Z*xCTpA0@4uQSp)AlEhy*~CJ0o~(ZP$A!&FyS zhnvC4GtI?{8Iu3{^(zSu=3@=Y3kDfhGA1D*>LQ)b{$IZe5fBoR($Yp~mKcP_{jPRm zci&sVA}1%$&CAQst}w?TCYIIKPSmR2@irbQ)_;|pOmpYXoq<|6C!2|{6e#=CLx-xW zDhWoC#$UgvWus`1jMlDQyOtp4C6tnq!gv`kBQG!X(R-np(K1uL#J_)EP*G8#Q09Fp z-zLt9@bRULj2Pp&buXbzD%-8y3Anhp25MaFQiPoG`uh4FYii2=$WbbLJ|R@*2!n32 z_l~ncG%YpNAZu=AV3Mm$`uh5R*MDEB zw3{mC={q^tWZ~fL+8p~Jt%q3)G zu*Sy5$ao1D#AxB>IBwj?cf8mfuf{MmG=zb&ZxCoW-3ix9Y`8e5F)%O~7##GAkEg6Z zUg1m;c6n6z@MzCvW$1duRlZB`XW-{g8GCyk1qFqeH*YX}e0=Ua8$c0K-)Wrx_NIxd zV9S+DuZm=GaS>L5y^~W*B#i*;Yea1i$(2FFT)V-E9)Y-Xe9MGwvr$>v?7)5)p zxxAu6TSJ4g#$oOfJ`s^Q+|JrShLp)dFcDqX+qk%~QsWja8yhZsLPE2oq@+UqnwN#y z*#VqO>+8LqUS4B%4QK5pCMF5d(Zm>yl9H0GD^5;Mrfh6%Jwropm6Gm+a%-0djQ#p` zTP&^ruI;bZh=>R+*qq@}QReVO=8_pP3kwVC)ygogJ)@%uN(nrEZLO`PZo7-T*4EZJ znVHS!=O=dURHjEqM_g=dL1;#2$9tx$BPABdU)Tg@7JVtk6JMQ8QB_Cqqmpf29svQ1 z;o;$)fq|H_)7^fXKi@yBh2N?-5}}WtsB(D4DPY=8pfQ-{zvJ{KK3*eh&V6}+X>oeG zk@~JpXIs5#2N8UlrKKeSDm*-Vu8Tp;GvPWXCm$y#=Uux=k-M}s(0d$i8OQyO-9Ng{!C_?k>yz>E@$uSHnvZ!|Sy|82R2meE zyD!(YiuI1lK7amfrj&fwtl|8aQ@6^V3Es-o-QRD4{0!aCS1Iv!pp2|6pM`~mxt(1< zsuZ>cZ#$Kuyu2}L^z}8x*8RMyss#RL1HOb;Z)bOTHMsBH_%S$$e4x$BP@YX&7=_Kw z{I{OIz9>$oxpu;wyu5D_k&)W2u7W3fBZi3|K3se93|`G=lbwy7iv-Rwrr%FdB!8^*J3x*4EY%0t2zaBO;zc1%2x7?%vbb z=nEA#f|E1c+J1j+ETUtsY_t9(ircV0rQk<%vwxlYUKr=WRKtCE1hejUw=7`snW0AO zy-ui1BanCYCh>=(8MOoxFL{@!K6z3g!VsVqNG8Fox!^NDl{Pdqq~+;Zt2Oq9UHLYO zi;3yJ&AA^2HtXUQN=gMAPC>y0%V*C@Gc%c=r$Uv)XuSJ;ytB8)X^9A<@$K8Ut@SZP z^dcOwwQAX)KE0*5awWXiV{e6m@6MgeC+NL>edef9*vPCaRQy(If%4i)QGV6c_b?hC zr3g0lc6W7|SXo)w%(jM(*Cw(0{uC{SXEA}zOlHQZfTE+N)yjGhISP+d?S?F2*mN+- zhRl5jZ(_X9j(NSjyh3g3N>24|X_fjWYwGFs*fxmV;}q|Q!gxiu0cN_Vx3{RvwsZD0 zAUK#0%GJxwNzpQ)j}H@@4?V@YJ3Eac&OB_KU%b%L(1_O>!@{TJL2>c&#wH&u_RFX!L8XrCWU1|4@Lo>GTy)d7xt!*f)HY=HfxQB-aAGD;h`Gr3}K0egd zO?KWGPfXVIK67(-4=dVSS?PjxOT%k|DNq6f#BcdsdhPTr1F@e(BfYqHPp`Poxd=vM zdo)9T@0)%8gw)hNxU*zFizuyX*rt4DW@cqa3nL3(omZ>e+S=NBIy*zWc6$ZJYs0VH z(pcRjX8I{p=_~)f)Mo59t(YfOFfl`W5Frgx-w5c0F2fx!b-|*;lFT51&EEoVs`~cr z8?KIyj!C5!7CPF&E_}-NY#aWIl_A`~z`!b}rKr&_pFe%tIy;zhN-HWV$|xwnr=g*N zZfL*T?c=>QeLwD`d1<#=Op`el=i+piSV2*7@QbakUbW)_OH^Njl_LSFad?>Wy^!;) z7!4H_G|=N$Cyx`8Xh&8$dwQfD9dDy!U}Sv!*wJhEBZm+kEXeC*U%rsfq!p*EAKujr z^Bw!{-9ghj)Dsq1ekX@ct9eN%e!3+U zx7R&;iaab_MDo=o!q%0)TU!#^+E-z*F=;FbOnJ6A-{NqYZbZBDe4NVA=Zs%KSzn*7 zC5Z6NC^j^f6mcK=yu7^e+JFGegruZqSUcan`uqCYL&@2Bo_3NXBqoM(b8ebE*3bx& z3MOj${hJ1s*g`yvj_GuxpP=)~+g3LMJUp`1%<0)#^mZzLfBz~sPca67Hbq-s_xJZ7 z%(X{W!xj7N9UNwgxwZ9P!2|`%vHF*kl#JfvyM0?SoRXWh7=xReyUbcqU7Z46%5dhP zAm{uTo#Pt&qZElWF)t$%6Lk9M;MUew>LNomwSdM(iP5pK`|zBTlamzjoHuW_-)cBT zhyDUsr>VIa-Fmn%w*6*jTtx++h=|Da%nX{ixcJ&c&6O`-zEFv}-+^s~LS4Upy|cGh z2KJi~fS0>~7g(U+6l^Z`CNGbaFa$_4y?F5ge${y9*W24m@%!Z)HYL{MM%+M27%GJx z493BC1t4fg&?(MM3F%WwkCPW96t?mrQpYJEwZ&=Y;p~Bb%F>75XdXSYI!*9GvssxDYK| zKB&mFPL~O%B-<@4EUen)4!7ok&x+vv^lVUawsxZ4OLsO!{jb3C6K`eQJR)~IEd@M0 zJR>{1XeOzkbf1fJeuIX>LIMC{rtJ|;Uu=Kh;9go?4PWX{n>(2Dk=!Au!BdHMSV8byp`p#!-pEM@VD7dPzo0HgUq`GrZ~u^#5SV?9i;va(X; zxPT`qC3P+8N?LzOX?@pX#l?#|C7n=W-6oy#2faeom zjiJ|3#B(bfb)Qwn?VBPP3~=fLNlEk^<+Lb3RGC#(hp&p#%2nS?DO(y~s2>fuk%vl8 zZ@Z2*`1k~xK}E4rV_+(mz)gSa>gLwe-Cgi~VS(EPLHdr4jsstvt)(A4Xr7p$LjlYU zO-P_ZH9|dⅇ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

    npWdp%+a4*>b82!^VTaD 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 IbxsLQ0K{d>jQ{`u diff --git a/docs/source/demonstrations/active_spectroscopy/CXS_spectrum.png b/docs/source/demonstrations/active_spectroscopy/CXS_spectrum.png index 75b083d70b5d0aa7758419b32a19629393701ecc..a258c6edf44d476882434bf440d92f28c16cbb39 100644 GIT binary patch literal 26470 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 literal 24577 zcmdSBbyQYsxGlau!UDx2RKmal1PcX}umu(AE=9T}l*YuZD2gHmrKogwV*m;YN;eot zclU3;=sxF+aqqeJ+&_O~>^)Ri-}=^i<9X&Y=X~CO{ zqO~kqfS(9|YxspfMD32L*(q8X+Fd$#(SSO0&d%E0%Ff*6{CWq2i?$|KmU{&E3+@tF zZ)|60Z7U`uWbq#t2wGh<5~^Ra$P+hNY<*JQmZDhBk^gC7lA$IPrE4jD^pMhJ|DHxi ztxDw|a|2@rs!9#&t5*oy?tj1g>FOQtUsu{Co%!;5V$iIpVqa>(>n&e63vJ5$*%Hqr z=6`;A`n5vY(PCl#Q(JeP-f(sMH?5kIK9=li=XBLyI)Yl)tA_^C_VlN>xk};kq3TO$xK^LQNhnPP;|Ip4}%m%d6?d$ z7EzSkm*o^b6|v_3mmdmym10r<<_i772=zq6CCiqnT)v!^aj7rHlgF*5S~)Y!xi4Gw z%k%wXoubYoov(GWoqQv@pYAalZm=!ws56UI&UPAEy?XVlEoYt?f4sjzMp`=F_qnL; z8v(XERneMlcK2BMRX(1NZ%=OvOyruH8EYQ-yr(qGwyh*WG4!5kbxic*9hzbRV}~zZ zytvn-j5pb=TK2_@7fYC!6rb<2*lS+1({-d$^|g~#t4ZVe(GH-%cp!y18|9d)Ta_E8&EX2z*mpF z-7fu`z1d?rmC>5jq4qSn5@rvXP@~eoHwMLzw@6Ahccj02cbKQ*qxh!dUORW~+NC3I zbolU9>60hZjMHalrk;CvP(z=#2Bo>o-Vw^#DLFeH(lZsCJ6p%FaN+as?>+19E#=sm zR}rIK&nag2(V@Hitfa8dXP?cdv;xP@&k3G-EU?Sxb7+uT=eI8YcZsDEX1q_6l9CjB zB^?JZOsFi9N`Jw`5q2H(R1tUX2qS~Y{<5hF(OIL+^Z83QZ`s1Pd$)qHX@y*UiiNPv zkE1iQQ+>-?=ieqm`Bby(Sit0HPvqCHa>tGzHy!P+^?4Ponc7L#kFjx>Myh4Bs;+D0 z_mNY6g1-6Z)Xp?ckJ?6Rl|$nUYi&jrOKJ@#G;_1^~(OR;y_jtGO-d)z}cxNti zbz4bS=-D8_JNYlQGaWic>#bA+#OznIvAuEbO+S%4J$R?wOf^dFMQ>v!dEjKzN`>Ye zSFB~h(2E$5@-O?FqZTY(yN#Fk=nl=4iZq*_=G88Eu+7r(@$m_20{E{k%_f95+;Ql4 zM#r1yNf%pX>^sViZP>7ZEQ)1ZV!Zh3*Qfl%v`f#&8&3{*$o6$%&6V-Yji0!2<3_mK z%;dA3=dLbUvSb)rEK)buEkZjp<=K9#7*X5bk1n63J-QORY8z#wU*LsFzQ!V9oMPE% zIyg8es5Xivv)6rYs`(3cyzxHE24glM9rN@0`e*u@vbl=xa_oA?z{vO%LE@SF^iQ5G zqN254oT6nXcR$>6W;Z5rpHS{}f_kcDKsp}Wi#O4A%AvkX*Rhp#)2E{+PCS6urPjyNreLG%rtT2%GphhU*zt`qOHtsg#p zYQn$YqHgb|PhQ;Obq;qCVV>N&b?Z(FB{4Hxe%xoGJ;Ynk?O_k1&l3#%^MbqUo@*qV zKE+lz;46LV)SfF>uDr4Ds``C*f78wr-rV+GYoG78+UN6GSykJh$$ifCVL*WE+k`6T z<*QcdbGr`9Ct5a&?XhY)xN7ZM(+sR(ulB&Zuyi>B zCQsfy7=X|*`-xV+=UanZ%6PYKeO}1z{Q2Z=mV^BsCgpGLs>kVFd0+Y)QM#?lb%C51 zgH-TJ1iO9JI?mA#F7T4kUB$y=W%%Wp!95;Hx3|@-tgKI`r(GWU_?+i`D(*a56S!{I z`RnCE75HI!nEa+qpSl~;%N-7G-C8(4HT8V)sx3A5)ahIDuF!MEOiZ)|t&{#89vXNt z?XfF$`pg+b5E5cNtGOjyY7ki89~`aGFW}I3n;B7EwQ}Xfw`N)b)RxVg*GT`)?91Vg z_vhD4G2i8L+S)o!^y05u#k?-4qaPNmt^uVXL|6|mq2mN)F!9M(Z1$b>4#gwF@G516kqYn{lUE% zU4qAs9{t$dtc&%Q@@WwQi>*j({`qenGKiXL_w>8ExMZC_e?IBl`Da(d2q8ImniEDI_slmaAN zC$Cm-J3G`?>K7CgRN^lhQYta^+2AR5SqDOQS%N|FUWabAV<%2@Vb#kaJp{Ur*51+i z`OnrsIVY!##{%jv=caP!6r(kglP(XO&(F^{nHX%9ZN1a%i6Pszetn)pY<09|=+&!q zOEDMUb8cS0zHP&Xw+^~vczGVT@k1deyj4z~y!HIj?=0B>v2biK?%bx%H_t6Ej^|j^ zr{LK;n{#t}o{SikSC+2#NZ3Tt`KndNXpfBl^zI6{)b#CJ$O&P$>C0S5Li_f0e)wlq z=eH0~f{adYbo(Y?YF?s|Cudj^NZSJT$bm)+n>ts^- zz^z-0{0gJ=8??x(H1LuKZlR;6=i=5(wM=Hz=n@sQyL%`8UeBphr!tMzo%^ze?lrkh zSpdqM1!O6sXE_)g$>ldo*XVeYd_pB zZ_$wU?c8FYL9N0ad-jyK#;ZhYX!i>*UAONMRu!NzMC$(paVTgP7_Jlt+OJQbeth!hzH|nh9OohQypgewY@y1bC1qC13Ac^SBryc`qZris{ zrS#yC7m4xNBUPFy7ez%yi;9bF9ZULhrew<|+}Ty#W+#jg2}QYt1OzGoew7jp&x9tA z-EQ_wZFbYd+om`-rdsJTGBC6wEm}M3W;*onNt-riBx|IYhvR!h7=@jyf@y%&@WL!H;s&2oq z`}ljF;ZVsL=}jl@RgCu3S0);ry>as<-=A!LUFK)pt)Bo9#Z@#ELrxf!b+x;a`09J=Ka4ZjQ{MVnPdC~IV0jt3U7 zXiEe99qG;N>%6j%TMo#8m76;TOS1iiCwCdbSoHY#Wnts5QtImJ8aXcMRWaI;fSi6c z`IER&m+su89|@G&_La(j_CTI9ul#hJq!GvO&JOzJZrir)<9A_x)fgoNa?7;u3tTZ~ zPCx$HT9IyRv~WOZt+P5dsIxLM6dPDw*Ja?w;UhO_u(c8!wdHd;Y^B#sv#H2aAPNk-UY* zbE5PQ_#rN$5Q#vfo&0e}2uUMBVaA!(ze2b4Fs;Qr|2FGy0La~b+73@ivRcf{y!Xp9 z;XO#%dz#&*qL0*K8P9_d1gmEl*{8eo3!VV&i+%abiR`*~=~DX)6+~l`=A7*E0RUCo zpLh4y<)oI->Eya)ui0n*BwRB!CR{$`=9-;)9-`JQ9`DVBl9mqxou5v%Y&;E+Onzxn zoA7##giG4{i+he8J7!oFrB2`!foBMnPYH;7t+S|(Lm&S#U=vW&Lmg+?SrM*JM@M%W z`!9WFw0<7tdF}fB@xi7ymW>%(cJ2DsDhhOMj4`y>Rk z^-!f*WY~8Z&Z6>pcJ=Djm0iOSt-&E@P&$>&s2$$76`In2S7;8N5Vm3Z@!RzKak3^sX*H)n%l*6cBC z$Hnn?hv+G*^UVLX@NcIB*%4-FO#5$XfNlnb1(*skr{ORRQ_n^3)8GRpIP~qN$8GTQ zPo=R=DK}P7#VzgMLT=x?H)<~o+F;=_9to`7CYHyK&S&z*ZgUIg$-_Pr;IpIr*0HNse3wsUHsed(7k%4%v)uxJXzJSa+Q zR^Pqw_Qtb?TX2OeKmxC#l-pqR}sR{G2td%k9*01}8NCME=RZ3llzx3;xKqSjhg z$igWS92T}Mzo4KKNdN2GYYR_B^???7^!#~OKi(m&Vtg-UXk-)_7bo}q`}bvq@87@o zefqRxQ26Q7r-H@ot5TXa7z&%Pvq2Im`Uq3yX0h3%Xz9pj*Htn!eDmYy zPv3_RZQo9oJQF^J9g(tdilV1HPCY0sq-07o<2m$Y8Upi`9aRfAn*a`y*sBtw6@LHz z^7;WpceAg-hdOKGLr^h>ut-kJ`3!eP#_IHEAd{5E>ld1PV~ljCzkt0`+AYz@lm-ks zI5u|4w5PbJ=%Q(B;_X|vtkosfua{c7cCWRF#HC&>anxRufO3`KF;;f1T)Q>`!`X@I zfL&k}CudYkOUueqdhCJPi6q9I+qRvwv9Xbnm30sa^!JZKj_`}^Xv)r1oEYh2$4xv%@TKcBz3S3kxPB-QtB(kY*ob${2aqNskpv)6}e!Cdcsjs zY;ZgNTA}$AWw9|LqejAoe@K=5`SWQ5Lqh^aQ+AWI37Gfj@#8mugC_kgZ;QTssl2sf z6JPHadP+_a!U`B?o*10-}*rI!IOnsb*2lNVO=a7^fC#7Q~m|j?pdmOg@C|X-^3mL~UZA!lE2r z&qZ9hQz96i^8JC%Z3~XhpKF!5r)OgrwDTw`Z|Zh;-K8aW+Uag?B8;W-{h7Gjs~g3p zT5chvNUn8;6~*bYlRrjusW7<(^!lWu4x^b+V;VRRg-?Jt# zGs{TL-{kz(<&Un43UNLcOUO)esq6?%} zuIK#o6`T%q8|Gec%#Y3T9Uj{j&wp3geKx5_9)CvYDAW4C&&;RCw)5{_F&di*IV{I0 zdXL8Y?~sMXwNF>u(LzN2+)g|1@9l0$umAhEk2@DE|N9iRm!`@Xz5acj#+?87+(i(f-bDrT6&1L)rZwv&NZnm48rMTu$`Q*widp_V*>%uw6a=_m-n~ z-Ms!C_~4C?uN?aKt2EzE#BhiC|M}u1>Lu^rx%qfv_Q>CPO1XOE@2XhFbZ8YNPgr&j zzFYQ`ypPNZihYO1tC|`$(0bbh1!chq^ToT!$lU(%oLo1jZG)Xj7CR>#->MTPw#8QZZ`rhkq8_Eq}CKF$vV%Nc*=Vz!|D2i;D+A zx~v8AavE-bf#Snt@Yg3&HZ?i-iEBPZ#_KFO&{qM}@#xvJ2v8~8LH(Y%$2l;bfh;1! znR<0dU06<9-OGDUpuH;kc}&)zjQHI2P>MbRNTo$;;~P-xqQ36a1BHZHMr12>5ZJz5 z8c1Ir>9E)gOkxCZxKnR>+g_J(YwwsY8B5C;Fwdxgc9BS{6icImH#Q757`l#>?~+!C zEna8R@iEd>E2QXJbF+jc?Q?N)IRf#cCr{d~58~;-ve-;K17#38h%%%SC4z5IP~V{N z*0X_1Zf-dM(0v2;n>#?VAh_9IM!;SZzkILEk8f(>})8=jsAI9X8HAPE&$UobSx z5YUyA3-0bV_ltchV)OFZUXzzV3zuEArAbQ0vzmLix3}NPIYCh_Z{2G)g6GdqpFZ98_P#Xn4E5LnUlc^y84f)W zpvvmwje=ZN&Pq(YA{7iE&f^I(#StW~NLbJwy+YP8u8BK`G!;oi5_X--hyF9`i~w3X z^(0sG1_TUDK_4-BcYSdNN=~wBfH2jI{e)5jY>}o(X#-rlTiB|pp{h(ZQe_7)N~F?! zT>rgu!B@`_sn%aZl!#rsTfU%2ZE5ez@KRRZDVU|D@k9=s-Nw%cm}Ae6VXLSS#RGXz;JivxC>X_ zsWn}TXcS*$*IbIcjAflX8H%r*%>#x8As%E(MnS$EX`BVise#q&OrHscI?WinL54-Q~`T?F4xMLaWTp&WwEba~9EAkzR#zONYN27x(1*7$AoeZAyH)|NSINlwvJW zifY-O;bC*`buV3x+IS-1+^b^2%+Dc9Xp~0+|4YPYYv+}>Dm%(UpGY{~YBQmY{yb%fSxUZmQ%IU7E zXvMp0g_Cd-6aw|OYI z?OlAKDa%nOoD;*utoil@FE?Xew)0p7)`rh3qGx>T-UqD$Gr36^}v3cB&BzFad%PLqI`k9>V!lTmDA;Xc^zTZDlgV`XvqOn2M{ zw?WOmZS&@1DC!k2T;Tmlp8V+TOA`sp_ud}bHoqJup;L@_N>0D6AHhcrln(YG3m)3V zeU}zR3Md5C9)t@@C>X%#r#$5{_-X5iU+(lVMBQOBK~if(mriV=FhLm}bEB!b9jKw@ zz%>Gb_M+M^2{@3gK-N~y588bLJvsjqwEmQO%b|;;XiQzrGjxJFYGcWmhpZKeWsY5~ z;aItlq8`Z)>~jAgxw@^2BTrB}BMe!`3Ax7i*)t6td#Ke0d56EIair4eLDV8bnZG~h zQce+4mk?!4B~~Yzz)e*yEZ%imUnUK98Q0kIQ(%7Ee?PaXIwh3-%FBy68w8MPMc4yU zu^_^2 zk}0i>($%2OmG)2^Aj-y?-E%+eMpg3Fz)$wI@5*OM*IDr92X;|$2=(Og2P}xFmjpJ(S z?yiKIIdz+oKyha!=+?Vc;lsFGQdi&AX2edJ&|l|DEugBv@v+@GhW{YLIV`#YAyCZb z$90s$Svu0l=uTCXaWmb=!(ZdtR49K*_Z+WXYlTf88Ge590-SffEtquErmS@2vEC6G zI?AIgQFgq4EK!7uo_9Ze3-&56ktZkL^UMZ&P~KZspzgeQVX5=7hf*Ki<9>eQ$V2cl zgU*@KCpq3iKVO_5bWA`Qlchoyv4mOGP@>ue3x>VCQZ)z{v(0leL6A^{q{X0{K-45W zKqpZI^u5J@S1!Rq9plJklj^$c`h%w%V?Y!K{+P7GKEps?5FrjhBQGy|eQE@-%rVgo z!*(V`JhZhYK=XYg0pIBz_R{{oT^b1b%G>dCIdR!yyCyZZRTw{$kCL7wi* zt0}O#5kzi4ILSb*X9qS;MlAD~7WSelKP(*$`r1Q%-DMU=-e7Xcv`q}PnYEP!IJI0^ zxYwX)HTYW@pJC9Ud=e5(k*uJHdPS?HFIh6T)W&{tC*z$YJKbIp{&2LnF%sd`1PLmo zEy#6LJ$te<(o*$2bZjInWpFXTvLJuotSkBA-+ih^0a5UWFPGRnIvodGTUQ(CXvEvN=0%kdHZgB#R&jcP+$8+01 zak&WEBiZ=d;Y6cS!Ce)}R?RvvL!=ZoWji-U8~|DKApR~bsfKR7c3xatl}rh!cGUY- z3wd0EL8}*Wgb-Grh-Z)%z()OWVEp~%c_=g>v(Ha<`II;a#cHO?mP*cL0amvvxdG0b zRz*FHR*ppwtIKiC{9O_di4l;G&&8WiLp@yr59KX94mIOjilLsLhSX5M$LV%Ev&{;xvBgDKP03U_G zkl$*ipr{y7G}cqETAymA3aEU*>IA}x9JojDX+$$21oY;|lD5CSF5231A?+ybYr8~u zZsj7aA>);|>_$DYCUh`bRRAcFjhUAsVd2oounWg}Ot$|1?oQyQ0|zv*S7rBj{x76Z zS~9~K@V$hmHvt>uG?32@)+ijr32>lRzc8421u$4WE4a9{JG*FA4NYWL0xg zcQU&ZiieS-x?tZZ#~24_bjX~KZ;p~Ps$L`DHV?psq=BC>6zho`*S)PpcLg0oSH|2I&9tJkdQ7(nO=W7e`@GoFG7O_-BPl8IS6)|TtvY-hJYP-r! zq*XFzL3$(c0cETouL&gg3@F@0&oCS5tn_JE=eboHtdcUaIMG6olkMBT9x|(nl80l3 zcRPf(b7%5UEfXKh&qD8Nh#`=;UNbVW)jali@Cw2$!B8oEh+~l`o%j$@A-6vIKhsRD zUm4BCk29_BR`XhS#sStWBIs~sIC}F)#$d=JfZClpUi(!0HgMQ6Bmd5!G>Y8F#lK2oi#3+PHwI&!~E z*Km-f%t|44Mj&B}Ma2V@@v;AmW8bOy_N{t2eG7KLY95|gQ1Lr7lEMylAz>7?O!M9! zI^$tSd#@8tuZPdtRVu5dYb{uSh{8Rp`4*W+RfdZ?3-1^h;*X`JA)K)hOG#n^U& zX}#9Ak=;8{$P&MhQl^82_{6V=t?Iy4ato@eBqZ+~du;2PhoH!BPj!QGMV70G)s=)u zX!_&BKdtIQ7_(h%(Jg9=){ZuGmiByoac!Sf(`ONoXMFJ+Go{zF)r)yZL@npzN?kaN zI#mn0qM-LYx{Zsb4;#{_Oi0!>v?KCQ{Rp6t&iU>_DuI<#OHT$jS3dpVy?|cI)oY0g zXX)bo9zG1ooo*GXi#vBMAt6CYN$FSr^xW*2WUSIMlxn5%jQtH_9>+FkQI+=qdIOz% zzk#j}MhN3Sc(7sgJ@BPV>BoGXyqw5od>a7LkVKLlhYam|8*2Yx@e39`iE09;>8_jt z8aIbA2GvRHA2c3!Gdr=10?+6VP(c_asEA}&5>@yQ`9U?u8;VN;du+Lq7kEND58%2I z^tfH4eYGg({sL&qVR3O^`F*h(diOZsUU*9&8X|+kaz$JhPZ;PZsm=PJ4n?Pg^#M~u zsBx?jP~6%X(}Ts?+B}$;5f<@R#K(pRs-&kE{C)`4(cpZUHpRL1%1$PZl7mY;5D$ELQKl6+d_Et) zb1?(uA(L10_)2^;(-VeUL%0B;rzr-@vEOH7mxpAgJypA88Cm}ix1Oyc%oN-X8Cjip zv7(PTOi!X)T5fATlmfe8SAJ$c{BH{GljT9fq}&Eo)T#_trG=KcKnnAE3b9pH$MKlt z+;k!_R=~BWiaCp0yU#ofP8w~58f6Cu2ggeA_m6zy>zDLS^Dd`bUt;ULh683cU=L#y zeUrdY!_Yd~3AYX9XZg3&nDgfUEYZdLBiM-?!(I!q;sfpgx_8ioSjO_Ar!PQ;@tQ&`8Wp+Y%q@?SK9m zFr1&e+uc>P3u`#ieyC8`ltLC}vldNR>RV;~XqZ+e~ zF45T>TyWBIK^_!IvL}dtD9drE0v|vj5Ro-M3Fo-z+l?OFFj;T?HkwL<;H2#~(b}qv z>2F~i?V=_Fp7*LB<=NStCd^b5&~IdHx`4{tYOYW97+&euTr-MD56m731LSrgq1X=> zfjPgbgz5PBE{2NlF6{V)JBN zfY@tr-2^PG1r`J;&5g~S${7hdQ0BaOqQ{N|HBN7u&%!PW4iyELfJcb*H4>!VAtWc;jR9oHsLotBoCN7bKfQNNGav#~Dj^Ah_x0i~L4 z8gO=DPlY0T7$d9`y$c16U%N}cTPT5T;_d9Gm z_BxN6!zg&U6**LkPYcm(_HEz^nl-YqQ)hVSBj}^>xYZA!up(7kv@LO}0kc&-*`;3r z@%u}Vr0%zG-<%Nhv>)rjmV@f~4a(9;6iRFN7QpXaIy+|ek5`gQb3|uV{Xu(1P&UU~JP`%0f;7h0l znXVF3zD2JSC12%xYe!{8{%p$Wg-?WtCiOr2zrVNCW925kYf$v`^dq3lh!pNL)ss3>u+FL-me+FZM?rV@IfbCD z{}VM_AeAi3aBE}QYJ~jX@=U?XSUVMPz3*1m1J#S{`h;i|s5ZK8ue1%$xGWrc*ga78uh)E00_#x(}aF5lLzCy7#&`CxhH3!%j{^DC(ro>~Nh! z^xUY?jJ@8%8He4vr7=@FM>6(R`o9fu)lQbV(%j5_k9H75*?YRLD)8(^-CYP;TV2yZ zAi)a3%U*6VVB!J15<;Q~mpe*D^h{*_%fO|#89lJrU_1{L^{JrCKNf?8JSjQde~ok) zNSDE-{j#;#FH?9HJpJXMpE`L)xrS>vYQP{jf&GU;-8ax}(j*&-1IdYa>{kfnYRJk> zF@X2h*F!W>DhRW!Oi8?tX!;gic5}m7tm503kSM-UIKr zw5nG}QW9%G7#m-5W?0b`h&`h-BJx*TTPVzIjn?m%lKv*3#@FktvcA=iAQ`>}cglvM zuKJ)UZ5kQ5GR;O`S^)rO1YE*NA1v@NI0l_!9q5B6TI^9vzm02y>5z{ z2Wd8H3;WIK1o#FG?#k6-D4|eV)>nfy{Dy5k3^Qjqq}6qHUPO5L++MpJ9=-v;%8tIJ1-t+;l^qm-MM_9IInmC6|`;!lYMWne=rr+3(e9z$N_ zCFy~v4TR2^(Ev@ZkSI9WZ4@8J%&awN%-jMEWEeD0&ATApiK!_w#AsqF`a5)5pXm%Y z7?aJZ1(N1xHCyiim%MekIstv`hXNRfUg;^K!7H#R2y_gd*PKyqPV zhP!CC`NJ^B_b{9%3pvjbgft;=Hf}sjJfBFq8>G=;PyrtO4RK}uC8Q;p#-~-RZ(>NG zMGX%Pm6PA2d*pO_db-btUx-h9iwQ?xzvQ0%e8p<)-GcPndeLp=tw!1PC=3Qfs2l_; zlFxht17l#6^K5hIZBYL^qu#U&LV9UGz6rE|V>15r@quYWT09|^Njn_TW?+k}%yoCS zs>T$jy&4XKBU#czDq?~K|^D$q+VqWmymg-P+@6%X)y==5-P_ohpA>`JnH-Y zZIljR!6XWp9nz#)Eb!)uU1yng2qkwVl6bb<>|LWa(=m%(qZ4AH*6+r88JQ#3V>Jl=tNNCgpo^&g%Op0bnf z6UDl~3rrS`8Ok6kx5s9XHM=tbInUi|mj|>7_I||4ky@i5lMlC6XklQ$!AZn&d- z5=~Wv4bz#ASW3S@Vc}UmJhD#x7#(rd%XDCJ5*C@ zDWpeovnI_NFSo3@*|t<%Cz@GMd!mhPK`NiX?!nX`w*^$#^3g-3rG4EBJ6F1gy?=nZ zAFsAO)~RKss$Bc{TOVCDUUx}%p2`JOgwPb#-e>e6_eM4{ni&*lCtFtJ4A$7o>~TRm z$SZKumg~r8F^S~fXIV@2GfsKygMF(C&}1^9yUsn)Uxk7E<~R{HlmLgP2_~#2rW`moB@(aT1^2ijG4iDJ9GZ3B3s_~^~}gX~9RgBq=z3TW?<NkyNINiOToU0%hXl$Nzt~ToKijG2 z1P3EY)oy+qFKP7OD?D*I|3%^HshWx|^4Fjidr&Q_s%mqp9!pMXKYg;8nLp?f?NnQ8 zc>L}H5w1+Ni64bR4kiCnx9_h2Wgc05ikN#O&RMh$*$OX!h0?a6Zb?Z!s*7#z?(U&g zIZ;spp=<{uNsm=4JslmPcvco`wfp<5{@*!%n-%SqUD&J#1kX`Y`EePB;Q3#9;I zTg%zUFN7Hn}rcLeo79u7FN_ruRSTy{#x1U-{=sa8QGcp)!r-n>~_tiDGKW*W_j zSFdK?384qJf6%u(&ZD-_^~FUyYBUru^Q(Xgk31$W zBqCLj{@~lJ{1wrf`!%1U@R7dM zZdKuu-`%F3=kk&A5K^0)P3fB)NmNt10K1Of&mloQrKPevJdoTd33-Q>eUP|(yA~>T|PFTo|lAeZ>4r5aE8sgs#gzm11rMgTLC?U zKu||q1ytaSy93?)ProNETc3!R*cx@$C8mH{N&UGwAOh2RYW(OxwntB{q202mMIQaP zf9TJ0&@ZBLz9%p}+2yef2QS3JdP^xS%V)p(7znkT(aRRsEvce0coe;@V*JdzoVayk zw@Li=4v>ObD9YVEQ)|()rZxUY@CK_{T%afYxg88{j>Zd+f|)unwcJJ0+;fW#4#?QS3t%WVA>&`z=Zh`Q`4l@kg-ud z&%X1$tu^^7^3;x&%z6?$yAeEM#^?;$cEa9_$Frx7m>1`LrNi}l&l%0nvF#K@D@iOw zosS&k3#*w91aMu8<+PD!ep|O`Rr{q` z)hx+G4`Scdv2o*1^GIyovtT*pva$IXQ}A5+&XY{*=ch62Y9f!w{1_*U<}pkL&9U-a z7GxeQ6eYi1(xvM!JRg`0xUO;Or80X7GymRoaN(bErQk59`ge4-S$SwP=}|CdlUWnV zT|Qj9%j3_q-OQ58+~rZVcmpGI>041O$Q$;|dUw}rP_#V1DE&D4X@5_$ck`kA6-m>gdzj zxnm|hbdSe_X_T&YBcr(m?U(HuqZO3A;RIuwq?~CTqtuqG2!giai+^b`tq&>|@uY6; zE>5(fF~35_aQReM)YfN|yL>ND)h+xc(Jh#Par{$h>Bg?Qkf{BP6l-Z+k&)!$>+?2y zd>>s)Itxer!le(TsNi?L2Nz$L{A|NUTW;`J*F5VVi>Spsdnvi^Q9+;J$|-Rb9P%Us zxh*k^uJCSQhbf$b@q%wz+g~z0k^GFMa`Y+6`r!j(b5SBJRE|Q+OU9kIBtMf$Gcc4~ zeAMSEeMt#}gd*(t9=8pbI>Jys{+<&qeBAK7K6QkybXF~(L}*w&fC|n?D4|OkEjl@> zjUE33=cN=0#OB3kZur%p;Smb|Cmw1;so21g^Bass;CTBi+ZHq<6>mvzq|H1@Jl=d- zIjC9NZUhe*(>`FT9@U0V(@D2o3xZo}Ei>>{=+}HP4;rl7aJO$uRCK(z!=q{!)KYq> z{6yKjMA?rI;Y}zZzkBFtq)C}AV4a5?QpXZ!2P|gvr@O0%Kj)!|z`%j8uiJAeIv-jz z3pna|nyX6BOD|a}F_k>R;^0B1U^ZEreJ3@p;`ZW!ed>NsievS9>B$*p%-tRQ`ha5$ zW84-Jrb1=e{pT8z!5F(q1QiJ>ZJ44R!5VwJU6VRE#lEGBY)pTNF-P-w|K`~YnKT8t?~ycXYmWi~;rM2LPBXUo>EjIUcucP?9w+Z-AYY|&_&Ej#G; zjAacKcCxCpv2MeaWraJ~iYIJ59H(uTApqn-3=*cQn3nqR&h(8P460mAMTGn`8r&?M ze3;HZFw)U-OMhPyb(lPs)z(tCmg|)wQ|w09Y-#<~4;cUlPqt}N0rbJijQzYjJeFg# z_IIT}&T$gH#yJ~A;G$Y?z|{Lp%ezvcZBfxz3uRkXjRWLFm2XpN@Droki4Xo7nCAe& z6#r>eL^Cuj)d2UzAcTV1fo}-jtZ=9iA|XVTE)88G^*J0 zTv+29AP+o=YS`oR>Ui?CfWSMXuCPpsYs9Nm%u#~<9NpLT?m@`jzAH$6lP)<~! zltedYra%x{b71ibgFg@!G(Xb%47Iiss5sFVeOr|_R%k4r78&YE$Tv=tc}d9dFXxke;#| z7gjIb-w;d=4M0~90k7nw8BqDnKfB%~&lv{X=RH;#{UX>SQBw90%?+(OeA0E!zn+wK zE}$yETnR#ntFdpHy^~S=v91M+vH!eZu?_i`U)>0d@)4YY}>YXa`0=c&nv-iBmRN88#7{fJ0A8W z*v&mZu)4jkabNImw=7+`++`~|t2ZS0m*}`o{NLN`G{KXx3+Ri7Zmsrx1!~I#bM)HH zJ#bQRC{3cqHK`%?=MI`1+ud8yDawzr&q zqBkW01PSGaU7#;KFnV3>n|@9Bsdbgv?sVs*h;S*!C-_`=kl$-IHRLbZZYMicUGJNm zD?DmYf4oXQvLKnW={@T=`(#U(v#ztv=B=!&MW>Is#!qRjHJCY-7`5X;Q}~fue)%fJ zN9hZiF0$B^+lLNj)aiT(8XI z{`_^2?b*nWCwBPub}B@9E^hi>PNBKs2zaam$r;dQMUzX^p;@H~@ccRF;E`H^lk8bY zKrnJGS>_O^-fCg9Yh*RMZO2?*;XdZ%yNnFfxQ?|)u4VU8v$ZY<7xVs)G>>>M%(WJj z(%^{xKLDQ`{A3Sz1nhIwOhQ?~{|WG&zxKJZ}`x(QyZ;0Pc{#J-cnCZp_|KW>`i#Cjk8Hqeof)=rZiUweD7 zmD~HLGFJ*PEX8x&K1P8X{(n7V?B`H)i=aW(86{W{UA5q_kkhDYuCH|5vlku1oRG_X zR&#On{CtMzxJ1-*Sh8|rZk~-}w_XFPr~|Pq0yZlsq17eN@qF#=#yDdjt*%#IO+Kxf z_1cuyuC!9}hfPtJ&bt5F%zW==2xA=c^z?KEOsf|UxWkh&ZXLe9q3iZr*BFk5Hu8r^ zt4(aAS#gl^h*vfqpb{t=Q;UPsR1Ra^5Ha=YwQH{0zI@)!+JC=?{Nd@W?M7@gI8TkB z)BpBZHHqD3RRRzL;ZG=O9hw_+pW6yQ36#(SoWzvLjWZY!{pCPG&$}6L*nqG54Iz2< zj8f>BiHD76i0{MjiHgu}XsF98o20p4NWMGvu+OQ+B^Ck>X6 z;`8w$2RClqXkNNbe=pjjK@X6g6=K%QnfSfV)7CmjDCX6aKl~ghC$)%e2QBHS^+-z> zm$*~Kcg}{kJDD}5iQ;Z7kA>)hSzK_ekJI-X>~Mi>^(#eFybLH2Mdph# zNgvbxrprgjyW>QJ5gbO;iSwW0e7{Th$>N77SkF0kH)d9kHu#I$?gc$VHq|5qL)H~5 zUV!g>o1&Wj!BsGtsj&^7AyZ<4Cf!T6ZL&lopBn3{#*qH#cj~q6cr-j@p5OO&iJsgG zoHb-AnAyGip})WR%9SfW!^{N77tVdSfFyW6XLiyIo!0uZu!qzKf?aMtt*004i0x!v z2-nYftgp|=?ELLqT5Dx8NEW(~K53YZL{@W4uT2JCGD8E+kF$x?DI6Y02sPZaVqSTtjh zZ$ziz?M*ze?0@Mh-z*ISR}l!LG*9dunUg2u8joP4wc8~!FGA+VHXy#Mm1DJBn;VVX zSX4Oa^cYE4BJ%?dqPTU4oP+{-j~ol+S88Nv$cK;&^P}y*{8Vi9n1XVdYe`X285S9F zDG=Wc&MB$}$!(9h;fGajB?d`wbSMj~1&CrZhG^Z~rgKqGE8$EY_I95>0u}?O;MjCE6mX>KDk^JtOR%%DO8Z!5kEoCi za369uoH3vfIkyDsPVlm2?~o&VW@%Y1l?K*7S|?irC+AIeCzcWtn;%C`8QginI#w%> zS+LN>?8+nMsNx1AXq4}SDeVDo=#=s^=Z~%qN@cEr$TdCbnEytbLN0Qi3q%Y-C!OBJ z5G=~Whv{+`@}psx^#V*JM3{wb86&?Mx%1JZ)x=r^&pO$va6nezWRm(Jaz-Dw+xR)+ zuk=}s{RV}Fh>y$KFdY|7X<;#f4ngbo8V7rLP#E`;@AfczB!8j%Euq_x-#dh3tYXMH z3uu?hz{wYA6x}M`pGQxc+CZA}|1;Gc4jUCQdc#c=_a#uEY4DsoOL}Qwd~T@<&NNuN z`@*fVvNBjt%*|#Vrj-UJBaqLYr>h2o?Lp?68_P}NT977DGOMZ!|PaOJC3{J!3^#!5X z91@lZQR~a$W`p)5m*bm#oi(=<=g;hg4)Ghlik%p+o#ZI4yO02DaST{A^H{BGejg@z z{tTB|h?Jt}Vj;)0okkM}>EFjC6FDo2Tx2;Kc53D5}51rE!4hw`<%+tNjlK6sEm z99L3janK9*xPGi2cz3JmsGmKf#ye|4|&9nhV(>rxDVbBH5iTI zZPCT?8^xM1nnt(q@_x|5wZE<0r=D-;<0HXJw*ghunF|+Qk+W`a&2I>O*KI2NSQ0Y2k1I3nXKntQ{M9P92` zHKbvz~HdYcjOO#VI;c(1;oFkTveL4waF#j(nijReEy@x_3oQTAx4>(hI z4@ZB=lO7dv$P}1h5zfBG%v9bEa+VwP7-iVJ0ms|1b`{VN%^MEk457I4NB$kIm}EqJ zqyw&lm*sqj18Sz@th)kWgmS1Xmo|jVI>6z7MqIIAiJJ@^^E8(;!N)Wyj|Lwrxhog09 z4=0xqi@;yQgr8b7IZ+GF1{DmJMIrd;2-01LyO44j_Q}^cP=z!vsF3A2-z|@m>@;9T zjlhveq*)DT(TPKsjL^-^A=r!@zNHn5<|H!mgy=q>O(}_k+_3Bb6oV62Z^{j2Kfz_l z0Oo~5gM(ZWGDRc7?}x<*(N;pF2x9CZwE$@`;VL4`1vyK^ zp|2?#0kR8CY)SSu?tql!z%SzNMylz$xz6${j>l51H$!`O1k`&qIPJ)RIPhTaMgMpG z&>q9jUqNBaA6<(Bb}}Hy3#sW!lN?r&Y^ECe69&VIhIHEzBo5N}qX*ir|L3};|JSlEJrO?YK7Ed5JnJY z;X(*olmfD}5IcjB;Vdc`b><+F%UtujWdfd9(@ZXlixnJlBq#8o8I2qn7v(ZI7Na8W zXH$f@g}vU)f1qDsJLh}8&+~kq_w#(-?^l?^ML`$0HfWE(DZbdv6e?b)-=?c@c95Ok{I`eQ#X zFj$Qd^M82}?C`cM+)Op5g?3s34z{s%tqO|DvD3myc|T*|Ib2Fs^J?>lnS2OC1rn2( z$-5nS#9>~v_VczwT94yb`CR^*p@%XDbL`kdsYrrp-dDC)F91pSXZ$aL3BK8+6l z*0uWlr=YVKDRqt;A0OMN!?lFG!QwpodR68=@s;Ujs;_bytD!-v(8Z?K$u zpEx}gV6%7KYxPysnz5HV{umLPY-y=Iufi}4WfoWZ@jYc*@2Kvh`P8y^(l3Afkstc* zGi9Wv5~bBrM81x)&BwDV`Uc=eQCxIsy5lUhUnR#{F_?ENiFX1mE<Uo>AIAl?83xT&g|TY#w7H5`7a*pZ0)Z&*4x^O|6af$a|<%*mv>$8FvSy* z%y(x0iN|g}QG7Zly#WB~lCi&n{Mordl5Gcs^k6wRzQfyyw z;}Y8A&0QArq&&D)_J*aO*(3hN%w#`O=jbj5YbB0}PVwxshjKUDOkY?@OIhhyfNV*x zG9xQdm!2u;@=^*G9UHMcmr~d)ZY{ zlLJHSM0zO4D)$iA67txH!}V<7V^e7>`5JllIh+qSc_*^3D<%jnt^S(Lt?Y(;2GLLM zyL6AF!Ow+03D|*ea){pm61T5sRsP1?Jo?yGBsKo;HPUUq{ql1G>$l7`S3Ww6I5TaJ MihIHK{OgDQ1N8Q7Jpcdz From b2230cb647e477dfe2f514a8536f0cb24a45e6c9 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 28 Jun 2024 00:48:09 +0200 Subject: [PATCH 101/134] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c70837fb..43f62a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ New: Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) * Fix IRVB calculate sensitivity method. +* Fix missing donor_metastable attribute in the core BeamCXPEC class (#411). +* **Fix the receiver ion density being passed to the BeamCXPEC instead of the total ion density in the BeamCXLine. Also fix incorrect BeamCXPEC dosctrings. Attention!!! The results of CX spectroscopy are affected by this change. (#441)** Release 1.4.0 (3 Feb 2023) ------------------- From 406651a68429503bb1132d338f2a876a7b21caf4 Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 29 Jun 2024 01:17:46 +0200 Subject: [PATCH 102/134] Limit NumPy version to 1.x until Raysect binaries built with NumPy>=2.0 are available. --- .github/workflows/ci.yml | 2 +- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4c15ee4..dbf0bca2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install --prefer-binary cython>=0.28 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4" + run: python -m pip install --prefer-binary cython>=0.28 "${{ matrix.numpy-version }}<2.0" scipy matplotlib "pyopencl[pocl]>=2022.2.4" - name: Install Raysect from pypi run: pip install raysect==0.8.1 - name: Build cherab diff --git a/requirements.txt b/requirements.txt index 24a9b6e8..3e98c385 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ cython>=0.28 -numpy>=1.14 +numpy>=1.14,<2.0 scipy matplotlib raysect==0.8.1 diff --git a/setup.py b/setup.py index 6cf51624..eb56a797 100644 --- a/setup.py +++ b/setup.py @@ -115,7 +115,7 @@ long_description=long_description, long_description_content_type="text/markdown", install_requires=[ - "numpy>=1.14", + "numpy>=1.14,<2.0", "scipy", "matplotlib", "raysect==0.8.1", From 422abfbd03c30cf9dc2d04cf37cee20f1f4586a1 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 10 Jul 2024 21:46:04 +0200 Subject: [PATCH 103/134] Moved numpy version restriction to the matrix element. --- .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 dbf0bca2..dd3ec227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - numpy-version: ["oldest-supported-numpy", "numpy"] + numpy-version: ["oldest-supported-numpy", "'numpy<2'"] python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - name: Checkout code @@ -23,7 +23,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install --prefer-binary cython>=0.28 "${{ matrix.numpy-version }}<2.0" scipy matplotlib "pyopencl[pocl]>=2022.2.4" + 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.8.1 - name: Build cherab From 645d697f4541d852adc16f5c3e2386c5c64cb8d7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 11 Jul 2024 00:32:46 +0200 Subject: [PATCH 104/134] Brake variable declaration line in BeamCXLine._composite_cx_rate() into several lines. --- cherab/core/model/beam/charge_exchange.pyx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 12650ddb..83fe237f 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -192,7 +192,10 @@ cdef class BeamCXLine(BeamModel): """ cdef: - double z_effective, b_field, rate, total_population, population, effective_rate, ion_density + double ion_density, z_effective + double b_field + double rate, effective_rate + double population, total_population BeamCXPEC cx_rate list population_data From 99c9df49ef13cb813fc48786f9e7ebdeeae0e39d Mon Sep 17 00:00:00 2001 From: vsnever Date: Sun, 14 Jul 2024 23:03:37 +0200 Subject: [PATCH 105/134] Add cython~=3 to the requirements. --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 1 + pyproject.toml | 2 +- requirements.txt | 2 +- setup.py | 7 +++++-- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd3ec227..eb7de4f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: python -m pip install --prefer-binary cython>=0.28 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4" + run: python -m pip install --prefer-binary cython~=3.0 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4" - name: Install Raysect from pypi run: pip install raysect==0.8.1 - name: Build cherab diff --git a/CHANGELOG.md b/CHANGELOG.md index c70837fb..35e3e6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Release 1.5.0 (TBD) New: * Support Raysect 0.8 +* Cython version 3 is now required to build the package. * Add custom line shape support to BeamCXLine model. (#394) * Add PeriodicTransformXD and VectorPeriodicTransformXD functions to support the data simulated with periodic boundary conditions. (#387) * Add CylindricalTransform and VectorCylindricalTransform to transform functions from cylindrical to Cartesian coordinates. (#387) diff --git a/pyproject.toml b/pyproject.toml index a4eabed0..ea724622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython>=0.28", "raysect==0.8.1"] +requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython~=3.0", "raysect==0.8.1"] build-backend="setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index 3e98c385..e070e4ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -cython>=0.28 +cython~=3.0 numpy>=1.14,<2.0 scipy matplotlib diff --git a/setup.py b/setup.py index eb56a797..b9058c27 100644 --- a/setup.py +++ b/setup.py @@ -33,8 +33,9 @@ source_paths = ["cherab", "demos"] compilation_includes = [".", numpy.get_include()] -compilation_args = ["-O3"] +compilation_args = ["-O3", "-Wno-unreachable-code-fallthrough"] cython_directives = {"language_level": 3} +macros = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")] setup_path = path.dirname(path.abspath(__file__)) num_processes = int(os.getenv("CHERAB_NCPU", "-1")) if num_processes == -1: @@ -61,6 +62,7 @@ [pyx_file], include_dirs=compilation_includes, extra_compile_args=compilation_args, + define_macros=macros, ), ) @@ -115,6 +117,7 @@ long_description=long_description, long_description_content_type="text/markdown", install_requires=[ + "cython~=3.0", "numpy>=1.14,<2.0", "scipy", "matplotlib", @@ -122,7 +125,7 @@ ], extras_require={ # Running ./dev/build_docs.sh runs setup.py, which requires cython. - "docs": ["cython", "sphinx", "sphinx-rtd-theme", "sphinx-tabs"], + "docs": ["cython~=3.0", "sphinx", "sphinx-rtd-theme", "sphinx-tabs"], }, packages=find_packages(include=["cherab*"]), package_data={"": [ From dc8e00c2dc835ce33ef4c1a7a4336d7a7c1fc01a Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 20 Jul 2024 00:52:16 +0200 Subject: [PATCH 106/134] Add setUp to the TestCases containing Plasma to ensure that Plasma state is unchanged between the tests. --- cherab/core/tests/test_beam.py | 58 +++++++++++++----------- cherab/core/tests/test_beamcxline.py | 46 +++++++++++-------- cherab/core/tests/test_bremsstrahlung.py | 19 +++++--- cherab/core/tests/test_lineshapes.py | 13 ++++-- 4 files changed, 80 insertions(+), 56 deletions(-) diff --git a/cherab/core/tests/test_beam.py b/cherab/core/tests/test_beam.py index ef10126f..7d2ec203 100644 --- a/cherab/core/tests/test_beam.py +++ b/cherab/core/tests/test_beam.py @@ -55,32 +55,38 @@ def beam_stopping_rate(self, beam_ion, plasma_ion, charge): class TestBeam(unittest.TestCase): - atomic_data = MockAtomicData() - - world = World() - - plasma_density = 1.e19 - plasma_temperature = 1.e3 - plasma_species = [(deuterium, 1, plasma_density, plasma_temperature, Vector3D(0, 0, 0))] - plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=plasma_density, - electron_temperature=plasma_temperature, - plasma_species=plasma_species) - plasma.atomic_data = atomic_data - plasma.parent = world - - beam = Beam(transform=translate(0.5, 0, 0)) - beam.atomic_data = atomic_data - beam.plasma = plasma - beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) - beam.energy = 50000 - beam.power = 1e6 - beam.temperature = 10 - beam.element = deuterium - beam.parent = world - beam.sigma = 0.2 - beam.divergence_x = 1. - beam.divergence_y = 2. - beam.length = 10. + def setUp(self): + + self.atomic_data = MockAtomicData() + + self.world = World() + + self.plasma_density = 1.e19 + self.plasma_temperature = 1.e3 + plasma_species = [(deuterium, 1, self.plasma_density, self.plasma_temperature, Vector3D(0, 0, 0))] + plasma = build_constant_slab_plasma(length=1, width=1, height=1, + electron_density=self.plasma_density, + electron_temperature=self.plasma_temperature, + plasma_species=plasma_species) + plasma.atomic_data = self.atomic_data + plasma.parent = self.world + + beam = Beam(transform=translate(0.5, 0, 0)) + beam.atomic_data = self.atomic_data + beam.plasma = plasma + beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) + beam.energy = 50000 + beam.power = 1e6 + beam.temperature = 10 + beam.element = deuterium + beam.parent = self.world + beam.sigma = 0.2 + beam.divergence_x = 1. + beam.divergence_y = 2. + beam.length = 10. + + self.plasma = plasma + self.beam = beam def test_beam_density(self): diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py index ccb0c855..250da0e8 100644 --- a/cherab/core/tests/test_beamcxline.py +++ b/cherab/core/tests/test_beamcxline.py @@ -76,25 +76,33 @@ def wavelength(self, ion, charge, transition): class TestBeamCXLine(unittest.TestCase): - world = World() - - atomic_data = MockAtomicData() - - plasma_species = [(deuterium, 1, 1.e19, 200., Vector3D(0, 0, 0))] - plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=200., - plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) - plasma.atomic_data = atomic_data - plasma.parent = world - - beam = Beam(transform=translate(0.5, 0, 0)) - beam.atomic_data = atomic_data - beam.plasma = plasma - beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) - beam.energy = 50000 - beam.power = 1e6 - beam.temperature = 10 - beam.element = deuterium - beam.parent = world + def setUp(self): + + self.world = World() + + self.atomic_data = MockAtomicData() + + plasma_species = [(deuterium, 1, 1.e19, 200., Vector3D(0, 0, 0))] + plasma = build_constant_slab_plasma(length=1, width=1, height=1, + electron_density=1e19, + electron_temperature=200., + plasma_species=plasma_species, + b_field=Vector3D(0, 10., 0)) + plasma.atomic_data = self.atomic_data + plasma.parent = self.world + + beam = Beam(transform=translate(0.5, 0, 0)) + beam.atomic_data = self.atomic_data + beam.plasma = plasma + beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) + beam.energy = 50000 + beam.power = 1e6 + beam.temperature = 10 + beam.element = deuterium + beam.parent = self.world + + self.plasma = plasma + self.beam = beam def test_default_lineshape(self): # setting up the model diff --git a/cherab/core/tests/test_bremsstrahlung.py b/cherab/core/tests/test_bremsstrahlung.py index 5373776b..a77a1da5 100644 --- a/cherab/core/tests/test_bremsstrahlung.py +++ b/cherab/core/tests/test_bremsstrahlung.py @@ -34,13 +34,18 @@ class TestBremsstrahlung(unittest.TestCase): - world = World() - - plasma_species = [(deuterium, 1, 1.e19, 2000., Vector3D(0, 0, 0)), (nitrogen, 7, 1.e18, 2000., Vector3D(0, 0, 0))] - plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=2000., - plasma_species=plasma_species) - plasma.parent = world - plasma.atomic_data = AtomicData() + def setUp(self): + + self.world = World() + + plasma_species = [(deuterium, 1, 1.e19, 2000., Vector3D(0, 0, 0)), + (nitrogen, 7, 1.e18, 2000., Vector3D(0, 0, 0))] + self.plasma = build_constant_slab_plasma(length=1, width=1, height=1, + electron_density=1e19, + electron_temperature=2000., + plasma_species=plasma_species) + self.plasma.parent = self.world + self.plasma.atomic_data = AtomicData() def test_bremsstrahlung_model(self): # setting up the model diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index da4dc41d..97a32f1f 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -42,10 +42,15 @@ class TestLineShapes(unittest.TestCase): - plasma_species = [(deuterium, 0, 1.e18, 5., Vector3D(2.e4, 0, 0)), - (nitrogen, 1, 1.e17, 10., Vector3D(1.e4, 5.e4, 0))] - plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=20., - plasma_species=plasma_species, b_field=Vector3D(0, 5., 0)) + def setUp(self): + + plasma_species = [(deuterium, 0, 1.e18, 5., Vector3D(2.e4, 0, 0)), + (nitrogen, 1, 1.e17, 10., Vector3D(1.e4, 5.e4, 0))] + self.plasma = build_constant_slab_plasma(length=1, width=1, height=1, + electron_density=1e19, + electron_temperature=20., + plasma_species=plasma_species, + b_field=Vector3D(0, 5., 0)) def test_gaussian_line(self): # setting up a line shape model From 3ad37ad8d014506a4dde2a3dbe75fcebae34afd4 Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 20 Jul 2024 17:54:26 +0200 Subject: [PATCH 107/134] Make atomic rates return zero if plasma or beam parameters <= 0. --- cherab/openadas/rates/atomic.pyx | 21 +++++------------ cherab/openadas/rates/beam.pyx | 30 +++++------------------- cherab/openadas/rates/cx.pyx | 4 ++-- cherab/openadas/rates/pec.pyx | 14 ++++------- cherab/openadas/rates/radiated_power.pyx | 21 +++++------------ 5 files changed, 24 insertions(+), 66 deletions(-) diff --git a/cherab/openadas/rates/atomic.pyx b/cherab/openadas/rates/atomic.pyx index 04500124..bccd1ff3 100644 --- a/cherab/openadas/rates/atomic.pyx +++ b/cherab/openadas/rates/atomic.pyx @@ -50,11 +50,8 @@ cdef class IonisationRate(CoreIonisationRate): cpdef double evaluate(self, double density, double temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(density), log10(temperature)) @@ -97,11 +94,8 @@ cdef class RecombinationRate(CoreRecombinationRate): cpdef double evaluate(self, double density, double temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(density), log10(temperature)) @@ -143,11 +137,8 @@ cdef class ThermalCXRate(CoreThermalCXRate): cpdef double evaluate(self, double density, double temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(density), log10(temperature)) diff --git a/cherab/openadas/rates/beam.pyx b/cherab/openadas/rates/beam.pyx index f40af8dd..58bdaa87 100644 --- a/cherab/openadas/rates/beam.pyx +++ b/cherab/openadas/rates/beam.pyx @@ -78,14 +78,8 @@ cdef class BeamStoppingRate(CoreBeamStoppingRate): """ # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if energy < 1.e-300: - energy = 1.e-300 - - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if energy <= 0 or density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** (self._npl_eb.evaluate(log10(energy), log10(density)) + self._tp.evaluate(log10(temperature))) @@ -152,14 +146,8 @@ cdef class BeamPopulationRate(CoreBeamPopulationRate): """ # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if energy < 1.e-300: - energy = 1.e-300 - - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if energy <= 0 or density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** (self._npl_eb.evaluate(log10(energy), log10(density)) + self._tp.evaluate(log10(temperature))) @@ -228,14 +216,8 @@ cdef class BeamEmissionPEC(CoreBeamEmissionPEC): """ # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if energy < 1.e-300: - energy = 1.e-300 - - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if energy <= 0 or density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** (self._npl_eb.evaluate(log10(energy), log10(density)) + self._tp.evaluate(log10(temperature))) diff --git a/cherab/openadas/rates/cx.pyx b/cherab/openadas/rates/cx.pyx index cb827a8b..342a9dbc 100644 --- a/cherab/openadas/rates/cx.pyx +++ b/cherab/openadas/rates/cx.pyx @@ -88,8 +88,8 @@ cdef class BeamCXPEC(CoreBeamCXPEC): cdef double rate # need to handle zeros for log-log interpolation - if energy < 1.e-300: - energy = 1.e-300 + if energy <= 0: + return 0 rate = 10 ** self._eb.evaluate(log10(energy)) diff --git a/cherab/openadas/rates/pec.pyx b/cherab/openadas/rates/pec.pyx index eafc6c64..c33e2bc2 100644 --- a/cherab/openadas/rates/pec.pyx +++ b/cherab/openadas/rates/pec.pyx @@ -56,11 +56,8 @@ cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): cpdef double evaluate(self, double density, double temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(density), log10(temperature)) @@ -108,11 +105,8 @@ cdef class RecombinationPEC(CoreRecombinationPEC): cpdef double evaluate(self, double density, double temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if density < 1.e-300: - density = 1.e-300 - - if temperature < 1.e-300: - temperature = 1.e-300 + if density <= 0 or temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(density), log10(temperature)) diff --git a/cherab/openadas/rates/radiated_power.pyx b/cherab/openadas/rates/radiated_power.pyx index 570ced92..bf9c1667 100644 --- a/cherab/openadas/rates/radiated_power.pyx +++ b/cherab/openadas/rates/radiated_power.pyx @@ -49,11 +49,8 @@ cdef class LineRadiationPower(CoreLineRadiationPower): cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if electron_density < 1.e-300: - electron_density = 1.e-300 - - if electron_temperature < 1.e-300: - electron_temperature = 1.e-300 + if electron_density <= 0 or electron_temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(electron_density), log10(electron_temperature)) @@ -95,11 +92,8 @@ cdef class ContinuumPower(CoreContinuumPower): cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if electron_density < 1.e-300: - electron_density = 1.e-300 - - if electron_temperature < 1.e-300: - electron_temperature = 1.e-300 + if electron_density <= 0 or electron_temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(electron_density), log10(electron_temperature)) @@ -140,11 +134,8 @@ cdef class CXRadiationPower(CoreCXRadiationPower): cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if electron_density < 1.e-300: - electron_density = 1.e-300 - - if electron_temperature < 1.e-300: - electron_temperature = 1.e-300 + if electron_density <= 0 or electron_temperature <= 0: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(electron_density), log10(electron_temperature)) From d81096abed284094f3f7c33339212783e499217d Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 22 Jul 2024 14:15:27 +0200 Subject: [PATCH 108/134] Correct and expand documentation for the core AtomicData class --- cherab/core/atomic/interface.pyx | 63 +++++++++++++++++--- docs/source/atomic/atomic_data.rst | 1 + docs/source/atomic/atomic_data_interface.rst | 19 ++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 docs/source/atomic/atomic_data_interface.rst diff --git a/cherab/core/atomic/interface.pyx b/cherab/core/atomic/interface.pyx index 286ebfb4..3e44d52c 100644 --- a/cherab/core/atomic/interface.pyx +++ b/cherab/core/atomic/interface.pyx @@ -1,6 +1,6 @@ -# Copyright 2016-2022 Euratom -# Copyright 2016-2022 United Kingdom Atomic Energy Authority -# Copyright 2016-2022 Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas +# Copyright 2016-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,70 +29,117 @@ cdef class AtomicData: cpdef double wavelength(self, Element ion, int charge, tuple transition): """ - Returns the natural wavelength of the specified transition in nm. + The natural wavelength of the specified transition in nm. """ raise NotImplementedError("The wavelength() virtual method is not implemented for this atomic data source.") cpdef IonisationRate ionisation_rate(self, Element ion, int charge): + """ + Electron impact ionisation rate for a given species in m^3/s. + """ + raise NotImplementedError("The ionisation_rate() virtual method is not implemented for this atomic data source.") cpdef RecombinationRate recombination_rate(self, Element ion, int charge): + """ + Recombination rate for a given species in m^3/s. + """ + raise NotImplementedError("The recombination_rate() virtual method is not implemented for this atomic data source.") cpdef ThermalCXRate thermal_cx_rate(self, Element donor_ion, int donor_charge, Element receiver_ion, int receiver_charge): + """ + Thermal charge exchange effective rate coefficient for a given donor and receiver species in m^3/s. + """ + raise NotImplementedError("The thermal_cx_rate() virtual method is not implemented for this atomic data source.") cpdef list beam_cx_pec(self, Element donor_ion, Element receiver_ion, int receiver_charge, tuple transition): """ - Returns a list of applicable charge exchange emission rates in W.m^3. + A list of Effective charge exchange photon emission coefficient for a given donor (beam) in W.m^3. """ raise NotImplementedError("The cxs_rates() virtual method is not implemented for this atomic data source.") cpdef BeamStoppingRate beam_stopping_rate(self, Element beam_ion, Element plasma_ion, int charge): """ - Returns a list of applicable beam stopping coefficients in m^3/s. + Beam stopping coefficient for a given beam and target species in m^3/s. """ raise NotImplementedError("The beam_stopping() virtual method is not implemented for this atomic data source.") cpdef BeamPopulationRate beam_population_rate(self, Element beam_ion, int metastable, Element plasma_ion, int charge): """ - Returns a list of applicable dimensionless beam population coefficients. + Dimensionless Beam population coefficient for a given beam and target species. """ raise NotImplementedError("The beam_population() virtual method is not implemented for this atomic data source.") cpdef BeamEmissionPEC beam_emission_pec(self, Element beam_ion, Element plasma_ion, int charge, tuple transition): """ - Returns a list of applicable beam emission coefficients in W.m^3. + The beam photon emission coefficient for a given beam and target species + and a given transition in W.m^3. """ raise NotImplementedError("The beam_emission() virtual method is not implemented for this atomic data source.") cpdef ImpactExcitationPEC impact_excitation_pec(self, Element ion, int charge, tuple transition): + """ + Electron impact excitation photon emission coefficient for a given species in W.m^3. + """ + raise NotImplementedError("The impact_excitation() virtual method is not implemented for this atomic data source.") cpdef RecombinationPEC recombination_pec(self, Element ion, int charge, tuple transition): + """ + Recombination photon emission coefficient for a given species in W.m^3. + """ + raise NotImplementedError("The recombination() virtual method is not implemented for this atomic data source.") cpdef TotalRadiatedPower total_radiated_power(self, Element element): + """ + The total (summed over all charge states) radiated power + in equilibrium conditions for a given species in W.m^3. + """ + raise NotImplementedError("The total_radiated_power() virtual method is not implemented for this atomic data source.") cpdef LineRadiationPower line_radiated_power_rate(self, Element element, int charge): + """ + Line radiated power coefficient for a given species in W.m^3. + """ + raise NotImplementedError("The line_radiated_power_rate() virtual method is not implemented for this atomic data source.") cpdef ContinuumPower continuum_radiated_power_rate(self, Element element, int charge): + """ + Continuum radiated power coefficient for a given species in W.m^3. + """ + raise NotImplementedError("The continuum_radiated_power_rate() virtual method is not implemented for this atomic data source.") cpdef CXRadiationPower cx_radiated_power_rate(self, Element element, int charge): + """ + Charge exchange radiated power coefficient for a given species in W.m^3. + """ + raise NotImplementedError("The cx_radiated_power_rate() virtual method is not implemented for this atomic data source.") cpdef FractionalAbundance fractional_abundance(self, Element ion, int charge): + """ + Fractional abundance of a given species in thermodynamic equilibrium. + """ + raise NotImplementedError("The fractional_abundance() virtual method is not implemented for this atomic data source.") cpdef ZeemanStructure zeeman_structure(self, Line line, object b_field=None): + r""" + Wavelengths and ratios of :math:`\pi`-/:math:`\sigma`-polarised Zeeman components + for any given value of magnetic field strength. + """ + raise NotImplementedError("The zeeman_structure() virtual method is not implemented for this atomic data source.") cpdef FreeFreeGauntFactor free_free_gaunt_factor(self): diff --git a/docs/source/atomic/atomic_data.rst b/docs/source/atomic/atomic_data.rst index 650d89b4..6eeb09a1 100644 --- a/docs/source/atomic/atomic_data.rst +++ b/docs/source/atomic/atomic_data.rst @@ -7,3 +7,4 @@ Atomic Data emission_lines rate_coefficients gaunt_factors + atomic_data_interface diff --git a/docs/source/atomic/atomic_data_interface.rst b/docs/source/atomic/atomic_data_interface.rst new file mode 100644 index 00000000..0d54ff67 --- /dev/null +++ b/docs/source/atomic/atomic_data_interface.rst @@ -0,0 +1,19 @@ + +Atomic Data Interface +===================== + +Abstract (interface) class +-------------------------- + +Abstract atomic data interface. + +.. autoclass:: cherab.core.atomic.interface.AtomicData + :members: + +OpenADAS atomic data source +--------------------------- + +Interface to local atomic data repository. + +.. autoclass:: cherab.openadas.openadas.OpenADAS + :members: From 8934c8ccec63b74d40fd599bbf57e188a437dccc Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 24 Jul 2024 22:51:09 +0200 Subject: [PATCH 109/134] Improve documentation for openadas repository install and manipulation functions. --- cherab/openadas/install.py | 61 ++++---- cherab/openadas/repository/atomic.py | 143 +++++++++++++++--- cherab/openadas/repository/beam/cx.py | 97 ++++++++++-- cherab/openadas/repository/beam/emission.py | 74 ++++++++- cherab/openadas/repository/beam/population.py | 72 ++++++++- cherab/openadas/repository/beam/stopping.py | 69 ++++++++- cherab/openadas/repository/pec.py | 125 ++++++++++++--- cherab/openadas/repository/radiated_power.py | 140 ++++++++++++++--- cherab/openadas/repository/wavelength.py | 42 ++++- docs/source/atomic/atomic_data.rst | 2 + docs/source/atomic/openadas.rst | 29 ++++ docs/source/atomic/repository.rst | 83 ++++++++++ 12 files changed, 798 insertions(+), 139 deletions(-) create mode 100644 docs/source/atomic/openadas.rst create mode 100644 docs/source/atomic/repository.rst diff --git a/cherab/openadas/install.py b/cherab/openadas/install.py index 27e0c5f4..d634cde7 100644 --- a/cherab/openadas/install.py +++ b/cherab/openadas/install.py @@ -269,14 +269,17 @@ def install_adf15(element, ionisation, file_path, download=False, repository_pat def install_adf21(beam_species, target_ion, target_charge, file_path, download=False, repository_path=None, adas_path=None): - # """ - # Adds the rate defined in an ADF21 file to the repository. - # - # :param file_path: Path relative to ADAS root. - # :param download: Attempt to download file if not present (Default=True). - # :param repository_path: Path to the repository in which to install the rates (optional). - # :param adas_path: Path to ADAS files repository (optional). - # """ + """ + Adds the beam stopping rate defined in an ADF21 file to the repository. + + :param beam_species: Beam neutral atom (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param file_path: Path relative to ADAS root. + :param download: Attempt to download file if not present (Default=True). + :param repository_path: Path to the repository in which to install the rates (optional). + :param adas_path: Path to ADAS files repository (optional). + """ print('Installing {}...'.format(file_path)) path = _locate_adas_file(file_path, download, adas_path, repository_path) @@ -289,15 +292,18 @@ def install_adf21(beam_species, target_ion, target_charge, file_path, download=F def install_adf22bmp(beam_species, beam_metastable, target_ion, target_charge, file_path, download=False, repository_path=None, adas_path=None): - pass - # """ - # Adds the rate defined in an ADF21 file to the repository. - # - # :param file_path: Path relative to ADAS root. - # :param download: Attempt to download file if not present (Default=True). - # :param repository_path: Path to the repository in which to install the rates (optional). - # :param adas_path: Path to ADAS files repository (optional). - # """ + """ + Adds the beam population rate defined in an ADF22 BMP file to the repository. + + :param beam_species: Beam neutral atom (Element/Isotope). + :param beam_metastable: Metastable/excitation level of beam neutral atom. + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param file_path: Path relative to ADAS root. + :param download: Attempt to download file if not present (Default=True). + :param repository_path: Path to the repository in which to install the rates (optional). + :param adas_path: Path to ADAS files repository (optional). + """ print('Installing {}...'.format(file_path)) path = _locate_adas_file(file_path, download, adas_path, repository_path) @@ -310,15 +316,18 @@ def install_adf22bmp(beam_species, beam_metastable, target_ion, target_charge, f def install_adf22bme(beam_species, target_ion, target_charge, transition, file_path, download=False, repository_path=None, adas_path=None): - pass - # """ - # Adds the rate defined in an ADF21 file to the repository. - # - # :param file_path: Path relative to ADAS root. - # :param download: Attempt to download file if not present (Default=True). - # :param repository_path: Path to the repository in which to install the rates (optional). - # :param adas_path: Path to ADAS files repository (optional). - # """ + """ + Adds the beam emission rate defined in an ADF22 BME file to the repository. + + :param beam_species: Beam neutral atom (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param transition: Tuple containing (initial level, final level). + :param file_path: Path relative to ADAS root. + :param download: Attempt to download file if not present (Default=True). + :param repository_path: Path to the repository in which to install the rates (optional). + :param adas_path: Path to ADAS files repository (optional). + """ print('Installing {}...'.format(file_path)) path = _locate_adas_file(file_path, download, adas_path, repository_path) diff --git a/cherab/openadas/repository/atomic.py b/cherab/openadas/repository/atomic.py index c24234f3..cb8add6c 100644 --- a/cherab/openadas/repository/atomic.py +++ b/cherab/openadas/repository/atomic.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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -34,8 +34,15 @@ def add_ionisation_rate(species, charge, rate, repository_path=None): function instead. The update function avoids repeatedly opening and closing the rate files. - :param repository_path: - :return: + :param species: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param rate: Ionisation rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with ionisation rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ update_ionisation_rates({ @@ -47,11 +54,21 @@ def add_ionisation_rate(species, charge, rate, repository_path=None): def update_ionisation_rates(rates, repository_path=None): """ - Ionisation rate file structure - - /ionisation/.json + Updates the ionisation rate files `/ionisation/.json` + in atomic data repository. File contains multiple rates, indexed by the ion charge state. + + :param rates: Dictionary in the form {: {: }}, where + + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the ionisation rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with ionisation rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -75,8 +92,15 @@ def add_recombination_rate(species, charge, rate, repository_path=None): function instead. The update function avoids repeatedly opening and closing the rate files. - :param repository_path: - :return: + :param species: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param rate: Recombination rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with recombination rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ update_recombination_rates({ @@ -88,11 +112,21 @@ def add_recombination_rate(species, charge, rate, repository_path=None): def update_recombination_rates(rates, repository_path=None): """ - Ionisation rate file structure - - /recombination/.json + Updates the recombination rate files `/recombination/.json` + in the atomic data repository. File contains multiple rates, indexed by the ion charge state. + + :param rates: Dictionary in the form {: {: }}, where + + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the recombination rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with recombination rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -109,7 +143,6 @@ def update_recombination_rates(rates, repository_path=None): def add_thermal_cx_rate(donor_element, donor_charge, receiver_element, rate, repository_path=None): - """ Adds a single thermal charge exchange rate to the repository. @@ -118,11 +151,16 @@ def add_thermal_cx_rate(donor_element, donor_charge, receiver_element, rate, rep the rate files. :param donor_element: Element donating the electron. - :param donor_charge: Charge of the donating atom/ion - :param receiver_element: Element receiving the electron - :param rate: rates - :param repository_path: - :return: + :param donor_charge: Charge of the donating atom/ion. + :param receiver_element: Element receiving the electron. + :param receiver_charge: Charge of the receiving atom/ion. + :param rate: Thermal CX rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with thermal CX rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ rates2update = RecursiveDict() @@ -133,11 +171,25 @@ def add_thermal_cx_rate(donor_element, donor_charge, receiver_element, rate, rep def update_thermal_cx_rates(rates, repository_path=None): """ - Thermal charge exchange rate file structure - - /thermal_cx///.json + Updates the thermal charge exchange rate files + `/thermal_cx///.json` + in the atomic data repository. File contains multiple rates, indexed by the ion charge state. + + :param rates: Dictionary in the form: + + | { : { : { : { : } } } }, where + | is the element donating the electron. + | is the charge of the donating atom/ion. + | is the element receiving the electron. + | is the charge of the receiving atom/ion. + | is the thermal CX rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with thermal CX rate in m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -203,6 +255,21 @@ def _update_and_write_adf11(species, rate_data, path): def get_ionisation_rate(element, charge, repository_path=None): + """ + Reads the ionisation rate for the given species and charge + from the atomic data repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param repository_path: Path to the atomic data repository. + + :return rate: Ionisation rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with ionisation rate in m^3.s^-1. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -224,6 +291,21 @@ def get_ionisation_rate(element, charge, repository_path=None): def get_recombination_rate(element, charge, repository_path=None): + """ + Reads the recombination rate for the given species and charge + from the atomic data repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param repository_path: Path to the atomic data repository. + + :return rate: Recombination rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with recombination rate in m^3.s^-1. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -245,6 +327,23 @@ def get_recombination_rate(element, charge, repository_path=None): def get_thermal_cx_rate(donor_element, donor_charge, receiver_element, receiver_charge, repository_path=None): + """ + Reads the thermal charge exchange rate for the given species and charge + from the atomic data repository. + + :param donor_element: Element donating the electron. + :param donor_charge: Charge of the donating atom/ion. + :param receiver_element: Element receiving the electron. + :param receiver_charge: Charge of the receiving atom/ion. + :param repository_path: Path to the atomic data repository. + + :return rate: Thermal CX rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with thermal CX rate in m^3.s^-1. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH diff --git a/cherab/openadas/repository/beam/cx.py b/cherab/openadas/repository/beam/cx.py index 65bc3ceb..ef79c102 100644 --- a/cherab/openadas/repository/beam/cx.py +++ b/cherab/openadas/repository/beam/cx.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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,19 +29,33 @@ def add_beam_cx_rate(donor_ion, donor_metastable, receiver_ion, receiver_charge, transition, rate, repository_path=None): """ - Adds a single beam CX rate to the repository. + Adds a single beam CX PEC to the repository. If adding multiple rate, consider using the update_beam_cx_rates() function instead. The update function avoid repeatedly opening and closing the rate files. - :param donor_ion: - :param donor_metastable: - :param receiver_ion: - :param receiver_charge: - :param rate: - :param repository_path: - :return: + :param donor_ion: Beam neutral atom (Element/Isotope) donating the electron. + :param donor_metastable: Metastable/excited level of beam neutral atom. + :param receiver_ion: Element/Isotope receiving the electron. + :param receiver_charge: Charge of the receiving atom/ion. + :param transition: Tuple containing (initial level, final level). + :param rate: Beam CX PEC dictionary containing the following entries: + + | 'eb': array-like of size (N) with beam energy in eV/amu, + | 'ti': array-like of size (M) with receiver ion temperature in eV, + | 'ni': array-like of size (K) with plasma ion density in m^-3, + | 'z': array-like of size (L) with plasma Z-effective, + | 'b': array-like of size (J) with magnetic field strength in Tesla, + | 'qeb': array-like of size (N) with CX PEC energy component in photon.m^3.s-1, + | 'qti': array-like of size (M) with CX PEC temperature component in photon.m^3.s-1, + | 'qni': array-like of size (K) with CX PEC density component in photon.m^3.s-1, + | 'qz': array-like of size (L) with CX PEC Zeff component in photon.m^3.s-1, + | 'qb': array-like of size (J) with CX PEC B-field component in photon.m^3.s-1, + | 'qref': reference CX PEC in photon.m^3.s-1. + | The total beam CX PEC: q = qeb * qti * qni * qz * qb / qref^4. + + :param repository_path: Path to the atomic data repository. """ update_beam_cx_rates({ @@ -58,10 +72,37 @@ def add_beam_cx_rate(donor_ion, donor_metastable, receiver_ion, receiver_charge, def update_beam_cx_rates(rates, repository_path=None): - # organisation in repository: - # beam/cx/donor_ion/receiver_ion/receiver_charge.json - # inside json file: - # transition: [list of donor_metastables with rates] + """ + Updates the beam CX PEC files + beam/cx///.json + in the atomic data repository. + + File contains multiple metastable-resolved rates, indexed by transition. + + :param rates: Dictionary in the form: + + | { : { : { : { : {: } } } } }, where + | is the beam neutral atom (Element/Isotope) donating the electron. + | is the metastable/excited level of beam neutral atom. + | is the Element/Isotope receiving the electron. + | is the charge of the receiving atom/ion. + | is the tuple containing (initial level, final level). + | is the beam CX PEC dictionary containing the following entries: + | 'eb': array-like of size (N) with beam energy in eV/amu, + | 'ti': array-like of size (M) with receiver ion temperature in eV, + | 'ni': array-like of size (K) with plasma ion density in m^-3, + | 'z': array-like of size (L) with plasma Z-effective, + | 'b': array-like of size (J) with magnetic field strength in Tesla, + | 'qeb': array-like of size (N) with CX PEC energy component in photon.m^3.s-1, + | 'qti': array-like of size (M) with CX PEC temperature component in photon.m^3.s-1, + | 'qni': array-like of size (K) with CX PEC density component in photon.m^3.s-1, + | 'qz': array-like of size (L) with CX PEC Zeff component in photon.m^3.s-1, + | 'qb': array-like of size (J) with CX PEC B-field component in photon.m^3.s-1, + | 'qref': reference CX PEC in photon.m^3.s-1. + | The total beam CX PEC: q = qeb * qti * qni * qz * qb / qref^4. + + :param repository_path: Path to the atomic data repository. + """ def sanitise_and_validate(data, x_key, x_name, y_key, y_name): """ @@ -167,6 +208,32 @@ def sanitise_and_validate(data, x_key, x_name, y_key, y_name): def get_beam_cx_rates(donor_ion, receiver_ion, receiver_charge, transition, repository_path=None): + """ + Reads a single beam CX PEC from the repository. + + :param donor_ion: Beam neutral atom (Element/Isotope) donating the electron. + :param donor_metastable: Metastable/excited level of beam neutral atom. + :param receiver_ion: Element/Isotope receiving the electron. + :param receiver_charge: Charge of the receiving atom/ion. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return rate: Beam CX PEC dictionary containing the following entries: + + | 'eb': 1D array of size (N) with beam energy in eV/amu, + | 'ti': 1D array of size (M) with receiver ion temperature in eV, + | 'ni': 1D array of size (K) with plasma ion density in m^-3, + | 'z': 1D array of size (L) with plasma Z-effective, + | 'b': 1D array of size (J) with magnetic field strength in Tesla, + | 'qeb': 1D array of size (N) with CX PEC energy component in photon.m^3.s-1, + | 'qti': 1D array of size (M) with CX PEC temperature component in photon.m^3.s-1, + | 'qni': 1D array of size (K) with CX PEC density component in photon.m^3.s-1, + | 'qz': 1D array of size (L) with CX PEC Zeff component in photon.m^3.s-1, + | 'qb': 1D array of size (J) with CX PEC B-field component in photon.m^3.s-1, + | 'qref': reference CX PEC in photon.m^3.s-1. + | The total beam CX PEC: q = qeb * qti * qni * qz * qb / qref^4. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH path = os.path.join(repository_path, 'beam/cx/{}/{}/{}.json'.format(donor_ion.symbol.lower(), receiver_ion.symbol.lower(), receiver_charge)) diff --git a/cherab/openadas/repository/beam/emission.py b/cherab/openadas/repository/beam/emission.py index b6cd14f2..c7c6e2e1 100644 --- a/cherab/openadas/repository/beam/emission.py +++ b/cherab/openadas/repository/beam/emission.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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -36,8 +36,24 @@ def add_beam_emission_rate(beam_species, target_ion, target_charge, transition, function instead. The update function avoid repeatedly opening and closing the rate files. - :param repository_path: - :return: + :param beam_species: Beam neutral species (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param transition: Tuple containing (initial level, final level). + :param rate: Beam emission rate dictionary containing the following entries: + + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n' array-like of size (M) with target electron density in m^-3, + | 't' array-like of size (K) with target electron temperature in eV, + | 'sen' array-like of size (N, M) with beam emission rate energy component in photon.m^3.s^-1. + | 'st' array-like of size (K) with beam emission rate temperature component in photon.m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam emission rate in photon.m^3.s^-1. + | The total beam emission rate: s = sen * st / sref. + + :param repository_path: Path to the atomic data repository. """ update_beam_emission_rates({ @@ -53,11 +69,32 @@ def add_beam_emission_rate(beam_species, target_ion, target_charge, transition, def update_beam_emission_rates(rates, repository_path=None): """ - Beam emission rate file structure - + Updates the beam emission rate files: /beam/emission///.json + in the atomic repository. File contains multiple rates, indexed by transition. + + :param rates: Dictionary in the form: + + | { : { : { : {: } } } }, where + | is the beam neutral species (Element/Isotope) + | is the target species (Element/Isotope). + | is the charge of the target species. + | is the tuple containing (initial level, final level). + | Beam emission rate dictionary containing the following entries: + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n' array-like of size (M) with target electron density in m^-3, + | 't' array-like of size (K) with target electron temperature in eV, + | 'sen' array-like of size (N, M) with beam emission rate energy component in photon.m^3.s^-1. + | 'st' array-like of size (K) with beam emission rate temperature component in photon.m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam emission rate in photon.m^3.s^-1. + | The total beam emission rate: s = sen * st / sref. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -136,6 +173,29 @@ def update_beam_emission_rates(rates, repository_path=None): def get_beam_emission_rate(beam_species, target_ion, target_charge, transition, repository_path=None): + """ + Reads a single beam emission rate from the repository. + + :param beam_species: Beam neutral species (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return rate: Beam emission rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n' 1D array of size (M) with target electron density in m^-3, + | 't' 1D array of size (K) with target electron temperature in eV, + | 'sen' 2D array of size (N, M) with beam emission rate energy component in photon.m^3.s^-1. + | 'st' 1D array of size (K) with beam emission rate temperature component in photon.m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam emission rate in photon.m^3.s^-1. + | The total beam emission rate: s = sen * st / sref. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH path = os.path.join(repository_path, 'beam/emission/{}/{}/{}.json'.format(beam_species.symbol.lower(), target_ion.symbol.lower(), target_charge)) diff --git a/cherab/openadas/repository/beam/population.py b/cherab/openadas/repository/beam/population.py index 54c57df1..9ecfa6eb 100644 --- a/cherab/openadas/repository/beam/population.py +++ b/cherab/openadas/repository/beam/population.py @@ -31,12 +31,24 @@ def add_beam_population_rate(beam_species, beam_metastable, target_ion, target_c """ Adds a single beam population rate to the repository. - :param beam_species: - :param beam_metastable: - :param target_ion: - :param target_charge: - :param rate: - :return: + :param beam_species: Beam neutral species (Element/Isotope). + :param beam_metastable: Metastable level of beam neutral atom. + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param rate: Beam population rate dictionary containing the following entries: + + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n': array-like of size (M) with target electron density in m^-3, + | 't': array-like of size (K) with target electron temperature in eV, + | 'sen': array-like of size (N, M) with dimensionless beam population rate energy component. + | 'st': array-like of size (K) with dimensionless beam population rate temperature component. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference dimensionless beam population rate. + | The total beam population rate: s = sen * st / sref. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -102,11 +114,32 @@ def add_beam_population_rate(beam_species, beam_metastable, target_ion, target_c def update_beam_population_rates(rates, repository_path=None): """ - Beam population rate file structure - + Updates the beam population rate files /beam/population////.json + in the atomic data repository. Each json file contains a single rate, so it can simply be replaced. + + :param rates: Dictionary in the form: + + | { : { : { : {: } } } }, where + | is the beam neutral species (Element/Isotope) + | is the metastable level of beam neutral atom. + | is the target species (Element/Isotope). + | is the charge of the target species. + | is the beam population rate dictionary containing the following fields: + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n': array-like of size (M) with target electron density in m^-3, + | 't': array-like of size (K) with target electron temperature in eV, + | 'sen': array-like of size (N, M) with dimensionless beam population rate energy component. + | 'st': array-like of size (K) with dimensionless beam population rate temperature component. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference dimensionless beam population rate. + | The total beam population rate: s = sen * st / sref. + + :param repository_path: Path to the atomic data repository. """ for beam_species, beam_metastables in rates.items(): @@ -117,6 +150,29 @@ def update_beam_population_rates(rates, repository_path=None): def get_beam_population_rate(beam_species, beam_metastable, target_ion, target_charge, repository_path=None): + """ + Reads a single beam population rate from the repository. + + :param beam_species: Beam neutral species (Element/Isotope). + :param beam_metastable: Metastable level of beam neutral atom. + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param repository_path: Path to the atomic data repository. + + :return rate: Beam population rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n': 1D array of size (M) with target electron density in m^-3, + | 't': 1D array of size (K) with target electron temperature in eV, + | 'sen': 2D array of size (N, M) with dimensionless beam population rate energy component. + | 'st': 1D array of size (K) with dimensionless beam population rate temperature component. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference dimensionless beam population rate. + | The total beam population rate: s = sen * st / sref. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH path = os.path.join(repository_path, 'beam/population/{}/{}/{}/{}.json'.format(beam_species.symbol.lower(), beam_metastable, target_ion.symbol.lower(), target_charge)) diff --git a/cherab/openadas/repository/beam/stopping.py b/cherab/openadas/repository/beam/stopping.py index cf8d1492..46d8caae 100644 --- a/cherab/openadas/repository/beam/stopping.py +++ b/cherab/openadas/repository/beam/stopping.py @@ -31,11 +31,23 @@ def add_beam_stopping_rate(beam_species, target_ion, target_charge, rate, reposi """ Adds a single beam stopping/excitation rate to the repository. - :param beam_species: - :param target_ion: - :param target_charge: - :param rate: - :return: + :param beam_species: Beam neutral atom (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param rate: Beam stopping rate dictionary containing the following entries: + + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n': array-like of size (M) with target electron density in m^-3, + | 't': array-like of size (K) with target electron temperature in eV, + | 'sen': array-like of size (N, M) with beam stopping rate energy component in m^3.s^-1. + | 'st': array-like of size (K) with beam stopping rate temperature component in m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam stopping rate in m^3.s^-1. + | The total beam stopping rate: s = sen * st / sref. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -98,11 +110,30 @@ def add_beam_stopping_rate(beam_species, target_ion, target_charge, rate, reposi def update_beam_stopping_rates(rates, repository_path=None): """ - Beam stopping rate file structure - - /beam/stopping///.json + Updates the beam stopping rate files + /beam/stopping////.json + in the atomic data repository. Each json file contains a single rate, so it can simply be replaced. + + :param rates: Dictionary in the form: + + | { : { : { : {: } } } }, where + | is the beam neutral species (Element/Isotope). + | is the target species (Element/Isotope). + | is the charge of the target species. + | is the beam stopping rate dictionary containing the following entries: + | 'e': array-like of size (N) with interaction energy in eV/amu, + | 'n': array-like of size (M) with target electron density in m^-3, + | 't': array-like of size (K) with target electron temperature in eV, + | 'sen': array-like of size (N, M) with beam stopping rate energy component in m^3.s^-1. + | 'st': array-like of size (K) with beam stopping rate temperature component in m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam stopping rate in m^3.s^-1. + | The total beam stopping rate: s = sen * st / sref. + """ for beam_species, target_ions in rates.items(): @@ -112,6 +143,28 @@ def update_beam_stopping_rates(rates, repository_path=None): def get_beam_stopping_rate(beam_species, target_ion, target_charge, repository_path=None): + """ + Reads a single beam stopping/excitation rate from the repository. + + :param beam_species: Beam neutral atom (Element/Isotope). + :param target_ion: Target species (Element/Isotope). + :param target_charge: Charge of the target species. + :param repository_path: Path to the atomic data repository. + + :return rate: Beam stopping rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n': 1D array of size (M) with target electron density in m^-3, + | 't': 1D array of size (K) with target electron temperature in eV, + | 'sen': 2D array of size (N, M) with beam stopping rate energy component in m^3.s^-1. + | 'st': 1D array of size (K) with beam stopping rate temperature component in m^3.s^-1. + | 'eref': reference interaction energy in eV/amu, + | 'nref': reference target electron density in m^-3, + | 'tref': reference target electron temperature in eV, + | 'sref': reference beam stopping rate in m^3.s^-1. + | The total beam stopping rate: s = sen * st / sref. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH path = os.path.join(repository_path, 'beam/stopping/{}/{}/{}.json'.format(beam_species.symbol.lower(), target_ion.symbol.lower(), target_charge)) diff --git a/cherab/openadas/repository/pec.py b/cherab/openadas/repository/pec.py index 8eb867fc..13fc055e 100644 --- a/cherab/openadas/repository/pec.py +++ b/cherab/openadas/repository/pec.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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -36,12 +36,16 @@ def add_pec_excitation_rate(element, charge, transition, rate, repository_path=N instead. The update function avoid repeatedly opening and closing the rate files. - :param element: - :param charge: - :param transition: - :param rate: - :param repository_path: - :return: + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param rate: Excitation PEC dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with excitation PEC in photon.m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ update_pec_rates({ @@ -63,12 +67,16 @@ def add_pec_recombination_rate(element, charge, transition, rate, repository_pat instead. The update function avoid repeatedly opening and closing the rate files. - :param element: - :param charge: - :param transition: - :param rate: - :param repository_path: - :return: + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param rate: Recombination PEC dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with recombination PEC in photon.m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ update_pec_rates({ @@ -84,18 +92,22 @@ def add_pec_recombination_rate(element, charge, transition, rate, repository_pat def add_pec_thermalcx_rate(element, charge, transition, rate, repository_path=None): """ - Adds a single PEC thermalcx rate to the repository. + Adds a single PEC thermal charge exchange rate to the repository. If adding multiple rate, consider using the update_pec_rates() function instead. The update function avoid repeatedly opening and closing the rate files. - :param element: - :param charge: - :param transition: - :param rate: - :param repository_path: - :return: + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param rate: Thermal CX PEC dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with thermal CX PEC in photon.m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ update_pec_rates({ @@ -111,9 +123,24 @@ def add_pec_thermalcx_rate(element, charge, transition, rate, repository_path=No def update_pec_rates(rates, repository_path=None): """ - PEC rate file structure + Updates the PEC files /pec///.json. + in the atomic data repository. + + File contains multiple PECs, indexed by the transition. + + :param rates: Dictionary in the form: - /pec///.json + | { : { : { : { : } } } }, where + | is the one of the following PEC types: 'excitation', 'recombination', 'thermalcx'. + | is the plasma species (Element/Isotope). + | is the charge of the plasma species. + | is the tuple containing (initial level, final level). + | is the PEC dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with PEC in photon.m^3.s^-1. + + :param repository_path: Path to the atomic data repository. """ valid_classes = [ @@ -184,14 +211,64 @@ def update_pec_rates(rates, repository_path=None): def get_pec_excitation_rate(element, charge, transition, repository_path=None): + """ + Reads the excitation PEC from the repository for the given + element, charge and transition. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return rate: Excitation PEC dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with excitation PEC in photon.m^3.s^-1. + + """ + return _get_pec_rate('excitation', element, charge, transition, repository_path) def get_pec_recombination_rate(element, charge, transition, repository_path=None): + """ + Reads the recombination PEC from the repository for the given + element, charge and transition. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return rate: Recombination PEC dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with recombination PEC in photon.m^3.s^-1. + + """ + return _get_pec_rate('recombination', element, charge, transition, repository_path) def get_pec_thermalcx_rate(element, charge, transition, repository_path=None): + """ + Reads the thermal charge exchange PEC from the repository for the given + element, charge and transition. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return rate: Thermal CX PEC dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with thermal CX PEC in photon.m^3.s^-1. + + """ return _get_pec_rate('thermalcx', element, charge, transition, repository_path) diff --git a/cherab/openadas/repository/radiated_power.py b/cherab/openadas/repository/radiated_power.py index a6a4b390..7b908cfc 100644 --- a/cherab/openadas/repository/radiated_power.py +++ b/cherab/openadas/repository/radiated_power.py @@ -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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,13 +29,21 @@ def add_line_power_rate(species, charge, rate, repository_path=None): """ - Adds a single LineRadiationPower rate to the repository. + Adds a single line radiated power rate to the repository. If adding multiple rates, consider using the update_line_power_rates() function instead. The update function avoids repeatedly opening and closing the rate files. - :param repository_path: + :param species: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param rate: Line radiated power rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with line radiated power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ update_line_power_rates({ @@ -47,13 +55,22 @@ def add_line_power_rate(species, charge, rate, repository_path=None): def update_line_power_rates(rates, repository_path=None): """ - Update the repository of LineRadiationPower rates. - - LineRadiationPower rate file structure - + Update the files for the line radiated power rates: /radiated_power/line/.json + in the atomic data repository. File contains multiple rates, indexed by the ion's charge state. + + :param rates: Dictionary in the form {: {: }}, where + + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the line radiated rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with line radiated power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -71,13 +88,21 @@ def update_line_power_rates(rates, repository_path=None): def add_continuum_power_rate(species, charge, rate, repository_path=None): """ - Adds a single ContinuumPower rate to the repository. + Adds a single continuum power rate to the repository. If adding multiple rates, consider using the update_continuum_power_rates() function instead. The update function avoids repeatedly opening and closing the rate files. - :param repository_path: + :param species: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param rate: Continuum power rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with continuum power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ update_line_power_rates({ @@ -89,13 +114,22 @@ def add_continuum_power_rate(species, charge, rate, repository_path=None): def update_continuum_power_rates(rates, repository_path=None): """ - Update the repository of ContinuumPower rates. - - ContinuumPower rate file structure - + Update the files for the continuum power rates: /radiated_power/continuum/.json + in the atomic data repository. File contains multiple rates, indexed by ion's charge state. + + :param rates: Dictionary in the form {: {: }}, where + + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the continuum power rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with continuum power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -113,13 +147,22 @@ def update_continuum_power_rates(rates, repository_path=None): def add_cx_power_rate(species, charge, rate, repository_path=None): """ - Adds a single CXRadiationPower rate to the repository. + Adds a single CX radiation power rate to the repository + (charge exchage with neutral hydrogen). If adding multiple rates, consider using the update_cx_power_rates() function instead. The update function avoids repeatedly opening and closing the rate files. - :param repository_path: + :param species: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param rate: CX power rate dictionary containing the following entries: + + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with CX power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ update_line_power_rates({ @@ -131,13 +174,23 @@ def add_cx_power_rate(species, charge, rate, repository_path=None): def update_cx_power_rates(rates, repository_path=None): """ - Update the repository of CXRadiationPower rates. - - CXRadiationPower rate file structure - + Update the files for the CX radiation power rates + (charge exchage with neutral hydrogen): /radiated_power/cx/.json + in the atomic data repository. File contains multiple rates, indexed by ion's charge state. + + :param rates: Dictionary in the form {: {: }}, where + + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the thermal CX power rate dictionary containing the following entries: + | 'ne': array-like of size (N) with electron density in m^-3, + | 'te': array-like of size (M) with electron temperature in eV, + | 'rate': array-like of size (N, M) with thermal CX power rate in W.m^3. + + :param repository_path: Path to the atomic data repository. """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -199,6 +252,21 @@ def _update_and_write_adf11(species, rate_data, path): def get_line_radiated_power_rate(element, charge, repository_path=None): + """ + Reads the line radiated power rate for the given species and charge + from the atomic data repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param repository_path: Path to the atomic data repository. + + :return rate: Line radiated power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with line radiated power rate in W.m^3. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -220,6 +288,21 @@ def get_line_radiated_power_rate(element, charge, repository_path=None): def get_continuum_radiated_power_rate(element, charge, repository_path=None): + """ + Reads the continuum power rate for the given species and charge + from the atomic data repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param repository_path: Path to the atomic data repository. + + :return rate: Continuum power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with continuum power rate in W.m^3. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -241,6 +324,21 @@ def get_continuum_radiated_power_rate(element, charge, repository_path=None): def get_cx_radiated_power_rate(element, charge, repository_path=None): + """ + Reads the CX radiation power rate for the given species and charge + from the atomic data repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param repository_path: Path to the atomic data repository. + + :return rate: CX radiation power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with CX radiation power rate in W.m^3. + + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH diff --git a/cherab/openadas/repository/wavelength.py b/cherab/openadas/repository/wavelength.py index 5981e9e6..83758f51 100644 --- a/cherab/openadas/repository/wavelength.py +++ b/cherab/openadas/repository/wavelength.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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -35,11 +35,11 @@ def add_wavelength(element, charge, transition, wavelength, repository_path=None function instead. The update function avoid repeatedly opening and closing the rate files. - :param element: - :param charge: - :param transition: - :param wavelength: - :param repository_path: + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param wavelength: Transition's wavelength in nm. + :param repository_path: Path to the atomic data repository. """ update_wavelengths({ @@ -52,6 +52,22 @@ def add_wavelength(element, charge, transition, wavelength, repository_path=None def update_wavelengths(wavelengths, repository_path=None): + """ + Updates the wavelength files `/wavelength//.json` + in atomic data repository. + + File contains multiple rates, indexed by the transitions. + + :param wavelengths: Dictionary in the form: + + | { : { : { : } } }, where + | is the plasma species (Element/Isotope), + | is the charge of the plasma species, + | is the tuple containing (initial level, final level), + | is the transition's wavelength in nm. + + :param repository_path: Path to the atomic data repository. + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH @@ -90,6 +106,16 @@ def update_wavelengths(wavelengths, repository_path=None): def get_wavelength(element, charge, transition, repository_path=None): + """ + Reads the wavelength for the given species, charge and transition from the repository. + + :param element: Plasma species (Element/Isotope). + :param charge: Charge of the plasma species. + :param transition: Tuple containing (initial level, final level). + :param repository_path: Path to the atomic data repository. + + :return wavelength: Wavelength in nm. + """ repository_path = repository_path or DEFAULT_REPOSITORY_PATH path = os.path.join(repository_path, 'wavelength/{}/{}.json'.format(element.symbol.lower(), charge)) diff --git a/docs/source/atomic/atomic_data.rst b/docs/source/atomic/atomic_data.rst index 650d89b4..802943af 100644 --- a/docs/source/atomic/atomic_data.rst +++ b/docs/source/atomic/atomic_data.rst @@ -7,3 +7,5 @@ Atomic Data emission_lines rate_coefficients gaunt_factors + repository + openadas \ No newline at end of file diff --git a/docs/source/atomic/openadas.rst b/docs/source/atomic/openadas.rst new file mode 100644 index 00000000..3dde4242 --- /dev/null +++ b/docs/source/atomic/openadas.rst @@ -0,0 +1,29 @@ +Open-ADAS +--------- + +Although a typical Open-ADAS data set is installed to the local atomic data repository +using the `populate()` function, additional atomic data can be installed manually. + +The following functions allow to parse the Open-ADAS files and install the rates of the atomic processes +to the local atomic data repository. + +Parse +^^^^^ + +.. autofunction:: cherab.openadas.parse.adf11.parse_adf11 + +.. autofunction:: cherab.openadas.parse.adf12.parse_adf12 + +.. autofunction:: cherab.openadas.parse.adf15.parse_adf15 + +.. autofunction:: cherab.openadas.parse.adf21.parse_adf21 + +.. autofunction:: cherab.openadas.parse.adf22.parse_adf22bmp + +.. autofunction:: cherab.openadas.parse.adf22.parse_adf22bme + +Install +^^^^^^^ + +.. automodule:: cherab.openadas.install + :members: diff --git a/docs/source/atomic/repository.rst b/docs/source/atomic/repository.rst new file mode 100644 index 00000000..3a93085e --- /dev/null +++ b/docs/source/atomic/repository.rst @@ -0,0 +1,83 @@ + +Atomic data repository +---------------------- + +The following functions allow to manipulate the local atomic data repository: +add the rates of the atomic processes, update existing ones or get the data +already present in the repository. + +The default repository is created at `~/.cherab/openadas/repository`. +Cherab supports multiple atomic data repositories. The user can configure different +repositories by setting the `repository_path` parameter. +The data in these repositories can be accessed through the `OpenADAS` atomic data provider +by specifying the `data_path` parameter. + +To create the new atomic data repository at the default location and populate it with a typical +set of rates and wavelengths from Open-ADAS, do: + +.. code-block:: pycon + + >>> from cherab.openadas.repository import populate + >>> populate() + +.. autofunction:: cherab.openadas.repository.create.populate + +Wavelength +^^^^^^^^^^ + +.. automodule:: cherab.openadas.repository.wavelength + :members: + +Ionisation +^^^^^^^^^^ + +.. autofunction:: cherab.openadas.repository.atomic.add_ionisation_rate + +.. autofunction:: cherab.openadas.repository.atomic.get_ionisation_rate + +.. autofunction:: cherab.openadas.repository.atomic.update_ionisation_rates + +Recombination +^^^^^^^^^^^^^ + +.. autofunction:: cherab.openadas.repository.atomic.add_recombination_rate + +.. autofunction:: cherab.openadas.repository.atomic.get_recombination_rate + +.. autofunction:: cherab.openadas.repository.atomic.update_recombination_rates + +Thermal Charge Exchange +^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: cherab.openadas.repository.atomic.add_thermal_cx_rate + +.. autofunction:: cherab.openadas.repository.atomic.get_thermal_cx_rate + +.. autofunction:: cherab.openadas.repository.atomic.update_thermal_cx_rates + +Photon Emissivity Coefficients +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: cherab.openadas.repository.pec + :members: + +Radiated Power +^^^^^^^^^^^^^^ + +.. automodule:: cherab.openadas.repository.radiated_power + :members: + +Beam +^^^^ + +.. automodule:: cherab.openadas.repository.beam.cx + :members: + +.. automodule:: cherab.openadas.repository.beam.emission + :members: + +.. automodule:: cherab.openadas.repository.beam.population + :members: + +.. automodule:: cherab.openadas.repository.beam.stopping + :members: From 9710d18a2956bbc815649d17450c6e8dcc495efe Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 27 Jul 2024 21:18:29 +0200 Subject: [PATCH 110/134] Improve documentation for openadas atomic data interpolators. --- cherab/openadas/rates/atomic.pyx | 78 +++++++++++++++----- cherab/openadas/rates/beam.pyx | 81 +++++++++++++++++---- cherab/openadas/rates/cx.pyx | 50 ++++++++++--- cherab/openadas/rates/pec.pyx | 58 +++++++++++---- cherab/openadas/rates/radiated_power.pyx | 69 ++++++++++++++++-- docs/source/atomic/atomic_data.rst | 1 + docs/source/atomic/data_interpolators.rst | 89 +++++++++++++++++++++++ docs/source/atomic/rate_coefficients.rst | 31 +++++++- 8 files changed, 393 insertions(+), 64 deletions(-) create mode 100644 docs/source/atomic/data_interpolators.rst diff --git a/cherab/openadas/rates/atomic.pyx b/cherab/openadas/rates/atomic.pyx index 04500124..903baf9f 100644 --- a/cherab/openadas/rates/atomic.pyx +++ b/cherab/openadas/rates/atomic.pyx @@ -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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,12 +24,26 @@ from raysect.core.math.function.float cimport Interpolator2DArray cdef class IonisationRate(CoreIonisationRate): + """ + Ionisation rate. + + Data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param dict data: Ionisation rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with ionisation rate in m^3.s^-1. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, dict data, extrapolate=False): - """ - :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). - """ self.raw_data = data @@ -62,7 +76,7 @@ cdef class IonisationRate(CoreIonisationRate): cdef class NullIonisationRate(CoreIonisationRate): """ - A PEC rate that always returns zero. + An ionisation rate that always returns zero. Needed for use cases where the required atomic data is missing. """ @@ -71,12 +85,26 @@ cdef class NullIonisationRate(CoreIonisationRate): cdef class RecombinationRate(CoreRecombinationRate): + """ + Recombination rate. + + Data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param dict data: Recombination rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with recombination rate in m^3.s^-1. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, dict data, extrapolate=False): - """ - :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). - """ self.raw_data = data @@ -109,7 +137,7 @@ cdef class RecombinationRate(CoreRecombinationRate): cdef class NullRecombinationRate(CoreRecombinationRate): """ - A PEC rate that always returns zero. + A recombination rate that always returns zero. Needed for use cases where the required atomic data is missing. """ @@ -118,12 +146,26 @@ cdef class NullRecombinationRate(CoreRecombinationRate): cdef class ThermalCXRate(CoreThermalCXRate): + """ + Thermal charge exchange rate. + + Data is interpolated with cubic spline in log-log space. + Linear extrapolation is used if extrapolate is True. + + :param dict data: CX rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with thermal CX rate in m^3.s^-1. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, dict data, extrapolate=False): - """ - :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). - """ self.raw_data = data @@ -155,7 +197,7 @@ cdef class ThermalCXRate(CoreThermalCXRate): cdef class NullThermalCXRate(CoreThermalCXRate): """ - A PEC rate that always returns zero. + A thermal CX rate that always returns zero. Needed for use cases where the required atomic data is missing. """ diff --git a/cherab/openadas/rates/beam.pyx b/cherab/openadas/rates/beam.pyx index f40af8dd..7059e243 100644 --- a/cherab/openadas/rates/beam.pyx +++ b/cherab/openadas/rates/beam.pyx @@ -1,6 +1,6 @@ -# 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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,8 +31,26 @@ cdef class BeamStoppingRate(CoreBeamStoppingRate): """ The beam stopping coefficient interpolation class. - :param data: A dictionary holding the beam coefficient data. - :param extrapolate: Set to True to enable extrapolation, False to disable (default). + Data is interpolated with cubic spline in log-log space. + Linear and quadratic extrapolations are used for "sen" and "st" respectively + if extrapolate is True. + + :param dict data: A beam stopping rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n': 1D array of size (M) with target electron density in m^-3, + | 't': 1D array of size (K) with target electron temperature in eV, + | 'sen': 2D array of size (N, M) with beam stopping rate energy component in m^3.s^-1. + | 'st': 1D array of size (K) with beam stopping rate temperature component in m^3.s^-1. + | 'sref': reference beam stopping rate in m^3.s^-1. + | The total beam stopping rate: s = sen * st / sref. + + :param bint extrapolate: Set to True to enable extrapolation, False to disable (default). + + :ivar tuple beam_energy_range: Interaction energy interpolation range. + :ivar tuple density_range: Target electron density interpolation range. + :ivar tuple temperature_range: Target electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. """ @cython.cdivision(True) @@ -93,7 +111,7 @@ cdef class BeamStoppingRate(CoreBeamStoppingRate): cdef class NullBeamStoppingRate(CoreBeamStoppingRate): """ - A beam rate that always returns zero. + A beam stopping rate that always returns zero. Needed for use cases where the required atomic data is missing. """ @@ -105,8 +123,26 @@ cdef class BeamPopulationRate(CoreBeamPopulationRate): """ The beam population coefficient interpolation class. - :param data: A dictionary holding the beam coefficient data. - :param extrapolate: Set to True to enable extrapolation, False to disable (default). + Data is interpolated with cubic spline in log-log space. + Linear and quadratic extrapolations are used for "sen" and "st" respectively + if extrapolate is True. + + :param dict data: Beam population rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n': 1D array of size (M) with target electron density in m^-3, + | 't': 1D array of size (K) with target electron temperature in eV, + | 'sen': 2D array of size (N, M) with dimensionless beam population rate energy component. + | 'st': 1D array of size (K) with dimensionless beam population rate temperature component. + | 'sref': reference dimensionless beam population rate. + | The total beam population rate: s = sen * st / sref. + + :param bint extrapolate: Set to True to enable extrapolation, False to disable (default). + + :ivar tuple beam_energy_range: Interaction energy interpolation range. + :ivar tuple density_range: Target electron density interpolation range. + :ivar tuple temperature_range: Target electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. """ @cython.cdivision(True) @@ -167,7 +203,7 @@ cdef class BeamPopulationRate(CoreBeamPopulationRate): cdef class NullBeamPopulationRate(CoreBeamPopulationRate): """ - A beam rate that always returns zero. + A beam population rate that always returns zero. Needed for use cases where the required atomic data is missing. """ @@ -179,9 +215,26 @@ cdef class BeamEmissionPEC(CoreBeamEmissionPEC): """ The beam emission coefficient interpolation class. - :param data: A dictionary holding the beam coefficient data. - :param wavelength: The natural wavelength of the emission line associated with the rate data in nm. - :param extrapolate: Set to True to enable extrapolation, False to disable (default). + Data is interpolated with cubic spline in log-log space. + Linear and quadratic extrapolations are used for "sen" and "st" respectively + if extrapolate is True. + + :param dict data: Beam emission rate dictionary containing the following entries: + + | 'e': 1D array of size (N) with interaction energy in eV/amu, + | 'n' 1D array of size (M) with target electron density in m^-3, + | 't' 1D array of size (K) with target electron temperature in eV, + | 'sen' 2D array of size (N, M) with beam emission rate energy component in photon.m^3.s^-1. + | 'st' 1D array of size (K) with beam emission rate temperature component in photon.m^3.s^-1. + | 'sref': reference beam emission rate in photon.m^3.s^-1. + + :param double wavelength: The natural wavelength of the emission line associated with the rate data in nm. + :param bint extrapolate: Set to True to enable extrapolation, False to disable (default). + + :ivar tuple beam_energy_range: Interaction energy interpolation range. + :ivar tuple density_range: Target electron density interpolation range. + :ivar tuple temperature_range: Target electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. """ @cython.cdivision(True) @@ -194,7 +247,7 @@ cdef class BeamEmissionPEC(CoreBeamEmissionPEC): e = data["e"] # eV/amu n = data["n"] # m^-3 t = data["t"] # eV - sen = np.log10(PhotonToJ.to(data["sen"], wavelength)) # W.m^3/s + sen = np.log10(PhotonToJ.to(data["sen"], wavelength)) # W.m^3 st = np.log10(data["st"] / data["sref"]) # dimensionless # store limits of data @@ -243,7 +296,7 @@ cdef class BeamEmissionPEC(CoreBeamEmissionPEC): cdef class NullBeamEmissionPEC(CoreBeamEmissionPEC): """ - A beam rate that always returns zero. + A beam emission PEC that always returns zero. Needed for use cases where the required atomic data is missing. """ diff --git a/cherab/openadas/rates/cx.pyx b/cherab/openadas/rates/cx.pyx index cb827a8b..ef6ff535 100644 --- a/cherab/openadas/rates/cx.pyx +++ b/cherab/openadas/rates/cx.pyx @@ -1,6 +1,6 @@ -# 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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -26,12 +26,42 @@ from raysect.core.math.function.float cimport Interpolator1DArray, Constant1D cdef class BeamCXPEC(CoreBeamCXPEC): """ - The effective cx rate interpolation class. - - :param donor_metastable: The metastable state of the donor species for which the rate data applies. - :param wavelength: The natural wavelength of the emission line associated with the rate data in nm. - :param data: A dictionary holding the rate data. - :param extrapolate: Set to True to enable extrapolation, False to disable (default). + Effective charge exchange photon emission coefficient. + + The data for "qeb" is interpolated with a cubic spline in log-log space. + The data for "qti", "qni", "qz" and "qb" are interpolated with a cubic spline + in linear space. + + Quadratic extrapolation is used for "qeb" and nearest neighbour extrapolation is used for + "qti", "qni", "qz" and "qb" if extrapolate is True. + + :param int donor_metastable: The metastable state of the donor species for which the rate data applies. + :param double wavelength: The natural wavelength of the emission line associated with the rate data in nm. + :param data: Beam CX PEC dictionary containing the following entries: + + | 'eb': 1D array of size (N) with beam energy in eV/amu, + | 'ti': 1D array of size (M) with receiver ion temperature in eV, + | 'ni': 1D array of size (K) with receiver ion density in m^-3, + | 'z': 1D array of size (L) with receiver Z-effective, + | 'b': 1D array of size (J) with magnetic field strength in Tesla, + | 'qeb': 1D array of size (N) with CX PEC energy component in photon.m^3.s-1, + | 'qti': 1D array of size (M) with CX PEC temperature component in photon.m^3.s-1, + | 'qni': 1D array of size (K) with CX PEC density component in photon.m^3.s-1, + | 'qz': 1D array of size (L) with CX PEC Zeff component in photon.m^3.s-1, + | 'qb': 1D array of size (J) with CX PEC B-field component in photon.m^3.s-1, + | 'qref': reference CX PEC in photon.m^3.s-1. + | The total beam CX PEC: q = qeb * qti * qni * qz * qb / qref^4. + + :param bint extrapolate: Set to True to enable extrapolation, False to disable (default). + + :ivar tuple beam_energy_range: Interaction energy interpolation range. + :ivar tuple density_range: Receiver ion density interpolation range. + :ivar tuple temperature_range: Receiver ion temperature interpolation range. + :ivar tuple zeff_range: Z-effective interpolation range. + :ivar tuple b_field_range: Magnetic field strength interpolation range. + :ivar int donor_metastable: The metastable state of the donor species. + :ivar double wavelength: The natural wavelength of the emission line in nm. + :ivar dict raw_data: Dictionary containing the raw data. """ @cython.cdivision(True) @@ -79,7 +109,7 @@ cdef class BeamCXPEC(CoreBeamCXPEC): :param energy: Interaction energy in eV/amu. :param temperature: Receiver ion temperature in eV. - :param density: Receiver ion density in m^-3 + :param density: Plasma total ion density in m^-3 :param z_effective: Plasma Z-effective. :param b_field: Magnetic field magnitude in Tesla. :return: The effective cx rate in W.m^3 diff --git a/cherab/openadas/rates/pec.pyx b/cherab/openadas/rates/pec.pyx index eafc6c64..fb6f392c 100644 --- a/cherab/openadas/rates/pec.pyx +++ b/cherab/openadas/rates/pec.pyx @@ -1,6 +1,6 @@ -# 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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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"); @@ -25,13 +25,27 @@ from cherab.core.utility.conversion import PhotonToJ cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): + """ + Electron impact excitation photon emission coefficient. + + The data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param double wavelength: Resting wavelength of corresponding emission line in nm. + :param dict data: Excitation PEC dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with excitation PEC in photon.m^3.s^-1. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, double wavelength, dict data, extrapolate=False): - """ - :param wavelength: Resting wavelength of corresponding emission line in nm. - :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). - """ self.wavelength = wavelength self.raw_data = data @@ -68,7 +82,7 @@ cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): cdef class NullImpactExcitationPEC(CoreImpactExcitationPEC): """ - A PEC rate that always returns zero. + A electron impact excitation PEC rate that always returns zero. Needed for use cases where the required atomic data is missing. """ @@ -77,13 +91,27 @@ cdef class NullImpactExcitationPEC(CoreImpactExcitationPEC): cdef class RecombinationPEC(CoreRecombinationPEC): + """ + Recombination photon emission coefficient. + + The data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param double wavelength: Resting wavelength of corresponding emission line in nm. + :param dict data: Rcombination PEC dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with recombination PEC in photon.m^3.s^-1. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, double wavelength, dict data, extrapolate=False): - """ - :param wavelength: Resting wavelength of corresponding emission line in nm. - :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). - """ self.wavelength = wavelength self.raw_data = data @@ -120,7 +148,7 @@ cdef class RecombinationPEC(CoreRecombinationPEC): cdef class NullRecombinationPEC(CoreRecombinationPEC): """ - A PEC rate that always returns zero. + A recombination PEC rate that always returns zero. Needed for use cases where the required atomic data is missing. """ diff --git a/cherab/openadas/rates/radiated_power.pyx b/cherab/openadas/rates/radiated_power.pyx index 570ced92..32371c8f 100644 --- a/cherab/openadas/rates/radiated_power.pyx +++ b/cherab/openadas/rates/radiated_power.pyx @@ -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-2024 Euratom +# Copyright 2016-2024 United Kingdom Atomic Energy Authority +# Copyright 2016-2024 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,26 @@ from raysect.core.math.function.float cimport Interpolator2DArray cdef class LineRadiationPower(CoreLineRadiationPower): - """Base class for radiated powers.""" + """ + Line radiated power coefficient. + + The data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param Element species: Element object defining the ion type. + :param int ionisation: Charge state of the ion. + :param dict data: Line radiated power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with radiated power rate in W.m^3. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, species, ionisation, dict data, extrapolate=False): @@ -70,7 +89,26 @@ cdef class NullLineRadiationPower(CoreLineRadiationPower): cdef class ContinuumPower(CoreContinuumPower): - """Base class for radiated powers.""" + """ + Recombination continuum radiated power coefficient. + + The data is interpolated with cubic spline in log-log space. + Nearest neighbour extrapolation is used if extrapolate is True. + + :param Element species: Element object defining the ion type. + :param int ionisation: Charge state of the ion. + :param dict data: Recombination continuum radiated power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with radiated power rate in W.m^3. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, species, ionisation, dict data, extrapolate=False): @@ -116,7 +154,26 @@ cdef class NullContinuumPower(CoreContinuumPower): cdef class CXRadiationPower(CoreCXRadiationPower): - """Base class for radiated powers.""" + """ + Charge exchange radiated power coefficient. + + The data is interpolated with cubic spline in log-log space. + Linear extrapolation is used if extrapolate is True. + + :param Element species: Element object defining the ion type. + :param int ionisation: Charge state of the ion. + :param dict data: CX radiated power rate dictionary containing the following entries: + + | 'ne': 1D array of size (N) with electron density in m^-3, + | 'te': 1D array of size (M) with electron temperature in eV, + | 'rate': 2D array of size (N, M) with radiated power rate in W.m^3. + + :param bint extrapolate: Enable extrapolation (default=False). + + :ivar tuple density_range: Electron density interpolation range. + :ivar tuple temperature_range: Electron temperature interpolation range. + :ivar dict raw_data: Dictionary containing the raw data. + """ def __init__(self, species, ionisation, dict data, extrapolate=False): diff --git a/docs/source/atomic/atomic_data.rst b/docs/source/atomic/atomic_data.rst index 650d89b4..edd24b0d 100644 --- a/docs/source/atomic/atomic_data.rst +++ b/docs/source/atomic/atomic_data.rst @@ -7,3 +7,4 @@ Atomic Data emission_lines rate_coefficients gaunt_factors + data_interpolators diff --git a/docs/source/atomic/data_interpolators.rst b/docs/source/atomic/data_interpolators.rst new file mode 100644 index 00000000..56fa0327 --- /dev/null +++ b/docs/source/atomic/data_interpolators.rst @@ -0,0 +1,89 @@ +Atomic data interpolators +========================= + +The following classes interpolate atomic data defined on a numerical grid. + + +Atomic Processes +^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.openadas.rates.atomic.IonisationRate + :members: + +.. autoclass:: cherab.openadas.rates.atomic.NullIonisationRate + :members: + +.. autoclass:: cherab.openadas.rates.atomic.RecombinationRate + :members: + +.. autoclass:: cherab.openadas.rates.atomic.NullRecombinationRate + :members: + +.. autoclass:: cherab.openadas.rates.atomic.ThermalCXRate + :members: + +.. autoclass:: cherab.openadas.rates.atomic.NullThermalCXRate + :members: + +Photon Emissivity Coefficients +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.openadas.rates.pec.ImpactExcitationPEC + :members: + +.. autoclass:: cherab.openadas.rates.pec.NullImpactExcitationPEC + :members: + +.. autoclass:: cherab.openadas.rates.pec.RecombinationPEC + :members: + +.. autoclass:: cherab.openadas.rates.pec.NullRecombinationPEC + :members: + +Beam-Plasma Interaction Rates +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.openadas.rates.cx.BeamCXPEC + :members: + +.. autoclass:: cherab.openadas.rates.cx.NullBeamCXPEC + :members: + +.. autoclass:: cherab.openadas.rates.beam.BeamStoppingRate + :members: + +.. autoclass:: cherab.openadas.rates.beam.NullBeamStoppingRate + :members: + +.. autoclass:: cherab.openadas.rates.beam.BeamPopulationRate + :members: + +.. autoclass:: cherab.openadas.rates.beam.NullBeamPopulationRate + :members: + +.. autoclass:: cherab.openadas.rates.beam.BeamEmissionPEC + :members: + +.. autoclass:: cherab.openadas.rates.beam.NullBeamEmissionPEC + :members: + +Radiated Power +^^^^^^^^^^^^^^ + +.. autoclass:: cherab.openadas.rates.radiated_power.LineRadiationPower + :members: + +.. autoclass:: cherab.openadas.rates.radiated_power.NullLineRadiationPower + :members: + +.. autoclass:: cherab.openadas.rates.radiated_power.ContinuumPower + :members: + +.. autoclass:: cherab.openadas.rates.radiated_power.NullContinuumPower + :members: + +.. autoclass:: cherab.openadas.rates.radiated_power.CXRadiationPower + :members: + +.. autoclass:: cherab.openadas.rates.radiated_power.NullCXRadiationPower + :members: diff --git a/docs/source/atomic/rate_coefficients.rst b/docs/source/atomic/rate_coefficients.rst index b6547820..82116bf3 100644 --- a/docs/source/atomic/rate_coefficients.rst +++ b/docs/source/atomic/rate_coefficients.rst @@ -15,6 +15,35 @@ provide theoretical equations. Cherab emission models only need to know how to c them after they have been instantiated. +Atomic Processes +^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.core.atomic.rates.IonisationRate + +.. autoclass:: cherab.core.atomic.rates.RecombinationRate + +.. autoclass:: cherab.core.atomic.rates.ThermalCXRate + +The `IonisationRate`, `RecombinationRate` and `ThermalCXRate` classes all share +the same call signatures. + +.. function:: __call__(density, temperature) + + Returns an effective rate coefficient at the specified plasma conditions. + + This function just wraps the cython evaluate() method. + +.. function:: evaluate(density, temperature) + + an effective recombination rate coefficient at the specified plasma conditions. + + This function needs to be implemented by the atomic data provider. + + :param float density: Electron density in m^-3 + :param float temperature: Electron temperature in eV. + :return: The effective ionisation rate in [m^3.s^-1]. + + Photon Emissivity Coefficients ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,8 +68,8 @@ the same call signatures. This function needs to be implemented by the atomic data provider. - :param float temperature: Receiver ion temperature in eV. :param float density: Receiver ion density in m^-3 + :param float temperature: Receiver ion temperature in eV. :return: The effective PEC rate [Wm^3]. Some example code for requesting PEC objects and sampling them with the __call__() From 58a3a9ff8bfbd9ede25c2d560c0a8153fac39c87 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 30 Jul 2024 12:49:24 +0200 Subject: [PATCH 111/134] Add the power radiated in spectral lines due to charge exchange with thermal neutral hydrogen to the TotalRadiatedPower model. --- CHANGELOG.md | 1 + cherab/core/atomic/rates.pxd | 2 +- cherab/core/atomic/rates.pyx | 2 +- .../model/plasma/total_radiated_power.pxd | 4 +- .../model/plasma/total_radiated_power.pyx | 66 ++++++++++++++++++- cherab/openadas/rates/radiated_power.pyx | 12 ++-- .../models/radiated_power/radiated_power.rst | 2 +- 7 files changed, 77 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c70837fb..91e0ba21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ New: * **Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414)** * Improved beam direction calculation to allow for natural broadening of the BES line shape due to beam divergence. (#414) * Add kwargs to invert_regularised_nnls to pass them to scipy.optimize.nnls. (#438) +* Add the power radiated in spectral lines due to charge exchange with thermal neutral hydrogen to the TotalRadiatedPower model. (#370) Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) diff --git a/cherab/core/atomic/rates.pxd b/cherab/core/atomic/rates.pxd index 1f844122..27cec455 100644 --- a/cherab/core/atomic/rates.pxd +++ b/cherab/core/atomic/rates.pxd @@ -84,7 +84,7 @@ cdef class _RadiatedPower: readonly Element element readonly int charge - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 cdef class LineRadiationPower(_RadiatedPower): diff --git a/cherab/core/atomic/rates.pyx b/cherab/core/atomic/rates.pyx index f3a653db..852725a7 100644 --- a/cherab/core/atomic/rates.pyx +++ b/cherab/core/atomic/rates.pyx @@ -263,7 +263,7 @@ cdef class _RadiatedPower: """ return self.evaluate(electron_density, electron_temperature) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: """ Evaluate the radiated power at the given plasma conditions. diff --git a/cherab/core/model/plasma/total_radiated_power.pxd b/cherab/core/model/plasma/total_radiated_power.pxd index 1900d965..59499ba2 100644 --- a/cherab/core/model/plasma/total_radiated_power.pxd +++ b/cherab/core/model/plasma/total_radiated_power.pxd @@ -18,7 +18,7 @@ from cherab.core.atomic.elements cimport Element -from cherab.core.atomic.rates cimport LineRadiationPower, ContinuumPower +from cherab.core.atomic.rates cimport LineRadiationPower, ContinuumPower, CXRadiationPower from cherab.core.plasma cimport PlasmaModel from cherab.core.species cimport Species @@ -30,7 +30,9 @@ cdef class TotalRadiatedPower(PlasmaModel): Element _element int _charge Species _line_rad_species, _recom_species + list _hydrogen_species LineRadiationPower _plt_rate ContinuumPower _prb_rate + CXRadiationPower _prc_rate cdef int _populate_cache(self) except -1 diff --git a/cherab/core/model/plasma/total_radiated_power.pyx b/cherab/core/model/plasma/total_radiated_power.pyx index 28350d4e..12deda94 100644 --- a/cherab/core/model/plasma/total_radiated_power.pyx +++ b/cherab/core/model/plasma/total_radiated_power.pyx @@ -20,9 +20,40 @@ from raysect.optical cimport Spectrum, Point3D, Vector3D from cherab.core cimport Plasma, AtomicData from cherab.core.utility.constants cimport RECIP_4_PI +from cherab.core.atomic.elements import hydrogen, deuterium, tritium cdef class TotalRadiatedPower(PlasmaModel): + r""" + Emitter that calculates total power radiated by a given ion, which includes: + + - line power due to electron impact excitation, + - continuum and line power due to recombination and Bremsstrahlung, + - line power due to charge exchange with thermal neutral hydrogen and its isotopes. + + The emission calculated by this model is spectrally unresolved, + which means that the total radiated power will be spread of the entire + observable spectral range. + + .. math:: + \epsilon_{\mathrm{total}} = \frac{1}{4 \pi \Delta\lambda} \left( + n_{Z_\mathrm{i}} n_\mathrm{e} C_{\mathrm{excit}}(n_\mathrm{e}, T_\mathrm{e}) + + n_{Z_\mathrm{i} + 1} n_\mathrm{e} C_{\mathrm{recomb}}(n_\mathrm{e}, T_\mathrm{e}) + + n_{Z_\mathrm{i} + 1} n_\mathrm{hyd} C_{\mathrm{cx}}(n_\mathrm{e}, T_\mathrm{e}) \right) + + where :math:`n_{Z_\mathrm{i}}` is the target species density; + :math:`n_{Z_\mathrm{i} + 1}` is the recombining species density; + :math:`n_{\mathrm{hyd}}` is the total density of all hydrogen isotopes; + :math:`C_{\mathrm{excit}}, C_{\mathrm{recomb}}, C_{\mathrm{cx}}` are the radiated power + coefficients in :math:`W m^3` due to electron impact excitation, recombination + + Bremsstrahlung and charge exchange with thermal neutral hydrogen, respectively; + :math:`\Delta\lambda` is the observable spectral range. + + :param Element element: The atomic element/isotope. + :param int charge: The charge state of the element/isotope. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + """ def __init__(self, Element element, int charge, Plasma plasma=None, AtomicData atomic_data=None): @@ -42,7 +73,9 @@ cdef class TotalRadiatedPower(PlasmaModel): cdef: int i - double ne, ni, ni_upper, te, plt_radiance, prb_radiance + double ne, ni, ni_upper, nhyd, te + double plt_radiance, prb_radiance, prc_radiance + Species hyd_species # cache data on first run if not self._cache_loaded: @@ -60,22 +93,37 @@ cdef class TotalRadiatedPower(PlasmaModel): ni_upper = self._recom_species.distribution.density(point.x, point.y, point.z) + nhyd = 0 + for hyd_species in self._hydrogen_species: + nhyd += hyd_species.distribution.density(point.x, point.y, point.z) + # add emission to spectrum if self._plt_rate and ni > 0: plt_radiance = RECIP_4_PI * self._plt_rate.evaluate(ne, te) * ne * ni / (spectrum.max_wavelength - spectrum.min_wavelength) else: plt_radiance = 0 + if self._prb_rate and ni_upper > 0: prb_radiance = RECIP_4_PI * self._prb_rate.evaluate(ne, te) * ne * ni_upper / (spectrum.max_wavelength - spectrum.min_wavelength) else: prb_radiance = 0 + + if self._prc_rate and ni_upper > 0 and nhyd > 0: + prc_radiance = RECIP_4_PI * self._prc_rate.evaluate(ne, te) * nhyd * ni_upper / (spectrum.max_wavelength - spectrum.min_wavelength) + else: + prc_radiance = 0 + for i in range(spectrum.bins): - spectrum.samples_mv[i] += plt_radiance + prb_radiance + spectrum.samples_mv[i] += plt_radiance + prb_radiance + prc_radiance return spectrum cdef int _populate_cache(self) except -1: + cdef: + Species hyd_species + Element hyd_isotope + # sanity checks if self._plasma is None: raise RuntimeError("The emission model is not connected to a plasma object.") @@ -100,6 +148,18 @@ cdef class TotalRadiatedPower(PlasmaModel): "recombination/continuum emission, (element={}, ionisation={})." "".format(self._element.symbol, self._charge+1)) + # cache hydrogen species and CX radiation rate + self._prc_rate = self._atomic_data.cx_radiated_power_rate(self._element, self._charge+1) + + self._hydrogen_species = [] + for hyd_isotope in (hydrogen, deuterium, tritium): + try: + hyd_species = self._plasma.composition.get(hyd_isotope, 0) + except ValueError: + pass + else: + self._hydrogen_species.append(hyd_species) + self._cache_loaded = True def _change(self): @@ -108,5 +168,7 @@ cdef class TotalRadiatedPower(PlasmaModel): self._cache_loaded = False self._line_rad_species = None self._recom_species = None + self._hydrogen_species = None self._plt_rate = None self._prb_rate = None + self._prc_rate = None diff --git a/cherab/openadas/rates/radiated_power.pyx b/cherab/openadas/rates/radiated_power.pyx index 570ced92..8e232a4e 100644 --- a/cherab/openadas/rates/radiated_power.pyx +++ b/cherab/openadas/rates/radiated_power.pyx @@ -46,7 +46,7 @@ cdef class LineRadiationPower(CoreLineRadiationPower): extrapolation_type = 'nearest' if extrapolate else 'none' self._rate = Interpolator2DArray(np.log10(ne), np.log10(te), rate, 'cubic', extrapolation_type, INFINITY, INFINITY) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation if electron_density < 1.e-300: @@ -65,7 +65,7 @@ cdef class NullLineRadiationPower(CoreLineRadiationPower): Needed for use cases where the required atomic data is missing. """ - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: return 0.0 @@ -92,7 +92,7 @@ cdef class ContinuumPower(CoreContinuumPower): extrapolation_type = 'nearest' if extrapolate else 'none' self._rate = Interpolator2DArray(np.log10(ne), np.log10(te), rate, 'cubic', extrapolation_type, INFINITY, INFINITY) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation if electron_density < 1.e-300: @@ -111,7 +111,7 @@ cdef class NullContinuumPower(CoreContinuumPower): Needed for use cases where the required atomic data is missing. """ - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: return 0.0 @@ -137,7 +137,7 @@ cdef class CXRadiationPower(CoreCXRadiationPower): extrapolation_type = 'linear' if extrapolate else 'none' self._rate = Interpolator2DArray(np.log10(ne), np.log10(te), rate, 'cubic', extrapolation_type, INFINITY, INFINITY) - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation if electron_density < 1.e-300: @@ -156,5 +156,5 @@ cdef class NullCXRadiationPower(CoreCXRadiationPower): Needed for use cases where the required atomic data is missing. """ - cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: + cpdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: return 0.0 diff --git a/docs/source/models/radiated_power/radiated_power.rst b/docs/source/models/radiated_power/radiated_power.rst index 0b8594e1..d00fcec2 100644 --- a/docs/source/models/radiated_power/radiated_power.rst +++ b/docs/source/models/radiated_power/radiated_power.rst @@ -2,4 +2,4 @@ Total Radiated Power ==================== -Documentation for this model will go here soon... +.. autoclass:: cherab.core.model.plasma.total_radiated_power.TotalRadiatedPower From 1236237c659e95d0e60601924877fc5051b46573 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 30 Jul 2024 12:50:33 +0200 Subject: [PATCH 112/134] Add the TestCase for the TotalRadiatedPower model. --- .../core/tests/test_total_radiated_power.py | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 cherab/core/tests/test_total_radiated_power.py diff --git a/cherab/core/tests/test_total_radiated_power.py b/cherab/core/tests/test_total_radiated_power.py new file mode 100644 index 00000000..2744e803 --- /dev/null +++ b/cherab/core/tests/test_total_radiated_power.py @@ -0,0 +1,142 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.core import Point3D, Vector3D +from raysect.optical import Ray, World + +from cherab.core.atomic import AtomicData, LineRadiationPower, ContinuumPower, CXRadiationPower +from cherab.core.atomic import deuterium, hydrogen, nitrogen +from cherab.tools.plasmas.slab import build_constant_slab_plasma +from cherab.core.model import TotalRadiatedPower + +from cherab.core.utility import EvAmuToMS, EvToJ + + +class ConstantLineRadiationPower(LineRadiationPower): + """ + Constant line radiation power coefficient. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, density, temperature): + + return self.value + + +class ConstantContinuumPower(ContinuumPower): + """ + Constant continuum power coefficient. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, density, temperature): + + return self.value + + +class ConstantCXRadiationPower(CXRadiationPower): + """ + Constant charge exchange radiation power coefficient. + """ + + def __init__(self, value): + self.value = value + + def evaluate(self, density, temperature): + + return self.value + + +class MockAtomicData(AtomicData): + """Fake atomic data for test purpose.""" + + def line_radiated_power_rate(self, element, charge): + + return ConstantLineRadiationPower(1.e-32) + + def continuum_radiated_power_rate(self, element, charge): + + return ConstantContinuumPower(1.e-33) + + def cx_radiated_power_rate(self, element, charge): + + return ConstantCXRadiationPower(1.e-31) + + +class TestTotalRadiatedPower(unittest.TestCase): + + def setUp(self): + + self.world = World() + + self.atomic_data = MockAtomicData() + + plasma_species = [(deuterium, 0, 1.e18, 500., Vector3D(0, 0, 0)), + (hydrogen, 0, 1.e18, 500., Vector3D(0, 0, 0)), + (nitrogen, 6, 5.e18, 1100., Vector3D(0, 0, 0)), + (nitrogen, 7, 1.e19, 1100., Vector3D(0, 0, 0))] + self.plasma = build_constant_slab_plasma(length=1.2, width=1.2, height=1.2, + electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species) + self.plasma.parent = self.world + self.plasma.atomic_data = self.atomic_data + + def test_beam_density(self): + + self.plasma.models = [TotalRadiatedPower(nitrogen, 6)] + + # observing + origin = Point3D(1.5, 0, 0) + direction = Vector3D(-1, 0, 0) + ray = Ray(origin=origin, direction=direction, + min_wavelength=500., max_wavelength=550., bins=2) + radiated_power = ray.trace(self.world).total() + + # validating + ne = self.plasma.electron_distribution.density(0.5, 0.5, 0.5) + n_n6 = self.plasma.composition[(nitrogen, 6)].distribution.density(0.5, 0.5, 0.5) + n_n7 = self.plasma.composition[(nitrogen, 7)].distribution.density(0.5, 0.5, 0.5) + n_h0 = self.plasma.composition[(hydrogen, 0)].distribution.density(0.5, 0.5, 0.5) + n_d0 = self.plasma.composition[(deuterium, 0)].distribution.density(0.5, 0.5, 0.5) + + integration_length = 1.2 + + plt_rate = self.atomic_data.line_radiated_power_rate(nitrogen, 6).value + plt_radiance = 0.25 / np.pi * plt_rate * ne * n_n6 * integration_length + + prb_rate = self.atomic_data.continuum_radiated_power_rate(nitrogen, 7).value + prb_radiance = 0.25 / np.pi * prb_rate * ne * n_n7 * integration_length + + prc_rate = self.atomic_data.cx_radiated_power_rate(nitrogen, 7).value + prc_radiance = 0.25 / np.pi * prc_rate * (n_h0 + n_d0) * n_n7 * integration_length + + test_radiated_power = plt_radiance + prb_radiance + prc_radiance + + self.assertAlmostEqual(radiated_power / test_radiated_power, 1., delta=1e-8) + + +if __name__ == '__main__': + unittest.main() From 554a02f2b2f404c8c836fbe9efd5e433ce0a7df3 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 30 Jul 2024 23:35:22 +0200 Subject: [PATCH 113/134] Move power density -> spectral radiance conversion to a separate line. --- .../core/model/plasma/total_radiated_power.pyx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cherab/core/model/plasma/total_radiated_power.pyx b/cherab/core/model/plasma/total_radiated_power.pyx index 12deda94..fbf1a491 100644 --- a/cherab/core/model/plasma/total_radiated_power.pyx +++ b/cherab/core/model/plasma/total_radiated_power.pyx @@ -74,7 +74,7 @@ cdef class TotalRadiatedPower(PlasmaModel): cdef: int i double ne, ni, ni_upper, nhyd, te - double plt_radiance, prb_radiance, prc_radiance + double plt_power_density, prb_power_density, prc_power_density, radiance Species hyd_species # cache data on first run @@ -99,22 +99,24 @@ cdef class TotalRadiatedPower(PlasmaModel): # add emission to spectrum if self._plt_rate and ni > 0: - plt_radiance = RECIP_4_PI * self._plt_rate.evaluate(ne, te) * ne * ni / (spectrum.max_wavelength - spectrum.min_wavelength) + plt_power_density = self._plt_rate.evaluate(ne, te) * ne * ni else: - plt_radiance = 0 + plt_power_density = 0 if self._prb_rate and ni_upper > 0: - prb_radiance = RECIP_4_PI * self._prb_rate.evaluate(ne, te) * ne * ni_upper / (spectrum.max_wavelength - spectrum.min_wavelength) + prb_power_density = self._prb_rate.evaluate(ne, te) * ne * ni_upper else: - prb_radiance = 0 + prb_power_density = 0 if self._prc_rate and ni_upper > 0 and nhyd > 0: - prc_radiance = RECIP_4_PI * self._prc_rate.evaluate(ne, te) * nhyd * ni_upper / (spectrum.max_wavelength - spectrum.min_wavelength) + prc_power_density = self._prc_rate.evaluate(ne, te) * nhyd * ni_upper else: - prc_radiance = 0 + prc_power_density = 0 + + radiance = RECIP_4_PI * (plt_power_density + prb_power_density + prc_power_density) / (spectrum.max_wavelength - spectrum.min_wavelength) for i in range(spectrum.bins): - spectrum.samples_mv[i] += plt_radiance + prb_radiance + prc_radiance + spectrum.samples_mv[i] += radiance return spectrum From 3400bef492e6b56b772256b34c94ba249e6fea00 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 30 Jul 2024 23:46:48 +0200 Subject: [PATCH 114/134] Tidy up the code for the TotalRadiatedPower. --- .../model/plasma/total_radiated_power.pyx | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/cherab/core/model/plasma/total_radiated_power.pyx b/cherab/core/model/plasma/total_radiated_power.pyx index fbf1a491..30167d26 100644 --- a/cherab/core/model/plasma/total_radiated_power.pyx +++ b/cherab/core/model/plasma/total_radiated_power.pyx @@ -74,7 +74,7 @@ cdef class TotalRadiatedPower(PlasmaModel): cdef: int i double ne, ni, ni_upper, nhyd, te - double plt_power_density, prb_power_density, prc_power_density, radiance + double power_density, radiance Species hyd_species # cache data on first run @@ -98,22 +98,18 @@ cdef class TotalRadiatedPower(PlasmaModel): nhyd += hyd_species.distribution.density(point.x, point.y, point.z) # add emission to spectrum - if self._plt_rate and ni > 0: - plt_power_density = self._plt_rate.evaluate(ne, te) * ne * ni - else: - plt_power_density = 0 - - if self._prb_rate and ni_upper > 0: - prb_power_density = self._prb_rate.evaluate(ne, te) * ne * ni_upper - else: - prb_power_density = 0 - - if self._prc_rate and ni_upper > 0 and nhyd > 0: - prc_power_density = self._prc_rate.evaluate(ne, te) * nhyd * ni_upper - else: - prc_power_density = 0 - - radiance = RECIP_4_PI * (plt_power_density + prb_power_density + prc_power_density) / (spectrum.max_wavelength - spectrum.min_wavelength) + power_density = 0 + + if self._plt_rate and ni > 0: # excitation + power_density += self._plt_rate.evaluate(ne, te) * ne * ni + + if self._prb_rate and ni_upper > 0: # recombination + bremsstrahlung + power_density += self._prb_rate.evaluate(ne, te) * ne * ni_upper + + if self._prc_rate and ni_upper > 0 and nhyd > 0: # charge exchange + power_density += self._prc_rate.evaluate(ne, te) * nhyd * ni_upper + + radiance = RECIP_4_PI * power_density / (spectrum.max_wavelength - spectrum.min_wavelength) for i in range(spectrum.bins): spectrum.samples_mv[i] += radiance From ccdd28072a2f4463ca3809c75319f318250b7137 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 30 Jul 2024 23:58:33 +0200 Subject: [PATCH 115/134] Update CHANGELOG. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c70837fb..a71951eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ New: * **Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414)** * Improved beam direction calculation to allow for natural broadening of the BES line shape due to beam divergence. (#414) * Add kwargs to invert_regularised_nnls to pass them to scipy.optimize.nnls. (#438) +* All interpolated atomic rates now return 0 if plasma parameters <= 0, which matches the behaviour of emission models. (#450) Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) From b173acc02df33cd199dcdb7ac00d2a09c91fc1a7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 31 Jul 2024 00:03:27 +0200 Subject: [PATCH 116/134] Add an empty line after code-block directive. --- cherab/core/model/beam/charge_exchange.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 83fe237f..9eb562a0 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -61,6 +61,7 @@ cdef class BeamCXLine(BeamModel): :ivar Line line: The emission line object. .. code-block:: pycon + >>> from cherab.core.model import BeamCXLine >>> from cherab.core.atomic import carbon >>> from cherab.core.model import ParametrisedZeemanTriplet From 87d3d6610dced05daf4ca02d0d55f9f3f905f4c1 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 31 Jul 2024 15:53:06 +0200 Subject: [PATCH 117/134] Pass atomic_data to lineshape initialiser in ThermalCXLine. --- cherab/core/model/plasma/thermal_cx.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx index 1325919c..88af9ae8 100644 --- a/cherab/core/model/plasma/thermal_cx.pyx +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -152,7 +152,7 @@ cdef class ThermalCXLine(PlasmaModel): # instance line shape renderer self._lineshape = self._lineshape_class(self._line, self._wavelength, self._target_species, self._plasma, - *self._lineshape_args, **self._lineshape_kwargs) + self._atomic_data, *self._lineshape_args, **self._lineshape_kwargs) def _change(self): From d74d66c8df5b1e9f503f5234764bca3d34d1e640 Mon Sep 17 00:00:00 2001 From: vsnever Date: Wed, 31 Jul 2024 15:54:48 +0200 Subject: [PATCH 118/134] Pass atomic_data to custom lineshapes in the line emission tests. --- cherab/core/tests/test_line_emission.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cherab/core/tests/test_line_emission.py b/cherab/core/tests/test_line_emission.py index b55af871..bf40aac0 100644 --- a/cherab/core/tests/test_line_emission.py +++ b/cherab/core/tests/test_line_emission.py @@ -125,7 +125,7 @@ def test_default_lineshape(self): ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length - gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -154,7 +154,7 @@ def test_custom_lineshape(self): ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length - zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -200,7 +200,7 @@ def test_default_lineshape(self): ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length - gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -229,7 +229,7 @@ def test_custom_lineshape(self): ni = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * ni * ne * self.slab_length - zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -279,7 +279,7 @@ def test_default_lineshape(self): receiver_density = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * receiver_density * donor_density * self.slab_length - gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma) + gaussian_line = GaussianLine(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = gaussian_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) @@ -311,7 +311,7 @@ def test_custom_lineshape(self): receiver_density = target_species.distribution.density(0.5, 0, 0) radiance = 0.25 / np.pi * rate * receiver_density * donor_density * self.slab_length - zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma) + zeeman_line = ZeemanTriplet(line, wavelength, target_species, self.plasma, self.atomic_data) spectrum = Spectrum(ray.min_wavelength, ray.max_wavelength, ray.bins) spectrum = zeeman_line.add_line(radiance, Point3D(0.5, 0, 0), direction, spectrum) From 239a6bf5ac63c074676abf0be81a29b0764cac81 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 1 Aug 2024 13:17:00 +0200 Subject: [PATCH 119/134] Remove Cython from install_requires. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index b9058c27..baab310b 100644 --- a/setup.py +++ b/setup.py @@ -117,7 +117,6 @@ long_description=long_description, long_description_content_type="text/markdown", install_requires=[ - "cython~=3.0", "numpy>=1.14,<2.0", "scipy", "matplotlib", From fd4449bc2e8c4138012e315195dcd8250827f45d Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 1 Aug 2024 13:51:15 +0200 Subject: [PATCH 120/134] Replace 'composition' Python property with get_composition() Cython getter in TotalRadiatedPower.populate_cache(). --- cherab/core/model/plasma/total_radiated_power.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cherab/core/model/plasma/total_radiated_power.pyx b/cherab/core/model/plasma/total_radiated_power.pyx index 30167d26..84b42424 100644 --- a/cherab/core/model/plasma/total_radiated_power.pyx +++ b/cherab/core/model/plasma/total_radiated_power.pyx @@ -131,7 +131,7 @@ cdef class TotalRadiatedPower(PlasmaModel): # cache line radiation species and rate self._plt_rate = self._atomic_data.line_radiated_power_rate(self._element, self._charge) try: - self._line_rad_species = self._plasma.composition.get(self._element, self._charge) + self._line_rad_species = self._plasma.get_composition().get(self._element, self._charge) except ValueError: raise RuntimeError("The plasma object does not contain the required ion species for calculating" "total line radiaton, (element={}, ionisation={})." @@ -140,7 +140,7 @@ cdef class TotalRadiatedPower(PlasmaModel): # cache recombination species and radiation rate self._prb_rate = self._atomic_data.continuum_radiated_power_rate(self._element, self._charge+1) try: - self._recom_species = self._plasma.composition.get(self._element, self._charge+1) + self._recom_species = self._plasma.get_composition().get(self._element, self._charge+1) except ValueError: raise RuntimeError("The plasma object does not contain the required ion species for calculating" "recombination/continuum emission, (element={}, ionisation={})." @@ -152,7 +152,7 @@ cdef class TotalRadiatedPower(PlasmaModel): self._hydrogen_species = [] for hyd_isotope in (hydrogen, deuterium, tritium): try: - hyd_species = self._plasma.composition.get(hyd_isotope, 0) + hyd_species = self._plasma.get_composition().get(hyd_isotope, 0) except ValueError: pass else: From 8dc0cf25a59a4b0b9e74161994df25872aaf0af7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 10:07:22 +0200 Subject: [PATCH 121/134] Fix copy-paste naming error in TestTotalRadiatedPower. --- cherab/core/tests/test_total_radiated_power.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/tests/test_total_radiated_power.py b/cherab/core/tests/test_total_radiated_power.py index 2744e803..aa6e4e68 100644 --- a/cherab/core/tests/test_total_radiated_power.py +++ b/cherab/core/tests/test_total_radiated_power.py @@ -104,7 +104,7 @@ def setUp(self): self.plasma.parent = self.world self.plasma.atomic_data = self.atomic_data - def test_beam_density(self): + def test_total_radiated_power(self): self.plasma.models = [TotalRadiatedPower(nitrogen, 6)] From 054e270fd6e61f2f00783bcc05e441b4e3f86092 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 10:29:34 +0200 Subject: [PATCH 122/134] Replace deprecated scipy.integrate.quadrature with GaussianQuadrature in test_lineshapes.py --- cherab/core/tests/test_lineshapes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index b6827256..c245e0c3 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -20,7 +20,6 @@ import numpy as np 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 @@ -372,13 +371,14 @@ def stark_lineshape_sigma_minus(x): weight_poly_coeff = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04] lorentz_weight = np.exp(np.poly1d(weight_poly_coeff[::-1])(np.log(fwhm_lorentz / fwhm_full))) + integrator_pi = GaussianQuadrature(stark_lineshape_pi, relative_tolerance=integrator.relative_tolerance) + integrator_sigma_plus = GaussianQuadrature(stark_lineshape_sigma_plus, relative_tolerance=integrator.relative_tolerance) + integrator_sigma_minus = GaussianQuadrature(stark_lineshape_sigma_minus, relative_tolerance=integrator.relative_tolerance) + for i in range(bins): - lorentz_bin = 0.5 * sin_sqr * quadrature(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], - rtol=integrator.relative_tolerance)[0] / delta - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quadrature(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], - rtol=integrator.relative_tolerance)[0] / delta - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quadrature(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], - rtol=integrator.relative_tolerance)[0] / delta + lorentz_bin = 0.5 * sin_sqr * integrator_pi(wavelengths[i], wavelengths[i + 1]) / delta + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * integrator_sigma_plus(wavelengths[i], wavelengths[i + 1]) / delta + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * integrator_sigma_minus(wavelengths[i], wavelengths[i + 1]) / delta ref_value = lorentz_bin * lorentz_weight + gaussian[i] * (1. - lorentz_weight) self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-9, msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) From a501470188004cd16f86026d868df0001f3c0acf Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 10:51:42 +0200 Subject: [PATCH 123/134] Fix uninitialised variable warning in GaussianQuadrature. --- cherab/core/math/integrators/integrators1d.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/cherab/core/math/integrators/integrators1d.pyx b/cherab/core/math/integrators/integrators1d.pyx index 54ec4059..7ff9be74 100644 --- a/cherab/core/math/integrators/integrators1d.pyx +++ b/cherab/core/math/integrators/integrators1d.pyx @@ -201,6 +201,7 @@ cdef class GaussianQuadrature(Integrator1D): double newval, oldval, error, x, c, d oldval = INFINITY + newval = 0 ibegin = 0 c = 0.5 * (a + b) d = 0.5 * (b - a) From b7d197177d672bb312941b394621bfdb49c288e5 Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Fri, 2 Aug 2024 11:41:54 +0100 Subject: [PATCH 124/134] Bump version to 1.5.0rc1 --- cherab/core/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/VERSION b/cherab/core/VERSION index c831dd27..6994d9ef 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.5.0.dev1 +1.5.0rc1 From 08a14b7214c2156412ccec2f62eb034d6bd66511 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 13:02:25 +0200 Subject: [PATCH 125/134] Replace GaussianQuadrature with scipy.integrate.quad in test_lineshapes.py --- cherab/core/tests/test_lineshapes.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index c245e0c3..4e51e38a 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -20,6 +20,7 @@ import numpy as np from scipy.special import erf, hyp2f1 +from scipy.integrate import quad from raysect.core import Point3D, Vector3D from raysect.core.math.function.float import Arg1D, Constant1D @@ -295,7 +296,7 @@ def test_stark_broadened_line(self): line = Line(deuterium, 0, (6, 2)) # D-delta line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - integrator = GaussianQuadrature(relative_tolerance=1.e-5) + integrator = GaussianQuadrature(relative_tolerance=1.e-8) stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma, self.atomic_data, integrator=integrator) # spectrum parameters @@ -371,16 +372,12 @@ def stark_lineshape_sigma_minus(x): weight_poly_coeff = [5.14820e-04, 1.38821e+00, -9.60424e-02, -3.83995e-02, -7.40042e-03, -5.47626e-04] lorentz_weight = np.exp(np.poly1d(weight_poly_coeff[::-1])(np.log(fwhm_lorentz / fwhm_full))) - integrator_pi = GaussianQuadrature(stark_lineshape_pi, relative_tolerance=integrator.relative_tolerance) - integrator_sigma_plus = GaussianQuadrature(stark_lineshape_sigma_plus, relative_tolerance=integrator.relative_tolerance) - integrator_sigma_minus = GaussianQuadrature(stark_lineshape_sigma_minus, relative_tolerance=integrator.relative_tolerance) - for i in range(bins): - lorentz_bin = 0.5 * sin_sqr * integrator_pi(wavelengths[i], wavelengths[i + 1]) / delta - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * integrator_sigma_plus(wavelengths[i], wavelengths[i + 1]) / delta - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * integrator_sigma_minus(wavelengths[i], wavelengths[i + 1]) / delta - ref_value = lorentz_bin * lorentz_weight + gaussian[i] * (1. - lorentz_weight) - self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-9, + lorentz_bin = 0.5 * sin_sqr * quad(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] + ref_value = lorentz_bin / delta * lorentz_weight + gaussian[i] * (1. - lorentz_weight) + self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-8, msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_beam_emission_multiplet(self): From 95197e51fd8eb7a7e9adb9c3fd8ecf0d70e01efe Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 14:52:35 +0200 Subject: [PATCH 126/134] Define relative_tolerance in test_stark_broadened_line() and compare test/true ratio to 1 instead of comparing absolute values. --- cherab/core/tests/test_lineshapes.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index 4e51e38a..4f2ef142 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -296,7 +296,8 @@ def test_stark_broadened_line(self): line = Line(deuterium, 0, (6, 2)) # D-delta line target_species = self.plasma.composition.get(line.element, line.charge) wavelength = 656.104 - integrator = GaussianQuadrature(relative_tolerance=1.e-8) + relative_tolerance = 1.e-8 + integrator = GaussianQuadrature(relative_tolerance=relative_tolerance) stark_line = StarkBroadenedLine(line, wavelength, target_species, self.plasma, self.atomic_data, integrator=integrator) # spectrum parameters @@ -373,12 +374,17 @@ def stark_lineshape_sigma_minus(x): lorentz_weight = np.exp(np.poly1d(weight_poly_coeff[::-1])(np.log(fwhm_lorentz / fwhm_full))) for i in range(bins): - lorentz_bin = 0.5 * sin_sqr * quad(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], epsrel=1.e-8)[0] + lorentz_bin = 0.5 * sin_sqr * quad(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] ref_value = lorentz_bin / delta * lorentz_weight + gaussian[i] * (1. - lorentz_weight) - self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=1e-8, - msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + if ref_value: + print(ref_value) + self.assertAlmostEqual(spectrum.samples[i] / ref_value, 1., delta=relative_tolerance, + msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) + else: + self.assertAlmostEqual(ref_value, spectrum.samples[i], delta=relative_tolerance, + msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) def test_beam_emission_multiplet(self): # Test MSE line shape From 1e08f1e2ab7254b60b7fb85a9aa636ed4902cf0a Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 2 Aug 2024 15:20:08 +0200 Subject: [PATCH 127/134] Remove print() in test_lineshapes.py. --- cherab/core/tests/test_lineshapes.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cherab/core/tests/test_lineshapes.py b/cherab/core/tests/test_lineshapes.py index 4f2ef142..e2122d41 100644 --- a/cherab/core/tests/test_lineshapes.py +++ b/cherab/core/tests/test_lineshapes.py @@ -374,12 +374,14 @@ def stark_lineshape_sigma_minus(x): lorentz_weight = np.exp(np.poly1d(weight_poly_coeff[::-1])(np.log(fwhm_lorentz / fwhm_full))) for i in range(bins): - lorentz_bin = 0.5 * sin_sqr * quad(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] - lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], epsrel=relative_tolerance)[0] + lorentz_bin = 0.5 * sin_sqr * quad(stark_lineshape_pi, wavelengths[i], wavelengths[i + 1], + epsrel=relative_tolerance)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_plus, wavelengths[i], wavelengths[i + 1], + epsrel=relative_tolerance)[0] + lorentz_bin += (0.25 * sin_sqr + 0.5 * cos_sqr) * quad(stark_lineshape_sigma_minus, wavelengths[i], wavelengths[i + 1], + epsrel=relative_tolerance)[0] ref_value = lorentz_bin / delta * lorentz_weight + gaussian[i] * (1. - lorentz_weight) if ref_value: - print(ref_value) self.assertAlmostEqual(spectrum.samples[i] / ref_value, 1., delta=relative_tolerance, msg='StarkBroadenedLine.add_line() method gives a wrong value at {} nm.'.format(wavelengths[i])) else: From d78f7f178eaf3c3294c902078feab92f01fe14d0 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 5 Aug 2024 01:15:02 +0200 Subject: [PATCH 128/134] Add r prefix to the strings used in re.match(). --- cherab/openadas/parse/adf11.py | 28 ++++++++++++++-------------- cherab/openadas/parse/adf15.py | 22 +++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cherab/openadas/parse/adf11.py b/cherab/openadas/parse/adf11.py index f97ab132..a91a1a85 100644 --- a/cherab/openadas/parse/adf11.py +++ b/cherab/openadas/parse/adf11.py @@ -38,7 +38,7 @@ def parse_adf11(element, adf_file_path): with open(adf_file_path, "r") as source_file: lines = source_file.readlines() # read file contents by lines - tmp = re.split("\s{2,}", lines[0].strip()) # split into relevant variables + tmp = re.split(r"\s{2,}", lines[0].strip()) # split into relevant variables # exctract variables z_nuclear = int(tmp[0]) n_densities = int(tmp[1]) @@ -53,15 +53,15 @@ def parse_adf11(element, adf_file_path): "specified ADF11 file, '{}'.".format(element.name, element_name)) # check if it is a resolved file - if re.match("\s*[0-9]+", lines[3]): # is it unresolved? + if re.match(r"\s*[0-9]+", lines[3]): # is it unresolved? startsearch = 2 else: startsearch = 4 # skip vectors with info about resolved states # get temperature and density vectors for i in range(startsearch, len(lines)): - if re.match("^\s*C{0}-{2,}", lines[i]): - tmp = re.sub("\n*\s+", "\t", + if re.match(r"^\s*C{0}-{2,}", lines[i]): + tmp = re.sub(r"\n*\s+", "\t", "".join(lines[startsearch:i]).strip()) # replace unwanted chars tmp = np.fromstring(tmp, sep="\t", dtype=float) # put into nunpy array densities = tmp[:n_densities] # read density values @@ -77,13 +77,13 @@ def parse_adf11(element, adf_file_path): blockrates_stop = None for i in range(startsearch, len(lines)): - if re.match("^\s*C*-{2,}", lines[i]): # is it a rates block header? + if re.match(r"^\s*C*-{2,}", lines[i]): # is it a rates block header? # is it a first data block found? if not blockrates_start is None: blockrates_stop = i # end of the requested block - rates_table = re.sub("\n*\s+", "\t", + rates_table = re.sub(r"\n*\s+", "\t", "".join(lines[ blockrates_start:blockrates_stop]).strip()) # replace unwanted chars rates_table = np.fromstring(rates_table, sep="\t", @@ -95,18 +95,18 @@ def parse_adf11(element, adf_file_path): rates[element][ion_charge]['rates'] = np.swapaxes(rates_table, 0, 1) # if end of data block beak the loop or reassign start of data block for next stage - if re.match("^\s*C{1}-{2,}", lines[i]) or re.match("^\s*C{0,1}-{2,}", lines[i]) and \ - re.match("^\s*C\n", lines[i + 1]): + if re.match(r"^\s*C{1}-{2,}", lines[i]) or re.match(r"^\s*C{0,1}-{2,}", lines[i]) and \ + re.match(r"^\s*C\n", lines[i + 1]): break - z1_pos = re.search("Z1\s*=*\s*[0-9]+\s*", lines[i]).group() # get Z1 part - ion_charge = int(re.sub("Z1[\s*=]", "", z1_pos)) # remove Z1 to avoid getting 1 later - if not re.search("IGRD\s*=*\s*[0-9]+\s*", lines[i]) is None: # get the IGRD part - igrd_pos = re.search("IGRD\s*=*\s*[0-9]+\s*", lines[i]).group() # get the IGRD part + z1_pos = re.search(r"Z1\s*=*\s*[0-9]+\s*", lines[i]).group() # get Z1 part + ion_charge = int(re.sub(r"Z1[\s*=]", "", z1_pos)) # remove Z1 to avoid getting 1 later + if not re.search(r"IGRD\s*=*\s*[0-9]+\s*", lines[i]) is None: # get the IGRD part + igrd_pos = re.search(r"IGRD\s*=*\s*[0-9]+\s*", lines[i]).group() # get the IGRD part else: igrd_pos = "No spec" - if not re.search("IPRT\s*=*\s*[0-9]+\s*", lines[i]) is None: - iptr_pos = re.search("IPRT\s*=*\s*[0-9]+\s*", lines[i]).group() # get the IPRT part + if not re.search(r"IPRT\s*=*\s*[0-9]+\s*", lines[i]) is None: + iptr_pos = re.search(r"IPRT\s*=*\s*[0-9]+\s*", lines[i]).group() # get the IPRT part else: iptr_pos = "No spec" blockrates_start = i + 1 # if block start not known, check if we are at the right position diff --git a/cherab/openadas/parse/adf15.py b/cherab/openadas/parse/adf15.py index 269edd0a..12aa01a9 100644 --- a/cherab/openadas/parse/adf15.py +++ b/cherab/openadas/parse/adf15.py @@ -60,7 +60,7 @@ def parse_adf15(element, charge, adf_file_path, header_format=None): # for check header line header = file.readline() - if not re.match('^\s*(\d*) {4}/(.*)/?\s*$', header): + if not re.match(r'^\s*(\d*) {4}/(.*)/?\s*$', header): raise ValueError('The specified path does not point to a valid ADF15 file.') # scrape transition information and wavelength @@ -104,14 +104,14 @@ def _scrape_metadata_hydrogen(file, element, charge): file.seek(0) lines = file.readlines() - pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' + pec_index_header_match = r'^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' while not re.match(pec_index_header_match, lines[0], re.IGNORECASE): lines.pop(0) index_lines = lines for i in range(len(index_lines)): - pec_hydrogen_transition_match = '^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*N=\s*([0-9]*) - N=\s*([0-9]*)\s*([A-Z]*)' + pec_hydrogen_transition_match = r'^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*N=\s*([0-9]*) - N=\s*([0-9]*)\s*([A-Z]*)' match = re.match(pec_hydrogen_transition_match, index_lines[i], re.IGNORECASE) if not match: continue @@ -147,14 +147,14 @@ def _scrape_metadata_hydrogen_like(file, element, charge): file.seek(0) lines = file.readlines() - pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' + pec_index_header_match = r'^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' while not re.match(pec_index_header_match, lines[0], re.IGNORECASE): lines.pop(0) index_lines = lines for i in range(len(index_lines)): - pec_full_transition_match = '^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)' + pec_full_transition_match = r'^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)' match = re.match(pec_full_transition_match, index_lines[i], re.IGNORECASE) if not match: continue @@ -193,10 +193,10 @@ def _scrape_metadata_full(file, element, charge): configuration_lines = [] configuration_dict = {} - configuration_header_match = '^C\s*Configuration\s*\(2S\+1\)L\(w-1/2\)\s*Energy \(cm\*\*-1\)$' + configuration_header_match = r'^C\s*Configuration\s*\(2S\+1\)L\(w-1/2\)\s*Energy \(cm\*\*-1\)$' while not re.match(configuration_header_match, lines[0], re.IGNORECASE): lines.pop(0) - pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' + pec_index_header_match = r'^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE' while not re.match(pec_index_header_match, lines[0], re.IGNORECASE): configuration_lines.append(lines[0]) lines.pop(0) @@ -204,7 +204,7 @@ def _scrape_metadata_full(file, element, charge): for i in range(len(configuration_lines)): - configuration_string_match = "^C\s*([0-9]*)\s*((?:[0-9][SPDFG][0-9]\s)*)\s*\(([0-9]*\.?[0-9]*)\)([0-9]*)\(\s*([0-9]*\.?[0-9]*)\)" + configuration_string_match = r"^C\s*([0-9]*)\s*((?:[0-9][SPDFG][0-9]\s)*)\s*\(([0-9]*\.?[0-9]*)\)([0-9]*)\(\s*([0-9]*\.?[0-9]*)\)" match = re.match(configuration_string_match, configuration_lines[i], re.IGNORECASE) if not match: continue @@ -220,7 +220,7 @@ def _scrape_metadata_full(file, element, charge): for i in range(len(index_lines)): - pec_full_transition_match = '^C\s*([0-9]*)\.?\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)' + pec_full_transition_match = r'^C\s*([0-9]*)\.?\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)' match = re.match(pec_full_transition_match, index_lines[i], re.IGNORECASE) if not match: continue @@ -255,8 +255,8 @@ def _extract_rate(file, block_num): # search from start of file file.seek(0) - wavelength_match = "^\s*[0-9]*\.[0-9]* ?a? +.*$" - block_id_match = "^\s*[0-9]*\.[0-9]* ?a?\s*([0-9]*)\s*([0-9]*).*/type *= *([a-zA-Z]*).*/isel *= * ([0-9]*)$" + wavelength_match = r"^\s*[0-9]*\.[0-9]* ?a? +.*$" + block_id_match = r"^\s*[0-9]*\.[0-9]* ?a?\s*([0-9]*)\s*([0-9]*).*/type *= *([a-zA-Z]*).*/isel *= * ([0-9]*)$" for block in _group_by_block(file, wavelength_match): match = re.match(block_id_match, block[0], re.IGNORECASE) From 9b1e92fcdf13dd0efa09258d5270ef1ee1e1bfa3 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 6 Aug 2024 14:15:33 +0200 Subject: [PATCH 129/134] Add r prefix to docstrings with escape characters to fix SyntaxWarnings in Python 3.12 --- cherab/core/model/laser/model.pyx | 2 +- cherab/core/plasma/node.pyx | 4 ++-- cherab/generomak/plasma/plasma.py | 2 +- cherab/tools/emitters/radiation_function.pyx | 2 +- cherab/tools/equilibrium/efit.pyx | 6 +++--- cherab/tools/inversions/lstsq.py | 2 +- cherab/tools/inversions/sart.pyx | 4 ++-- cherab/tools/raytransfer/emitters.pyx | 4 ++-- cherab/tools/raytransfer/raytransfer.py | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cherab/core/model/laser/model.pyx b/cherab/core/model/laser/model.pyx index dbab8803..dfd10712 100644 --- a/cherab/core/model/laser/model.pyx +++ b/cherab/core/model/laser/model.pyx @@ -38,7 +38,7 @@ cdef class SeldenMatobaThomsonSpectrum(LaserModel): 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}, + 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, diff --git a/cherab/core/plasma/node.pyx b/cherab/core/plasma/node.pyx index 1122aa54..08d31069 100644 --- a/cherab/core/plasma/node.pyx +++ b/cherab/core/plasma/node.pyx @@ -394,7 +394,7 @@ cdef class Plasma(Node): @cython.cdivision(True) cpdef double z_effective(self, double x, double y, double z) except -1: - """ + r""" Calculates the effective Z of the plasma. .. math:: @@ -435,7 +435,7 @@ cdef class Plasma(Node): @cython.boundscheck(False) @cython.wraparound(False) cpdef double ion_density(self, double x, double y, double z): - """ + r""" Calculates the total ion density of the plasma. .. math:: diff --git a/cherab/generomak/plasma/plasma.py b/cherab/generomak/plasma/plasma.py index 45c5ed3e..e8799aca 100644 --- a/cherab/generomak/plasma/plasma.py +++ b/cherab/generomak/plasma/plasma.py @@ -305,7 +305,7 @@ def get_double_parabola(v_min, v_max, convexity, concavity, xmin=0, xmax=1): def get_exponential_growth(initial_value, growth_rate, initial_position=1): - """ + r""" returns exponentially growing Function1D The returned Function1D is of the form: diff --git a/cherab/tools/emitters/radiation_function.pyx b/cherab/tools/emitters/radiation_function.pyx index a9b0800d..a4a89f63 100644 --- a/cherab/tools/emitters/radiation_function.pyx +++ b/cherab/tools/emitters/radiation_function.pyx @@ -24,7 +24,7 @@ cimport cython cdef class RadiationFunction(InhomogeneousVolumeEmitter): - """ + r""" A general purpose radiation material. Radiates power over 4 pi according to the supplied 3D radiation diff --git a/cherab/tools/equilibrium/efit.pyx b/cherab/tools/equilibrium/efit.pyx index cb23496a..5e4366a3 100644 --- a/cherab/tools/equilibrium/efit.pyx +++ b/cherab/tools/equilibrium/efit.pyx @@ -36,7 +36,7 @@ from cherab.core.math cimport IsoMapper2D, AxisymmetricMapper, VectorAxisymmetri from cherab.core.math cimport ClampOutput2D cdef class EFITEquilibrium: - """ + r""" An object representing an EFIT equilibrium time-slice. EFIT is a code commonly used throughout the Fusion research community @@ -278,7 +278,7 @@ cdef class EFITEquilibrium: return AxisymmetricMapper(self.map2d(profile, value_outside_lcfs)) def map_vector2d(self, object toroidal, object poloidal, object normal, Vector3D value_outside_lcfs=None): - """ + r""" Maps velocity components in flux coordinates onto flux surfaces in the r-z plane. It is often convenient to express the plasma velocity components in flux coordinates, @@ -344,7 +344,7 @@ cdef class EFITEquilibrium: return VectorBlend2D(value_outside_lcfs, v, self.inside_lcfs) def map_vector3d(self, object toroidal, object poloidal, object normal, Vector3D value_outside_lcfs=None): - """ + r""" Maps velocity components in flux coordinates onto flux surfaces in 3D space. It is often convenient to express the plasma velocity components in flux coordinates, diff --git a/cherab/tools/inversions/lstsq.py b/cherab/tools/inversions/lstsq.py index 204d1090..e352c68e 100644 --- a/cherab/tools/inversions/lstsq.py +++ b/cherab/tools/inversions/lstsq.py @@ -21,7 +21,7 @@ def invert_regularised_lstsq(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None): - """ + r""" Solves :math:`\mathbf{b} = \mathbf{W} \mathbf{x}` for the vector :math:`\mathbf{x}`, using Tikhonov regulariastion. diff --git a/cherab/tools/inversions/sart.pyx b/cherab/tools/inversions/sart.pyx index 40c19bfd..12d28769 100644 --- a/cherab/tools/inversions/sart.pyx +++ b/cherab/tools/inversions/sart.pyx @@ -25,7 +25,7 @@ cimport cython @cython.boundscheck(False) cpdef invert_sart(geometry_matrix, measurement_vector, object initial_guess=None, int max_iterations=250, double relaxation=1.0, double conv_tol=1.0E-4): - """ + r""" Performs a SART inversion on the specified measurement vector. This function implements the Simultaneous Algebraic Reconstruction Technique (SART), as published in @@ -161,7 +161,7 @@ cpdef invert_sart(geometry_matrix, measurement_vector, object initial_guess=None cpdef invert_constrained_sart(geometry_matrix, laplacian_matrix, measurement_vector, object initial_guess=None, int max_iterations=250, double relaxation=1.0, double beta_laplace=0.01, double conv_tol=1.0E-4): - """ + r""" Performs a constrained SART inversion on the specified measurement vector. diff --git a/cherab/tools/raytransfer/emitters.pyx b/cherab/tools/raytransfer/emitters.pyx index 632c4a52..c0883baa 100644 --- a/cherab/tools/raytransfer/emitters.pyx +++ b/cherab/tools/raytransfer/emitters.pyx @@ -71,7 +71,7 @@ cdef class RayTransferIntegrator(VolumeIntegrator): cdef class CylindricalRayTransferIntegrator(RayTransferIntegrator): - """ + r""" Calculates the distances traveled by the ray through the voxels defined on a regular grid in cylindrical coordinate system: :math:`(R, \phi, Z)`. This integrator is used with the `CylindricalRayTransferEmitter` material class to calculate ray transfer matrices @@ -338,7 +338,7 @@ cdef class RayTransferEmitter(InhomogeneousVolumeEmitter): cdef class CylindricalRayTransferEmitter(RayTransferEmitter): - """ + r""" A unit emitter defined on a regular 3D :math:`(R, \phi, Z)` grid, which can be used to calculate ray transfer matrices (geometry matrices) for a single value of wavelength. diff --git a/cherab/tools/raytransfer/raytransfer.py b/cherab/tools/raytransfer/raytransfer.py index edea5ebc..67ba1b00 100644 --- a/cherab/tools/raytransfer/raytransfer.py +++ b/cherab/tools/raytransfer/raytransfer.py @@ -127,7 +127,7 @@ def invert_voxel_map(self): class RayTransferCylinder(RayTransferObject): - """ + r""" Ray transfer object for cylindrical emitter defined on a regular 3D :math:`(R, \phi, Z)` grid. This emitter is periodic in :math:`\phi` direction. The base of the cylinder is located at `Z = 0` plane. Use `transform` From de28d00e456f9b40bef2a4383755b4f878ec2f29 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 6 Aug 2024 15:07:08 +0200 Subject: [PATCH 130/134] Add r prefix to the docstring of the SeldenMatobaThomsonSpectrum model --- cherab/core/model/laser/model.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cherab/core/model/laser/model.pyx b/cherab/core/model/laser/model.pyx index dfd10712..5c78a055 100644 --- a/cherab/core/model/laser/model.pyx +++ b/cherab/core/model/laser/model.pyx @@ -30,7 +30,7 @@ from cherab.core.utility.constants cimport SPEED_OF_LIGHT, ELECTRON_CLASSICAL_RA cdef class SeldenMatobaThomsonSpectrum(LaserModel): - """ + r""" 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 @@ -38,7 +38,7 @@ cdef class SeldenMatobaThomsonSpectrum(LaserModel): 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}, + 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, From 970786636a4e936a84484cf7e5e69b8881ae31f3 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 8 Aug 2024 13:08:12 +0200 Subject: [PATCH 131/134] Change Raysect version to 0.8.1.* in dependencies. --- .github/workflows/ci.yml | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb7de4f2..b32bde9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Install Python dependencies run: python -m pip install --prefer-binary cython~=3.0 ${{ matrix.numpy-version }} scipy matplotlib "pyopencl[pocl]>=2022.2.4" - name: Install Raysect from pypi - run: pip install raysect==0.8.1 + run: pip install raysect==0.8.1.* - name: Build cherab run: dev/build.sh - name: Run tests diff --git a/pyproject.toml b/pyproject.toml index ea724622..4849f0b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython~=3.0", "raysect==0.8.1"] +requires = ["setuptools>=62.3", "oldest-supported-numpy", "cython~=3.0", "raysect==0.8.1.*"] build-backend="setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index e070e4ce..9a13464d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ cython~=3.0 numpy>=1.14,<2.0 scipy matplotlib -raysect==0.8.1 +raysect==0.8.1.* diff --git a/setup.py b/setup.py index baab310b..f11dd08f 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ "numpy>=1.14,<2.0", "scipy", "matplotlib", - "raysect==0.8.1", + "raysect==0.8.1.*", ], extras_require={ # Running ./dev/build_docs.sh runs setup.py, which requires cython. From 44e83c22c6f34eba9d01f0d8899983459e3cae0b Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 15 Aug 2024 15:55:29 +0100 Subject: [PATCH 132/134] Fix kwargs syntax in invert_regularised_nnls docstring Double asterisk is interpreted as bold font in RST, so needs to be escaped to be interpreted as a literal unpack operator in the argument name by Sphinx. --- cherab/tools/inversions/nnls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/tools/inversions/nnls.py b/cherab/tools/inversions/nnls.py index 823bb7c2..34779f71 100644 --- a/cherab/tools/inversions/nnls.py +++ b/cherab/tools/inversions/nnls.py @@ -39,7 +39,7 @@ def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None the regularisation strength of the tikhonov matrix. :param np.ndarray tikhonov_matrix: The tikhonov regularisation matrix operator, an array with shape :math:`(N_s, N_s)`. If None, the identity matrix is used. - :param **kwargs: Keyword arguments passed to scipy.optimize.nnls. + :param \**kwargs: Keyword arguments passed to scipy.optimize.nnls. :return: (x, norm), the solution vector and the residual norm. .. code-block:: pycon From 5ef847c486b01ebdb4db847f5fd840185757e28a Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Thu, 15 Aug 2024 16:55:23 +0100 Subject: [PATCH 133/134] Bump version for 1.5.0rc2 --- cherab/core/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/core/VERSION b/cherab/core/VERSION index 6994d9ef..ba33a7c1 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.5.0rc1 +1.5.0rc2 From a84427079bcac07c88a26ee905cf5da4ec514f8b Mon Sep 17 00:00:00 2001 From: Jack Lovell Date: Tue, 27 Aug 2024 10:01:17 +0100 Subject: [PATCH 134/134] Release 1.5.0 --- CHANGELOG.md | 2 +- cherab/core/VERSION | 2 +- docs/source/conf.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f5bcca4..5fe4cc06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Project Changelog ================= -Release 1.5.0 (TBD) +Release 1.5.0 (27 Aug 2024) ------------------- API changes: diff --git a/cherab/core/VERSION b/cherab/core/VERSION index ba33a7c1..bc80560f 100644 --- a/cherab/core/VERSION +++ b/cherab/core/VERSION @@ -1 +1 @@ -1.5.0rc2 +1.5.0 diff --git a/docs/source/conf.py b/docs/source/conf.py index d17ec793..b2f22c70 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -56,16 +56,16 @@ # General information about the project. project = 'Cherab' -copyright = '2022, Cherab Team' +copyright = '2024, 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.4' +version = '1.5' # The full version, including alpha/beta/rc tags. -release = '1.4.0' +release = '1.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.

    A6jwN}YD7@+ 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 diff --git a/docs/source/demonstrations/plasmas/beam_density_xz.png b/docs/source/demonstrations/plasmas/beam_density_xz.png index 67c04faecf921c1290f488ba42ed55e6f50e9da0..73aaf0c83d17424794f4eb856381159064e80314 100644 GIT binary patch literal 31660 zcmdqJby!yI*Dbo}mQq?e1VKOn1u5}SX{5WPB$SYDR2o52Kna6x6qN2%LXlERx+Fy! z&RqV!Z=cwEpX>a6ye_Wy)dv=9-S<7`9AnHeSNL7kJ7mNR#0Y|rDad0q5Cl6AL9nI> z@!?-YzmCnqf5csH>A7k;TDy9fyI3JA=B`c-j;;>2_gLMnTprmvKD=`2>Lp=**88rm zPLCu61t0v6Z@A>>Vk7vIm@pJBLgXZ`{|G_I&CxHcT$vnO1o5F)z}(RCeD!n#iow#->)+Pj>&VN)8|7jH$76jIXS z>g%y(QW;_2cy9L^BSyI-uaF|%AanWAqxA&k9_7nkGLpM&kLHseUFrIAHODuG^C<-@ zJ3IRugPVWd@xtIw=X*MYijtCY@G>b94u7|!#}CB7p9)eO*?->{g2f6Ss|&Os&*9_T zBy&U_KGymygmA*=`Bwk`{P9LRv$lwH&p8#|i$3^q*6-j?lTC)-{O@J)-+z_{KAoN% ze$7?qkdJ%nJku`jdK5;0rb+|tK? zG)umB7+*53e#(>KOZDs5ujiaZ>M1F=g!Uj=2I8lc+qf#7jUwjA!>ESrnl81Mk*UuMf5A9V{H%_qnO!fxz+vAaxMl@ zJ6xo*r>E__$C!H7@ZVO^u+EP{Na5`tA01;763)(T24dm#mzv)^nqfK3jm4VsnDh%R zHm(_3BzN=Kkolvt0{T> zXJBt<*i`he$nfJCTU*<(hzL1(dBPu+4{oN};#k!cIXOA>sU`o8SnMt3WQZK@EDaF+{`s+^-tVygy^Y%U zJWT=tgAa{``tLJ`S40m@YTU`Fc~mjJzLMBDIA3eM9z`);?Oi?ORiJ{WdQw=(|KY(; znj1H6bnmVfl+?@F+3`Jo@&x_9qdCEVCIS}U%>8u9!`1iqzvoguoT#;RH{Bh3L{3Fb z{b1tor(?sjkr6#y0)m<1nziPIjDUyErgzqD2lKV>|Ev^%)qbLyCXqs>jxN{c(RLq) zgoMHV=<3(E1hD_77Y>u%v6SMTGG2W+1&^-fuXFHc>B&HjTFc}lOG-+L_0B|{&s9!^ zW|`A{wSq~4nZEDKg9Wj%u|b`k@=u>W&GcBG= z%X$s3Eq;BAJ_jy7{+nBoG|88(h$A8+u@cDL;E12@_XRkujtI{&N6CEt>)tm%H}?x3 z7W4M@GYTfAsCVx;3r!m)=ADy@&5xvhRz2b|@%)7+V*3sE8ROMQmWX42w{tMy`z2I8oxs^Zsp{_-n8o+{QRWP z7(~%kKG`V=&_6mE+1@5WCIe2T(!G~?pN58VT)05^;AdsqV>}YRA`{ zFgP?3-@a+6YiO_&6BDB(+G8xFZ7D-u>)D_OAnXlGeF_T|fq5Zt$7 zn58fe9~MKzp8M!Hsk62N2Qk&ceQk^v2}`@~VYhsak+EP@P)tmRltp@Me)mt+LOL}T z7S_Gyr$p#eOuvnhrh4$>1Eh7zH@Ug;qb0DpZy=LR_g-H=tFNzr@9^*Mdw&+cVh8SR zw2&trKOFnSCgHKhpDJR@KK~_!13ge-=)ISg3OV;l?Go0Rl&q4-ia1Ufxvq|=`~LZQ z=Uzwj`RSVVdg@94!y>{nXK2n}cRy=CTzn1=;Mt|6CH6~~RJWz}|NeBuLpFCCPK_U_ z-HB$RL|W0!sN2oc%ppdmM{P3BWdxj5>6KfC-=C@H#l@u+R+sPOV7MWo!hV?VFuA_Cs;Y{e zlN0kUC+DKfMfBIo5pKeRMkXiIsq5%uS9Nr-1Q?0f4=E=kB^ew`(*KeVIz^cK_j9u+T0+TbVQJCFv9z|15Uk&mJ35>^rTO@9oCMFX#^5{K6&rtZ`Hk&aLd=P`!2I`-F=k; z65|`2zVv&Lz0i%j*BVBN9jI6DTeU-LxO0$O0s&+H^-ZwnQr3&shSOumO^BaB*=jOup?drlzC2aPQu|)|Za;yl?=y;d!`^j*rokmN`27 z$K$j+@gn+L?m=2XxA5O@oo)@6OS0-&FWx+~=<8dL<+y{<(@S3LfBi(S$|Y;0^1;hw zA@j%2t@NRY^~~=+%jGsl^vLKY3spX5X0nz!{gUgcKi%t)!9`-z(u#N|KRRY0W|a-G zCMC34Sy}gBAK{?ENl07>A!m8$-V34k6>>k8EYme7S>6WE%{Lp{+tl>*^kVB5;ZTnS zoF3DxSMcF9j*S^%$<`jO)ue7cevC~?Ma9molKL)TXKmaNp_ugDer7%C{`*Jx$?@UE zm@8MVpg}xY@0WTkeX?hPCF?L+ev21}wYj;uHB0G*$RYge%$tYf)zibK0ovWJ5^48` zi_=DT-=V?rXrVWC#uV}rB?ALrv1!A@oe4qHdVWaXZIDe1c~1{kO7IXBWN}gI76uar z2c6gI)~#ExY_TaRc?p9Bx@u@GuseQQdQ?4BWH>y%Vs}(EQfmH01L9Wv*ZR6(wpxbC zl3P^Agxj!js@Y$F8SJdAvQbRp8k#)FJt)&~72Ro@-A1Az(5DGX|n%&;J zYXVRsdX~-AkdbT^#@K`eOPYS^jYb@HK0d`o*J{P}K?CHP_n-Sc{*DdDl?W>_3CTsN zjEeP8DgXUB+OKbJk;En^U$|si?=b8JDJzFJ0H6fK<)E%9uT`c0?&t1qjIC{+=igDA zTN)a1^Wr`M{)Z4`YoBJ$aCu&3xMCdzAue{9#+~0af@k!s(5Q+EQb-Hb$&LMm42!*u zU!I$7w88-=zE&B1(ubsE3?hx0H=f*uU~_hHda;y zf*l`Wy6&$0=z&7q^Yrv$$-le1cOdvE&YcT}!jV<0{`NK@IeACQ6`TCUnyG5^;&Gh6 zVgiod&ikCDrw>PKqT7`_w{O3JJxMK(K7ZYAZS#Gf^i8k;H3?!$zQK=&+V(m>&2sWX*;#^qIH?FWd2d6dKduLg{ zZWj*XSiRp*2PT8vwQ)*DM*F{hF0<(ee}6|pg1Q*P!Os50pQhUVcf`4iIw-;!sqqk0 zR8lG$8e|4&c_T+PO~lv<5-(KCKAF>6;|0&tG&8V6=Jb4=ek3B0^i{XagsjMW(pcdU=>#O6q)YaPT$f>8F|t{!pJp zV<4RnBJQUr`3)@Ibtalu(^PFLPocs(ogVMb0D{{C$nZ7u22M?}#liN$cTF{{z_wxe z-x;SzJl=m6gWx%v04YGK)2nvP8$Dz+J6zaBe{4EZa07DZTUco8k`^dtWghD$HESK` z-Pb3c-FSlkTK~QEbIz_``dd(|+5sH*zrIPZcX(*X6#1a&@MLduX03`Se~1pz;ec9v zOHq+nLqkKztP$ZisigzJmn>pSRcus+jqJ_6Fm;;iK4;~S>3tDEa4-oif;1feW%T~L z8tz!XkAKDbi>#{Z8MvuXHR*j^#A#(npr>L5fYH>HnTo!dS?1;VuOE)!bM1}uPBEtb z`*?DMKxLBsV*aj;7x>0RFzkopdUXNj5VC#gwY6>EZi}Ra znrW`{FwbFQswpfoQWHA^wsflf86$eYFV^iw(h6c&}q7r2ujWLdcf&*W}j8z$ld$l73;y9K2};e zs^_1EgxGaoGN}!NxKKfe5Fid4ySrDfhh9OW7inw_r|vO{{QC9|PT;7$x!}MsKj50c z{q+W}?8z9&WyYFU2f8G!tgJj&%Q~F4W)&du;eGx3)qU{my(&0gCo8A^CNkmbWzhy9vnu9t!4%OIWC= zsQ8Li6dv3E4#YGGqdnVR=>kVwCCoguS*bCAT^` zXm-nXs&(-O);oY@#1Jrh%6d5*`22w=n~4;xZv@cNpQp)FQ=+M(69R|E&V+o%-kSIR z6@xl&(dn6)vWMdmD#j|4hO2`>K_DHPL%r1rsE3czBtBljKf@FjghOE9SO4xEZb{u= z1|U06F6tI%`W^oDTq*jPV3YyKqYc0~f|v#zyJf8(!?AJyI25;ThUoR2m-M7SP=19p zLgfzxTqKt>@XMp&Fl}%q$}ue4@f+MyacabmF1kP_30L{;*OksQw?7)s1Q9T!bjJy5 zR^)g=Z~7yEgZIZil?||9L#}Rzr*it$j5l}Fv}D*YtxLjXRvs_`+078rrf_PWjUhwF z(c0SpjZoY#YCm+pdj0R}=o6r(_pxO?#x$(}nh05cq4R}UQq-*!%@zo^YierpJlS7B zYsc#7djhmt0RCMq?vOdzB)7D*ywE^x)BCC^UCKwuVMOR<$6sJ1E5junP)s61S^V{Q zO#!29)fT}1JHbJm1|HS+CFP2-w5hi3P$7M)sJ-=O)v$>V+2`Pt)8PL|U&sRk=AIg%uyStGD zk2}6wT|6W8e%G1(_sr*T4!U?UTHzj{_C*6T>w}QFh>eZC0cXl$p*M|A(o+ciM%VR; z^FZiZbJgDpxh-n}+B54(6-6;somb@Iw=5EPdLid&OhRJfYe+wUJ8Q+k|5DTIHo! zu4#ibn%sdR89W%hyxjj92he?~?Ep8h{n!4-2dOd?a5kQOob+deqTT^~+RP&@aBHX< zwv`TO3RAI3T?8--c90x^YgKx!!VZ<1HDLoyV#-m=xE*@t{03kib2!w1(LMM^$}Bgh z+vu)2u>z%tq2#!o38d$f>k<{fh!7ltGg_%@?LSWd8BF(FokZ1@5J>m-?Ok|yc(&~n zhE#NPVIY;zqD89Ob?LE`&tJ6oqt{!jKYUowAq5z1Z#G^{2(THdGN6x-YBRaH7kr^C ztB!xJ_1bpY9rt((B*61mFfE8op>Vio;DH(ex|c%6%;{ZuD08yMak3c`Fa@fB&f_fz zH59j_JYhUQ9+g76;<)5hRc|{x3+oga1jAB+#zX*no!@PkX%~oPCb|Xtf6+8FIG9aZ zni+yxy&VfHFe8IS?BN(n^vr<{bbk2)e1F!)8R2LkcPHG+y|%5DlZCJXKpR4Z z7gAQg4Xp0sP!S`I zph*;zbGee?w}2KvO9&l(O6~(3n-=mtmvv8SFqcx&6tG;7nQka6ldje6n!McaMKU#W zRMA-BR(%zQR#o_-lW@HNfcP1dZ+dfcbL$St%~oo?5BDiiQQAvVd3FTp9LGDp#Erkufl$8K*63y!?Ne) z)vq-kjy;?SfSG5Y)U*R$NFc)gn+j?ldQc$+F#x%_314OIzYS`rGzbU?(6B}IkqNJb z^GCoM(0YsBKb%Vmt(fjVzkkxAddKGcOUHX-pPWDW?q3F25(PY_s_0xzK9jf$F0uhM zcV;F=W=N72ux8TsfR?N*B7L}e1uecP3<8A=@T_?4cJK8>=3Us^8ITM?g=j9f`rHq2 zzjmwp;vOJ#VZQ^{oV6?vF0MIEArqc6WVL^P*k21ckz^3LA9#JeR`ZHN@6R(#*AO;b zgO3l3cm*$AD*o_uOiNDg@jQ9a_wQO~&z?1d98QYZV<`}_)fyAM`N1zBKnBs&-WAW& zlm1f392*UJ;Af!|BYr*m+Y5k?BVU+!6QNwhxMOy9HWO0e-h#}jtytuX{7?w9DR{Pa z_;~(zzLtF)Cu}QOxAFw+ntA7Qwrei4ghxk5B8Ni-x_yNe0l-#2xy(fvUkL}sr&4HA z_hVYwZcD%ci*qY8DvFeoi;GP{g5kv_<7UXCrGAI*{KnNG;9wxb z?!m?`B-I&|gTQm+=qL3IQE=x!19qM{u;g@dwxAE!IwMd1AXDo-X5n>>vJO-e| z#>t5r9v+@7>E%+vur9CN!GqlhdY?H^$ZoTl)>h0BC~ZX|q2E9qeh%6pD$+qjqZl3% zz}#yFYJksPF=~alE*_(xqZEGGcU1 z{mxl*L|eVT3O_nXEd3Sxz5*=J21r#?pbv@qtN|}*2LLYm{uP>J;BIL^-$^gzhVPH< z*vF7$rmE(&{HU-;u_`>C!5GigtF*u@Qp-UYfTGto*S3OoKJu?;q~Q7PZJ5J1O+)XtA=jS7T~)gSwqn@b26Of3%!BZMjnpprxm@oTOLlSvL9zt>YRRR4AEGcra3m1>(@U^Y*+7AOk(g99xnpP$Vd+sh>bt z0==j{d}oUmO?7oa+SgKnWI+b( z0gbt~ES7P%XCMN;`oR*}f6)8!vPn%bAQ*b1I`6~`N9k@G14M<0O{6K+0Jx9XgCFmB zL9t*2f>($iAiLi@4_dhO-0Rn`kMP(Ej<{B2G({sSkbXt4Tl6A9>Ce9hrAu7<^9*^u zj%OLk6GA#zq+2hpw7}R9twofPjJvg5P%;gvxpiqE#~HRwIps2*xVSjoWlKWDY-_fQ zPOR|FIqUDa>en94lU}}j8P%Zq4J%1OUTp);ROdzli;H2Np%w^iINBl!IdeXuxmoUM zaPVt5ZzI)i7a^rfBeLe^Y`y7H1*90c zvm^3V^hceTzVSV8=SIJ%%miW^RFYv(jpva zNpfffCl;D@m1y&BpH2$b)dB0BBHEK19=+0aGp)341rTVftrR{&n~E z>Cz(OWsSs>?C_(pIN5Xbl(8S!q+B6-7arE>H-asXDR#C@x!Q}%y+xRIVbVxE-IwwI za;r6g;hj=^D??T<1S!=knrYCsBFo!Q-1DzaTa?zDVYUAISE_pvtNKLqOtxP#`%mjp zAP_U_9%sIEt!WT9tX6K}9&PEx4vn5hXhj<3q|UM_{3uCe?H?+4!pw@1B3(4@Sk$rG zOkG%u-NG2HBIwN!f(8ph?FG4;H)B&%BS2gkTRwq90*Zvgb^oJJ1op-?Ww7L7ogIt8 zxOGnEG^I~ECY(FYCyUzC7*yEdA*cc_?73k!yRg6^Ec|uZnMd`NEJ(jwpKJ%Rd$p69 z+TmtaZSL2-uS|GZeW+_}ZOfm@sYK!3G|4STotGRDr{6zBdvuJ991ed-oF6JSjs&%wRaCU%n_qlf9IAN% zbTbD4@vzFHoc!MsmtTP520*7fDJ8|$Z4&axTa!9(LSo`(-~uQC`CvcXJ6*4?t_~tP zQThA#n*d!{+1Ri^MQm9cuPIp8>BL2MNPyq%G(W>ak&eBO@=`R`*$@j7Vqy`CKhTIP zWR8FSJn#tt`824*oxQ#78^2o0?1wK!-axY$P>Y0)yu3UVyaGe}TA(YKv!e3X0@}oI zEYeJ~AG2){dOX7t>0Pe4gNiUjP?GUbc@V%DK;M zZj;FW!ZH054W+h{9AQm6DZ?CW03TmIu|38PYCHR`27!M$>7sbci_QU8xe(QBlkIOG zmA*a!{NC0WghP4${4+qx7QcT~bar($0WQZv0Q0v)nJaafQ$!kp@>2quhMJWCUB{i} zL3ps+?(UTXZrmN{d0hAu*PoJS8kV2HO!eoI>(~6R1V?f=DxQ*--FVL>fTv81t-hO0Z8;AkTfAl z4z3APX&sL7$gDx;y*(~a(2H}Q6d|)f5*1jpjT=H}Wao<7ui@Wa{Jf1Y)a!iT`1tPk zLBPK>>@|I*#q(KDs6pmKKbf4qM>ExDc0G>5A?rq=QFFPVDWgoeWI@@CoqXD$(URmp z0XcI(Cwo#hzjBQv`9IBk5>`ra%=^NvB!KX7}|DQ^E@%1N7F;BkF7PClBmCqpt?#cnwmP`-~0+^j|&{g z%xZSsxN^WhAdcP?1|xl&eDrO2X5Kt%B3QG?3yv9Y#4zVjQ@J(=O9oy-bKIeESf_Kg zab*&f?90pJq2u)!j7iIX<%~zpG^NqTjnXk-NvO&wtB8U9sg)D7Tr^I`D_kfg%jl4% zV|R(RrMj=Qg2mV%cAjxj8QYef-G%YL(xf|9ya3o7I&Gt85iJ&ukvfc4)*q(_+=2v- zd(vp~u;+K%m)Md7kivwfNeLcRBdCtG=PC=s%UnPm>y^ji*Ri_7{hgu%?Zp(hxE z9uB*q@ASFeFJCZ#2ch0~5p3+k zblXUEd934hxUMI#3sieVWmo7%o(CaHUP-A1lwAjAAJ7%yT=M-)^O#r)VxS&0i({M= z8}quv4$q0>WvfrnT&Pr|KIX$ZP-r++sL4Oz9RZ&!w)6zN%T6ZYGP@QT=AewN%lYO1?x=wG;G&@8?0j+s5@O{;5a$4 z?Ek+_Sw+n8DJf{{(JC@Jx;Vm+H9z*+P)u0v{6_dCvkGVgPl3d|2jGBjTo4i+5fM@M z%>T($bZVaQsHH~%Gnm^1-HQ&FYBXr&l*%i~q_e+9DE{A>-^fEYvR;@p1F9_Z*?l+$ z_23N4WRc-jlwB-u18RW;>yj0LsqgtVBC9{G2V`L2#^c^#%U-ljKT*RClq0PD^GyjA zT0oR+FgYqd=~&=VYGHQ2WX_L;u=^U6ye_cMs=Z!3fG31 zy6Tx~XMa_H!~%+%{tE|SL&A;1I;)H`vrRWm9oxcZywmbS!zudJ69DOF(YW-FGupQ# zztcsPxu9&$?qNYIG}^429A@<93!+018mA>ZdUJ=#@HkBJrXc5wt9SVSS3scln*|{I z;MrMA@Kn4776bg)WiF}f#y=laCzr>VGXW|+K>K|b91Fmwo1g>iJljbbd5AJKXkssp zzUP5m-GsAt59C6lsHP_<$?APCLajI6&7%-#o31*K&6)2afj(pV%wieD)i+6h=&qIB z|H{5PUPB9P%KFg8%8HGf8y^T39)em#yu7?XkPu_``=kD^ltPl$pYK^Sa6d;~)jD9@ zXeW_&1~kSt2=eEtsdmm8AlARj%ChZ7Wh%H|=p{(_Jt${Emr6YQM(VRfSrL0B;YN;p zN;E9Jn9iPk3Svy;A+xkE9hB}*;F^H0b|^@QnE>x;b_MwO}0+5GJ6*pN^@IsL+Z z^XARiBOJ?f=r!Hi2c@He(l`w-aj?hq-*orF30z#f56GF<(AqkC(4L!YB$HzYbBNKvfLk0m=g7+|nL6jKe=Npi@ zP)!i{m-*7SEU5o)K0bUsWR6x>m2aFrNqp4-&&Hd*%`XhRIIYUNC=-&_h|yLer1z%P z*`_$deE9zbSb&3Mq}+<6J%%L)kAw#0)t(>{reL=}`B#Ngit!37U5ulVmBI^yQIH7xL#aYay<0w!)k6PAhNP4y4+Je~{ESIJ}cPue_1G)X|EZ z@o=OTpOUhQ3_UB-N!8wIL_heso>-sAuz&n^^YKKm0_%U%8JAayLy}pC!+Q-BAs+;3 z@XtD96RIaV4=b|H)}WzO9Lx3lbh%MAj(`0oWRf-j0xoG%SAwz>xgS%+8ah)EjyA(q zztXThBV!RkS2ySEXGHci*KX+-fF35C#cgzHEWX?CrB)s(#QpWszeJ85EF^wgcT;j3 zi>fNk$(bt53|ZVBX;;D&3xB}X8DhULCjK-mtTqBgQ4#}P=kxU&Q~gFp?#xnp|GGyj zWP^1m2@c6w>GCY2pw)S*}8rxFee!xWF5pLRY+0sSvidAjD{1?JG1{m?_%Bl7J??x zb!u~8sy{MfVKt4Ay(_Tvm27I*Y75Hm01Sy|mmB%Vy*p2PK`D?qsRR3m1TCfbGc4B8 zW?)j6ZFq&^D`xgtyUt-{ERD$6FKGQute7DZP~kRrlE-LSp$u-#MuH@{i7ECDupx(k z=#0ZK8IK>Q-#JMzmPZ)jG(=bOL83B#Eay!P8>J=F?*f74qzYPb8@J+`aDy;wY*Pk& zv?pEjhu837Ll!#u&2^)J*?hbQOQ5az?aOk-^W|Ib-&bJzxqlNa56Wus-s9AN-2eV} zP^UJEgV5`*DD!yf84yQP-w2Zg-|1*`Jsj02etW}HNHiKiL3?tb3C{pY0>28_LXHB_W&;T7P%0o7mgE%N!RXu{ zS`(e!idW_iBqX^Qbiu0KIY{wyX5o)#EMl0occHUex9fXm1OkCqw6FW`UWW$C>wwb} zuty60T!6mSKbX);tu|FLY|`?mPikSwjw<$?brUo`do&p6NDdX_TF(){`E8uRLfZk+ zkS%qq)f^o7(VRs@%{>JdVa}(g9JPFX?(X(0IIePfozxd@rVxJ_g7}=_q$qEjF>b7U zEK||gC_Co0kS4bK`G1K|9hg4?3FPGA;Q-YfxVEg0PSUs=2fJSSqPefb0TYfCdyH^2 zma7%Vw7dF3(6sVU617}FQxx^xq1Lkt7cbs4Fkk@J znfXpKNc?P4Qs<#}g7Np4Hu2lhMn&$?(a}re)apeRsV8mC8cwMTbc9se;ua*C7a8}( zO7H_ucLPpCp+sUKXIP}s$7Y3V$Q=Kn1|4Wsx)11}FqlSB90k{hMu|8`JdwwTyM|KS z7cY_s3kxS-_qc?bOu_6lonn*T1k}#BUXVS*CKdVdAyKWe^dQuB%7!akgpyKQs)YNo zwA=Do&=?62lpmw2FE~=Bfrp~R@1PbsS$K$u-S^IAY0xGCpARJLtWUCl5_JoFHEmtI zn2Mw$d38#jweQ6<^P|G_Tx4Suwgo*qQY&hg1liovGcn&OZLU5O0$&BZEL1?aAa-T zrx#{t!GQe!H&`)>$0SC(3Iqb7YjqV?s{idB@+0uSw1M9QK>#+lf=gh)F zQq64T8?e98%u*3>VIA*PAUhNc4Ed4|e|)gj)_pNGhk)P?qL#M^>eNEf8^jgC-E=dv9J$XfCQ;Dm#Yx$BQZ=q| zvd2&fskJOEZlVHT!Tjdd+-FK==4jaYx3EWv%#83Cd@_b>p0`{Qb#WTO5agCYlN-YQT3L6(!+wxumAS+xvJaMh7>=|sIQ!9MA zv?pZP>Y5v0m+|Cp zRNGYm>dj!M`C4pz9c{`i{`^=%Ey{hz%U39RiLSa^%%=`mgC4jAqz@K3zL%J?fZyx_ z_+lV)B>4R|K#92nk~riiRt^pv=-IYF|EJV-NkjS7wXa3HN?H~v+}@Q{4T)z3d*Asx z|M`K@;sU25B@Ios19*bPV#+-D{ejq_>OV9R(AKoPyu56K|8XhMB994d{a>3jjj&!L zb!$1jy);39qW`zV12r{ReWrsVr>iNhF5Tl+x#hr5oO&RRDLOy>TUXHiV zO2iNZUiVnjs+aaV5HazZI~PSKj15o^=^l8G+P>Y76U{kS=@v`xwGqEBhxX45gr~b{ zXjmp0p^D9(fjyQ;wx6&epK1js6J^yi6Zz+3rDu%*fpQ}s+G5N0l3Z7Y$bfRW?_G0w z)q&FpSGZ`}aB{Gce8uL>$wsKm1uJstJyr?V#X#s*nt|sny_lIYPkzYdSfPA}Mv6W^ z^RC{*{&-IO0E~2N3S1DC6#4kNdn!1TY9)v`uU;LVKB(d4<)ssMc}Hu9!JN@l!xy&y z70=3fpF3(qGL8QR2p#qE7>Z`FAOL7Y8iW>=2E%P0^M)EGR2D2}h&${jj#n_Wn!YLe(#DT}*?My%{LvW+z9?r{9ONLtYhl z8+E%AEpTTM0jGzPXoD9q0|gv4=kC)Yu^+X*Ddwa!TFDb}SZc4NzdqEB$G=S@B!ZKa z^Y|-$%pmW=haV%X3dN!p6oVS+Cd9q4R`J{_&F>WAnX1oJ#poI$0|Nt_$GejjV8TK> zFQ_>Rym_dn=u#1&Ms4}ZxQ)@2ZJ^h>o<(@2A3mIgeh+j;geRay=&1c9Y?W1i<(qa7 zLbL1~xGW;n?i|=|H7QaQcFt-}Ajsi2hYNv64>tGYTm>QoHbk&zq>YPXh3BRUpRA=8 z<2X3W#2W=3)$!zOq^IM`iN@&G1Oj;q0UN3Jdt%Hiv74-o?^WUKpfas&=@2N!?FI3` zTol2G$6^Kr4kaDnvJma-OS#gaB+BuoTVBWDI6*`x^|Dr>d_G@^>lOiJ)w^1}e$WB` zp>Pv2q~UPw`WSdakwDNf?b0oVh15mgnfz`MA=LV~XB&-`d9{x`_sEfnKpyjOW=X$M zYui&4(Gn!NgAsk6)x{Q~z=E(gaXyPo>&grd%eC`@4-cUQ%%(tETgF#CX=i?Mub5jv^P74pGeNAjC*6^*H$~=$tBxre_ zQdAKf26o{CH~^_l?xom+FWo;s*gk<8?DhIK#xk~Sr;o0*t9+YUA|dc=Kc^n^`SNqm zJJXDQw_DMc_H8fIHG;*_9O{zw-1b7>9q??{-sNC-NtW2naJYZ0*?m`dBIZh9xMce$ zs<3}3q!Ot={`(W1721TNO#rC~3~WAsS3y3)s&p8g?#ZW&l~Q&&B-&9nOUMbLjN57b z3fka#lLuI!3|v)BmkdXZE4sx-O>nR_Aik!cZ3UV`&eek3n4!)eLz3o%M^ABOFaKf6 z!?}@n&mr?Nqu%&E*#8dwM$5?IZA8p|C@Nkm%#P0nL|OGgsyO?ZJv!t-vV{xVm6Zx!9_$-d@Qc`qtA8NTUv%8S@0!4unC=GTJ)CZ0F^T7LnLhN?HeZpX1I{HN( zuwgwI)=8z(Y@_|6BuIiQN`LuHmtp zl$XOioU^-nsV2}b15geWHL(A^1}PWy^lSJped7Wtg+oN-dt?O%dnlcc!?U6wp%3iK z!7m72QVhyLmr%@e=fOT|f`Mo6=^idIjRF7YHxg0yH;M|ESs5q9qT;1VuLK5f$!G~Y z?U&3+xqzLJ5ovJ@9kJPvXTB-AlU@zro&>kcaA7SBVC_MB3=OC9$gJ@5xr8TA-yQ#0 zr2|Et(kl^?E6mj;cCpxj9p{tVO47gDC~Uhlpp1XROE8X8DUee{;0Z>n4XCvk9I8@? zX>?>n-%Ce_CN4g{4!yAaxVHV}iz6e=JR(JYxrEr-;ja@HRFa+#xC&6mt{5p`wP*=d zJcinWx=2vUhq~v!mYB*QjkKn|LC|?O2f1jXkbzSF9?hq`?R|Qd0v+twU^arN{ z(4q>6iq7A)2Rt6&PkjFJWh6*qFxbQlnMMvA>rRXPY^a3##NX3+K;hl43O7; zAI+}{sSoCNwyz!CmhOQ}iS&ap9bA^}KL=Uaedvd_@V;LRpflX<9wwwwEakaD)hr}d zdN2NNeMYP7^ISzj_4+Co$$hai)ogEwv$0b6^;ur#ybDr)k$3V!xjV{~QL{tO;s@6k z)>!oZCV=dVVypz*^O-qbGvE;F%YI|jY}WE$+d8$9&3UBzEZj~I&N|~i;l$EnnRaG2 zCZgnXO+Z$}W*EDb?>MdSKlgKfg*KFY%koS*!z#DprZ}ziz&y5}By?V_dJ@KJ#s0 zAT}=Uam`w_(tApEQH%1p(Ck}kqDyb@@m`Bx_!9lAn-!zo+!CG%BOPC}lxR`x3+7f& zXse?}>X#kR^CRi%>VlDk+b~%3JTY-@B)H2K(yH%#iVf-jLcMmVld>i-EdPp<{eE{y zYoC9}S;3I3sVLifnY8Du{6mstxST&&?vH^8L>_}RR$)(T;=6eZ6g4~$L{S45Ou#w) zSyTs?+VHe6R0BD0Zwb_N1w8@=u-uBGRz&dVO#{Gnt6#IEg~Idlsv~NVMQN3z&XX{s z%rAS|28YTx*Iz3V_sGU{(mwc~8#02C9iRdHujFAIS@I1#S|_N0>J9#WjygeMOu#p| z#-G{|U1MX^Y;2q!Zl9ZeTD|iei%orSW6PlNe?vndjb9KDa4W*LjE8K1Iu=pWbMGth znI1ceSOJ=$7463|$zQfaNBR4xbfkp1Q|D6~F>5o~$OeReY7vO4f3t%B@7)_9BcPV_ zC%`leO8KBPI4!z&IRlQ-)X;DO8_S3LU$N0v2Y7iz7%9DCEj@kiR(op4h*ID9AmF3; zIYaB~R?av9O`pZ40cKVk94CY%B$}09z9_#0`gsK(Y*5Xp1^DG*YVI#hMtQ-44B@d+-tO1GB znVFf21260qmaoU;QY)NKOyR)#PPe3k!c^cBsNdhe>7=uob!}}wZ<`2Ja-fG&<03qVh<$*Rx@~r zzUHX0fWegaClL7rKbR{*Ex~inFaZk)=kcpoOs;EV^e94{2sri24&0-Ymu0kdZN+6vVm zaa^IoRN;r!yn&idja!X52Ll@47&E9-Hv{D2x(yE#bTH94oGy%4pJ9FQ@i0FOgb|J8 zM3@R=Bsjz5w0dur29BY}+SpR%qw!D9H~=G?2h?PK_nVS~x1&GG@&1nwc|Vsivnx-( z5$kO>STDvp&kH`C4|ttQ3J$vTg+ZhgX2Kp`9|i&`>@+1C8bk7*mg$%dIlC!qUdMbF zjlp~Z*EnCb-$g3vn>Ck83&-!!libg1QcJ=jFgSriAr80j3}$@80SrJd52HCzX3@dd z;0~rOz{Ag=GxZA;zV{;n4q8S=g$j?moFiuA`Bow-K12&DYcwSL3Se1>XMQeuyL?ii zM@A$kh$FY&szNdNkcx?=!^RuR0puHCrx#IAx-U4qX)-)4Ni*L<8>iH=iwab`x1T=I zL0ysqHV61aYIQVi0`66JTXbSs;8IjEQ^#2FcT zMTC7HOTZ8DeF6CbIxfFJMnRiBs3RWTIdpagVsrxxR60u))KpZ@pFhu(R8>*A1@UIx zm;OK$c?Aa({v>pu23c2E*TkXt+#kY~V|g{+SlgEUA(y*2Z$)vliP+!(1PF#-14f=H z)6)Z5Fj8adC!rovnEN9K?-=-!TY(F%e?9mwf?j#xd-v}19{xp)bk~TAeO#ly{(`Pf z1#b7UQIMRMk@Sh09`y4o0>j5TTHXid&2QRG1fxD1Nl7N8AEx?IS1uBG=DdWbm4PQ{ zj<95s{>E<@Zt1xF;sY6)YckXJ9B!0xYko=#=|Nb2h*Qgd>Na$mVd4rGW>e7uj?OQE z7ZdF&UZXht{c#eEL^2m*)Y2q^!AkQ6&=zVf2FmIT!xZSm93tegX4Kl&#wILG2^<@h zcA&KjeEs*34`=?@?^gl`i`?6V1D27#ksfNq7mZ-BEl-5H%2rm~_8X^isz}#sdy2u^aapV?)Qz@J zWO@`?L)v6MTnQ40;?XjW%(#wu*a+^Cn-zLKdC=QNT~=mhtZUUPmqMS&u;*iE&umAH zH+ApHJh)nVnI5zK>0bD?$nl2mD$DqXW|hQLl-gWy;;%YRw8C^>q=efttFW;8(i=E7 z{LsanL&+9b{?K(dMcN16v?K+OlUF(TUAd@Z8POgRFbEio>Ovu3EGT<0FP3!#tOnIR zYxSI*CoP?a?c;pGHC=m!cf_k&z_v^NuD zQbr!By)@m`GWrc}-F-blC3*Ml4eMtO{Os09L~w-T|ow!R1IDU5+$0M;So zI3bBz$w7@Csjz>663W22KY}4dtn=^vRJa2g6|20DGHfI0dr!DrNamrhU;6`lq|V0! zCGBW$1bwFiDtSVK7*(`YRsW*QC&BvINg^Y`d1Z)AJ~KgG+>&ctRknr5l?19j<`vj zM@_h3$-EKAi8jX}ctRQ)hT)FI=sHeQDzNk0@tU9SjgB1fyjn7h!u3fytFfKKEm- z57>%|vKQ$4R8WWLpl~2KnKAe7vHPFw+W@ykppeKyE<-giV1N@SsOL~x(oaCnSdTY8 zH<#aMCpEQ?m(^vmG3TNH2i!mY$Ovn=2Z?|k25lfsYIQS+IfPxE^reRF+`QKl*2&j1 zduCgKwY`xXc}g+2l=h|lB;{3P*jVJhJ*+=Q`|6Neg)hp0Lm%9UbHNOhl<@Wt(XJ7$ z8B4O!JsB;mw0&F(`5r51Vy)XiYy-_mIG7=O|5PI*Qe4~|{0unKCxj;Tv(zyN5S!*{6F`g=O>?pJ8Y0j_T;lZh8~UdN&E{WR03q zoA*B&?Vmqf;hpFCD~3`&oaEVHYr z+kagvdKaIS@gU}@<$XMnN!^#Kw?*5*2il>)YWbw&j(&!e&(P~*u*&G(5@lo0Nolo} zD|akdWr9}iKRI*urzA7t4)`6N1Ui8keS`4@&_nm&|IjAcOuU-(rrB@Ugqc>d(SG6! zO5N5D`_zzc{X=x5Tap_i7RWm23{Ndp@!9e(f5DPZ-0-B`fTAt*XkHne@&X!<>UyAS zqtk6LO*R9AEa!Cy5zA zVN}dUXCPZ(au1yWKu!PX8!Heqn0y#%2#~2iIj{!-c`$g%!K{iM%e8Gp`t=8-nLIEb z@2;0>8cF}ifz59Q%&cTFuAG?X1Fq+g%g}K|&)L{Jwc2>>E`i?$?ej#{6xR4j=jJk) z<2dQNqvGZHnd}!CtlzT69-@LuvrJXE{7sFX*2KbReVPJTIjYBRnAdaYY44AHZ^~$k zd|61?tltDN-CSUgMXgTu_6%WSI8(mLV{-vB^>m!OVwKoBs}@NI!PK&0V==a7o%o>7 zS*Ca^r}Nz%c;}}65oxA4%By)AY<|ST`ME=WenGdmXuxOsQn)WwO<)%d`rR^vdeSqv z6v>s?-PjvfxF6s4al*UeMAMme$*<|k!yfmVS(JbwOhhR7FA}H9L1=W0pSv5 z+-UhkmnR5S{YPrO-fy9{rThJvSk)fu(Lf74%d9aK36{SEZp0;CGBBs>(^1qwq zC0zMO!5=#=<2BdaL~i{2aMo^8vmQ^`i^DwZ9iG?7zJOmAeeaHl{SZ224w$jX^%O9! ze29YSGTJ_iHcoyuKj<+8y+3%r1TKQA05I?}12KOa6l@`QNe?JIVGgCq#P|p&R#&O^ z2VA>d(PAEutXl?@n*JM&!m!SHi8T=Wz_q1S3F9aOWhC(fwLkETK&gTWE-VCnnGnii z(b+wi{3VBubq6G&{Biy)Q{l=JdY#=1EPXuM?>vP+?C}-_Ae?MkU764F%s5+kNE2vo zx#Lj}I=}#%tKDYE#)cEfxQ)l>*k3|^(kn5MGOG2YL`s2;pfU*39gmr;cKeK`xJDON zLTy%?cLvz1ahSH77MhQp-Ah4IOC*R+G>I2S!e8m$)WKHxzqtXu=ilHqeijIXB+PO< znkgI_>VW;Iz&bC3Oo7q|L)6<)I^=ukqVfz*MLsvu+h8*qoLV+)oxOzZ(soVOt^9ru zF+1sA*jTShye#Tp;ypu5EC&#egy#}1c%q@(!YVFK-`3Wq2SfR&2@&=L1{FWgZO1b#>Q(zqbl#K({St{4^!Y{Z(s<^Qt3nDc%l;STlwoa&Qfg}^;#tPoX@r)a zeHF-``0-@FYtFb4e5Ogbu5n!$3b&1g#R@PMsg>ZpdhsF@UMIm9bLH}71our4DAy_ zKoiqD^B-LHbmX}`3%X^vuY{5B{6sbqn=PJT_+w>X7<>*x8B!)H zIA9^1{O18dUsZwHIu6$xI#62!06WR;FIP~ZGKC_}GP&*IjrZ9l=JeM_OC*n#0yCKX zL$EMd{@w|DOKkf@i_ZiCp#|3dh$_~wN}?;hA+f%%04oz@bkzC?o$T7pc1C#H06Lrm zR>S2UJIdHPts?KaGV@y`tcbk#=w1A68rgGf3P+7>E zBs*mVg(e^*C@P20xIq|%PWQ%0ZV{$?&C|p1UO(C)xR_AplSW*y{opSbNe<{Y2v1aeNcu;2N$X;0bz826qUZkG!@mu=`uU zyT_P|$tXu}m2Xs7exc%Ez1cX~kF6IGG27ps(wxc~v&oD$B%$#JFG8b%_kW05X;&?Z zP>$Y6%hgY;yp@LC{)wiv&~9h@#pf5!f8vj)1XtKTdvlRz4rWd98^?#e>d=wQe+qiI z1$Xr1xYf zjichdf%ijuXm-U{HX>UfU_(uk{>zMUO5%rx4$No=hW|E2SsK_ohB<{2YzE)1Zn(H|#r1hI`i zJ%2e7l#`=|SiRzshmg1?3nz8nu}NG>X=$l_f6Fa~jangP5!n37(P!_e&kuE;{Ulb$ zA#xXI2*MYZ!DwSpjDd`0-@&IS%=ec;CwM=N;}v3*m`pUFPf@0hzYjl~F)Em;L(PAc zB`JV2P!q18q5|7^_TVQ}daZ2NCifVBf7baYgj?*(@yv`x=cK+@+4R;@agU>Ph)i#c z$SP0KtPcrcJfSVYN&#mUH^@zwWbmaW<>bm2HY)x_phT+Tn(!)AaIl%`n(HS!-(OR3 zW2I2ojRPFfd5hWQma11rC%NzVhNkjvW2%B|b$@MEZamKtd3c(R95k*Jx1pUH(->LV z8!h8sf7Q)Q;#}rXn>;^v^~z7{S8|Ij_cys#!x`)NK>pU^4ZP#eVUr>&5h?$nD!AIG z@BI5B^i_F5z|uV2Cx#9JDGhd`YSWscXI7hB)n(mUcus1Z&J^qgk|%K@{6F8lU61dWlf^H#-lv>&O8*{=Dou}zp79?p+|Fl zmz?wn>%gc);Uf@PJq+)!2OMYkIs z^H=qrAwFl*nHr<_*ZRMUWuL5)V679)3Oi|Y|7?$D$Mzok`oxu%gW4O7XUj-Q-g^J# zHF*ZSiUdA^j4>VAeLWl+j#ez`K|Pn;HVuanL9WwbQTk!(>~YCgd-=Fes|PqX%(W1o zTxPqLO%cYWv-7yj}S?Y@RCJ zVoui_H+Z0NgD1&&k38H&LR7{*IEXd4HP=aKVysKw0Y#Rrg|3%Yq^&mI66g7n6DBT$ z*rE47N%*Dg6CP4W>^F4gbd+PBha09D^rXf!VM z`Q*9;g41O(0{#nA&F}NgIkN}pknWhHE~m_V&MRunq5;n3(d*B$Wd!(abp7hh(~=vt zYILsPPi)iSapW7x>Ns>CbS-j>L1$tBsxBRB!86v>g*n_pP5-JsgyCvZF1b zh*wyP9eQy}8j+oyE%I>Y%$eYUb+|FA4e+C5T=ZL!6}LiYIb)bF|N%&uCRX<_%WN5OLH>*5g4@`aSFtUF3|2{$3^tXl?C|!?4Ll>WT%9HC%|B(g@@|eD zoD93ZV@$?Dt$R@9-eAe5eDQ=6AG5xks~rEPrB1G{D&OPZ5gG3!D4B;0Lf%(w8>3`i z!sn=;_VE6k%+#-C6c#f5q(IB2t{cwP}~(og)L~K&{o^uu+H3 zW0U^)@10Abw>5HiS0nf+4m`9MwGNxPPu=cEwzeV;lKb{?44oSeI$zQ;(}45Rl9z%} zMqzb0H(fPY(l6b=BA@uNYZkan{-ZqCYP*FRnce z2~$;`yt-n)W6&5IPv~i#nVTB#abpz^3(@!Rb1mD3(`sAlIb_~>dCN>~5R*kBbpYs~ zVEa#sWDDurdvjKTlu+2ETP1Zy0vFvlLzLel_z<_-XxZ@l`h(hcI1C`>hQ=v+F)a9t zhrt^A4=>E|Z5f6!SWv=N8-E_NvG~_JW%~!E(;Z&#WjfN1f8TL^SM?@GsZSP3=U{GZ zSof{PSb;s_fOf&2!7nO;x2BmsyzV?z!m#o3kCloZTH@@dDtA(3c18T*$-(hNG$*6^ zR!+4HZ?FHWzP0a<-bgK4rsrd_y{;;3lQH;#V@UnRIN3*)&&lo*=~_B2VAhb*@6A|P zSY(5cW7>p>;S2&`Hk+MVq{;D03~%Atd=v4G#eN+JRbp+qWB4Uw+KgGrb2|`WKY*If z4JQ)Q@xui9MZ|{XWXFZjKW(ZzT87zogyO z<5uf^ua$>#onRu_*H>tpF(#x7eES8`A+8rPs`t(EcUgPcBD`UkIb9gmeAz#Zu77) z3l@U8WFau!BJYxN=V;7ReGdHxPs-2V&)GF2B)>hhvKD>YI3H2ku;Px+2)@lzU)XnH zr(^E1IYYu$w|evh;_i|YwW(rR%99P;zgb`Qi?noLgrW?>(%C)sza5gxIwMmagaR^I zR9eb6@pC_1r3Hb#de~4%YHaXc4vX3cWL}Yvxz_j5B7K@5YJ+oBV%je`R&^HUwS-Yfqg>|@m(vfw_=Np0O@t!fq%_3E{l zK|w=csuKNOky!bFIQ_N8#;x?yv{WCO!6)p^Va+Kur3_hLeJ((~X6S_X`lKsr(y6u^ zxK>S>S_+~T=Z#Za;h{ak&k1 zKgD&QPl<1XX(7M?OGCCipB=NEo!Y;1xKVIMC@f%KjlXlL8jANVS+Rn<-Z-%=#-MVF z74SUk!nu$XC-^;qEjtks5wR9o0~A9a-~zFIvEskkZ|zTq`XsGf6#eXm!J9eY6Id@F z{6X&t&t1sC0QUTsfT)TwWItqQ$9~3w+imrR4Pg!K8xu4Gkh65SK_jkfhTNd;kla$? zTM?I+Okol()a5ac&Z|!Ywo;jjV|Yn-Bff{*q#OlcMO_sncwjJBcXoAIznuWaVgDyQ zk%p*EcUMu#1Do5Fm*Co!E4!dah>^Gu9UX{z4^f#Rg}zbk3^;^0FV+xmQa5KYTzrRC z&IegxSqC^2(2D1HZTP0{h6{3*sHg+@H3aX12B01`{!ix#=wCfSBn88Tr~Q6}**TwGj{2?=4)!n;%hr4o)R>~r5l zkk=qQu<_Lc>=0ex_|p)?HMb$6zQ6Z28$}}3L*1K6O@R=7eXR0*cwd(yjAlC0kD&PV z03IC<1{JH-fboEx3exj&s2LW5!~$nY>s=vy=|+Ag5vjjcvLfq zpW(2F02c0sys|DpcrJPCd~1+$<_XFG6NaS|^<7vaEw)_=?r8Cv=_P|J`TkfiaFIYt zKuaM?l_In!pq38kO;BfW_wL;{B#uBtWonxb?b1pM2X__(TyRJ^0t$AYI;$|mBqQA+ ze+;Ywu(2kuo0325*>gDuxv+OidHy%}NBi3t5gyU{XEylg3Uy5)XBec5U!#@(~MgMl)d{zan%8`!YX!D$nu2f)Cv4x4^_lJ ztlg6TkyyZ}tZE02$&_lh<9v|=$NoPTuJi1vgJfFygj<4SWS3@Cb=i>D-Y^N>xgb#8 z{L}&w!LkT>NN!Ok)$*M0PK*R;>7DIYP2k|v)vjn?P{TzO6cmK6AhR z5V;ovrWPlrO-wB_pYT(Q*)=cUl5G=Mo4J%>p)`X}Or3Hm8ozNF;#Bc$cNfX3Bf!Xk zj=e4%<{5lOmcxO?J{}!jgFoQXkpu}(E(moHAqrb$b4sUwm)x_4PdCujPdZqMI5t=~_|MeEr+nTu0{%px z>${N<2gX<8>n&QiU7V|usqf1EZ@dn=lf4{OCql&&xL^PCopK1=F}=-5RP`hCS?}9d zL0^oESTFC!WQy}y!d9r_Vd>qMJX3jUh||^a{Fd z$II4>@Le|bt;06)u<6``*bA8iZPAbo<~$xh@}I8y!Of<4CaGuY?-XkVBt6gwuH$C+ zf)QBg)~@PUJqtD%;u{hck>uEJ|D01uWN2X>9pf)@_4b&hAM7H6%@n4{pD>z3gc|ge zH$y{XymqYv5*v|`y6S4`P7pPLPS!V-kzP=MQjc|dV*(atXM2}x?dzFS-VEBN2#Xg# zh`a&Smx|X*|Bp2>w2v7!T!y!f3cPtxZG62T&3;FKGlmLyh6+xa9sWg3)=;+agxc7a)Cy03-LbLiI}sVZL!Z(&L~JW_Tr`@_ zeS2P6{ma`zZ*Q_QwjR%bhSGA{y_^|*c1!p8E)6W^X34UW1&z8T12dAfQ+Wk9bbaBl zaTsfQU&?(-&8+Xx3Kb^$(wOhVp)M^PkH|cnz8vfEh-;|;90HHb;Mx-iH;gPKgoOi= zlcjOa&z^15a__57)4k61~Gmskn< z&0ci|@9qxZAI!j=mrZ5@qI-Tk#too=+(rW3V^H`|I753&13YzwGW_8=Rd}nX0 zF`otg4IAs8E}bDP9(-a9DKJSrfSz!I?lBSc{y8_d`f7t=>o^H*SR1OpxG5$0k*xhq zYG4-50xNas>0*3X!`3nJ3>%+h0Wl`g>oq^MYe~a_IOMlt-(!?Hp^U?@kxZGYB>#Ob z62U+IgY7wMZNNQimh2yFqh=xwICYse;t>lnYuOFOB#}eF9M$ z1xYl71aGe8kE>sFLqsSBdMCMyL_~tX(j?qejD*F^oxOmAl8+kqAC>)bNMLN52A98Q5XV z9BD!bXxJyq#&N#Oz?%Du!j~sW6EdjeZgKRy@m+Z92)bT8jN9m-gFBCK|ABXkRv9;golSm?H>a# z@97T?4sN!!wRQCLl;7L`99Bnu1c;Q%py_QB(=fDhr&qe2*@Yc0siWX6TcDvrRAu7l z()fsH1qT-pffDJ?u{+LI<%Nq1-~QT$Q{PD-$a?M9p7{!;>C>lY=1pk4&HtK6{k$p@ zzaz429McgfdsIwI)5h}t7MT8(w5Dt3nEC!hWAmK+5BXQ)>-NST|A<}j>MpzjG5xs* zU+N)S(e#{~ zZTmYD4a4~vVZUTXUKw8;V|^sN<;dSs*)oAYUol$k@-C$Lv#mvKIa6=qP#$(4vL~Cj zdJw7}xU&Pv;BE&8a#r4Xm+&a1`b&6N829GZlOWGsi3a7o^@i2`sJ@~utnN0_p&jc7 zP1iGJqM;-ICfu2Fz#gc(45#*K08N=!h)E}8#@_zzp`86DZRpR~k>4henSbZgXBShA z1zZO58lbR4Gy37#Fc~>v^)BXnMRPU#|7T#s)juB}8}nr&mq$^59+fqg!Xq91uxqP=^YBEs=j#`}q|Y`iirM%(ZC*@1{`h!|pR!b;C|eJ!^v;1WLxz2+&z3f{fT#XGxw1*A%GYSA1O z`fH_lZ_7xJ0zj0NL;o@K5R##IlwPsI`b}r&NobKnh#;T4$<#j`?5i zQIs)Wk}`PjPq2(AgxL)nSM>yRsZqd`hl}a^-KDpk<~9H$!GcXJyWqfm1-!nH_sBzB zDG95g0w8wUAF7oy`50+!?dRJHUS$EFr*yA?DMjlE=!IfXG{acG9@*)#cHp@vUL;>J z=4c5>fILj@k4Sl|^_WuZ;P?vQ6j&hP3q=BXlFep-8C{4;87heFdVikhdiIx*#m3%> zad`hvVxWQWtXVRcEg=g`L*Hn%_S@HGr*=c?mJHA#{g?jT>ADo21gH@KA*WBz25S8^ z24Ky=_HUjzPtT{Y`^B9CCz4@dk;rgzaUrEofO9!A*riV*yk%tD!6I3RRTU7Tu124{ z?8T9ni}6nNov_uei)$@CL?7{|%aV~2TzH6%xs)yTbH%0(KA=dxrT~-`{9ld0OP&j* zJ!ZPK;V9df)DB4(t!Tt791L1t1_(J?EbiNuM-IF|3>yS(RQK@b)bv@xk@JxOHArG1 zuLXY;M`D6r1pq|Eo~GM!;KrP$L7@9_w36kcHRC&BVJ?H@cv|kL7lCehz1<&DRT^l3 zl7HR>m(J%%#arAHiAnAM<@w7NcR=8*kqNo~ir9gkL`{O0g~Av3@L0f>Bg41RC9>`s z1nOoOLP2&NnqP!n_B#eM-$)&esc`n&lfU57NXd?`$HX1sNJ66$mt}-|O+0YoA0pJ* zk_nr^QA~>9hp{HO$pgo*0)Wz|p-yap;Rx)=dYyZyQ~#SLQi6^!4P(;L-}^}XP+&h4WL;DA5Dr6B1gDdJK-G=&l7m?+vFY8h8?xZ^r=)(K0mN z*aTrBy^h$BW+HYLg-l`wP3lw9?L57rl?4Y`zNTkS^s_>@U$Ev_2aASd2gkrKRmPPq zLPnz$$Up!iEE!l=>+{fR|@@5;bb;yC;n-DQe4 zA4f0VS-c%Q;1HN)v-j3XpiLr=5jBbsNoMrr;*6(JN2pIp`Hg*8n+-J}aGCOWr?2*X z#KRATus#G&(S&9-NJ~i>-hKb8fK%fw`kfd|QHuT87)rallvhPvZ5IAX~F;3T8OQ$0d9-^v{2 z9hhk0hALII&!{_1eZthXjD&?44F#6*$s?`aYl)u!8l^?iV zk;YKOB{<4D>OVjYcN6jG)97>rfhd?<7s+Z#0S1Jk$RhJsW zLQ)h_0;%Ld|7Q(mzdVJ{7o#yGH8l$#k7MY71CeL2p&0`=JvC|)X?#Q9N7x_QBiBk8 zIyuR;`i|Hk@N4cFEf1f63MBN4D2*fKP{fgD`F0opO2_Q*iHQjWJ7y9EVDZyrDBK!m zN}<`Cd5;DxDLe=0C>ZI?6agp8qiZ8w4uSJ~8Z#rnVQa&wfnzYpy%l%=A|Pzl?Z9-y zN^=Pr=#;)=TTw6`Z8Bi5 zVOQ5P5d5JK_B_+`Z`&c1z$~sA7;UqJq;WV!f4dQm&8K=KETod)RkvOdJ025s^t*?T z;>^7I*ca>f6pi|%aFh0T)T{xiF57>cByHI!I8aJ@+D=`A+~ZIJQTju?BIG0M% zMSP@B30Fd8%hif@MBs>T$NNxy7_@6z;z&lzp*5!AAPADEdQG}kw9FWTIqiI;NO$Tg zu-syk8VwR{S{?s`XC~`A%8!}(+cmjiJI(KHaYkR_0ZO_w1q$!Me2hFJyT!$(E`%4P z(yaBsekF^V&_+e@L$K1`13pDlEXV}^0ob~&FDCJr)*w=)kssJg2pJ{vQ#mw=2CZ@M!O?z zI5hV)q{cMKJ*52)xCfznZ|2i z4#57Gi-gT)4~}4Nu5&6zoxDO2NY`ct$@J89(jCExgh~otW6fiKC=Eqf@vCD;nXF*;($0Y#E?d|lc6r#A$Ugj*2j3)Y2jh;ZE0zi&{z>VDH<+e3?W(_M z_z51jNEoAQ3L35=5~CM`NDhfUgC=zEFbSao0X+@O*$u{u7m_1tZIRQOzBYg%;m9(% z7ER9rWAHdBgs^#VyL))hm>#GyvmW;Xhd4F44C1!a!4gOL=&s_*V!H6opP2k|Z}I`1 z*r-m7VI&eIU_`rgOacN-deNfF8@+KPkXy+anG)1|%i|g(sG>Cc$^0;aaBSalkSy*S zE~L+yiOg68mH3&oe-Q;g+JqY@C>qoiUnK$|DouWSO_mjRyA-(q+_N=#`7#6+#)cP`As@m@N1C9foZhZ7~JZ@uEb-6n%Ahn?r(*OJ9Ze08iMBD zZ}~3P+3-|$jB0swhG6%mBZ+2EZpqkw{d(GmmCi_YXTxRsI9h0d-gb;Rc-EuKCuI!S zba%_GTzKj8k9iTRSKy$^4~b~a|C(`pTu^$8quyXFqMe2gkg~n?;OPloewyM z&)XdVZ}N4xc0UVy(QZ81-I&Hvtvg#(ly)9-ltNFTB!>!_`ce#5sx7?;yHfFEeiisw zrTKSNl!!f(I`A@O-$4UMJ!DC5014Edc&6up-Xq;hWfBe4y Df32g| literal 31858 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 diff --git a/docs/source/demonstrations/plasmas/beam_into_plasma.png b/docs/source/demonstrations/plasmas/beam_into_plasma.png index 2a52ef75d2246d8711b491bb40192bebe30faf5f..d4ae9ce1f166867fe61a71c4c43135e2066e0087 100644 GIT binary patch literal 4390 zcmeHLeNYo;9$vJi#mcqS&RLkYFrA)`b3N%@4!wksVEw`?6k9q?dzhrPH9=yCC4`TV zWVL#<-gy^#%t(=swL{0WOKS^H*O@{#BiN?JYs4@!#k?^^H3QUyZpY@ z!#r`<7-ExzP;al!5JIeR)cHA01w&+w z+pRKM-8KRHWpxHS+-oKER+Nb!dxU-%0FqMx;OQ3t;GJ7WYjURih?0uYrwgJr7z@2; zb36reXuhG5z?_@J@@sgJg{yO+*}14# zKJO>X`)$rqsh;tNU?D1kgL`4g`ru;NHy7~zHTu|3MC&YUO`hbr*j=@>=wch^mQ<*- zOHaqK^kaOaHRW;|tC{sUtzwZ!HmkhW1UC^)oG>3BN9=}{FRj@relh}iqn7McI4512 z)Lj^2_h@DfZJ#09bL_k~HT_&hCVB-d zC)Tpkn+%tuLQ(R1+CG60zT^x;oQ^8#i7M%bIz!6`GZf;m+)-zG{Nqgm37RCTROTpAvqWthZ+a+~dyXNZq#lBM$>4`wKy~r8_ugW9Ehx zFD&3LI&g7<@ctafd~Tz&xwf1r;L90dQ@=VHqS%tdMEX%(j~{m;7d8(xQ{gQ_qwzEc zX&suWa(;mg3gvgNfC7&MZj-j$AqFox#^G?`03i)1Zb=ZwBt}B!v@Y}JF7wHn?u}F3 z$y0L$O!w}o7;REK*Y4TyZ8u1jH|}>0&%d`IV-jfnXyA8AH?SXm1x@>-a{vxAXtQ1t zhVb7sU{}DB)eRI8ou&x;?~ak-Ut5c9fjnm)Hpq6fn!-L!;dTrW_d^Q4*&P?!Rehm| zG8q&X-yc%^!W`z-XnW}JJ>DfSaRg3G1gDfj)l40Eo%K*F+7OklZWyzlacOqnmcZhs z+6n1__^25dwj)`7-mEX+Jue1v*b`^W1ZHS2GKPoD38lg1tbi#U16kE9SoD)vvfMmX zVzte!EhoYkh)oR`m%QQQk@P}{S%=kT9OFYh#_f`im^)!7JG4Kb2kr9$FvIM=V+}lp zdaXFRWFSLh)xWa%#xP1Bva?v!u%w~szwHm%wn)<)Q#fU`^zmpP@Nj z@VR53TIT7Ma^+jO-a#+R?Ib0Puzx8lK}ni>i9N_Q^g`pvg(nA~(srq*j<^0LA)+(f zK&{UPcwgW2&Ybzn5>4xGH1E%mULImteaA%F9?HZN0>FONmF(e-5(>v2ZPc zcKAVid8ea+40ky07SH3oMuD-($cheIv)4rp--NnjzClF12bT@_d!KXrMqQf1yfUJ? zEh?3dM(HK${=KxgCb?ab%K>9CKmJi=e2dX{=7jGI^!Q3i6o9FDElI?nMZBklgXs*; zQT4Uuv+gW&PZ6hbq*?BVrh{shFG;jUkR)%AC<;F-w30N8L+zZeI(JXbY3|hu+-YG~ zbD3?MfeL))h`4OUY(VCX)hKpfP>QH!c$7E&G>b8#BaNSc>_> zY%D64BDFEn-V6Dn{M-Z?sPI*N!a$81e^5TDDmAb1dc{P!dsqtF&@`RE^You1~Y4c+zL4!ENn+S4A;$h2|V zY(%uIuYlI}oHq$kQ~xXO>?9>?lGy8Kj<594Kw@*3c{MT*PLwO2cwOL;y{TkxI|IYZ zziDS&nrox7MYz`-?PgtRQ!d{*$XOuG-2sM3Tu;MW)l6Bta}oT#^t<@Vbu(0{eZ|Ua zIz1EO-4O}d2{Wh1XJ{|33bcwGPh literal 5541 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 Date: Thu, 9 May 2024 17:27:16 +0100 Subject: [PATCH 073/134] Fix Sphinx warnings about documents not being included in toctree The demos are in fact not included in any toctree, but they are not intended to be so mark them as orphans as recommended in the Sphinx documentation. These warnings make it harder to spot other - genuine - warnings, so removing them is useful for ensuring documentation is produced correctly. --- docs/source/demonstrations/active_spectroscopy/beam_bes.rst | 2 ++ docs/source/demonstrations/active_spectroscopy/beam_cxs.rst | 2 ++ .../demonstrations/atomic_data/beam_plasma_interactions.rst | 2 ++ docs/source/demonstrations/atomic_data/fractional_abundance.rst | 2 ++ .../atomic_data/photon_emissivity_coefficients.rst | 2 ++ docs/source/demonstrations/atomic_data/radiated_powers.rst | 2 ++ docs/source/demonstrations/bolometry/calculate_etendue.rst | 2 ++ .../bolometry/camera_from_mesh_and_coordinates.rst | 2 ++ docs/source/demonstrations/bolometry/camera_from_primitives.rst | 2 ++ .../demonstrations/bolometry/geometry_matrix_from_voxels.rst | 2 ++ .../bolometry/geometry_matrix_with_raytransfer.rst | 2 ++ .../demonstrations/bolometry/inversion_with_raytransfer.rst | 2 ++ docs/source/demonstrations/bolometry/inversion_with_voxels.rst | 2 ++ .../demonstrations/bolometry/observing_radiation_function.rst | 2 ++ docs/source/demonstrations/demonstrations.rst | 2 ++ docs/source/demonstrations/jet_cxrs/jet_demo_76666.rst | 2 ++ docs/source/demonstrations/jet_cxrs/quickstart.rst | 2 ++ .../demonstrations/line_emission/balmer_series_spectra.rst | 2 ++ docs/source/demonstrations/line_emission/custom_emitter.rst | 2 ++ .../demonstrations/line_emission/mastu_forward_cameras.rst | 2 ++ .../demonstrations/passive_spectroscopy/balmer_series.rst | 2 ++ docs/source/demonstrations/passive_spectroscopy/multiplet.rst | 2 ++ .../demonstrations/passive_spectroscopy/stark_broadening.rst | 2 ++ .../demonstrations/passive_spectroscopy/zeeman_spectroscopy.rst | 2 ++ docs/source/demonstrations/plasmas/analytic_function_plasma.rst | 2 ++ docs/source/demonstrations/plasmas/beams_into_plasmas.rst | 2 ++ docs/source/demonstrations/plasmas/equilibrium.rst | 2 ++ docs/source/demonstrations/plasmas/mesh2d_plasma.rst | 2 ++ docs/source/demonstrations/plasmas/slab_plasma.rst | 2 ++ .../demonstrations/radiation_loads/radiation_function.rst | 2 ++ .../demonstrations/radiation_loads/surface_radiation_loads.rst | 2 ++ .../demonstrations/radiation_loads/symmetric_power_load.rst | 2 ++ .../source/demonstrations/radiation_loads/wall_from_polygon.rst | 2 ++ docs/source/demonstrations/ray_transfer/ray_transfer_box.rst | 2 ++ .../demonstrations/ray_transfer/ray_transfer_cylinder.rst | 2 ++ docs/source/demonstrations/ray_transfer/ray_transfer_map.rst | 2 ++ docs/source/demonstrations/ray_transfer/ray_transfer_mask.rst | 2 ++ docs/source/demonstrations/solps/solps_plasma.rst | 2 ++ 38 files changed, 76 insertions(+) diff --git a/docs/source/demonstrations/active_spectroscopy/beam_bes.rst b/docs/source/demonstrations/active_spectroscopy/beam_bes.rst index a37de688..c91957d9 100644 --- a/docs/source/demonstrations/active_spectroscopy/beam_bes.rst +++ b/docs/source/demonstrations/active_spectroscopy/beam_bes.rst @@ -1,3 +1,5 @@ +:orphan: + .. _beam_bes: diff --git a/docs/source/demonstrations/active_spectroscopy/beam_cxs.rst b/docs/source/demonstrations/active_spectroscopy/beam_cxs.rst index 5952ae3c..8859cad7 100644 --- a/docs/source/demonstrations/active_spectroscopy/beam_cxs.rst +++ b/docs/source/demonstrations/active_spectroscopy/beam_cxs.rst @@ -1,3 +1,5 @@ +:orphan: + .. _beam_cxs: diff --git a/docs/source/demonstrations/atomic_data/beam_plasma_interactions.rst b/docs/source/demonstrations/atomic_data/beam_plasma_interactions.rst index 58760f5d..eba6f3b1 100644 --- a/docs/source/demonstrations/atomic_data/beam_plasma_interactions.rst +++ b/docs/source/demonstrations/atomic_data/beam_plasma_interactions.rst @@ -1,3 +1,5 @@ +:orphan: + .. _beam_plasma_interaction_rates: diff --git a/docs/source/demonstrations/atomic_data/fractional_abundance.rst b/docs/source/demonstrations/atomic_data/fractional_abundance.rst index 00bc95e0..d1a80309 100644 --- a/docs/source/demonstrations/atomic_data/fractional_abundance.rst +++ b/docs/source/demonstrations/atomic_data/fractional_abundance.rst @@ -1,3 +1,5 @@ +:orphan: + .. _fractional_abundances: diff --git a/docs/source/demonstrations/atomic_data/photon_emissivity_coefficients.rst b/docs/source/demonstrations/atomic_data/photon_emissivity_coefficients.rst index 08948319..bfce073e 100644 --- a/docs/source/demonstrations/atomic_data/photon_emissivity_coefficients.rst +++ b/docs/source/demonstrations/atomic_data/photon_emissivity_coefficients.rst @@ -1,3 +1,5 @@ +:orphan: + .. _photon_emissivity_coefficients: diff --git a/docs/source/demonstrations/atomic_data/radiated_powers.rst b/docs/source/demonstrations/atomic_data/radiated_powers.rst index f0ba1249..a002aceb 100644 --- a/docs/source/demonstrations/atomic_data/radiated_powers.rst +++ b/docs/source/demonstrations/atomic_data/radiated_powers.rst @@ -1,3 +1,5 @@ +:orphan: + .. _radiated_powers: diff --git a/docs/source/demonstrations/bolometry/calculate_etendue.rst b/docs/source/demonstrations/bolometry/calculate_etendue.rst index 82f90379..4ab99b86 100644 --- a/docs/source/demonstrations/bolometry/calculate_etendue.rst +++ b/docs/source/demonstrations/bolometry/calculate_etendue.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_etendue: diff --git a/docs/source/demonstrations/bolometry/camera_from_mesh_and_coordinates.rst b/docs/source/demonstrations/bolometry/camera_from_mesh_and_coordinates.rst index 95d54b13..bec12605 100644 --- a/docs/source/demonstrations/bolometry/camera_from_mesh_and_coordinates.rst +++ b/docs/source/demonstrations/bolometry/camera_from_mesh_and_coordinates.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_from_mesh: diff --git a/docs/source/demonstrations/bolometry/camera_from_primitives.rst b/docs/source/demonstrations/bolometry/camera_from_primitives.rst index 8d08146b..1f51713a 100644 --- a/docs/source/demonstrations/bolometry/camera_from_primitives.rst +++ b/docs/source/demonstrations/bolometry/camera_from_primitives.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_from_primitives: diff --git a/docs/source/demonstrations/bolometry/geometry_matrix_from_voxels.rst b/docs/source/demonstrations/bolometry/geometry_matrix_from_voxels.rst index ff0a548d..08b50a94 100644 --- a/docs/source/demonstrations/bolometry/geometry_matrix_from_voxels.rst +++ b/docs/source/demonstrations/bolometry/geometry_matrix_from_voxels.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_geometry_voxels: diff --git a/docs/source/demonstrations/bolometry/geometry_matrix_with_raytransfer.rst b/docs/source/demonstrations/bolometry/geometry_matrix_with_raytransfer.rst index a450b8b8..d5fbd367 100644 --- a/docs/source/demonstrations/bolometry/geometry_matrix_with_raytransfer.rst +++ b/docs/source/demonstrations/bolometry/geometry_matrix_with_raytransfer.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_geometry_raytransfer: diff --git a/docs/source/demonstrations/bolometry/inversion_with_raytransfer.rst b/docs/source/demonstrations/bolometry/inversion_with_raytransfer.rst index 235fc029..c4e538d5 100644 --- a/docs/source/demonstrations/bolometry/inversion_with_raytransfer.rst +++ b/docs/source/demonstrations/bolometry/inversion_with_raytransfer.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_raytransfer_inversion: diff --git a/docs/source/demonstrations/bolometry/inversion_with_voxels.rst b/docs/source/demonstrations/bolometry/inversion_with_voxels.rst index 0aaab42c..68301d57 100644 --- a/docs/source/demonstrations/bolometry/inversion_with_voxels.rst +++ b/docs/source/demonstrations/bolometry/inversion_with_voxels.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_voxel_inversion: diff --git a/docs/source/demonstrations/bolometry/observing_radiation_function.rst b/docs/source/demonstrations/bolometry/observing_radiation_function.rst index aeedac6d..de52a7d3 100644 --- a/docs/source/demonstrations/bolometry/observing_radiation_function.rst +++ b/docs/source/demonstrations/bolometry/observing_radiation_function.rst @@ -1,3 +1,5 @@ +:orphan: + .. _bolometer_observing_radiation: diff --git a/docs/source/demonstrations/demonstrations.rst b/docs/source/demonstrations/demonstrations.rst index 6201aae1..477f6620 100644 --- a/docs/source/demonstrations/demonstrations.rst +++ b/docs/source/demonstrations/demonstrations.rst @@ -1,3 +1,5 @@ +:orphan: + Atomic Data =========== diff --git a/docs/source/demonstrations/jet_cxrs/jet_demo_76666.rst b/docs/source/demonstrations/jet_cxrs/jet_demo_76666.rst index f1f79f69..ccf89ed3 100644 --- a/docs/source/demonstrations/jet_cxrs/jet_demo_76666.rst +++ b/docs/source/demonstrations/jet_cxrs/jet_demo_76666.rst @@ -1,3 +1,5 @@ +:orphan: + .. _jet_cxrs_76666: diff --git a/docs/source/demonstrations/jet_cxrs/quickstart.rst b/docs/source/demonstrations/jet_cxrs/quickstart.rst index 28deddd0..6ed48f7e 100644 --- a/docs/source/demonstrations/jet_cxrs/quickstart.rst +++ b/docs/source/demonstrations/jet_cxrs/quickstart.rst @@ -1,3 +1,5 @@ +:orphan: + .. _jet_cxrs_quickstart: diff --git a/docs/source/demonstrations/line_emission/balmer_series_spectra.rst b/docs/source/demonstrations/line_emission/balmer_series_spectra.rst index 98ad540d..532df8f1 100644 --- a/docs/source/demonstrations/line_emission/balmer_series_spectra.rst +++ b/docs/source/demonstrations/line_emission/balmer_series_spectra.rst @@ -1,3 +1,5 @@ +:orphan: + .. _balmer_series_spectra: diff --git a/docs/source/demonstrations/line_emission/custom_emitter.rst b/docs/source/demonstrations/line_emission/custom_emitter.rst index 56f3c442..73ffde60 100644 --- a/docs/source/demonstrations/line_emission/custom_emitter.rst +++ b/docs/source/demonstrations/line_emission/custom_emitter.rst @@ -1,3 +1,5 @@ +:orphan: + .. _custom_emitter: diff --git a/docs/source/demonstrations/line_emission/mastu_forward_cameras.rst b/docs/source/demonstrations/line_emission/mastu_forward_cameras.rst index f3d54084..f2d77bac 100644 --- a/docs/source/demonstrations/line_emission/mastu_forward_cameras.rst +++ b/docs/source/demonstrations/line_emission/mastu_forward_cameras.rst @@ -1,3 +1,5 @@ +:orphan: + .. _mastu_forward_cameras: diff --git a/docs/source/demonstrations/passive_spectroscopy/balmer_series.rst b/docs/source/demonstrations/passive_spectroscopy/balmer_series.rst index 77fe64f3..53fbc9d1 100644 --- a/docs/source/demonstrations/passive_spectroscopy/balmer_series.rst +++ b/docs/source/demonstrations/passive_spectroscopy/balmer_series.rst @@ -1,3 +1,5 @@ +:orphan: + .. _impact_recom_lines: diff --git a/docs/source/demonstrations/passive_spectroscopy/multiplet.rst b/docs/source/demonstrations/passive_spectroscopy/multiplet.rst index 9caa3470..0aa1def1 100644 --- a/docs/source/demonstrations/passive_spectroscopy/multiplet.rst +++ b/docs/source/demonstrations/passive_spectroscopy/multiplet.rst @@ -1,3 +1,5 @@ +:orphan: + .. _multiplet_lines: diff --git a/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst b/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst index bd670a49..9a2f20e8 100644 --- a/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst +++ b/docs/source/demonstrations/passive_spectroscopy/stark_broadening.rst @@ -1,3 +1,5 @@ +:orphan: + .. _stark_broadening: diff --git a/docs/source/demonstrations/passive_spectroscopy/zeeman_spectroscopy.rst b/docs/source/demonstrations/passive_spectroscopy/zeeman_spectroscopy.rst index c5fc4466..2af89a7f 100644 --- a/docs/source/demonstrations/passive_spectroscopy/zeeman_spectroscopy.rst +++ b/docs/source/demonstrations/passive_spectroscopy/zeeman_spectroscopy.rst @@ -1,3 +1,5 @@ +:orphan: + .. _zeeman_spectroscopy: diff --git a/docs/source/demonstrations/plasmas/analytic_function_plasma.rst b/docs/source/demonstrations/plasmas/analytic_function_plasma.rst index 627f0c66..6bbdfd8a 100644 --- a/docs/source/demonstrations/plasmas/analytic_function_plasma.rst +++ b/docs/source/demonstrations/plasmas/analytic_function_plasma.rst @@ -1,3 +1,5 @@ +:orphan: + .. _analytic_function_plasma: diff --git a/docs/source/demonstrations/plasmas/beams_into_plasmas.rst b/docs/source/demonstrations/plasmas/beams_into_plasmas.rst index 1e2d79aa..3479983c 100644 --- a/docs/source/demonstrations/plasmas/beams_into_plasmas.rst +++ b/docs/source/demonstrations/plasmas/beams_into_plasmas.rst @@ -1,3 +1,5 @@ +:orphan: + .. _beams_into_plasmas: diff --git a/docs/source/demonstrations/plasmas/equilibrium.rst b/docs/source/demonstrations/plasmas/equilibrium.rst index 09c35136..56e1b7a5 100644 --- a/docs/source/demonstrations/plasmas/equilibrium.rst +++ b/docs/source/demonstrations/plasmas/equilibrium.rst @@ -1,3 +1,5 @@ +:orphan: + .. _flux_function_plasmas: Flux Function Plasmas diff --git a/docs/source/demonstrations/plasmas/mesh2d_plasma.rst b/docs/source/demonstrations/plasmas/mesh2d_plasma.rst index 09fe8603..78c2e75d 100644 --- a/docs/source/demonstrations/plasmas/mesh2d_plasma.rst +++ b/docs/source/demonstrations/plasmas/mesh2d_plasma.rst @@ -1,3 +1,5 @@ +:orphan: + .. _mesh2d_plasma: diff --git a/docs/source/demonstrations/plasmas/slab_plasma.rst b/docs/source/demonstrations/plasmas/slab_plasma.rst index 2752bc2e..af20b701 100644 --- a/docs/source/demonstrations/plasmas/slab_plasma.rst +++ b/docs/source/demonstrations/plasmas/slab_plasma.rst @@ -1,3 +1,5 @@ +:orphan: + .. _slab_plasma: diff --git a/docs/source/demonstrations/radiation_loads/radiation_function.rst b/docs/source/demonstrations/radiation_loads/radiation_function.rst index 26d45d57..3ca6ccc1 100644 --- a/docs/source/demonstrations/radiation_loads/radiation_function.rst +++ b/docs/source/demonstrations/radiation_loads/radiation_function.rst @@ -1,3 +1,5 @@ +:orphan: + .. _radiation_function: diff --git a/docs/source/demonstrations/radiation_loads/surface_radiation_loads.rst b/docs/source/demonstrations/radiation_loads/surface_radiation_loads.rst index 63bb2ad9..eeb8c129 100644 --- a/docs/source/demonstrations/radiation_loads/surface_radiation_loads.rst +++ b/docs/source/demonstrations/radiation_loads/surface_radiation_loads.rst @@ -1,3 +1,5 @@ +:orphan: + .. _aug_solps_radiation_load: diff --git a/docs/source/demonstrations/radiation_loads/symmetric_power_load.rst b/docs/source/demonstrations/radiation_loads/symmetric_power_load.rst index a9d7be13..195a6028 100644 --- a/docs/source/demonstrations/radiation_loads/symmetric_power_load.rst +++ b/docs/source/demonstrations/radiation_loads/symmetric_power_load.rst @@ -1,3 +1,5 @@ +:orphan: + .. _symmetric_power_load: diff --git a/docs/source/demonstrations/radiation_loads/wall_from_polygon.rst b/docs/source/demonstrations/radiation_loads/wall_from_polygon.rst index 53b8a8e7..8de97008 100644 --- a/docs/source/demonstrations/radiation_loads/wall_from_polygon.rst +++ b/docs/source/demonstrations/radiation_loads/wall_from_polygon.rst @@ -1,3 +1,5 @@ +:orphan: + .. _wall_from_polygon: diff --git a/docs/source/demonstrations/ray_transfer/ray_transfer_box.rst b/docs/source/demonstrations/ray_transfer/ray_transfer_box.rst index 57ac572f..5188d532 100644 --- a/docs/source/demonstrations/ray_transfer/ray_transfer_box.rst +++ b/docs/source/demonstrations/ray_transfer/ray_transfer_box.rst @@ -1,3 +1,5 @@ +:orphan: + .. _ray_transfer_box: diff --git a/docs/source/demonstrations/ray_transfer/ray_transfer_cylinder.rst b/docs/source/demonstrations/ray_transfer/ray_transfer_cylinder.rst index 2eb55cbb..48e6011a 100644 --- a/docs/source/demonstrations/ray_transfer/ray_transfer_cylinder.rst +++ b/docs/source/demonstrations/ray_transfer/ray_transfer_cylinder.rst @@ -1,3 +1,5 @@ +:orphan: + .. _ray_transfer_cylinder: diff --git a/docs/source/demonstrations/ray_transfer/ray_transfer_map.rst b/docs/source/demonstrations/ray_transfer/ray_transfer_map.rst index e74f7962..04378b21 100644 --- a/docs/source/demonstrations/ray_transfer/ray_transfer_map.rst +++ b/docs/source/demonstrations/ray_transfer/ray_transfer_map.rst @@ -1,3 +1,5 @@ +:orphan: + .. _ray_transfer_map: diff --git a/docs/source/demonstrations/ray_transfer/ray_transfer_mask.rst b/docs/source/demonstrations/ray_transfer/ray_transfer_mask.rst index 6bd34451..e124e2d8 100644 --- a/docs/source/demonstrations/ray_transfer/ray_transfer_mask.rst +++ b/docs/source/demonstrations/ray_transfer/ray_transfer_mask.rst @@ -1,3 +1,5 @@ +:orphan: + .. _ray_transfer_mask: diff --git a/docs/source/demonstrations/solps/solps_plasma.rst b/docs/source/demonstrations/solps/solps_plasma.rst index b8bd788b..c924431e 100644 --- a/docs/source/demonstrations/solps/solps_plasma.rst +++ b/docs/source/demonstrations/solps/solps_plasma.rst @@ -1,3 +1,5 @@ +:orphan: + .. _mastu_solps_plasma: From af1c2ac88e2da113cd52033e88ee1c129eb7ba17 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 9 May 2024 23:00:01 +0200 Subject: [PATCH 074/134] Revert positional argument names change as this can break user code. --- cherab/core/atomic/rates.pxd | 6 +++--- cherab/core/atomic/rates.pyx | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cherab/core/atomic/rates.pxd b/cherab/core/atomic/rates.pxd index 006dbf8a..1f844122 100644 --- a/cherab/core/atomic/rates.pxd +++ b/cherab/core/atomic/rates.pxd @@ -75,7 +75,7 @@ cdef class TotalRadiatedPower: cdef: readonly Element element - cdef double evaluate(self, double density, double temperature) except? -1e999 + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 cdef class _RadiatedPower: @@ -84,7 +84,7 @@ cdef class _RadiatedPower: readonly Element element readonly int charge - cdef double evaluate(self, double density, double temperature) except? -1e999 + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 cdef class LineRadiationPower(_RadiatedPower): @@ -106,4 +106,4 @@ cdef class FractionalAbundance: readonly int charge public str name - cdef double evaluate(self, double density, double temperature) except? -1e999 + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999 diff --git a/cherab/core/atomic/rates.pyx b/cherab/core/atomic/rates.pyx index a8f10141..f3a653db 100644 --- a/cherab/core/atomic/rates.pyx +++ b/cherab/core/atomic/rates.pyx @@ -227,20 +227,20 @@ cdef class TotalRadiatedPower(): self.element = element - def __call__(self, double density, double temperature): + def __call__(self, double electron_density, double electron_temperature): """ Evaluate the total radiated power rate at the given plasma conditions. This function just wraps the cython evaluate() method. """ - return self.evaluate(density, temperature) + return self.evaluate(electron_density, electron_temperature) - cdef double evaluate(self, double density, double temperature) except? -1e999: + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: """ Evaluate the total radiated power rate at the given plasma conditions. - :param float density: Electron density in m^-3. - :param float temperature: Electron temperature in eV. + :param float electron_density: Electron density in m^-3. + :param float electron_temperature: Electron temperature in eV. :return: The total radiated power rate in W.m^3. """ @@ -255,15 +255,15 @@ cdef class _RadiatedPower: self.element = element self.charge = charge - def __call__(self, double density, double temperature): + def __call__(self, double electron_density, double electron_temperature): """ Evaluate the radiated power rate at the given plasma conditions. This function just wraps the cython evaluate() method. """ - return self.evaluate(density, temperature) + return self.evaluate(electron_density, electron_temperature) - cdef double evaluate(self, double density, double temperature) except? -1e999: + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: """ Evaluate the radiated power at the given plasma conditions. @@ -320,24 +320,24 @@ cdef class FractionalAbundance: raise ValueError("Charge state must be neutral or positive.") self.charge = charge - cdef double evaluate(self, double density, double temperature) except? -1e999: + cdef double evaluate(self, double electron_density, double electron_temperature) except? -1e999: """ Evaluate the fractional abundance of this ionisation stage at the given plasma conditions. - :param float density: Electron density in m^-3. - :param float temperature: Electron temperature in eV. + :param float electron_density: Electron density in m^-3. + :param float electron_temperature: Electron temperature in eV. :return: Fractional abundance. """ raise NotImplementedError("The evaluate() virtual method must be implemented.") - def __call__(self, double density, double temperature): + def __call__(self, double electron_density, double electron_temperature): """ Evaluate the fractional abundance of this ionisation stage at the given plasma conditions. This function just wraps the cython evaluate() method. """ - return self.evaluate(density, temperature) + return self.evaluate(electron_density, electron_temperature) def plot_temperature(self, temp_low=1, temp_high=1000, num_points=100, dens=1E19): From fb2a5da69b1a264f2518d23c3da9432e085372b2 Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 10 May 2024 17:22:49 +0200 Subject: [PATCH 075/134] Added equations for Beam density and direction calculation. Moved SingleRayAttenuator from CXS models section to Beam Attenuation section. --- cherab/core/beam/node.pyx | 17 +++++++- cherab/core/model/attenuator/singleray.pyx | 43 +++++++++++++++++++-- docs/source/models/beam/beam_attenuator.rst | 10 +++++ docs/source/models/cxs/cxs_model.rst | 9 ----- docs/source/models/emission_models.rst | 1 + docs/source/plasmas/particle_beams.rst | 2 +- 6 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 docs/source/models/beam/beam_attenuator.rst diff --git a/cherab/core/beam/node.pyx b/cherab/core/beam/node.pyx index 9fede133..ec25742b 100644 --- a/cherab/core/beam/node.pyx +++ b/cherab/core/beam/node.pyx @@ -144,6 +144,7 @@ cdef class Beam(Node): :ivar float sigma: The Gaussian beam width at the origin in m. :ivar float temperature: The broadening of the beam (eV). + .. code-block:: pycon >>> # This example shows how to initialise and populate a basic beam @@ -234,7 +235,21 @@ cdef class Beam(Node): cpdef Vector3D direction(self, double x, double y, double z): """ - Calculates the beam direction vector at a point in space. + Calculates the beam direction vector at a point in beam coordinate space. + + The beam direction (non-normalised) is calculated as follows (z > 0): + + :math:`e_x = x\\frac{(ztg(\\alpha_x))^2}{\\sigma^2 + (ztg(\\alpha_x))^2}`, + :math:`e_y = y\\frac{(ztg(\\alpha_y))^2}{\\sigma^2 + (ztg(\\alpha_y))^2}`, + :math:`e_z = z`, + + where :math:`\\sigma` is the Gaussian beam deviation at origin, + :math:`\\alpha_x` and :math:`\\alpha_y` are the beam divergence angles + in the x and y dimensions respectively. + + For z <= 0 the beam direction is (0, 0, 1). + + The function returns normalised beam direction. Note the values of the beam outside of the beam envelope should be treated with caution. diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index ca611d09..12904524 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -37,6 +37,21 @@ cimport cython # todo: attenuation calculation could be optimised further using memory views etc... cdef class SingleRayAttenuator(BeamAttenuator): + r""" + Calculates beam attenuation in the single-ray approximation. + Attenuation is calculated along the beam axis and extrapolated across the beam. + + :param double step: Distance between sample points along the beam axis in meters + for beam stopping calculation. Defaults to 0.01. + :param bint clamp_to_zero: Omptimises beam density calculation. + If True, the beam density outside the clamping range is zero. Defaults to False. + :param double clamp_sigma: The clamping range as a factor of beam :math:`\sigma(z)`. + Defaults to 5. + :param Beam beam: The beam instance to which this attenuator is attached. Defaults to None. + :param Plasma plasma: The plasma instance with which this beam interacts. Defaults to None. + :param AtomicData atomic_data: The atomic data provider class for this attenuator. + Defaults to None. + """ def __init__(self, double step=0.01, bint clamp_to_zero=False, double clamp_sigma=5.0, Beam beam=None, Plasma plasma=None, AtomicData atomic_data=None): @@ -86,10 +101,30 @@ cdef class SingleRayAttenuator(BeamAttenuator): @cython.cdivision(True) cpdef double density(self, double x, double y, double z) except? -1e999: - """ - Returns the beam density at the specified point. - - The point is specified in beam space. + r""" + Returns the beam density at the specified point in beam coordinate space. + The beam density is calculated as follows: + + :math:`n(x, y, z) = \frac{R}{2\pi v_0 \sigma_x\sigma_y} exp\left(-\frac{1}{2}\left(\frac{x^2}{\sigma_x^2}+\frac{y^2}{\sigma_y^2}\right)\right)exp\left(-\int_{0}^{z}\frac{S(z')}{v_0}dz'\right)`, + + :math:`\sigma_x = \sqrt{\sigma^2 + (ztg(\alpha_x))^2}\hspace{0.5cm}\sigma_y = \sqrt{\sigma^2 + (ztg(\alpha_y))^2}`, + + where :math:`R=\frac{P}{E}` is the particle rate of the beam defined as the power + of the beam divided by the kinetic energy of the single particle, :math:`v_0=\sqrt{2E/m}` + is the particle speed, :math:`\sigma` is the Gaussian beam deviation at origin, + :math:`\alpha_x` and :math:`\alpha_y` are the beam divergence angles in the x and y + dimensions respectively, :math:`S(z)` is the composite beam attenuation coefficient due to + collisional-radiative interaction with the plasma species: + + :math:`S(z) = \sum_{i=1}^{N}Z_i n_i S_i(E_{int}, n_{i,e}^{(eq)}, T_i)`, + :math:`n_{i,e}^{(eq)} = \frac{1}{Z_i}\sum_{j=1}^{N}Z_j^2 n_j`. + + Here :math:`Z_i` is the charge of the i-th type of plasma ions, + :math:`n_i` is density of the i-th type of plasma ions, :math:`N` is the number of type of plasma + ions, :math:`E_{int}` is the kinetic energy of the beam atoms in the frame of reference where + ions of the i-th type are at rest, :math:`T_{i}` is the temperature of ions of the i-th type. + + The values of partial beam attenuation coefficients, :math:`S_i`, are provided by the atomic data source. :param x: x coordinate in meters. :param y: y coordinate in meters. diff --git a/docs/source/models/beam/beam_attenuator.rst b/docs/source/models/beam/beam_attenuator.rst new file mode 100644 index 00000000..d0cfceb7 --- /dev/null +++ b/docs/source/models/beam/beam_attenuator.rst @@ -0,0 +1,10 @@ + +Beam Attenuation +---------------- + + +Single Ray Attenuator +^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: cherab.core.model.attenuator.singleray.SingleRayAttenuator + :members: diff --git a/docs/source/models/cxs/cxs_model.rst b/docs/source/models/cxs/cxs_model.rst index a211801c..b069c522 100644 --- a/docs/source/models/cxs/cxs_model.rst +++ b/docs/source/models/cxs/cxs_model.rst @@ -2,15 +2,6 @@ CXS models ---------- -CXS models. - - -Single Ray Attenuator -^^^^^^^^^^^^^^^^^^^^^ - -.. autoclass:: cherab.core.model.attenuator.singleray.SingleRayAttenuator - :members: - CXS Beam Plasma Intersection ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/models/emission_models.rst b/docs/source/models/emission_models.rst index 3a2c3b9c..52c76e19 100644 --- a/docs/source/models/emission_models.rst +++ b/docs/source/models/emission_models.rst @@ -6,6 +6,7 @@ Cherab contains a number of independent physics models for spectroscopic emissio .. toctree:: custom_models + beam/beam_attenuator cxs/cxs_model cxs/charge_exchange_calculation bes/bes_model diff --git a/docs/source/plasmas/particle_beams.rst b/docs/source/plasmas/particle_beams.rst index 615358a3..cc9de35a 100644 --- a/docs/source/plasmas/particle_beams.rst +++ b/docs/source/plasmas/particle_beams.rst @@ -2,7 +2,7 @@ Mono-energetic Particle Beams ============================= -.. autoclass:: cherab.core.Beam +.. autoclass:: cherab.core.beam.node.Beam :members: From 70b188babca4aa58b00301e042fede09a594daaa Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 10 May 2024 17:30:39 +0200 Subject: [PATCH 076/134] Highlighted the line about beam density and direction change in CHANGELOG.md. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index caeae178..b4db5b0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ New: * Replace the coarse numerical constant in the Bremsstrahlung model with an exact expression. (#409) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) * Improved parsing of metadata from the ADAS ADF15 'bnd' files for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) -* Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414) +* **Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414)** * Improved beam direction calculation to allow for natural broadening of the BES line shape due to beam divergence. (#414) Bug fixes: From 9a9a1919ba84ad1d905cd132ba50139a26e4f881 Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 11 May 2024 21:54:28 +0200 Subject: [PATCH 077/134] Added tests for the beam density and direction calculation. --- cherab/core/tests/test_beam.py | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 cherab/core/tests/test_beam.py diff --git a/cherab/core/tests/test_beam.py b/cherab/core/tests/test_beam.py new file mode 100644 index 00000000..9be75996 --- /dev/null +++ b/cherab/core/tests/test_beam.py @@ -0,0 +1,156 @@ +# Copyright 2016-2023 Euratom +# Copyright 2016-2023 United Kingdom Atomic Energy Authority +# Copyright 2016-2023 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.core import World, Vector3D, translate + +from cherab.core import Beam +from cherab.core.atomic import AtomicData, BeamStoppingRate +from cherab.core.atomic import deuterium +from cherab.tools.plasmas.slab import build_constant_slab_plasma +from cherab.core.model import SingleRayAttenuator + +from cherab.core.utility import EvAmuToMS, EvToJ + + +class ConstantBeamStoppingRate(BeamStoppingRate): + """ + Constant beam CX PEC for test purpose. + """ + + def __init__(self, donor_metastable, value): + self.donor_metastable = donor_metastable + self.value = value + + def evaluate(self, energy, density, temperature): + + return self.value + + +class TestAtomicData(AtomicData): + """Fake atomic data for test purpose.""" + + def beam_stopping_rate(self, beam_ion, plasma_ion, charge): + + return ConstantBeamStoppingRate(1, 1.e-13) + + +class TestBeam(unittest.TestCase): + + atomic_data = TestAtomicData() + + world = World() + + plasma_density = 1.e19 + plasma_temperature = 1.e3 + plasma_species = [(deuterium, 1, plasma_density, plasma_temperature, Vector3D(0, 0, 0))] + plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=plasma_density, + electron_temperature=plasma_temperature, + plasma_species=plasma_species) + plasma.atomic_data = atomic_data + plasma.parent = world + + beam = Beam(transform=translate(0.5, 0, 0)) + beam.atomic_data = atomic_data + beam.plasma = plasma + beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) + beam.energy = 50000 + beam.power = 1e6 + beam.temperature = 10 + beam.element = deuterium + beam.parent = world + beam.sigma = 0.2 + beam.divergence_x = 1. + beam.divergence_y = 2. + beam.length = 10. + + def test_beam_density(self): + + z0 = 0.8 + x0, y0 = 0.5, 0.5 + + density_on_axis = self.beam.density(0, 0, z0) + density_off_axis = self.beam.density(x0, y0, z0) + density_outside_beam = self.beam.density(0, 0, -1) + + # validating + + speed = EvAmuToMS.to(self.beam.energy) + # constant stopping rate + stopping_rate = self.atomic_data.beam_stopping_rate(deuterium, deuterium, 1)(0, 0, 0) + attenuation_factor = np.exp(-z0 * self.plasma_density * stopping_rate / speed) + + beam_particle_rate = self.beam.power / EvToJ.to(self.beam.energy * deuterium.atomic_weight) + + sigma0_sqr = self.beam.sigma**2 + tanxdiv = np.tan(np.deg2rad(self.beam.divergence_x)) + tanydiv = np.tan(np.deg2rad(self.beam.divergence_y)) + sigma_x = np.sqrt(sigma0_sqr + (z0 * tanxdiv)**2) + sigma_y = np.sqrt(sigma0_sqr + (z0 * tanydiv)**2) + + norm_radius_sqr = ((x0 / sigma_x)**2 + (y0 / sigma_y)**2) + + gaussian_sample_on_axis = 1. / (2 * np.pi * sigma_x * sigma_y) + gaussian_sample_off_axis = np.exp(-0.5 * norm_radius_sqr) / (2 * np.pi * sigma_x * sigma_y) + + test_density_on_axis = beam_particle_rate / speed * gaussian_sample_on_axis * attenuation_factor + test_density_off_axis = beam_particle_rate / speed * gaussian_sample_off_axis * attenuation_factor + + self.assertAlmostEqual(density_on_axis / test_density_on_axis, 1., delta=1.e-12, + msg='Beam.density() gives a wrong value on the beam axis.') + self.assertAlmostEqual(density_off_axis / test_density_off_axis, 1., delta=1.e-12, + msg='Beam.density() gives a wrong value off the beam axis.') + self.assertEqual(density_outside_beam, 0, + msg='Beam.density() gives a non-zero value outside beam.') + + def test_beam_direction(self): + # setting up the model + + z0 = 0.8 + x0, y0 = 0.5, 0.5 + + direction_on_axis = self.beam.direction(0, 0, z0) + direction_off_axis = self.beam.direction(x0, y0, z0) + direction_outside_beam = self.beam.direction(0, 0, -1) + + # validating + + sigma0_sqr = self.beam.sigma**2 + z_tanx_sqr = (z0 * np.tan(np.deg2rad(self.beam.divergence_x)))**2 + z_tany_sqr = (z0 * np.tan(np.deg2rad(self.beam.divergence_y)))**2 + + ex = x0 * z_tanx_sqr / (sigma0_sqr + z_tanx_sqr) + ey = y0 * z_tany_sqr / (sigma0_sqr + z_tany_sqr) + ez = z0 + + test_direction_off_axis = Vector3D(ex, ey, ez).normalise() + + self.assertEqual(direction_on_axis, Vector3D(0, 0, 1), + msg='Beam.density() gives a wrong value on the beam axis.') + for v, test_v in zip(direction_off_axis, test_direction_off_axis): + self.assertAlmostEqual(v, test_v, delta=1.e-12, + msg='Beam.direction() gives a wrong value off the beam axis.') + self.assertEqual(direction_outside_beam, Vector3D(0, 0, 1), + msg='Beam.density() gives a non-zero value outside beam.') + + +if __name__ == '__main__': + unittest.main() From b2f4bac7905cd911877135b497347afa5b8a12fd Mon Sep 17 00:00:00 2001 From: vsnever Date: Sat, 11 May 2024 23:56:28 +0200 Subject: [PATCH 078/134] Added beam direction plot to the docs. --- docs/source/plasmas/beam_direction.png | Bin 0 -> 120786 bytes docs/source/plasmas/particle_beams.rst | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 docs/source/plasmas/beam_direction.png diff --git a/docs/source/plasmas/beam_direction.png b/docs/source/plasmas/beam_direction.png new file mode 100644 index 0000000000000000000000000000000000000000..782bfe461dd91a838c272c4d4b8f66e40c8e19dd GIT binary patch literal 120786 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 O7*b+#qQ$~rz5fe+=@_v9 literal 0 HcmV?d00001 diff --git a/docs/source/plasmas/particle_beams.rst b/docs/source/plasmas/particle_beams.rst index cc9de35a..e08d56d2 100644 --- a/docs/source/plasmas/particle_beams.rst +++ b/docs/source/plasmas/particle_beams.rst @@ -5,4 +5,8 @@ Mono-energetic Particle Beams .. autoclass:: cherab.core.beam.node.Beam :members: +.. figure:: ./beam_direction.png + :align: center + The beam direction pattern in XZ-plane for the beam with ``sigma`` = 0.1 m + and ``divergence_x`` = 5 :math:`^{\circ}`. From e2f3c6d1f8c03ca975da441fecfccc6249235b26 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 00:14:43 +0200 Subject: [PATCH 079/134] Undo adding docstrings to ExcitationLine and RecombinationLine as they will be added in a separate PR. --- .../core/model/plasma/impact_excitation.pyx | 23 ------------------- cherab/core/model/plasma/recombination.pyx | 23 ------------------- .../models/basic_line/basic_line_emission.rst | 4 ---- 3 files changed, 50 deletions(-) diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 70abdfe0..343059ad 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -23,29 +23,6 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ExcitationLine(PlasmaModel): - """ - Emitter that calculates spectral line emission from a plasma object - as a result of excitation of the target species by electron impact. - - .. math:: - \\epsilon_{\\mathrm{excit}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i}} n_\\mathrm{e} - \\mathrm{PEC}_{\\mathrm{excit}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), - - where :math:`n_{Z_\\mathrm{i}}` is the target species density, - :math:`\\mathrm{PEC}_{\\mathrm{excit}}` is the electron impact excitation photon emission coefficient - for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, - :math:`f(\\lambda)` is the normalised spectral line shape, - - :param Line line: Spectroscopic emission line object. - :param Plasma plasma: The plasma to which this emission model is attached. Default is None. - :param AtomicData atomic_data: The atomic data provider for this model. Default is None. - :param object lineshape: Line shape model class. Default is None (GaussianLine). - :param object lineshape_args: A list of line shape model arguments. Default is None. - :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. - - :ivar Plasma plasma: The plasma to which this emission model is attached. - :ivar AtomicData atomic_data: The atomic data provider for this model. - """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/cherab/core/model/plasma/recombination.pyx b/cherab/core/model/plasma/recombination.pyx index c025cb26..6edac138 100644 --- a/cherab/core/model/plasma/recombination.pyx +++ b/cherab/core/model/plasma/recombination.pyx @@ -23,29 +23,6 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class RecombinationLine(PlasmaModel): - """ - Emitter that calculates spectral line emission from a plasma object - as a result of dielectronic recombination of the target species. - - .. math:: - \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} n_\\mathrm{e} - \\mathrm{PEC}_{\\mathrm{recomb}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), - - where :math:`n_{Z_\\mathrm{i} + 1}` is the recombining species density, - :math:`\\mathrm{PEC}_{\\mathrm{recomb}}` is the dielectronic recombination photon emission coefficient - for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, - :math:`f(\\lambda)` is the normalised spectral line shape, - - :param Line line: Spectroscopic emission line object. - :param Plasma plasma: The plasma to which this emission model is attached. Default is None. - :param AtomicData atomic_data: The atomic data provider for this model. Default is None. - :param object lineshape: Line shape model class. Default is None (GaussianLine). - :param object lineshape_args: A list of line shape model arguments. Default is None. - :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. - - :ivar Plasma plasma: The plasma to which this emission model is attached. - :ivar AtomicData atomic_data: The atomic data provider for this model. - """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/docs/source/models/basic_line/basic_line_emission.rst b/docs/source/models/basic_line/basic_line_emission.rst index b71fc03c..b849d1a9 100644 --- a/docs/source/models/basic_line/basic_line_emission.rst +++ b/docs/source/models/basic_line/basic_line_emission.rst @@ -2,8 +2,4 @@ Basic Line Emission =================== -.. autoclass:: cherab.core.model.plasma.impact_excitation.ExcitationLine - -.. autoclass:: cherab.core.model.plasma.recombination.RecombinationLine - .. autoclass:: cherab.core.model.plasma.thermal_cx.ThermalCXLine From c78e020d25c67ae5461231596bdc01c9bd8469db Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 00:20:06 +0200 Subject: [PATCH 080/134] Add comments for atomic rates caching. --- cherab/core/model/plasma/thermal_cx.pyx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx index 5f157bb7..4be9b44c 100644 --- a/cherab/core/model/plasma/thermal_cx.pyx +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -29,7 +29,7 @@ cdef class ThermalCXLine(PlasmaModel): as a result of thermal charge exchange of the target species with the donor species. .. math:: - \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} + \\epsilon_{\\mathrm{CX}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} \\sum_j{n_{Z_\\mathrm{j}} \\mathrm{PEC}_{\\mathrm{cx}}(n_\\mathrm{e}, T_\\mathrm{e}, T_{Z_\\mathrm{j}})} f(\\lambda), @@ -100,6 +100,7 @@ cdef class ThermalCXLine(PlasmaModel): if receiver_density <= 0.0: return spectrum + # obtain composite CX PEC by iterating over all possible CX donors weighted_rate = 0 for species, rate in self._rates: donor_density = species.distribution.density(point.x, point.y, point.z) @@ -136,7 +137,10 @@ cdef class ThermalCXLine(PlasmaModel): # obtain rate functions self._rates = [] + # iterate over all posible electron donors in plasma composition + # and for each donor, cache the PEC rate function for the CX reaction with this receiver for species in self._plasma.composition: + # exclude the receiver species from the list of donors and omit fully ionised species if species != self._target_species and species.charge < species.element.atomic_number: rate = self._atomic_data.thermal_cx_pec(species.element, species.charge, # donor self._line.element, receiver_charge, # receiver From e799a6b393a9b5420e67fc18cadbcbf09a15bf30 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 00:55:08 +0200 Subject: [PATCH 081/134] Make ThermalCXPEC to return zero if plasma parameters are <=zero threshold. --- cherab/openadas/rates/pec.pyx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cherab/openadas/rates/pec.pyx b/cherab/openadas/rates/pec.pyx index 7a17b6a6..7b888b5b 100644 --- a/cherab/openadas/rates/pec.pyx +++ b/cherab/openadas/rates/pec.pyx @@ -24,6 +24,9 @@ from raysect.core.math.function.float cimport Interpolator2DArray, Interpolator3 from cherab.core.utility.conversion import PhotonToJ +DEF ZERO_THRESHOLD = 1.e-300 + + cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): def __init__(self, double wavelength, dict data, extrapolate=False): @@ -134,7 +137,7 @@ cdef class ThermalCXPEC(CoreThermalCXPEC): """ :param wavelength: Resting wavelength of corresponding emission line in nm. :param data: Dictionary containing rate data. - :param extrapolate: Enable extrapolation (default=False). + :param extrapolate: Enable nearest-neighbour extrapolation (default=False). """ self.wavelength = wavelength @@ -162,14 +165,14 @@ cdef class ThermalCXPEC(CoreThermalCXPEC): cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if electron_density < 1.e-300: - electron_density = 1.e-300 + if electron_density < ZERO_THRESHOLD: + return 0 - if electron_temperature < 1.e-300: - electron_temperature = 1.e-300 + if electron_temperature < ZERO_THRESHOLD: + return 0 - if donor_temperature < 1.e-300: - donor_temperature = 1.e-300 + if donor_temperature < ZERO_THRESHOLD: + return 0 # calculate rate and convert from log10 space to linear space return 10 ** self._rate.evaluate(log10(electron_density), log10(electron_temperature), log10(donor_temperature)) From 751878f8228176540023a0c067222880ed5b14fd Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 00:59:08 +0200 Subject: [PATCH 082/134] Pass RecursiveDict as normal dict to functions. --- cherab/openadas/repository/pec.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cherab/openadas/repository/pec.py b/cherab/openadas/repository/pec.py index cd07dbea..eda90e8a 100644 --- a/cherab/openadas/repository/pec.py +++ b/cherab/openadas/repository/pec.py @@ -102,7 +102,7 @@ def add_pec_thermal_cx_rate(donor_element, donor_charge, receiver_element, recei rates2update = RecursiveDict() rates2update[donor_element][donor_charge][receiver_element][receiver_charge][transition] = rate - update_pec_thermal_cx_rates(rates2update, repository_path) + update_pec_thermal_cx_rates(rates2update.freeze(), repository_path) def update_pec_rates(rates, repository_path=None): @@ -248,11 +248,11 @@ def update_pec_thermal_cx_rates(rates, repository_path=None): # create directory structure if missing directory = os.path.dirname(path) if not os.path.isdir(directory): - os.makedirs(directory) + os.makedirs(directory, exist_ok=True) # write new data with open(path, 'w') as f: - json.dump(content, f, indent=2, sort_keys=True) + json.dump(content.freeze(), f, indent=2, sort_keys=True) def get_pec_excitation_rate(element, charge, transition, repository_path=None): From ea9366462a9dd1a90d2065aabacd40d51cd82165 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 01:42:36 +0200 Subject: [PATCH 083/134] Added docstrings for ExcitationLine and RecombinationLine models. --- .../core/model/plasma/impact_excitation.pyx | 23 +++++++++++++++++++ cherab/core/model/plasma/recombination.pyx | 23 +++++++++++++++++++ .../models/basic_line/basic_line_emission.rst | 4 +++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 343059ad..70abdfe0 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -23,6 +23,29 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ExcitationLine(PlasmaModel): + """ + Emitter that calculates spectral line emission from a plasma object + as a result of excitation of the target species by electron impact. + + .. math:: + \\epsilon_{\\mathrm{excit}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i}} n_\\mathrm{e} + \\mathrm{PEC}_{\\mathrm{excit}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + + where :math:`n_{Z_\\mathrm{i}}` is the target species density, + :math:`\\mathrm{PEC}_{\\mathrm{excit}}` is the electron impact excitation photon emission coefficient + for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, + :math:`f(\\lambda)` is the normalised spectral line shape, + + :param Line line: Spectroscopic emission line object. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + :param object lineshape: Line shape model class. Default is None (GaussianLine). + :param object lineshape_args: A list of line shape model arguments. Default is None. + :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. + + :ivar Plasma plasma: The plasma to which this emission model is attached. + :ivar AtomicData atomic_data: The atomic data provider for this model. + """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/cherab/core/model/plasma/recombination.pyx b/cherab/core/model/plasma/recombination.pyx index 6edac138..c025cb26 100644 --- a/cherab/core/model/plasma/recombination.pyx +++ b/cherab/core/model/plasma/recombination.pyx @@ -23,6 +23,29 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class RecombinationLine(PlasmaModel): + """ + Emitter that calculates spectral line emission from a plasma object + as a result of dielectronic recombination of the target species. + + .. math:: + \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} n_\\mathrm{e} + \\mathrm{PEC}_{\\mathrm{recomb}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + + where :math:`n_{Z_\\mathrm{i} + 1}` is the recombining species density, + :math:`\\mathrm{PEC}_{\\mathrm{recomb}}` is the dielectronic recombination photon emission coefficient + for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, + :math:`f(\\lambda)` is the normalised spectral line shape, + + :param Line line: Spectroscopic emission line object. + :param Plasma plasma: The plasma to which this emission model is attached. Default is None. + :param AtomicData atomic_data: The atomic data provider for this model. Default is None. + :param object lineshape: Line shape model class. Default is None (GaussianLine). + :param object lineshape_args: A list of line shape model arguments. Default is None. + :param object lineshape_kwargs: A dictionary of line shape model keyword arguments. Default is None. + + :ivar Plasma plasma: The plasma to which this emission model is attached. + :ivar AtomicData atomic_data: The atomic data provider for this model. + """ def __init__(self, Line line, Plasma plasma=None, AtomicData atomic_data=None, object lineshape=None, object lineshape_args=None, object lineshape_kwargs=None): diff --git a/docs/source/models/basic_line/basic_line_emission.rst b/docs/source/models/basic_line/basic_line_emission.rst index c07995db..f8c80c60 100644 --- a/docs/source/models/basic_line/basic_line_emission.rst +++ b/docs/source/models/basic_line/basic_line_emission.rst @@ -2,4 +2,6 @@ Basic Line Emission =================== -Documentation for this model will go here soon... +.. autoclass:: cherab.core.model.plasma.impact_excitation.ExcitationLine + +.. autoclass:: cherab.core.model.plasma.recombination.RecombinationLine From e91e2a48cc93a3a527a2634c2e46d61167737862 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 13:03:22 +0200 Subject: [PATCH 084/134] Placed the equations for the beam density and direction in the math block instead of inline. --- cherab/core/beam/node.pyx | 9 +++++--- cherab/core/model/attenuator/singleray.pyx | 27 ++++++++++++---------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/cherab/core/beam/node.pyx b/cherab/core/beam/node.pyx index ec25742b..9c3cff65 100644 --- a/cherab/core/beam/node.pyx +++ b/cherab/core/beam/node.pyx @@ -239,9 +239,12 @@ cdef class Beam(Node): The beam direction (non-normalised) is calculated as follows (z > 0): - :math:`e_x = x\\frac{(ztg(\\alpha_x))^2}{\\sigma^2 + (ztg(\\alpha_x))^2}`, - :math:`e_y = y\\frac{(ztg(\\alpha_y))^2}{\\sigma^2 + (ztg(\\alpha_y))^2}`, - :math:`e_z = z`, + .. math:: + e_x = x\\frac{(ztg(\\alpha_x))^2}{\\sigma^2 + (ztg(\\alpha_x))^2}, + + e_y = y\\frac{(ztg(\\alpha_y))^2}{\\sigma^2 + (ztg(\\alpha_y))^2}, + + e_z = z, where :math:`\\sigma` is the Gaussian beam deviation at origin, :math:`\\alpha_x` and :math:`\\alpha_y` are the beam divergence angles diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index 12904524..54672766 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -101,23 +101,26 @@ cdef class SingleRayAttenuator(BeamAttenuator): @cython.cdivision(True) cpdef double density(self, double x, double y, double z) except? -1e999: - r""" + """ Returns the beam density at the specified point in beam coordinate space. The beam density is calculated as follows: - - :math:`n(x, y, z) = \frac{R}{2\pi v_0 \sigma_x\sigma_y} exp\left(-\frac{1}{2}\left(\frac{x^2}{\sigma_x^2}+\frac{y^2}{\sigma_y^2}\right)\right)exp\left(-\int_{0}^{z}\frac{S(z')}{v_0}dz'\right)`, - - :math:`\sigma_x = \sqrt{\sigma^2 + (ztg(\alpha_x))^2}\hspace{0.5cm}\sigma_y = \sqrt{\sigma^2 + (ztg(\alpha_y))^2}`, - - where :math:`R=\frac{P}{E}` is the particle rate of the beam defined as the power - of the beam divided by the kinetic energy of the single particle, :math:`v_0=\sqrt{2E/m}` - is the particle speed, :math:`\sigma` is the Gaussian beam deviation at origin, - :math:`\alpha_x` and :math:`\alpha_y` are the beam divergence angles in the x and y + + .. math:: + n(x, y, z) = \\frac{R}{2\\pi v_0 \\sigma_x\\sigma_y} exp\\left(-\\frac{1}{2}\\left(\\frac{x^2}{\\sigma_x^2}+\\frac{y^2}{\\sigma_y^2}\\right)\\right)exp\\left(-\\int_{0}^{z}\\frac{S(z')}{v_0}dz'\\right), + + \\sigma_x = \\sqrt{\\sigma^2 + (ztg(\\alpha_x))^2}\\hspace{0.5cm}\\sigma_y = \\sqrt{\\sigma^2 + (ztg(\\alpha_y))^2}, + + where :math:`R=\\frac{P}{E}` is the particle rate of the beam defined as the power + of the beam divided by the kinetic energy of the single particle, :math:`v_0=\\sqrt{2E/m}` + is the particle speed, :math:`\\sigma` is the Gaussian beam deviation at origin, + :math:`\\alpha_x` and :math:`\\alpha_y` are the beam divergence angles in the x and y dimensions respectively, :math:`S(z)` is the composite beam attenuation coefficient due to collisional-radiative interaction with the plasma species: - :math:`S(z) = \sum_{i=1}^{N}Z_i n_i S_i(E_{int}, n_{i,e}^{(eq)}, T_i)`, - :math:`n_{i,e}^{(eq)} = \frac{1}{Z_i}\sum_{j=1}^{N}Z_j^2 n_j`. + .. math:: + S(z) = \\sum_{i=1}^{N}Z_i n_i S_i(E_{int}, n_{i,e}^{(eq)}, T_i), + + n_{i,e}^{(eq)} = \\frac{1}{Z_i}\\sum_{j=1}^{N}Z_j^2 n_j. Here :math:`Z_i` is the charge of the i-th type of plasma ions, :math:`n_i` is density of the i-th type of plasma ions, :math:`N` is the number of type of plasma From e46860778c644aaa4dc2046f48c3e50cc626ddba Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 13:22:06 +0200 Subject: [PATCH 085/134] Replace deprecated cumtrapz with cumulative_trapezoid. --- cherab/core/model/attenuator/singleray.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index a2cfbde1..cc8be0aa 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -18,7 +18,7 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from scipy.integrate import cumtrapz +from scipy.integrate import cumulative_trapezoid import numpy as np cimport numpy as np @@ -218,7 +218,7 @@ cdef class SingleRayAttenuator(BeamAttenuator): for i in range(naxis): stopping_coeff[i] = self._beam_stopping(x[i], y[i], z[i], beam_velocity) - return beam_density * np.exp(-cumtrapz(stopping_coeff, axis, initial=0.0) / speed) + return beam_density * np.exp(-cumulative_trapezoid(stopping_coeff, axis, initial=0) / speed) @cython.cdivision(True) cdef double _beam_stopping(self, double x, double y, double z, Vector3D beam_velocity): From 858ad983e716486ee7acdbf30624cb23c38682d7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Mon, 13 May 2024 14:15:45 +0200 Subject: [PATCH 086/134] Handle ImportError for cumulative_trapezoid for SciPy < 1.7. --- cherab/core/model/attenuator/singleray.pyx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index cc8be0aa..fdc09b98 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -18,7 +18,11 @@ # See the Licence for the specific language governing permissions and limitations # under the Licence. -from scipy.integrate import cumulative_trapezoid +try: + from scipy.integrate import cumulative_trapezoid +except ImportError: + from scipy.integrate import cumtrapz as cumulative_trapezoid + import numpy as np cimport numpy as np From eb21099d7ba213de83f460a4b6ac180b70b8245f Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 12:37:09 +0200 Subject: [PATCH 087/134] Add normalisation to invert_regularised_nnls(). Add **kwargs to pass to scipy.optimize.nnls. --- cherab/tools/inversions/nnls.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cherab/tools/inversions/nnls.py b/cherab/tools/inversions/nnls.py index 76f8149a..f7580b09 100644 --- a/cherab/tools/inversions/nnls.py +++ b/cherab/tools/inversions/nnls.py @@ -21,7 +21,7 @@ import scipy -def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None): +def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None, **kwargs): """ Solves :math:`\mathbf{b} = \mathbf{W} \mathbf{x}` for the vector :math:`\mathbf{x}`, using Tikhonov regulariastion. @@ -36,6 +36,7 @@ def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None the regularisation strength of the tikhonov matrix. :param np.ndarray tikhonov_matrix: The tikhonov regularisation matrix operator, an array with shape :math:`(N_s, N_s)`. If None, the identity matrix is used. + :param **kwargs: Keyword arguments passed to scipy.optimize.nnls. :return: (x, norm), the solution vector and the residual norm. .. code-block:: pycon @@ -60,6 +61,9 @@ def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None d_vector = np.zeros(m+n) d_vector[0:m] = b_vector[:] - x_vector, rnorm = scipy.optimize.nnls(c_matrix, d_vector) + # Normalise c_matrix and d_vector to avoid possible issues with the nnls termination criteria. + vmax = d_vector.max() + + x_vector, rnorm = scipy.optimize.nnls(c_matrix / vmax, d_vector / vmax, **kwargs) return x_vector, rnorm From a8d8cce35334ada24a1c31c8bfa7aa010b8af0e4 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 15:18:06 +0200 Subject: [PATCH 088/134] Updated invert_regularised_nnls docstring and CHANGELOG.md. --- CHANGELOG.md | 1 + cherab/tools/inversions/nnls.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8156571a..02a1146e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ New: * Replace the coarse numerical constant in the Bremsstrahlung model with an exact expression. (#409) * Add the kind attribute to RayTransferPipelineXD that determines whether the ray transfer matrix is multiplied by sensitivity ('power') or not ('radiance'). (#412) * Improved parsing of metadata from the ADAS ADF15 'bnd' files for H-like ions. Raises a runtime error if the metadata cannot be parsed. (#424) +* Add kwargs to invert_regularised_nnls to pass them to scipy.optimize.nnls. (#438) Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) diff --git a/cherab/tools/inversions/nnls.py b/cherab/tools/inversions/nnls.py index f7580b09..87e1d09c 100644 --- a/cherab/tools/inversions/nnls.py +++ b/cherab/tools/inversions/nnls.py @@ -22,13 +22,16 @@ def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None, **kwargs): - """ + r""" Solves :math:`\mathbf{b} = \mathbf{W} \mathbf{x}` for the vector :math:`\mathbf{x}`, using Tikhonov regulariastion. This is a thin wrapper around scipy.optimize.nnls, which modifies the arguments to include the supplied Tikhonov regularisation matrix. + The values of w_matrix, b_vector and alpha * tikhonov_matrix are notmalised + by max(b_vector) before passing them to scipy.optimize.nnls(). + :param np.ndarray w_matrix: The sensitivity matrix describing the coupling between the detectors and the voxels. Must be an array with shape :math:`(N_d, N_s)`. :param np.ndarray b_vector: The measured power/radiance vector with shape :math:`(N_d)`. From 753d7de67ccca95ea40fdb579d9e7882c8322997 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 17:04:05 +0200 Subject: [PATCH 089/134] Converted docstrings to raw string format. --- cherab/core/beam/node.pyx | 10 +++++----- cherab/core/model/attenuator/singleray.pyx | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cherab/core/beam/node.pyx b/cherab/core/beam/node.pyx index 9c3cff65..32d7291d 100644 --- a/cherab/core/beam/node.pyx +++ b/cherab/core/beam/node.pyx @@ -234,20 +234,20 @@ cdef class Beam(Node): return self._attenuator.density(x, y, z) cpdef Vector3D direction(self, double x, double y, double z): - """ + r""" Calculates the beam direction vector at a point in beam coordinate space. The beam direction (non-normalised) is calculated as follows (z > 0): .. math:: - e_x = x\\frac{(ztg(\\alpha_x))^2}{\\sigma^2 + (ztg(\\alpha_x))^2}, + e_x = x\frac{(ztg(\alpha_x))^2}{\sigma^2 + (ztg(\alpha_x))^2}, - e_y = y\\frac{(ztg(\\alpha_y))^2}{\\sigma^2 + (ztg(\\alpha_y))^2}, + e_y = y\frac{(ztg(\alpha_y))^2}{\sigma^2 + (ztg(\alpha_y))^2}, e_z = z, - where :math:`\\sigma` is the Gaussian beam deviation at origin, - :math:`\\alpha_x` and :math:`\\alpha_y` are the beam divergence angles + where :math:`\sigma` is the Gaussian beam deviation at origin, + :math:`\alpha_x` and :math:`\alpha_y` are the beam divergence angles in the x and y dimensions respectively. For z <= 0 the beam direction is (0, 0, 1). diff --git a/cherab/core/model/attenuator/singleray.pyx b/cherab/core/model/attenuator/singleray.pyx index 54672766..3c86fb06 100644 --- a/cherab/core/model/attenuator/singleray.pyx +++ b/cherab/core/model/attenuator/singleray.pyx @@ -101,26 +101,26 @@ cdef class SingleRayAttenuator(BeamAttenuator): @cython.cdivision(True) cpdef double density(self, double x, double y, double z) except? -1e999: - """ + r""" Returns the beam density at the specified point in beam coordinate space. The beam density is calculated as follows: .. math:: - n(x, y, z) = \\frac{R}{2\\pi v_0 \\sigma_x\\sigma_y} exp\\left(-\\frac{1}{2}\\left(\\frac{x^2}{\\sigma_x^2}+\\frac{y^2}{\\sigma_y^2}\\right)\\right)exp\\left(-\\int_{0}^{z}\\frac{S(z')}{v_0}dz'\\right), + n(x, y, z) = \frac{R}{2\pi v_0 \sigma_x\sigma_y} exp\left(-\frac{1}{2}\left(\frac{x^2}{\sigma_x^2}+\frac{y^2}{\sigma_y^2}\right)\right)exp\left(-\int_{0}^{z}\frac{S(z')}{v_0}dz'\right), - \\sigma_x = \\sqrt{\\sigma^2 + (ztg(\\alpha_x))^2}\\hspace{0.5cm}\\sigma_y = \\sqrt{\\sigma^2 + (ztg(\\alpha_y))^2}, + \sigma_x = \sqrt{\sigma^2 + (ztg(\alpha_x))^2}\hspace{0.5cm}\sigma_y = \sqrt{\sigma^2 + (ztg(\alpha_y))^2}, - where :math:`R=\\frac{P}{E}` is the particle rate of the beam defined as the power - of the beam divided by the kinetic energy of the single particle, :math:`v_0=\\sqrt{2E/m}` - is the particle speed, :math:`\\sigma` is the Gaussian beam deviation at origin, - :math:`\\alpha_x` and :math:`\\alpha_y` are the beam divergence angles in the x and y + where :math:`R=\frac{P}{E}` is the particle rate of the beam defined as the power + of the beam divided by the kinetic energy of the single particle, :math:`v_0=\sqrt{2E/m}` + is the particle speed, :math:`\sigma` is the Gaussian beam deviation at origin, + :math:`\alpha_x` and :math:`\alpha_y` are the beam divergence angles in the x and y dimensions respectively, :math:`S(z)` is the composite beam attenuation coefficient due to collisional-radiative interaction with the plasma species: .. math:: - S(z) = \\sum_{i=1}^{N}Z_i n_i S_i(E_{int}, n_{i,e}^{(eq)}, T_i), + S(z) = \sum_{i=1}^{N}Z_i n_i S_i(E_{int}, n_{i,e}^{(eq)}, T_i), - n_{i,e}^{(eq)} = \\frac{1}{Z_i}\\sum_{j=1}^{N}Z_j^2 n_j. + n_{i,e}^{(eq)} = \frac{1}{Z_i}\sum_{j=1}^{N}Z_j^2 n_j. Here :math:`Z_i` is the charge of the i-th type of plasma ions, :math:`n_i` is density of the i-th type of plasma ions, :math:`N` is the number of type of plasma From a433d5f5136968be2a6cd0906010e9b524e5e654 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 17:05:17 +0200 Subject: [PATCH 090/134] Rename TestAtomicData to MockAtomicData to avoid unittest warnings. --- cherab/core/tests/test_beam.py | 4 ++-- cherab/core/tests/test_beamcxline.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cherab/core/tests/test_beam.py b/cherab/core/tests/test_beam.py index 9be75996..ef10126f 100644 --- a/cherab/core/tests/test_beam.py +++ b/cherab/core/tests/test_beam.py @@ -45,7 +45,7 @@ def evaluate(self, energy, density, temperature): return self.value -class TestAtomicData(AtomicData): +class MockAtomicData(AtomicData): """Fake atomic data for test purpose.""" def beam_stopping_rate(self, beam_ion, plasma_ion, charge): @@ -55,7 +55,7 @@ def beam_stopping_rate(self, beam_ion, plasma_ion, charge): class TestBeam(unittest.TestCase): - atomic_data = TestAtomicData() + atomic_data = MockAtomicData() world = World() diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py index 6de8ad58..ccb0c855 100644 --- a/cherab/core/tests/test_beamcxline.py +++ b/cherab/core/tests/test_beamcxline.py @@ -58,7 +58,7 @@ def evaluate(self, energy, density, temperature): return self.value -class TestAtomicData(AtomicData): +class MockAtomicData(AtomicData): """Fake atomic data for test purpose.""" def beam_cx_pec(self, donor_ion, receiver_ion, receiver_charge, transition): @@ -78,7 +78,7 @@ class TestBeamCXLine(unittest.TestCase): world = World() - atomic_data = TestAtomicData() + atomic_data = MockAtomicData() plasma_species = [(deuterium, 1, 1.e19, 200., Vector3D(0, 0, 0))] plasma = build_constant_slab_plasma(length=1, width=1, height=1, electron_density=1e19, electron_temperature=200., From f92ff9d54c1a1bc9fce5ca8409a6afec2ac935f2 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 17:25:54 +0200 Subject: [PATCH 091/134] Convert docstrings for ExcitationLine and RecombinationLine to raw string format. --- cherab/core/model/plasma/impact_excitation.pyx | 14 +++++++------- cherab/core/model/plasma/recombination.pyx | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cherab/core/model/plasma/impact_excitation.pyx b/cherab/core/model/plasma/impact_excitation.pyx index 70abdfe0..8a5d993c 100644 --- a/cherab/core/model/plasma/impact_excitation.pyx +++ b/cherab/core/model/plasma/impact_excitation.pyx @@ -23,18 +23,18 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ExcitationLine(PlasmaModel): - """ + r""" Emitter that calculates spectral line emission from a plasma object as a result of excitation of the target species by electron impact. .. math:: - \\epsilon_{\\mathrm{excit}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i}} n_\\mathrm{e} - \\mathrm{PEC}_{\\mathrm{excit}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + \epsilon_{\mathrm{excit}}(\lambda) = \frac{1}{4 \pi} n_{Z_\mathrm{i}} n_\mathrm{e} + \mathrm{PEC}_{\mathrm{excit}}(n_\mathrm{e}, T_\mathrm{e}) f(\lambda), - where :math:`n_{Z_\\mathrm{i}}` is the target species density, - :math:`\\mathrm{PEC}_{\\mathrm{excit}}` is the electron impact excitation photon emission coefficient - for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, - :math:`f(\\lambda)` is the normalised spectral line shape, + where :math:`n_{Z_\mathrm{i}}` is the target species density, + :math:`\mathrm{PEC}_{\mathrm{excit}}` is the electron impact excitation photon emission coefficient + for the specified spectral line of the :math:`Z_\mathrm{i}` ion, + :math:`f(\lambda)` is the normalised spectral line shape, :param Line line: Spectroscopic emission line object. :param Plasma plasma: The plasma to which this emission model is attached. Default is None. diff --git a/cherab/core/model/plasma/recombination.pyx b/cherab/core/model/plasma/recombination.pyx index c025cb26..004205a0 100644 --- a/cherab/core/model/plasma/recombination.pyx +++ b/cherab/core/model/plasma/recombination.pyx @@ -23,18 +23,18 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class RecombinationLine(PlasmaModel): - """ + r""" Emitter that calculates spectral line emission from a plasma object as a result of dielectronic recombination of the target species. .. math:: - \\epsilon_{\\mathrm{recomb}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} n_\\mathrm{e} - \\mathrm{PEC}_{\\mathrm{recomb}}(n_\\mathrm{e}, T_\\mathrm{e}) f(\\lambda), + \epsilon_{\mathrm{recomb}}(\lambda) = \frac{1}{4 \pi} n_{Z_\mathrm{i} + 1} n_\mathrm{e} + \mathrm{PEC}_{\mathrm{recomb}}(n_\mathrm{e}, T_\mathrm{e}) f(\lambda), - where :math:`n_{Z_\\mathrm{i} + 1}` is the recombining species density, - :math:`\\mathrm{PEC}_{\\mathrm{recomb}}` is the dielectronic recombination photon emission coefficient - for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, - :math:`f(\\lambda)` is the normalised spectral line shape, + where :math:`n_{Z_\mathrm{i} + 1}` is the recombining species density, + :math:`\mathrm{PEC}_{\mathrm{recomb}}` is the dielectronic recombination photon emission coefficient + for the specified spectral line of the :math:`Z_\mathrm{i}` ion, + :math:`f(\lambda)` is the normalised spectral line shape, :param Line line: Spectroscopic emission line object. :param Plasma plasma: The plasma to which this emission model is attached. Default is None. From 4e2d169dc22c27389dbb86795025b158a86c0092 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 14 May 2024 18:21:35 +0200 Subject: [PATCH 092/134] Convert docstring for ThermalCXLine to raw string format. --- cherab/core/model/plasma/thermal_cx.pyx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cherab/core/model/plasma/thermal_cx.pyx b/cherab/core/model/plasma/thermal_cx.pyx index 4be9b44c..1325919c 100644 --- a/cherab/core/model/plasma/thermal_cx.pyx +++ b/cherab/core/model/plasma/thermal_cx.pyx @@ -24,21 +24,21 @@ from cherab.core.utility.constants cimport RECIP_4_PI cdef class ThermalCXLine(PlasmaModel): - """ + r""" Emitter that calculates spectral line emission from a plasma object as a result of thermal charge exchange of the target species with the donor species. .. math:: - \\epsilon_{\\mathrm{CX}}(\\lambda) = \\frac{1}{4 \\pi} n_{Z_\\mathrm{i} + 1} - \\sum_j{n_{Z_\\mathrm{j}} \\mathrm{PEC}_{\\mathrm{cx}}(n_\\mathrm{e}, T_\\mathrm{e}, T_{Z_\\mathrm{j}})} - f(\\lambda), - - where :math:`n_{Z_\\mathrm{i} + 1}` is the receiver species density, - :math:`n_{Z_\\mathrm{j}}` is the donor species density, - :math:`\\mathrm{PEC}_{\\mathrm{cx}}` is the thermal CX photon emission coefficient - for the specified spectral line of the :math:`Z_\\mathrm{i}` ion, - :math:`T_{Z_\\mathrm{j}}` is the donor species temperature, - :math:`f(\\lambda)` is the normalised spectral line shape, + \epsilon_{\mathrm{CX}}(\lambda) = \frac{1}{4 \pi} n_{Z_\mathrm{i} + 1} + \sum_j{n_{Z_\mathrm{j}} \mathrm{PEC}_{\mathrm{cx}}(n_\mathrm{e}, T_\mathrm{e}, T_{Z_\mathrm{j}})} + f(\lambda), + + where :math:`n_{Z_\mathrm{i} + 1}` is the receiver species density, + :math:`n_{Z_\mathrm{j}}` is the donor species density, + :math:`\mathrm{PEC}_{\mathrm{cx}}` is the thermal CX photon emission coefficient + for the specified spectral line of the :math:`Z_\mathrm{i}` ion, + :math:`T_{Z_\mathrm{j}}` is the donor species temperature, + :math:`f(\lambda)` is the normalised spectral line shape, :param Line line: Spectroscopic emission line object. :param Plasma plasma: The plasma to which this emission model is attached. Default is None. From 6d034321e03ecfdf1da74d87e41be36b3422e8e0 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 16 May 2024 18:02:04 +0200 Subject: [PATCH 093/134] Moved plasma setup to setUp() function in the unit tests for line emission. --- cherab/core/tests/test_line_emission.py | 61 ++++++++++++++----------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/cherab/core/tests/test_line_emission.py b/cherab/core/tests/test_line_emission.py index eb5a8599..b55af871 100644 --- a/cherab/core/tests/test_line_emission.py +++ b/cherab/core/tests/test_line_emission.py @@ -68,7 +68,7 @@ def evaluate(self, electron_density, electron_temperature, donor_temperature): return self.value -class TestAtomicData(AtomicData): +class MockAtomicData(AtomicData): """Fake atomic data for test purpose.""" def impact_excitation_pec(self, ion, charge, transition): @@ -90,16 +90,19 @@ def wavelength(self, ion, charge, transition): class TestExcitationLine(unittest.TestCase): - world = World() + def setUp(self): - atomic_data = TestAtomicData() + self.world = World() - plasma_species = [(carbon, 5, 2.e18, 800., Vector3D(0, 0, 0))] - slab_length = 1.2 - plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., - plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) - plasma.atomic_data = atomic_data - plasma.parent = world + self.atomic_data = MockAtomicData() + + plasma_species = [(carbon, 5, 2.e18, 800., Vector3D(0, 0, 0))] + self.slab_length = 1.2 + self.plasma = build_constant_slab_plasma(length=self.slab_length, width=1, height=1, + electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + self.plasma.atomic_data = self.atomic_data + self.plasma.parent = self.world def test_default_lineshape(self): # setting up the model @@ -162,16 +165,19 @@ def test_custom_lineshape(self): class TestRecombinationLine(unittest.TestCase): - world = World() + def setUp(self): + + self.world = World() - atomic_data = TestAtomicData() + self.atomic_data = MockAtomicData() - plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0))] - slab_length = 1.2 - plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., - plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) - plasma.atomic_data = atomic_data - plasma.parent = world + plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0))] + self.slab_length = 1.2 + self.plasma = build_constant_slab_plasma(length=self.slab_length, width=1, height=1, + electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + self.plasma.atomic_data = self.atomic_data + self.plasma.parent = self.world def test_default_lineshape(self): # setting up the model @@ -234,17 +240,20 @@ def test_custom_lineshape(self): class TestThermalCXLine(unittest.TestCase): - world = World() + def setUp(self): + + self.world = World() - atomic_data = TestAtomicData() + self.atomic_data = MockAtomicData() - plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0)), - (deuterium, 0, 1.e19, 100., Vector3D(0, 0, 0))] - slab_length = 1.2 - plasma = build_constant_slab_plasma(length=slab_length, width=1, height=1, electron_density=1e19, electron_temperature=1000., - plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) - plasma.atomic_data = atomic_data - plasma.parent = world + plasma_species = [(carbon, 6, 1.67e18, 800., Vector3D(0, 0, 0)), + (deuterium, 0, 1.e19, 100., Vector3D(0, 0, 0))] + self.slab_length = 1.2 + self.plasma = build_constant_slab_plasma(length=self.slab_length, width=1, height=1, + electron_density=1e19, electron_temperature=1000., + plasma_species=plasma_species, b_field=Vector3D(0, 10., 0)) + self.plasma.atomic_data = self.atomic_data + self.plasma.parent = self.world def test_default_lineshape(self): # setting up the model From c04bac7e989689ae16fc813631daef890f07d813 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 16 May 2024 18:05:47 +0200 Subject: [PATCH 094/134] Replaced zero threshold with 0 in openadas ThermalCXPEC. --- cherab/openadas/rates/pec.pyx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/cherab/openadas/rates/pec.pyx b/cherab/openadas/rates/pec.pyx index 7b888b5b..d8c66d9b 100644 --- a/cherab/openadas/rates/pec.pyx +++ b/cherab/openadas/rates/pec.pyx @@ -24,9 +24,6 @@ from raysect.core.math.function.float cimport Interpolator2DArray, Interpolator3 from cherab.core.utility.conversion import PhotonToJ -DEF ZERO_THRESHOLD = 1.e-300 - - cdef class ImpactExcitationPEC(CoreImpactExcitationPEC): def __init__(self, double wavelength, dict data, extrapolate=False): @@ -165,13 +162,7 @@ cdef class ThermalCXPEC(CoreThermalCXPEC): cpdef double evaluate(self, double electron_density, double electron_temperature, double donor_temperature) except? -1e999: # need to handle zeros, also density and temperature can become negative due to cubic interpolation - if electron_density < ZERO_THRESHOLD: - return 0 - - if electron_temperature < ZERO_THRESHOLD: - return 0 - - if donor_temperature < ZERO_THRESHOLD: + if electron_density <= 0 or electron_temperature <= 0 or donor_temperature <= 0: return 0 # calculate rate and convert from log10 space to linear space From b413c4f4bf15a202094d461358698042be7ad382 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 16 May 2024 18:27:17 +0200 Subject: [PATCH 095/134] Moved C5+ PEC installation from the demo to populate(). --- cherab/openadas/repository/create.py | 1 + demos/emission_models/thermal_charge_exchange.py | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/cherab/openadas/repository/create.py b/cherab/openadas/repository/create.py index 6b9a4f33..b42e5651 100644 --- a/cherab/openadas/repository/create.py +++ b/cherab/openadas/repository/create.py @@ -147,6 +147,7 @@ def populate(download=True, repository_path=None, adas_path=None): (carbon, 0, 'adf15/pec96#c/pec96#c_vsu#c0.dat'), (carbon, 1, 'adf15/pec96#c/pec96#c_vsu#c1.dat'), (carbon, 2, 'adf15/pec96#c/pec96#c_vsu#c2.dat'), + (carbon, 5, 'adf15/pec96#c/pec96#c_pju#c5.dat'), # (neon, 0, 'adf15/pec96#ne/pec96#ne_pju#ne0.dat'), #TODO: OPENADAS DATA CORRUPT # (neon, 1, 'adf15/pec96#ne/pec96#ne_pju#ne1.dat'), #TODO: OPENADAS DATA CORRUPT (nitrogen, 0, 'adf15/pec96#n/pec96#n_vsu#n0.dat'), diff --git a/demos/emission_models/thermal_charge_exchange.py b/demos/emission_models/thermal_charge_exchange.py index f4cead84..42c7b88a 100644 --- a/demos/emission_models/thermal_charge_exchange.py +++ b/demos/emission_models/thermal_charge_exchange.py @@ -1,5 +1,4 @@ -import numpy as np import matplotlib.pyplot as plt from scipy.constants import electron_mass, atomic_mass @@ -16,9 +15,6 @@ from cherab.openadas.install import install_adf15 -# Install PECs for CVI spectral lines -install_adf15(carbon, 5, 'adf15/pec96#c/pec96#c_pju#c5.dat', download=True) - ############### # Make Plasma # From 76b533f977cf2e6623f0d7000903107452d62b95 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 16 May 2024 18:31:25 +0200 Subject: [PATCH 096/134] Removed isdir() check before makedirs because exist_ok is set to True. --- cherab/openadas/repository/pec.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cherab/openadas/repository/pec.py b/cherab/openadas/repository/pec.py index eda90e8a..a2bc17b5 100644 --- a/cherab/openadas/repository/pec.py +++ b/cherab/openadas/repository/pec.py @@ -247,8 +247,7 @@ def update_pec_thermal_cx_rates(rates, repository_path=None): # create directory structure if missing directory = os.path.dirname(path) - if not os.path.isdir(directory): - os.makedirs(directory, exist_ok=True) + os.makedirs(directory, exist_ok=True) # write new data with open(path, 'w') as f: From b0113d9174aefc5c8f916858a541c63d42aff259 Mon Sep 17 00:00:00 2001 From: vsnever Date: Tue, 21 May 2024 23:43:21 +0200 Subject: [PATCH 097/134] Updated CHANGELOG with the info about the C VI lines. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 716df7d7..5d7798c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ New: * **Beam dispersion calculation has changed from sigma(z) = sigma + z * tan(alpha) to sigma(z) = sqrt(sigma^2 + (z * tan(alpha))^2) for consistancy with the Gaussian beam model. Attention!!! The results of BES and CX spectroscopy are affected by this change. (#414)** * Improved beam direction calculation to allow for natural broadening of the BES line shape due to beam divergence. (#414) * Add thermal charge-exchange emission model. (#57) +* PECs for C VI spectral lines for n <= 5 are now included in populate(). Rerun populate() after upgrading to 1.5 to update the atomic data repository. Bug fixes: * Fix deprecated transforms being cached in LaserMaterial after laser.transform update (#420) From e96b381601790ef239e551d545ab9b2ea4cdd3c7 Mon Sep 17 00:00:00 2001 From: vsnever Date: Thu, 23 May 2024 01:38:07 +0200 Subject: [PATCH 098/134] Rescale rnorm after parameter normalisation in invert_regularised_nnls. --- cherab/tools/inversions/nnls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cherab/tools/inversions/nnls.py b/cherab/tools/inversions/nnls.py index 87e1d09c..823bb7c2 100644 --- a/cherab/tools/inversions/nnls.py +++ b/cherab/tools/inversions/nnls.py @@ -69,4 +69,4 @@ def invert_regularised_nnls(w_matrix, b_vector, alpha=0.01, tikhonov_matrix=None x_vector, rnorm = scipy.optimize.nnls(c_matrix / vmax, d_vector / vmax, **kwargs) - return x_vector, rnorm + return x_vector, rnorm * vmax From b5866254e86f81cae2b9dfa6cface41730b25daf Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 28 Jun 2024 00:37:53 +0200 Subject: [PATCH 099/134] Add donor_metastable attribute to core BeamCXPEC (#411). Replace receiver ion density with the total ion density in BeamCXPEC (#441). --- cherab/core/atomic/rates.pxd | 2 ++ cherab/core/atomic/rates.pyx | 7 ++++++- cherab/core/model/beam/charge_exchange.pxd | 2 +- cherab/core/model/beam/charge_exchange.pyx | 14 +++++++------- cherab/core/tests/test_beamcxline.py | 2 +- cherab/openadas/rates/cx.pxd | 1 - cherab/openadas/rates/cx.pyx | 4 ++-- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/cherab/core/atomic/rates.pxd b/cherab/core/atomic/rates.pxd index 1f844122..ac8d69ce 100644 --- a/cherab/core/atomic/rates.pxd +++ b/cherab/core/atomic/rates.pxd @@ -51,6 +51,8 @@ cdef class ThermalCXPEC(_PECRate): cdef class BeamCXPEC: + cdef readonly int donor_metastable + cpdef double evaluate(self, double energy, double temperature, double density, double z_effective, double b_field) except? -1e999 diff --git a/cherab/core/atomic/rates.pyx b/cherab/core/atomic/rates.pyx index f3a653db..52510e4b 100644 --- a/cherab/core/atomic/rates.pyx +++ b/cherab/core/atomic/rates.pyx @@ -144,8 +144,13 @@ cdef class BeamCXPEC: transition :math:`n\rightarrow n'` of ion :math:`Z^{(\alpha+1)+}` with electron donor :math:`H^0` in metastable state :math:`m_{i}`. Equivalent to :math:`q^{eff}_{n\rightarrow n'}` in `adf12 _`. + + :param donor_metastable: The metastable state of the donor species for which the rate data applies. """ + def __init__(self, int donor_metastable): + self.donor_metastable = donor_metastable + def __call__(self, double energy, double temperature, double density, double z_effective, double b_field): """Evaluates the Beam CX rate at the given plasma conditions. @@ -158,7 +163,7 @@ cdef class BeamCXPEC: :param float energy: Interaction energy in eV/amu. :param float temperature: Receiver ion temperature in eV. - :param float density: Receiver ion density in m^-3 + :param float density: Plasma total ion density in m^-3 :param float z_effective: Plasma Z-effective. :param float b_field: Magnetic field magnitude in Tesla. :return: The effective rate diff --git a/cherab/core/model/beam/charge_exchange.pxd b/cherab/core/model/beam/charge_exchange.pxd index cd0b48db..46e22891 100644 --- a/cherab/core/model/beam/charge_exchange.pxd +++ b/cherab/core/model/beam/charge_exchange.pxd @@ -36,7 +36,7 @@ cdef class BeamCXLine(BeamModel): object _lineshape_class, _lineshape_args, _lineshape_kwargs cdef double _composite_cx_rate(self, double x, double y, double z, double interaction_energy, - Vector3D donor_velocity, double receiver_temperature, double receiver_density) except? -1e999 + Vector3D donor_velocity, double receiver_temperature) except? -1e999 cdef double _beam_population(self, double x, double y, double z, Vector3D beam_velocity, list population_data) except? -1e999 diff --git a/cherab/core/model/beam/charge_exchange.pyx b/cherab/core/model/beam/charge_exchange.pyx index 295d5f5f..12650ddb 100644 --- a/cherab/core/model/beam/charge_exchange.pyx +++ b/cherab/core/model/beam/charge_exchange.pyx @@ -158,7 +158,7 @@ cdef class BeamCXLine(BeamModel): interaction_energy = ms_to_evamu(interaction_speed) # calculate the composite charge-exchange emission coefficient - emission_rate = self._composite_cx_rate(x, y, z, interaction_energy, donor_velocity, receiver_temperature, receiver_density) + emission_rate = self._composite_cx_rate(x, y, z, interaction_energy, donor_velocity, receiver_temperature) # spectral line emission in W/m^3/str radiance = RECIP_4_PI * donor_density * receiver_density * emission_rate @@ -169,7 +169,7 @@ cdef class BeamCXLine(BeamModel): @cython.wraparound(False) @cython.cdivision(True) cdef double _composite_cx_rate(self, double x, double y, double z, double interaction_energy, - Vector3D donor_velocity, double receiver_temperature, double receiver_density) except? -1e999: + Vector3D donor_velocity, double receiver_temperature) except? -1e999: """ Performs a beam population weighted average of the effective cx rates. @@ -188,23 +188,23 @@ cdef class BeamCXLine(BeamModel): :param interaction_energy: The donor-receiver interaction energy in eV/amu. :param donor_velocity: A Vector defining the donor particle velocity in m/s. :param receiver_temperature: The receiver species temperature in eV. - :param receiver_density: The receiver species density in m^-3 :return: The composite charge exchange rate in W.m^3. """ cdef: - double z_effective, b_field, rate, total_population, population, effective_rate + double z_effective, b_field, rate, total_population, population, effective_rate, ion_density BeamCXPEC cx_rate list population_data - # calculate z_effective and the B-field magnitude + # calculate ion density, z_effective and the B-field magnitude + ion_density = self._plasma.ion_density(x, y, z) z_effective = self._plasma.z_effective(x, y, z) b_field = self._plasma.get_b_field().evaluate(x, y, z).get_length() # rate for the ground state (metastable = 1) rate = self._ground_beam_rate.evaluate(interaction_energy, receiver_temperature, - receiver_density, + ion_density, z_effective, b_field) @@ -219,7 +219,7 @@ cdef class BeamCXLine(BeamModel): effective_rate = cx_rate.evaluate(interaction_energy, receiver_temperature, - receiver_density, + ion_density, z_effective, b_field) diff --git a/cherab/core/tests/test_beamcxline.py b/cherab/core/tests/test_beamcxline.py index ccb0c855..f20c4861 100644 --- a/cherab/core/tests/test_beamcxline.py +++ b/cherab/core/tests/test_beamcxline.py @@ -36,7 +36,7 @@ class ConstantBeamCXPEC(BeamCXPEC): """ def __init__(self, donor_metastable, value): - self.donor_metastable = donor_metastable + super().__init__(donor_metastable) self.value = value def evaluate(self, energy, temperature, density, z_effective, b_field): diff --git a/cherab/openadas/rates/cx.pxd b/cherab/openadas/rates/cx.pxd index 393027d0..4afcadfc 100644 --- a/cherab/openadas/rates/cx.pxd +++ b/cherab/openadas/rates/cx.pxd @@ -25,7 +25,6 @@ cdef class BeamCXPEC(CoreBeamCXPEC): cdef readonly: dict raw_data double wavelength - int donor_metastable Function1D _eb, _ti, _ni, _zeff, _b readonly tuple beam_energy_range readonly tuple density_range diff --git a/cherab/openadas/rates/cx.pyx b/cherab/openadas/rates/cx.pyx index cb827a8b..dfdcf8e8 100644 --- a/cherab/openadas/rates/cx.pyx +++ b/cherab/openadas/rates/cx.pyx @@ -37,7 +37,7 @@ cdef class BeamCXPEC(CoreBeamCXPEC): @cython.cdivision(True) def __init__(self, int donor_metastable, double wavelength, dict data, bint extrapolate=False): - self.donor_metastable = donor_metastable + super().__init__(donor_metastable) self.wavelength = wavelength self.raw_data = data @@ -79,7 +79,7 @@ cdef class BeamCXPEC(CoreBeamCXPEC): :param energy: Interaction energy in eV/amu. :param temperature: Receiver ion temperature in eV. - :param density: Receiver ion density in m^-3 + :param density: Plasma total ion density in m^-3 :param z_effective: Plasma Z-effective. :param b_field: Magnetic field magnitude in Tesla. :return: The effective cx rate in W.m^3 From 5208b016b9deb9c2abbe0ba7d0f89c4fa822112c Mon Sep 17 00:00:00 2001 From: vsnever Date: Fri, 28 Jun 2024 00:39:23 +0200 Subject: [PATCH 100/134] Update figures for CX spectroscopy in documentation. --- .../CXS_multi_sightlines.png | Bin 59708 -> 56528 bytes .../active_spectroscopy/CXS_spectrum.png | Bin 24577 -> 26470 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/demonstrations/active_spectroscopy/CXS_multi_sightlines.png b/docs/source/demonstrations/active_spectroscopy/CXS_multi_sightlines.png index b04c94c0f5d8951b21edaff49875ea09ffebd141..f94e95ab45872636f40cb1cdda7bac29b4abbbc0 100644 GIT binary patch literal 56528 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