Skip to content

impurity_radiation

Module for impurity radiation calculations and data handling.

ImpurityDataHeader dataclass

Represents a header or metadata section of an impurity data file.

If this is a header for some section of data then the data section will be populated with the array of data for which this is a header of.

Source code in process/models/physics/impurity_radiation.py
214
215
216
217
218
219
220
221
222
223
224
225
@dataclasses.dataclass
class ImpurityDataHeader:
    """Represents a header or metadata section of an impurity data
    file.

    If this is a header for some section of data then the data section
    will be populated with the array of data for which this is a
    header of.
    """

    content: str
    data: list[float] | None = None

content instance-attribute

data = None class-attribute instance-attribute

ImpurityRadiation

Calculates the impurity radiation losses for given temperature and density profiles. The considers the total impurity radiation from the core (pden_impurity_core_rad_total_mw) and total impurity radiation (pden_impurity_rad_total_mw) [MW/(m³)]. The class is used to sum the impurity radiation loss from each impurity element to find the total impurity radiation loss.

Source code in process/models/physics/impurity_radiation.py
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
class ImpurityRadiation:
    """Calculates the impurity radiation losses for given temperature and
    density profiles. The considers the  total impurity radiation from the core
    (pden_impurity_core_rad_total_mw) and total impurity radiation
    (pden_impurity_rad_total_mw) [MW/(m³)]. The class is used to sum the impurity
    radiation loss from each impurity element to find the total impurity radiation loss.
    """

    def __init__(self, plasma_profile: PlasmaProfile, data_structure: DataStructure):
        """Initialize the ImpurityRadiation class.

        Parameters
        ----------
        plasma_profile :
            Parameterises the density and temperature profiles.
        """
        self.data = data_structure
        self.plasma_profile = plasma_profile
        self.imp = np.nonzero(
            self.data.impurity_radiation.f_nd_impurity_electron_array > 1.0e-30
        )[0]

        self.pden_impurity_radiation_profile = np.zeros(
            self.data.physics.n_plasma_profile_elements
        )
        self.pden_impurity_rad_profile = np.zeros(
            self.data.physics.n_plasma_profile_elements
        )
        self.pden_impurity_core_rad_profile = np.zeros(
            self.data.physics.n_plasma_profile_elements
        )
        self.pden_impurity_rad_edge_profile = np.zeros(
            self.data.physics.n_plasma_profile_elements
        )

        self.pden_impurity_rad_total_mw = 0.0
        self.pden_impurity_core_rad_total_mw = 0.0
        self.pden_impurity_rad_edge_total_mw = 0.0

    def run(self):
        """ImpurityRadiation model isn't run"""

    def output(self):
        """ImpurityRadiation model has no output"""

    def map_imprad_profile(self):
        """Map imprad_profile() over each impurity element index."""
        list(map(self.imprad_profile, self.imp))

    def imprad_profile(self, imp_element_index: int) -> None:
        """Calculates the impurity radiation losses for given temperature
        and density profiles.

        Parameters
        ----------
        imp_element_index:
            Index used to access different impurity radiation elements

        """
        pden_impurity_radiation_profile = calculate_impurity_radiation_power_density(
            imp_element_index=imp_element_index,
            nd_electron_profile=self.plasma_profile.neprofile.profile_y,
            temp_electron_profile_kev=self.plasma_profile.teprofile.profile_y,
            data=self.data,
        )

        self.pden_impurity_radiation_profile = np.add(
            self.pden_impurity_radiation_profile, pden_impurity_radiation_profile
        )

    def calculate_radiation_loss_profiles(self):
        """Calculate the Bremsstrahlung (radb), line radiation (radl), total impurity
        radiation from the core (pden_impurity_core_rad_total_mw) and total impurity
        radiation  (pden_impurity_rad_total_mw). Update the stored arrays with the
        values.
        """
        pden_impurity_rad_total = (
            self.pden_impurity_radiation_profile
            * self.plasma_profile.neprofile.profile_x
        )
        pden_impurity_core_rad_total = self.pden_impurity_radiation_profile * (
            self.plasma_profile.neprofile.profile_x
            * create_f_rad_core_profile(
                rho=self.plasma_profile.neprofile.profile_x,
                radius_plasma_core_norm=self.data.impurity_radiation.radius_plasma_core_norm,
                f_p_plasma_core_rad_reduction=self.data.impurity_radiation.f_p_plasma_core_rad_reduction,
            )
        )

        self.pden_impurity_rad_profile = np.add(
            self.pden_impurity_rad_profile, pden_impurity_rad_total
        )
        self.pden_impurity_core_rad_profile = np.add(
            self.pden_impurity_core_rad_profile, pden_impurity_core_rad_total
        )

    def integrate_radiation_loss_profiles(self):
        """Integrate the radiation loss profiles using the Simpson rule.
        Store the total values for each aspect of impurity radiation loss.
        """
        # 1e-6 converts from W/m^3 to MW/m^3
        # The factor 2 below and and normalised radius profile_x above may be unexpected,
        # but are correct:
        # see github.com/ukaea/PROCESS/issues/3968#issuecomment-3491154712
        # and github.com/ukaea/PROCESS/issues/3968#issuecomment-4935567006
        self.pden_impurity_rad_total_mw = 2.0e-6 * integrate.simpson(
            self.pden_impurity_rad_profile,
            x=self.plasma_profile.neprofile.profile_x,
            dx=self.plasma_profile.neprofile.profile_dx,
        )
        self.pden_impurity_core_rad_total_mw = 2.0e-6 * integrate.simpson(
            self.pden_impurity_core_rad_profile,
            x=self.plasma_profile.neprofile.profile_x,
            dx=self.plasma_profile.neprofile.profile_dx,
        )

    def calculate_imprad(self):
        """Call the map function to calculate impurity radiation parameters for each
        impurity element. Calculate the radiation loss profiles, and integrate them to
        find the total values for radiation loss.
        """
        self.map_imprad_profile()
        self.calculate_radiation_loss_profiles()
        self.integrate_radiation_loss_profiles()

data = data_structure instance-attribute

plasma_profile = plasma_profile instance-attribute

imp = np.nonzero(self.data.impurity_radiation.f_nd_impurity_electron_array > 1e-30)[0] instance-attribute

pden_impurity_radiation_profile = np.zeros(self.data.physics.n_plasma_profile_elements) instance-attribute

pden_impurity_rad_profile = np.zeros(self.data.physics.n_plasma_profile_elements) instance-attribute

pden_impurity_core_rad_profile = np.zeros(self.data.physics.n_plasma_profile_elements) instance-attribute

pden_impurity_rad_edge_profile = np.zeros(self.data.physics.n_plasma_profile_elements) instance-attribute

pden_impurity_rad_total_mw = 0.0 instance-attribute

pden_impurity_core_rad_total_mw = 0.0 instance-attribute

pden_impurity_rad_edge_total_mw = 0.0 instance-attribute

run()

ImpurityRadiation model isn't run

Source code in process/models/physics/impurity_radiation.py
671
672
def run(self):
    """ImpurityRadiation model isn't run"""

output()

ImpurityRadiation model has no output

Source code in process/models/physics/impurity_radiation.py
674
675
def output(self):
    """ImpurityRadiation model has no output"""

map_imprad_profile()

Map imprad_profile() over each impurity element index.

Source code in process/models/physics/impurity_radiation.py
677
678
679
def map_imprad_profile(self):
    """Map imprad_profile() over each impurity element index."""
    list(map(self.imprad_profile, self.imp))

imprad_profile(imp_element_index)

Calculates the impurity radiation losses for given temperature and density profiles.

Parameters:

Name Type Description Default
imp_element_index int

Index used to access different impurity radiation elements

required
Source code in process/models/physics/impurity_radiation.py
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
def imprad_profile(self, imp_element_index: int) -> None:
    """Calculates the impurity radiation losses for given temperature
    and density profiles.

    Parameters
    ----------
    imp_element_index:
        Index used to access different impurity radiation elements

    """
    pden_impurity_radiation_profile = calculate_impurity_radiation_power_density(
        imp_element_index=imp_element_index,
        nd_electron_profile=self.plasma_profile.neprofile.profile_y,
        temp_electron_profile_kev=self.plasma_profile.teprofile.profile_y,
        data=self.data,
    )

    self.pden_impurity_radiation_profile = np.add(
        self.pden_impurity_radiation_profile, pden_impurity_radiation_profile
    )

calculate_radiation_loss_profiles()

Calculate the Bremsstrahlung (radb), line radiation (radl), total impurity radiation from the core (pden_impurity_core_rad_total_mw) and total impurity radiation (pden_impurity_rad_total_mw). Update the stored arrays with the values.

Source code in process/models/physics/impurity_radiation.py
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
def calculate_radiation_loss_profiles(self):
    """Calculate the Bremsstrahlung (radb), line radiation (radl), total impurity
    radiation from the core (pden_impurity_core_rad_total_mw) and total impurity
    radiation  (pden_impurity_rad_total_mw). Update the stored arrays with the
    values.
    """
    pden_impurity_rad_total = (
        self.pden_impurity_radiation_profile
        * self.plasma_profile.neprofile.profile_x
    )
    pden_impurity_core_rad_total = self.pden_impurity_radiation_profile * (
        self.plasma_profile.neprofile.profile_x
        * create_f_rad_core_profile(
            rho=self.plasma_profile.neprofile.profile_x,
            radius_plasma_core_norm=self.data.impurity_radiation.radius_plasma_core_norm,
            f_p_plasma_core_rad_reduction=self.data.impurity_radiation.f_p_plasma_core_rad_reduction,
        )
    )

    self.pden_impurity_rad_profile = np.add(
        self.pden_impurity_rad_profile, pden_impurity_rad_total
    )
    self.pden_impurity_core_rad_profile = np.add(
        self.pden_impurity_core_rad_profile, pden_impurity_core_rad_total
    )

integrate_radiation_loss_profiles()

Integrate the radiation loss profiles using the Simpson rule. Store the total values for each aspect of impurity radiation loss.

Source code in process/models/physics/impurity_radiation.py
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
def integrate_radiation_loss_profiles(self):
    """Integrate the radiation loss profiles using the Simpson rule.
    Store the total values for each aspect of impurity radiation loss.
    """
    # 1e-6 converts from W/m^3 to MW/m^3
    # The factor 2 below and and normalised radius profile_x above may be unexpected,
    # but are correct:
    # see github.com/ukaea/PROCESS/issues/3968#issuecomment-3491154712
    # and github.com/ukaea/PROCESS/issues/3968#issuecomment-4935567006
    self.pden_impurity_rad_total_mw = 2.0e-6 * integrate.simpson(
        self.pden_impurity_rad_profile,
        x=self.plasma_profile.neprofile.profile_x,
        dx=self.plasma_profile.neprofile.profile_dx,
    )
    self.pden_impurity_core_rad_total_mw = 2.0e-6 * integrate.simpson(
        self.pden_impurity_core_rad_profile,
        x=self.plasma_profile.neprofile.profile_x,
        dx=self.plasma_profile.neprofile.profile_dx,
    )

calculate_imprad()

Call the map function to calculate impurity radiation parameters for each impurity element. Calculate the radiation loss profiles, and integrate them to find the total values for radiation loss.

Source code in process/models/physics/impurity_radiation.py
748
749
750
751
752
753
754
755
def calculate_imprad(self):
    """Call the map function to calculate impurity radiation parameters for each
    impurity element. Calculate the radiation loss profiles, and integrate them to
    find the total values for radiation loss.
    """
    self.map_imprad_profile()
    self.calculate_radiation_loss_profiles()
    self.integrate_radiation_loss_profiles()

initialise_imprad(data)

Initialises the impurity radiation data structure

This routine initialises the impurity radiation data.

Source code in process/models/physics/impurity_radiation.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def initialise_imprad(data: DataStructure):
    """Initialises the impurity radiation data structure

    This routine initialises the impurity radiation data.
    """
    errorflag = 0

    table_length = 200  # Number of temperature and Lz values in data file

    f_nd_species_electron = 1.0e0

    #  Hydrogen

    init_imp_element(
        n_species_index=1,
        name_label=data.impurity_radiation.imp_label[0],
        z=1,
        m_species_amu=constants.M_PROTIUM_AMU,  # 1.00782503223 1H
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    f_nd_species_electron = 0.0e0

    #  Helium
    init_imp_element(
        n_species_index=2,
        name_label=data.impurity_radiation.imp_label[1],
        z=2,
        m_species_amu=constants.M_HELIUM_AMU,  # 4.002602 (3He,4He) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Beryllium
    init_imp_element(
        n_species_index=3,
        name_label=data.impurity_radiation.imp_label[2],
        z=4,
        m_species_amu=constants.M_BERYLLIUM_AMU,  # 9.0121831 9Be
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Carbon
    init_imp_element(
        n_species_index=4,
        name_label=data.impurity_radiation.imp_label[3],
        z=6,
        m_species_amu=constants.M_CARBON_AMU,  # 12.0096, (12C,13C,14C) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Nitrogen
    init_imp_element(
        n_species_index=5,
        name_label=data.impurity_radiation.imp_label[4],
        z=7,
        m_species_amu=constants.M_NITROGEN_AMU,  # 14.00643, (14N,15N) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Oxygen
    init_imp_element(
        n_species_index=6,
        name_label=data.impurity_radiation.imp_label[5],
        z=8,
        m_species_amu=constants.M_OXYGEN_AMU,  # 15.99903, (16O,17O,18O) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Neon
    init_imp_element(
        n_species_index=7,
        name_label=data.impurity_radiation.imp_label[6],
        z=10,
        m_species_amu=constants.M_NEON_AMU,  # 20.1797 (20Ne,21Ne,22Ne) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Silicon
    init_imp_element(
        n_species_index=8,
        name_label=data.impurity_radiation.imp_label[7],
        z=14,
        m_species_amu=constants.M_SILICON_AMU,  # 28.084 (28Si,29Si,30Si) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Argon
    init_imp_element(
        n_species_index=9,
        name_label=data.impurity_radiation.imp_label[8],
        z=18,
        m_species_amu=constants.M_ARGON_AMU,  # 39.948 (40Ar,36Ar,38Ar) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Iron
    init_imp_element(
        n_species_index=10,
        name_label=data.impurity_radiation.imp_label[9],
        z=26,
        m_species_amu=constants.M_IRON_AMU,  # 55.845 (56Fe,54Fe,57Fe,58Fe) Average mass
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Nickel
    init_imp_element(
        n_species_index=11,
        name_label=data.impurity_radiation.imp_label[10],
        z=28,
        # 58.6934 (58Ni,60Ni,61Ni,62Ni,64Ni) Average mass
        m_species_amu=constants.M_NICKEL_AMU,
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Krypton
    init_imp_element(
        n_species_index=12,
        name_label=data.impurity_radiation.imp_label[11],
        z=36,
        # 83.798 (84Kr,86Kr,82Kr,80Kr,78Kr) Average mass
        m_species_amu=constants.M_KRYPTON_AMU,
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Xenon
    init_imp_element(
        n_species_index=13,
        name_label=data.impurity_radiation.imp_label[12],
        z=54,
        # 131.293 (132Xe,129Xe,131Xe,134Xe,136Xe) Average mass
        m_species_amu=constants.M_XENON_AMU,
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

    #  Tungsten
    init_imp_element(
        n_species_index=14,
        name_label=data.impurity_radiation.imp_label[13],
        z=74,
        # 183.84 (184W,186W,182W,183W,180W) Average mass
        m_species_amu=constants.M_TUNGSTEN_AMU,
        f_nd_species_electron=f_nd_species_electron,
        len_tab=table_length,
        error=errorflag,
        data=data,
    )

read_impurity_file(impurity_file)

Reads an impurity data file and returns a list of ImpurityDataHeader objects representing the headers and associated data in the file.

Parameters:

Name Type Description Default
impurity_file Path

Path to the impurity data file to read.

required

Returns:

Type Description
list[ImpurityDataHeader]

A list of ImpurityDataHeader objects representing the headers and associated data in the impurity data file.

Source code in process/models/physics/impurity_radiation.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
def read_impurity_file(impurity_file: Path):
    """Reads an impurity data file and returns a list of ImpurityDataHeader
    objects representing the headers and associated data in the file.

    Parameters
    ----------
    impurity_file : Path
        Path to the impurity data file to read.

    Returns
    -------
    list[ImpurityDataHeader]
        A list of ImpurityDataHeader objects representing the headers and
        associated data in the impurity data file.
    """
    with open(impurity_file) as f:
        data = f.readlines()

    file_contents: list[ImpurityDataHeader] = []

    for line in data:
        # do not parse comments
        clean_line = line.strip().replace("\n", "")
        if clean_line[0:3].upper() in {"C  ", "C ", "C", "C--"}:
            continue

        if re.fullmatch(r"[0-9\.e+\- ]+", clean_line) is not None:
            header = file_contents[-1]

            new_data = clean_line.split(" ")
            if header.data is None:
                header.data = new_data
            else:
                header.data += new_data
        else:
            file_contents.append(ImpurityDataHeader(clean_line))

    return file_contents

init_imp_element(n_species_index, name_label, z, m_species_amu, f_nd_species_electron, len_tab, error, data)

Initialise the impurity radiation data for a species.

This routine initialises the impurity radiation data structure for a given impurity species. The Lz versus temperature data are read in from file.

Parameters:

Name Type Description Default
n_species_index int

Position of species in impurity array

required
name_label str

Species name

required
z int

Species charge number

required
m_species_amu float

Species atomic mass (amu)

required
f_nd_species_electron float

Number density / electron density

required
len_tab int

Length of temperature and Lz tables

required
error int

Error flag; 0 = okay, 1 = missing impurity data

required

Raises:

Type Description
ProcessValueError

If illegal impurity number is provided

FileNotFoundError

If impurity data files are missing

ProcessError

If required data cannot be located in files

Source code in process/models/physics/impurity_radiation.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
def init_imp_element(
    n_species_index: int,
    name_label: str,
    z: int,
    m_species_amu: float,
    f_nd_species_electron: float,
    len_tab: int,
    error: int,
    data: DataStructure,
):
    """Initialise the impurity radiation data for a species.

    This routine initialises the impurity radiation data structure
    for a given impurity species. The Lz versus temperature data are
    read in from file.

    Parameters
    ----------
    n_species_index : int
        Position of species in impurity array
    name_label : str
        Species name
    z : int
        Species charge number
    m_species_amu : float
        Species atomic mass (amu)
    f_nd_species_electron : float
        Number density / electron density
    len_tab : int
        Length of temperature and Lz tables
    error : int
        Error flag; 0 = okay, 1 = missing impurity data

    Raises
    ------
    ProcessValueError
        If illegal impurity number is provided
    FileNotFoundError
        If impurity data files are missing
    ProcessError
        If required data cannot be located in files
    """
    if error == 1:
        return

    if n_species_index > len(data.impurity_radiation.impurity_arr_label):
        raise ProcessValueError(
            "Illegal impurity number",
            number=n_species_index,
            max=len(data.impurity_radiation.impurity_arr_label),
        )

    data.impurity_radiation.impurity_arr_label[n_species_index - 1] = name_label
    data.impurity_radiation.impurity_arr_z[n_species_index - 1] = z
    data.impurity_radiation.m_impurity_amu_array[n_species_index - 1] = m_species_amu
    data.impurity_radiation.f_nd_impurity_electron_array[n_species_index - 1] = (
        f_nd_species_electron
    )
    data.impurity_radiation.impurity_arr_len_tab[n_species_index - 1] = len_tab

    if len_tab > 200:
        print(
            f"ERROR: len_tab is {len_tab} but has a maximum value of "
            f"{data.impurity_radiation.all_array_hotfix_len}"
        )

    impurity_dir = resources.files("process") / "data/lz_non_corona_14_elements/"

    lz_file = impurity_dir / f"{name_label}_lz_tau.dat"
    z_file = impurity_dir / f"{name_label}_z_tau.dat"

    if not lz_file.exists() or not z_file.exists():
        raise FileNotFoundError(
            f"Cannot find one or both of the impurity datafiles: {lz_file}, {z_file}"
        )

    lz_data = read_impurity_file(lz_file)
    z_data = read_impurity_file(z_file)

    Te = None
    lz = None

    for header in lz_data:
        if "Te[eV]" in header.content:
            Te = np.asarray(header.data, dtype=float)

        if "infinite confinement" in header.content:
            lz = np.asarray(header.data, dtype=float)

    if Te is None:
        raise ProcessError(f"Cannot locate Te data in {lz_file}")
    if lz is None:
        raise ProcessError(
            f"Cannot locate Lz for infinite confinement data in {lz_file}"
        )

    zav = None
    for header in z_data:
        if "infinite confinement" in header.content:
            zav = np.asarray(header.data, dtype=float)

    if zav is None:
        raise ProcessError(
            f"Cannot locate Zav for infinite confinement data in {z_file}"
        )

    data.impurity_radiation.temp_impurity_keV_array[n_species_index - 1, :] = Te * 1e-3
    data.impurity_radiation.pden_impurity_lz_nd_temp_array[n_species_index - 1, :] = lz
    data.impurity_radiation.impurity_arr_zav[n_species_index - 1, :] = zav

create_f_rad_core_profile(rho, radius_plasma_core_norm, f_p_plasma_core_rad_reduction)

Creates an array of the same length as rho filled with the value of f_p_plasma_core_rad_reduction for values of rho less than radius_plasma_core_norm and 0 for values of rho greater than or equal to radius_plasma_core_norm.

Parameters:

Name Type Description Default
rho array

normalised minor radius

required
radius_plasma_core_norm float

normalised radius defining the 'core' region

required
f_p_plasma_core_rad_reduction float

fraction of radiation from the core region

required

Returns:

Type Description
f_rad_core_profile - array filled with the f_p_plasma_core_rad_reduction
Source code in process/models/physics/impurity_radiation.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
def create_f_rad_core_profile(
    rho: np.array, radius_plasma_core_norm: float, f_p_plasma_core_rad_reduction: float
) -> np.array:
    """
    Creates an array of the same length as `rho` filled with the value of
    `f_p_plasma_core_rad_reduction` for values of `rho` less than
    `radius_plasma_core_norm` and 0 for values of `rho` greater than or equal to
    `radius_plasma_core_norm`.

    Parameters
    ----------
    rho: np.array
        normalised minor radius
    radius_plasma_core_norm: float
        normalised radius defining the 'core' region
    f_p_plasma_core_rad_reduction: float
        fraction of radiation from the core region

    Returns
    -------
        f_rad_core_profile - array filled with the f_p_plasma_core_rad_reduction
    """
    f_rad_core_profile = np.zeros(len(rho))
    rho_mask = rho < radius_plasma_core_norm
    f_rad_core_profile[rho_mask] = f_p_plasma_core_rad_reduction

    return f_rad_core_profile

calculate_average_charge_at_temp(imp_element_index, temp_electron_kev, data)

Calculates electron temperature dependent average atomic charge (Z) for a given impurity element.

Parameters:

Name Type Description Default
imp_element_index int

Impurity element index

required
temp_electron_kev array | float

electron temperature in keV

required
data DataStructure

DataStructure containing impurity radiation data

required

Returns:

Type Description
array

zav_of_te - electron temperature dependent average atomic charge

Source code in process/models/physics/impurity_radiation.py
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
def calculate_average_charge_at_temp(
    imp_element_index: int, temp_electron_kev: np.array | float, data: DataStructure
) -> np.array | float:
    """Calculates electron temperature dependent average atomic charge (Z) for a given
    impurity element.

    Parameters
    ----------
    imp_element_index:
        Impurity element index
    temp_electron_kev:
        electron temperature in keV
    data:
        DataStructure containing impurity radiation data

    Returns
    -------
    numpy.array
        zav_of_te - electron temperature dependent average atomic charge
    """
    return _calculate_average_charge_at_temp_compiled(
        imp_element_index=imp_element_index,
        temp_electron_kev=temp_electron_kev,
        temp_impurity_keV_array=data.impurity_radiation.temp_impurity_keV_array,
        impurity_arr_zav=data.impurity_radiation.impurity_arr_zav,
        impurity_arr_len_tab=data.impurity_radiation.impurity_arr_len_tab,
    )

calculate_impurity_radiation_power_density(imp_element_index, nd_electron_profile, temp_electron_profile_kev, data)

Calculates the impurity radiation density [W/m³] based on the electron density and temperature profiles.

Parameters:

Name Type Description Default
imp_element_index int

Impurity element index

required
nd_electron_profile array

electron density profile [m⁻³]

required
temp_electron_profile_kev array

electron temperature profile [keV]

required

Returns:

Type Description
pden_impurity_profile - total impurity radiation density [W/m³]
Notes

-Temperatures outside the range of the L(Z,Tₑ) table are handled by using the L(Z,Tₑ) value at the closest temperature in the table,

Source code in process/models/physics/impurity_radiation.py
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
def calculate_impurity_radiation_power_density(
    imp_element_index: int,
    nd_electron_profile: np.array,
    temp_electron_profile_kev: np.array,
    data: DataStructure,
) -> np.array:
    """
    Calculates the impurity radiation density [W/m³] based on the electron density and
    temperature profiles.

    Parameters
    ----------
    imp_element_index:
        Impurity element index
    nd_electron_profile:
        electron density profile [m⁻³]
    temp_electron_profile_kev:
        electron temperature profile [keV]

    Returns
    -------
    pden_impurity_profile - total impurity radiation density [W/m³]

    Notes
    -----
    -Temperatures outside the range of the L(Z,Tₑ) table are handled by using the
    L(Z,Tₑ) value at the closest temperature in the table,
    """
    bins = data.impurity_radiation.temp_impurity_keV_array[imp_element_index]
    indices = np.digitize(temp_electron_profile_kev, bins)
    indices[indices >= bins.shape[0]] = bins.shape[0] - 1
    indices[indices < 0] = 0

    # Use numpy.interp for linear interpolation in log-log space to find the
    # loss function values for the given temperature profile L(Z, Tₑ).
    power_loss_function = np.exp(
        np.interp(
            np.log(temp_electron_profile_kev),
            np.log(
                data.impurity_radiation.temp_impurity_keV_array[imp_element_index, :]
            ),
            np.log(
                data.impurity_radiation.pden_impurity_lz_nd_temp_array[
                    imp_element_index, :
                ]
            ),
        )
    )

    # W/m³ = nᵢ * nₑ * L(Z, Tₑ)
    # nᵢ = f_nd_species_electron * nₑ
    pden_impurity_profile = (
        data.impurity_radiation.f_nd_impurity_electron_array[imp_element_index]
        * nd_electron_profile
        * nd_electron_profile
        * power_loss_function
    )

    # less_than_imp_temp_mask = temp_electron_profile_kev values less than impurity
    # temperature.

    less_than_imp_temp_mask = (
        temp_electron_profile_kev
        <= data.impurity_radiation.temp_impurity_keV_array[imp_element_index, 0]
    )
    # This is okay because line radiation will dominate at lower temp, and the L(Z,Tₑ)
    # value at the lowest temperature in the table is likely to be an overestimate of the
    # radiation loss at lower temperatures, so this is a conservative approach.
    pden_impurity_profile[less_than_imp_temp_mask] = (
        data.impurity_radiation.pden_impurity_lz_nd_temp_array[imp_element_index, 0]
    )

    # greater_than_imp_temp_mask = temp_electron_profile_kev values higher than
    # impurity temperature.
    greater_than_imp_temp_mask = (
        temp_electron_profile_kev
        >= data.impurity_radiation.temp_impurity_keV_array[
            imp_element_index,
            data.impurity_radiation.impurity_arr_len_tab[imp_element_index] - 1,
        ]
    )
    #  This is okay because Bremsstrahlung will dominate at higher temp.
    pden_impurity_profile[greater_than_imp_temp_mask] = (
        data.impurity_radiation.pden_impurity_lz_nd_temp_array[
            imp_element_index,
            data.impurity_radiation.impurity_arr_len_tab[imp_element_index] - 1,
        ]
    )

    return pden_impurity_profile

element2index(element, data)

Returns the index of the element in the impurity array with a given name

Parameters:

Name Type Description Default
element str
required

Raises:

Type Description
ProcessValueError

If the element is not found in impurity_arr_label

Source code in process/models/physics/impurity_radiation.py
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
def element2index(element: str, data: DataStructure):
    """Returns the index of the `element` in the impurity array with
    a given name

    Parameters
    ----------
    element: str :

    Raises
    ------
    ProcessValueError
        If the element is not found in impurity_arr_label

    """
    try:
        return (
            data.impurity_radiation.impurity_arr_label
            .astype(str)
            .tolist()
            .index(element)
        )
    except ValueError as e:
        raise ProcessValueError(
            f"Element {element} is not found in impurity_arr_label"
        ) from e