Plasma state and atomic data

src/physics/plasma.jl

Public

Runes2P.IonComposition — Type
IonComposition(ions)

The species of a plasma and their charge states, without densities: the names, the nuclear charges Z, and the components of each species in an unknown of the ion densities (ion_densities_unknown), where species s holds the charge states Z0 = 0, …, Z_s at the components species_components(composition, s), in the order of ions. ions is a list of IonSpecies, a PlasmaState, or pairs name => Z.

source
Runes2P.IonSpecies — Type
IonSpecies{T}

Ion species with nuclear charge nuclear_charge (Z) and charge-state densities charge_state_densities[Z0 + 1, ir] [m⁻³] for charge states Z0 = 0, …, Z at each radius.

source
Runes2P.IonSpecies — Method
IonSpecies(name, Z, Z0, density)

Species in a single charge state Z0 with density profile density [m⁻³] (vector over radius).

source
Runes2P.PlasmaState — Type
PlasmaState{T}

Background ("cold") plasma seen by the kinetic electrons at each radius: cold-electron temperature [eV] and density [m⁻³], ion species, the electric field [V/m] (DREAM's E_field, ⟨E·B⟩/√⟨B²⟩) and the minimum magnetic field on each flux surface [T]. cold_density_given records whether the cold-electron density was given, or is the free-electron density of the ions by default: a system that holds the electrons of the grids as unknowns refuses that default as the target of the e-e collisions.

source
Runes2P.PlasmaState — Method
PlasmaState(; cold_electron_temperature, ions, electric_field, magnetic_field,
            cold_electron_density=nothing)

Keyword constructor; without cold_electron_density the cold-electron density is the free-electron density from quasi-neutrality (free_electron_density) at each radius.

source
Runes2P.component_count — Method
component_count(composition::IonComposition)

The number of charge states of all species, Σ (Z_s + 1): the components of the unknown of the ion densities.

source
Runes2P.free_electron_density — Method
free_electron_density(ions, ir)

Σ Z0 n_{Z0} [m⁻³] over all ion charge states (DREAM GetFreeElectronDensityFromQuasiNeutrality).

source
Runes2P.species_components — Method
species_components(composition, s)

The components of the charge states Z0 = 0, …, Z of species s (an index or a name) in the unknown of the ion densities.

source
Runes2P.with_electric_field — Method
with_electric_field(plasma, electric_field)

The plasma state with another electric field [V/m] (one value per radius), e.g. the field of the state of a run in place of a prescribed one.

source

Internal

Runes2P.atomic_element — Method
atomic_element(name)

The element of an ion species named name, whose atomic data the species takes (ADAS tables, ionization energies, cross sections, masses): the name itself, or for a label of the form "<element>_<label>" the part before the underscore. A label keeps two species of one element apart, such as the injected deuterium "D_inj" of DREAM's disruption example next to the background deuterium, which DREAM calls Dinj and evolves with an ion energy of its own (examples/Disruptions/generate.py:439).

source

src/physics/plasma_source.jl

Public

Runes2P.PlasmaFromUnknowns — Type
PlasmaFromUnknowns(prescribed; temperature=:cold_electron_temperature,
                   density=:cold_electron_density, ions=nothing, temperature_floor=0.5)

A PlasmaSource whose cold-electron temperature [eV] and density [m⁻³] are the values of radial unknowns of the system at each radius, with the rest of the plasma (ions, fields) from prescribed, a PlasmaState, a function of time that returns one, or a PrescribedPlasma. density = nothing keeps the density of prescribed, which a PlasmaState built without cold_electron_density takes as the free-electron density: in a fully kinetic case the target of the e-e collisions then counts the hot and runaway electrons as well, where DREAM's target is the closure n_cold = n_free − n_hot − n_re. The values are taken without their derivative parts, so the coefficients of a term hold numbers. Their derivatives enter the Jacobian as columns of their own.

ions names the radial unknown of the densities of the ion charge states, whose components follow the species of prescribed in their order (IonComposition, ion_densities_unknown); the densities of the prescribed ions are then ignored. A plasma whose ions are unknowns takes its density from an unknown too: the free-electron density changes with the charge states, and the prescribed density would not follow it.

A temperature at or below temperature_floor [eV] raises a DomainError, which the Newton iteration reports as a failed solve and the stepper answers with a shorter step: a step that would end below the floor is refused. The default is 0.5 eV (DEFAULT_TEMPERATURE_FLOOR). DREAM has no floor, so a comparison that follows DREAM below 0.5 eV sets a lower value. The floor is also the lower limit of the temperature unknown in the Newton iteration, which damps an update that would cross it (LowerLimit, NewtonSolver). The numerical limit lies far below: the collision model needs the scaled Bessel functions of mₑc²/T, which fail above an argument of about 1e9, near 5e-4 eV.

source
Runes2P.PrescribedPlasma — Type
PrescribedPlasma(plasma_at)

A PlasmaSource prescribed in time: plasma_at is a PlasmaState or a function of the time [s] that returns one. The state of the last time is kept, since the evaluations of a term at one time share it. A PlasmaState given directly is constant for the whole run, and a term reads it once.

source
Runes2P.plasma_columns — Method
plasma_columns(source::PlasmaFromUnknowns, layout, ir; with_ions=true)

The entries of the state at radius ir that the partials of dual_plasma belong to: the temperature, the density when the source reads it, and with with_ions the components of the ion densities when the source reads them, as the argument of dual_plasma.

source
Runes2P.plasma_values — Function
plasma_values(source::PlasmaSource, u, layout, t)

The PlasmaState of source at the state u of the system with the layout layout, and the time t [s]. A prescribed source ignores u and layout, which may then be nothing.

source

Internal

Runes2P.PlasmaKey — Type
PlasmaKey()

The plasma values per radius that a cache of coefficients was built from. update_key! tells whether a new plasma differs from them at any radius.

source
Runes2P.changed_plasma! — Method
changed_plasma!(key::PlasmaKey, source::PlasmaSource, u, layout, t; electric_field=true)

The plasma of source at the state u and the time t when its values differ from those of key (update_key!, which stores them), and nothing when they do not. A prescribed source is compared once per time, so the evaluations of a term at one time cost no comparison. ion_state is the state the ion densities of a PlasmaFromUnknowns are taken from, the start of the step for a term that lags them.

source
Runes2P.dual_plasma — Function
dual_plasma(plasma::PlasmaState, with_density::Bool, with_ions::Bool=false)

The plasma with the cold-electron temperature as a dual number of partial 1 at every radius, with with_density the cold-electron density as partial 2, and with with_ions the densities of the charge states as the partials after them, in the order of the components of the ion densities (IonComposition); the rest holds zero partials. The coefficients at a radius depend only on the plasma of that radius, so partial k of a coefficient at radius ir is its derivative with respect to plasma scalar k at ir, for all radii in one evaluation. plasma_columns gives the entries of the state of the partials.

source
Runes2P.update_key! — Method
update_key!(key::PlasmaKey, plasma::PlasmaState; electric_field=true)
update_key!(key::Vector{Float64}, values)

Store the values of plasma (or the numbers values, one per radius) in key, and return whether they differ from the stored ones at any radius (true for an empty key). The values are compared with isequal. electric_field = false leaves the electric field out, for coefficients that do not hold it.

source
Runes2P.with_state_ions — Method
with_state_ions(plasma::PlasmaState, u, layout, name)

The plasma with the densities of its ion charge states taken from the radial unknown name of the state u, in the number type of u: a term that evaluates its residual with dual numbers keeps their derivative parts, so its Jacobian holds the columns of the ion densities. The species are those of plasma, in its order (IonComposition).

source

src/physics/data_files.jl

Internal

Runes2P.DATA_DIRECTORY — Constant
DATA_DIRECTORY

The directory data/ at the root of the package, which holds the tables of numbers as TOML files (data/README.md).

source
Runes2P.DATA_FILE_METADATA_KEYS — Constant
DATA_FILE_METADATA_KEYS

The keys that the table [metadata] of every data file holds: source (the full citation or DREAM's file), version_or_date (the version of the source or the date of retrieval), licence (its licence or terms of use) and generator (the script that writes the file).

source
Runes2P.float_matrix — Method
float_matrix(rows) -> Matrix{Float64}

The TOML array of rows rows as a Matrix{Float64} with one row per element, so that a file reads like the Julia literal: [[1.0, 2.0, 3.0]] is the 1×3 matrix [1.0 2.0 3.0]. The elements are converted as by float_vector. It throws an error if the rows differ in length or if rows is empty.

source
Runes2P.float_vector — Method
float_vector(values) -> Vector{Float64}

The TOML array values as a Vector{Float64}. Floats are kept bit for bit (nan gives NaN); integers become the Float64 of the same value and throw an error if that value has no exact Float64. Any other element (a boolean, a string, an array) throws an error.

source
Runes2P.int_vector — Method
int_vector(values) -> Vector{Int}

The TOML array of integers values as a Vector{Int}, also when it is empty. Any other element throws an error.

source
Runes2P.parse_data_file — Method
parse_data_file(path) -> Dict{String, Any}

The parsed content of the TOML data file at path, after a check of its header. It throws an error if the file has no table metadata, if one of DATA_FILE_METADATA_KEYS is missing there or is not a non-empty string, or if the table holds a value that is not a string: TOML puts every key after the header [metadata] into it, so the numbers go into a table of their own.

source
Runes2P.read_data_file — Method
read_data_file(name) -> Dict{String, Any}

The parsed content of the data file data/<name>.toml, its table metadata included, as parse_data_file gives it.

It is called in the body of the module, so the file is read once when the package is precompiled and the constants built from it cost nothing at run time. The file is registered with include_dependency by its content, so a change of the content recompiles the package and a copy of the depot elsewhere does not.

The arrays of the result are converted with float_vector, float_matrix and int_vector.

source

src/physics/atomic_data.jl

Internal

Runes2P.ION_SIZE_A — Constant
ION_SIZE_A

Normalized effective ion size aj (multiply by 2/α) of the effective ion size table (Hesslow et al., J. Plasma Phys. 84, 905840605 (2018), Table 1, through DREAM's PitchScatterFrequency.cpp). Read from `data/ionsizes.toml`.

source
Runes2P.ION_SIZE_Z — Constant
ION_SIZE_Z

Effective ion size table (Hesslow et al., J. Plasma Phys. 84, 905840605 (2018), Table 1, through DREAM's PitchScatterFrequency.cpp): nuclear charge. Read from data/ion_sizes.toml.

source
Runes2P.ION_SIZE_Z0 — Constant
ION_SIZE_Z0

Charge state for each entry of the effective ion size table (Hesslow et al., J. Plasma Phys. 84, 905840605 (2018), Table 1, through DREAM's PitchScatterFrequency.cpp). Read from data/ion_sizes.toml.

source
Runes2P.ION_SIZE_Z_DREAM — Constant
ION_SIZE_Z_DREAM

Nuclear charges as stored in DREAM's PitchScatterFrequency.cpp (ionSizeAj_Zs), where the Ar row has one entry too few, shifting all later entries (zero-padded to the declared length). Read from data/ion_sizes.toml.

source
Runes2P.MEAN_EXCITATION_ENERGY — Constant
MEAN_EXCITATION_ENERGY

Mean excitation energy [eV], row Z, column Z0 + 1, for Z = 1 to 18 (Sauer, Sabin and Oddershede, J. Chem. Phys. 148, 174307 (2018), through DREAM's SlowingDownFrequency.cpp). Read from data/mean_excitation_energies.toml.

source
Runes2P.MEAN_EXCITATION_ENERGY_D — Constant
MEAN_EXCITATION_ENERGY_D

Parameter DN of the analytic mean excitation energy formula, index Nbound (Sauer, Sabin and Oddershede, J. Chem. Phys. 148, 174307 (2018), through DREAM's SlowingDownFrequency.cpp). Read from data/mean_excitation_energies.toml.

source
Runes2P.MEAN_EXCITATION_ENERGY_EXTENDED — Constant
MEAN_EXCITATION_ENERGY_EXTENDED

Extended mean excitation energy [eV], row Z, column Nbound = Z - Z0 (semi-empirical model of DREAM's `tools/GenerateMeanExcitationEnergy.py, rows 19 to 86, through DREAM'sMeanExcitationEnergyExtended.cpp). Read fromdata/meanexcitation_energies.toml`.

source
Runes2P.MEAN_EXCITATION_ENERGY_S0 — Constant
MEAN_EXCITATION_ENERGY_S0

Parameter SN0 of the analytic mean excitation energy formula, index Nbound (Sauer, Sabin and Oddershede, J. Chem. Phys. 148, 174307 (2018), through DREAM's SlowingDownFrequency.cpp). Read from data/mean_excitation_energies.toml.

source

src/physics/adas.jl

Public

Runes2P.ADF11Table — Type
ADF11Table

Rate coefficients of one ADF11 file of OpenADAS for the element with nuclear charge nuclear_charge, read by read_adf11:

  • class: :acd (recombination), :scd (ionization), :ccd (charge exchange), all in m³/s, :plt (line radiation) and :prb (recombination and bremsstrahlung radiation), in W m³;
  • log_density [log₁₀ m⁻³] and log_temperature [log₁₀ eV]: the nodes;
  • log_coefficient[i, j, b]: log₁₀ of the coefficient at the density node i, the temperature node j, in block b of the file (one block per charge state);
  • derivatives: the derivatives at the nodes that DREAM's bicubic interpolation uses, with respect to the log density, the log temperature, and both;
  • source: the file.

adas_coefficient evaluates a coefficient.

source
Runes2P.adas_cache_directory — Method
adas_cache_directory()

The directory of the ADF11 files: ENV["RUNES2P_ADAS_CACHE"], or ~/.cache/Runes2P/adas. It is filled by scripts/fetch_adas.py on a machine with internet access (the compute nodes of clusters often have none) and is never part of the repository.

source
Runes2P.adas_coefficient — Method
adas_coefficient(table, Z0, free_electron_density, temperature; extrapolation=:dream)

The coefficient of table (ADF11Table) for the ion of charge Z0 at the free-electron density free_electron_density (n) [m⁻³] and the temperature temperature (T) [eV], in m³/s (W m³ for :plt and :prb). Zero where the class has no such charge state (recombination of the neutral, ionization of the bare nucleus) and at n ≤ 0 or T ≤ 0, as in DREAM.

The coefficients are those of ADAS (H. P. Summers, The ADAS User Manual, version 2.6 (2004), http://www.adas.ac.uk, with the data of OpenADAS, https://open.adas.ac.uk). The interpolation is DREAM's (src/ADASRateInterpolator.cpp): GSL's bicubic interpolation of log₁₀ of the coefficient in log₁₀ n and log₁₀ T inside the table, and outside it the bilinear interpolation of the edge cell, continued linearly (extrapolation = :dream). extrapolation = :clamp_density takes the density at the nearest edge of the table and extrapolates in the temperature alone: at low densities the coefficients tend to their coronal limit, which does not depend on the density. The default is :dream (a choice of this package; see the docs page Choices of this package).

Derivatives with respect to n and T are exact through ForwardDiff, in place of DREAM's one-sided differences.

source
Runes2P.read_adas — Method
read_adas(element; directory=adas_cache_directory())

The five ADF11Tables of element as a named tuple (acd, scd, ccd, plt, prb), read from the cache. Throws an ArgumentError naming the missing file and the script that fetches it.

source
Runes2P.read_adf11 — Method
read_adf11(path; class=nothing)

Read an ADF11 file of OpenADAS (unresolved, one block per charge state) into an ADF11Table, in SI units: the densities from cm⁻³ to m⁻³ and the coefficients from cm³ to m³ (log₁₀ + 6 and − 6, as DREAM's tools/ADAS/data.py). class is taken from the file name (acd96_ne.dat is :acd) unless given.

Throws an ArgumentError for a file whose blocks do not match its header, and for a metastable-resolved file (IGRD or IPRT above 1), which DREAM does not read either.

source

Internal

Runes2P.ADAS_FILES — Constant
ADAS_FILES

The ADF11 file of each element and class (DREAM's tools/elements.json): a year, which names the file <class><yy>_<element>.dat of OpenADAS, or a file name. The data are those of ADAS: H. P. Summers, The ADAS User Manual, version 2.6 (2004), http://www.adas.ac.uk, with the data of OpenADAS, https://open.adas.ac.uk. Deuterium and tritium take the files of hydrogen, except charge exchange. The high-density files of hydrogen are not served by OpenADAS; DREAM's cache carries them. The table is read from data/adas_files.toml.

source
Runes2P._bicubic_derivatives — Method
_bicubic_derivatives(x, y, z) -> (zx, zy, zxy)

Node derivatives of tabulated values z[i, j, b] at (x[i], y[j]) for independent blocks b: zx and zy are natural-spline slopes along x and y, and zxy is the natural-spline slope along x of zy.

source
Runes2P._natural_spline_slopes — Method
_natural_spline_slopes(x, y) -> Vector{Float64}

First derivative at each node x[i] of the natural cubic spline through the points (x[i], y[i]), with x strictly increasing and length(x) ≥ 2.

source

src/physics/ionization_energies.jl

Public

Runes2P.IONIZATION_ENERGIES — Constant
IONIZATION_ENERGIES

The ionization energy [eV] of each charge state, IONIZATION_ENERGIES[element][Z0 + 1] for the ion of charge Z0 = 0, …, Z − 1: A. Kramida, Yu. Ralchenko, J. Reader and the NIST ASD Team, NIST Atomic Spectra Database, https://physics.nist.gov/asd, doi:10.18434/T4W30F, as retrieved on 2023-04-28 by DREAM's tools/get_nist.py into its src/Atomics/nistdata_ionization.cpp. Deuterium and tritium take the energy of hydrogen. The values are read from data/ionization_energies.toml.

source
Runes2P.ion_mass — Method
ion_mass(species; table=:standard)

The mass [kg] of an ion species. For nuclear charge 1 it is the mass of hydrogen, deuterium (the default, as in DREAM) or tritium by the name "H", "D" or "T"; a species of charge 1 with another name takes the mass of deuterium.

  • table = :standard: the nuclei of HYDROGEN_ISOTOPE_MASSES and the standard atomic weight of ATOMIC_MASSES for the other elements, up to uranium. An element without a standard atomic weight needs its mass given (masses of IonEnergyExchangeTerm). masses holds one mass in kg per species and replaces the masses of all species. The weight is that of the neutral atom: an ion lacks up to Z electron masses, which this neglects, at most 2.7e-4 of its mass for the elements beyond hydrogen.
  • table = :dream: DREAM's rule (src/IonHandler.cpp:153-170), the masses of DREAM_HYDROGEN_ISOTOPE_MASSES, the standard atomic weights up to Z = 40, and 2.3 Z atomic mass units beyond, which puts tungsten at 170 u against 183.84 u and xenon at 124 u against 131.3 u.
source

Internal

Runes2P.ATOMIC_MASSES — Constant
ATOMIC_MASSES

The standard atomic weights [u] of the elements Z = 1 to 92, NaN for the elements without one (technetium, promethium, polonium to actinium).

  • Z = 1 to 40: DREAM's atomicMassInMu (src/IonHandler.cpp:35-46), the IUPAC standard atomic weights, which DREAM does not name; they agree with those of CIAAW 2024 to 2.2e-5 (zirconium) or better.
  • Z = 41 to 92: the standard atomic weights of the Commission on Isotopic Abundances and Atomic Weights of IUPAC (CIAAW, https://www.ciaaw.org/atomic-weights.htm, read 2026-10-12), and for thallium and lead, which have an interval, its conventional values 204.38 and 207.2.

The values are read from data/atomic_masses.toml.

source
Runes2P.ATOMIC_MASS_UNIT — Constant
ATOMIC_MASS_UNIT

The unified atomic mass unit [kg], CODATA 2018 (1.66053906660e-27) rounded as DREAM's src/Constants.cpp (mu).

source
Runes2P.DREAM_HYDROGEN_ISOTOPE_MASSES — Constant
DREAM_HYDROGEN_ISOTOPE_MASSES

DREAM's masses [kg] of the hydrogen isotopes, mH, mD and mT of src/Constants.cpp: the proton and the deuteron of CODATA 2018 and the atomic mass of ³H (3.01604928 u).

source
Runes2P.HYDROGEN_ISOTOPE_MASSES — Constant
HYDROGEN_ISOTOPE_MASSES

The masses [kg] of the nuclei of the hydrogen isotopes, CODATA 2018 (E. Tiesinga, P. J. Mohr, D. B. Newell, B. N. Taylor, Rev. Mod. Phys. 93, 025010 (2021), doi:10.1103/RevModPhys.93.025010): the proton, the deuteron and the triton. DREAM's mH and mD (src/Constants.cpp) are the first two; its mT is the atomic mass of ³H (3.01604928 u, 5.00826765e-27 kg), which exceeds the triton by one electron mass (DREAM_HYDROGEN_ISOTOPE_MASSES).

source

src/physics/ionization_cross_sections.jl

Public

Runes2P.KINETIC_IONIZATION_FITS — Constant
KINETIC_IONIZATION_FITS

The parameters of the cross section of each element, one row per charge state Z0 = 0, …, Z − 1: the prefactor C, the effective ionization potential I [eV] and the near-threshold factor β* of ionization_cross_section. DREAM's fits to the ADAS SCD coefficients at zero density, copied from DREAM's src/Atomics/kineticionizationdata.cpp, generated by its tools/fit_kineticionization.py (MIT License, Copyright (c) 2020 Mathias Hoppe and Ola Embreus). DREAM gives their error as at most around tens of %. Against the ADAS SCD at the lowest density of the tables, the Maxwellian average at T = I lies between 1.03 and 1.57 of it for the states of argon and between 0.93 and 1.24 for those of neon (test/test_kinetic_ionization.jl). The values are read from the data file data/kinetic_ionization_fits.toml.

source
Runes2P.SECONDARY_SPECTRUM_WIDTHS — Constant
SECONDARY_SPECTRUM_WIDTHS

The width Ē [eV] of the energy spectrum of the secondary electrons of electron-impact ionization, dσ/dE_s ∝ 1/(1 + (E_s/Ē)²), per element, from Table II of C. B. Opal, W. K. Peterson and E. C. Beaty, J. Chem. Phys. 55, 4100 (1971), doi:10.1063/1.1676707, measured on neutral gases with primaries of 500 eV. Their fit, eq. (2), has the exponent 2.1; the exponent 2 used here is the one they allow for the integral of the spectrum (p. 4105). With it the mean over every secondary grows as ln E (argon at 1 MeV: 68.9 eV, against 44.3 eV with 2.1). The widths: helium 15.8, neon 24.2, argon 10.0, krypton 9.6 and xenon 8.7 eV; hydrogen, deuterium and tritium take the 8.3 eV of H₂. The authors call their fits to argon, krypton and xenon poor, so that "the values of Ē are not very significant for these gases"; Ē varies by about 15 % over primary energies of 100 to 2000 eV (their Table I). The widths were measured on neutral atoms and molecules with ionization energies of 9.5 to 24.6 eV and are applied to every charge state. They give no law for ions: the authors find Ē "poorly correlated with the ionization potential" (p. 4106). The default spectrum of the heating by secondaries and of the friction of bound_friction = :ionization_split is the binary-encounter-Bethe spectrum (beb_mean_secondary_energy; a choice of this package; see the docs page Choices of this package), and these widths serve spectrum = :opal. Other elements take their width from the widths of SecondaryHeatingTerm and the secondary_widths of CollisionSettings.

source
Runes2P.beb_mean_secondary_energy — Method
beb_mean_secondary_energy(E, B, cut)

The mean kinetic energy [eV] of the secondary electron of an ionization of a subshell with the binding energy B by an electron of kinetic energy E, the energies and cut in eV, with the singly differential cross section of the binary-encounter-Bethe (BEB) model of Y.-K. Kim and M. E. Rudd, Phys. Rev. A 50, 3954 (1994), doi:10.1103/PhysRevA.50.3954, eqs. (52) to (54):

\[\frac{dσ}{dW} ∝ \sum_{n=1}^{3} F_n(t) \left[\frac{1}{(w + 1)^n} + \frac{1}{(t − w)^n}\right], \qquad F_1 = −\frac{F_2}{t + 1}, \quad F_2 = 2 − Q, \quad F_3 = Q \ln t,\]

with t = E/B and w = W/B, up to the smaller of (t − 1)/2 and cut/B, conditional on lying there, in closed form. The common factor of the F_n (1/(t + u + 1), or 1/(t + 1) for ions with open-shell valence electrons, their Sec. VI) cancels in the mean, so the spectrum needs only B. Q = 1, their approximation where the dipole oscillator strength is not known (Sec. IV), also for the one-electron ions, for which their eq. (60) gives Q = 0.5668: there the dipole term, which lies at small w, is too large, and the mean too low. Zero below the threshold. Within 1e-6 B of the threshold, where the closed form loses its digits, the mean is that of the flat spectrum there, W_top/2. The mean of a charge state takes the valence shell alone, with B its ionization energy; inner shells, which Kim and Rudd sum over, are left out. Normalized by the same integral, the spectrum gives their total cross section of the BEB model, (ln t/2)(1 − 1/t²) + 1 − 1/t − ln t/(t + 1) at Q = 1, which the tests check.

source
Runes2P.ionization_cross_section — Method
ionization_cross_section(element, Z0, p; form=:chianti)

The cross section [m²] of the ionization of the ion of element in charge state Z0 by an electron of momentum p (in units of mₑc), DREAM's EvaluateBCGSingleSubshell (src/Equations/Fluid/IonKineticIonizationTerm.cpp): the formula of A. Burgess and M. C. Chidichimo, Mon. Not. R. Astron. Soc. 203, 1269 (1983), doi:10.1093/mnras/203.4.1269,

\[σ_{BC} = C π a_0^2 \left(\frac{R_y}{I}\right)^2 \frac{\ln(U)^{1 + β^*/U}}{U}\]

with U = E/I and the kinetic energy E, blended into DREAM's form of the relativistic Bethe formula of N. A. Garland et al., Phys. Plasmas 27, 040702 (2020), doi:10.1063/5.0003638, C π a₀² α² (R_y/I) (ln(p²/2I) − β²) with I in units of mₑc² (IonKineticIonizationTerm.cpp:221-223), by the weight S = 1/(1 + exp(1 − E/100 keV)) of DREAM's code (:224). That form lacks the factor 1/β² of a Bethe cross section: it is β² times the continuation that joins the large-U limit of σ_{BC}, so it is about 1e-4 of σ_{BC} at thermal energies, while S gives it the weight 0.27 already at E = 0. The fitted C absorbs that weight at the energies of the fit, and the shape at hot-tail and runaway energies differs from a consistent blend. Zero below the threshold U < 1 and for the bare nucleus. The parameters are those of KINETIC_IONIZATION_FITS.

form selects the cross section:

  • :chianti (the default, a choice of this package; see the docs page Choices of this package): the parameters of CHIANTI, continued relativistically by the Bethe theory (chianti_cross_section, CHIANTI_IONIZATION_FITS), for hydrogen, its isotopes (with the parameters of hydrogen), neon and argon. The other elements, tungsten among them, keep DREAM's form below: the table holds no CHIANTI data for them.
  • :dream: DREAM's form above, with DREAM's fits. From 0.1 to 1 keV it lies within 7 % of the measurements of hydrogen (Shah et al. 1987), and at 0.5 to 1 keV at 0.75 of the single ionization of argon (Straub et al. 1995); at 1 MeV it is 1.72 times the relativistic Bethe cross section of hydrogen and grows with the energy.
  • :bethe: the term divided by β², C π a₀² α² (R_y/I)(ln(p²/2I) − β²)/β², which joins the large-U limit of σ_{BC}, with the parameters KINETIC_IONIZATION_BETHE_FITS refitted by DREAM's method. The blend is floored at zero: just above the threshold of a state with I of several keV the logarithm ln(p²/2I) lies below β² while S is already 0.3 at 15 keV, so the blend would be negative there (W⁶⁴⁺, I = 15.6 keV).
source
Runes2P.mean_secondary_energy — Method
mean_secondary_energy(width, E, I, cut)

The mean kinetic energy [eV] of the secondary electron of an ionization with the ionization potential I by an electron of kinetic energy E, with the spectrum of Opal et al. of width width (SECONDARY_SPECTRUM_WIDTHS) up to the smaller of (E − I)/2 (the slower of the two electrons is the secondary) and cut. All four are in eV. The mean is (Ē/2) ln(1 + (E_top/Ē)²) / arctan(E_top/Ē). Zero below the threshold. This is the mean of the secondaries below the top, conditional on lying there. With cut = Inf it is the mean over every secondary, which SecondaryHeatingTerm with spectrum = :opal gives to the cold electrons and which the friction of bound_friction = :ionization_split with that spectrum takes from f (CollisionSettings): every secondary joins the cold electrons through the closure of their density, so both take the same moment.

source

Internal

Runes2P.chianti_element — Method
chianti_element(element)

The element of CHIANTI_IONIZATION_FITS whose parameters element takes: hydrogen for deuterium and tritium, whose cross sections differ from those of hydrogen by the reduced mass of the bound electron (2.7e-4 of the binding energy for deuterium), and the element itself otherwise.

source

src/physics/ionization_bethe_fits.jl

Public

Runes2P.KINETIC_IONIZATION_BETHE_FITS — Constant
KINETIC_IONIZATION_BETHE_FITS

The parameters C, I [eV] and β* of ionization_cross_section with form = :bethe, one row per charge state Z0 = 0, …, Z − 1, as KINETIC_IONIZATION_FITS for DREAM's form. They are DREAM's fit run unchanged (tools/fit_kineticionization.py with tools/ADAS/fit.py, method "single", MIT License, Copyright (c) 2020 Mathias Hoppe and Ola Embreus) on the cross section with the factor 1/β²: per charge state, a least-squares fit of the logarithm of the Maxwell-Jüttner average of v σ to the ADAS SCD coefficient at the lowest density over the temperature window of DREAM's tools/kineticCrossSectionParams.json (2 to 100 eV where it has none), with C and β* free in [1e-4, 200] and [0, 1] and I the NIST ionization energy of DREAM's table. The constants are those of SciPy's scipy.constants in the pixi environment of reference/dream (CODATA 2022 from SciPy 1.15 on, 1e-9 from CODATA 2018), where DREAM's tool takes a₀ = 5.29e-11 m, R_y = 13.6 eV and α = 1/137; with DREAM's constants and form the same script reproduces KINETIC_IONIZATION_FITS (D: C = 3.05406, β* = 0.296804). Fitted by scripts/fit_kinetic_ionization.py on 2026-10-12 from the ADAS files of DREAM's cache (tools/cache). The values are in data/kinetic_ionization_bethe_fits.toml, which the script writes: in [data], one array per element with one row [C, I, β*] per charge state.

Against the SCD coefficient, the Maxwellian average lies between 0.71 and 1.15 at the two ends and the middle of the window of each state of argon (DREAM's form: 0.64 to 1.13). C is 0.72 to 0.74 of DREAM's for hydrogen and the states of argon up to Ar⁵⁺, near the weight 1 − S(0) = 0.73 of the Burgess-Chidichimo term at thermal energies that DREAM's C absorbs. β* reaches its upper bound 1 for Ne⁰ to Ne²⁺, Ar⁰ and Ar⁷⁺, as some of DREAM's fits approach it.

source

src/physics/ionization_chianti.jl

Public

Runes2P.ChiantiIonization — Type
ChiantiIonization(ion, direct, autoionization_scale, autoionization)

The CHIANTI parameters of the ionization of one ion (ion, CHIANTI's name, "ar_4" for Ar IV, the charge state 3): the shells of its direct ionization (ChiantiDirectShell), and its excitation-autoionization (ChiantiAutoionization) with the scale of the last line of its .diparams file (1 for every ion of the table). The constructor derives the coefficients of the relativistic Bethe form that continues the fit above CHIANTI_MATCHING_ENERGY (chianti_cross_section): bethe_m2, the coefficient M² of the high-energy limit of the fit, the sum of Σ(1)/(4π a₀² R_y I) over the shells and s Ω_s(1)/4 over the allowed transitions of the autoionization; bethe_c, the constant C that makes the Bethe form equal the continued fit at the matching energy.

source
Runes2P.chianti_cross_section — Method
chianti_cross_section(fit::ChiantiIonization, p)

The cross section [m²] of the ionization of the ion of fit by an electron of momentum p (in units of mₑc): CHIANTI's direct ionization and excitation-autoionization (K. P. Dere, Astron. Astrophys. 466, 771 (2007), doi:10.1051/0004-6361:20066728), continued relativistically by the Bethe theory.

The relativistic Bethe cross section of M. Inokuti, Rev. Mod. Phys. 43, 297 (1971), doi:10.1103/RevModPhys.43.297, eq. (4.26), for an electron (z = 1),

\[σ_B = \frac{4π a_0^2 R_y}{T} \left\{M^2 \left[\ln\frac{β^2}{1 - β^2} - β^2\right] + C\right\}, \qquad T = \frac{m_e v^2}{2},\]

splits, by Fano's analysis (Inokuti, p. 328), into the nonrelativistic Bethe form (his eq. (4.18)) at the energy T = mₑv²/2 and the relativistic rise (4π a₀² R_y/T) M² [−ln(1 − β²) − β²]. The fits of CHIANTI are nonrelativistic: their high-energy limit is the nonrelativistic Bethe limit (Dere, Sect. 2.1). They are continued in the same way:

  • each shell and each transition of the autoionization is evaluated at the speed of the electron, u = β²/β_I² with β_I the speed of an electron of the threshold energy, which is E/I at low energies and keeps the threshold at E = I;
  • the relativistic rise is added with the coefficient M² of the high-energy limit of each shell and allowed transition (ChiantiIonization), weighted by 1 − 1/u so that it starts at zero at the threshold of each (a choice of this package; see the docs page Choices of this package).

Above CHIANTI_MATCHING_ENERGY the cross section is σ_B with the M² of the fit and the C at which σ_B equals the continued fit at that energy, joined to it by the weight of CHIANTI_MATCHING_WIDTH. Below about 10 keV the continuation changes the fit by less than the spread of the data: by 0.3 % at 1 keV and about 3 % at 10 keV for the neutral atoms.

Implied by the fits: M² = 0.2842 for H (0.2823 from the fit of M. B. Shah, D. S. Elliott, H. B. Gilbody, J. Phys. B 20, 3501 (1987)), 4.180 for Ar (4.22, F. F. Rieke, W. Prepejchal, Phys. Rev. A 6, 1507 (1972), Table II) and 1.705 for Ne (2.02, the same table, 16 % low, within Dere's caveat of the Bethe limit, ChiantiDirectShell); C = 3.92 for H (4.007) and 40.8 for Ar (37.93).

The threshold is CHIANTI's ionization energy, which differs from that of ionization_energy (NIST) by up to 1.4 eV for the argon ions; the energy loss of the friction of bound_friction = :ionization_split takes NIST's. Generic in the number type of p.

source

Internal

Runes2P.CHIANTI_MATCHING_ENERGY — Constant
CHIANTI_MATCHING_ENERGY

The kinetic energy [eV], 100 keV, above which the cross section of chianti_cross_section follows the relativistic Bethe form (a choice of this package; see the docs page Choices of this package). At 100 keV β² = 0.30; the fits rest on measurements below a few keV and on distorted-wave calculations to about 16 times the threshold (R. Bao and B. Li, At. Data Nucl. Data Tables 159, 101663 (2024), Figs. 19 and 20 for Ar¹³⁺ and Ar¹⁴⁺).

source
Runes2P.CHIANTI_MATCHING_WIDTH — Constant
CHIANTI_MATCHING_WIDTH

The factor, 2, by which the weight of chianti_cross_section passes from the continued fit to the Bethe form, from CHIANTI_MATCHING_ENERGY/2 to 2 CHIANTI_MATCHING_ENERGY, with the cubic w = x²(3 − 2x) in x = ln(2E/E_m)/ln 4. The weight and its slope are continuous at both ends, so the cross section and its slope are continuous. The factor is a choice of this package (see the docs page Choices of this package).

source
Runes2P.ChiantiAutoionization — Type
ChiantiAutoionization(kind, oscillator_strength, energy, scale, values)

One transition of the excitation-autoionization of an ion in CHIANTI's .easplom files (K. P. Dere, Astron. Astrophys. 466, 771 (2007), Sect. 2.2): the Burgess-Tully type of the transition (kind), its weighted oscillator strength, its excitation energy [Ry, the unit of the files] (energy), the Burgess-Tully parameter C (scale) and the five spline nodes of the scaled collision strength at equally spaced scaled energies from 0 to 1 (values). With X = E/ΔE, the collision strength Ω is, by type, as ChiantiPy's util.splomDescale evaluates it:

  • type 1: Ω = Ω_s(1 − ln C/ln(X − 1 + C)) ln(X − 1 + e);
  • type 2: Ω = Ω_s((X − 1)/(X − 1 + C));
  • type 3: Ω = Ω_s((X − 1)/(X − 1 + C))/X²;
  • type 4: Ω = Ω_s(1 − ln C/ln(X − 1 + C)) ln(X − 1 + C).

The cross section is s π a₀² Ω/(E/R_y) with the scale s of the ion (ChiantiIonization); the statistical weight of the ground level is in Ω (ChiantiPy's Ion.eaCross). Types 1 and 4, the allowed transitions, grow as ln X and add s Ω_s(1)/4 to the Bethe coefficient M² of the ion.

source
Runes2P.ChiantiDirectShell — Type
ChiantiDirectShell(scale, threshold, knots, values)

The fit of the direct ionization of one shell of an ion in CHIANTI's .diparams files (K. P. Dere, Astron. Astrophys. 466, 771 (2007), doi:10.1051/0004-6361:20066728, Sect. 2.1): the Burgess-Tully parameter f (scale), the ionization energy of the shell [eV] (threshold), and the spline nodes of the scaled cross section Σ (values, in 1e-14 cm² eV², the unit of the files) at the scaled energies U (knots, from 0 to 1). Dere's eqs. (2) and (3):

\[U = 1 - \frac{\ln f}{\ln(u - 1 + f)}, \qquad Σ = \frac{u σ I^2}{\ln u + 1},\]

with u = E/I. Σ(0) = 0, and Σ(U = 1) is the Bethe limit of the shell. That limit is not fitted: Dere computes it from the photoionization cross sections of Verner and Yakovlev (1995) by Younger's formula, his eq. (1), and notes that measured cross sections "have often been found to approach a high-energy limit that is a factor of 1.4 to 2.0 higher than the Bethe limit" (his Sect. 2.1). The spline is the cubic interpolating spline with not-a-knot ends, SciPy's splrep(s = 0), with which the fits were made (the comment of ChiantiPy's Ion.diCross, the CHIANTI package for Python; fiasco interpolates the same nodes with a monotone cubic, PchipInterpolator). curvatures holds its second derivatives at the nodes.

source

src/physics/ionization_chianti_fits.jl

Public

Runes2P.CHIANTI_IONIZATION_FITS — Constant
CHIANTI_IONIZATION_FITS

The parameters of the cross sections of the ionization of each element, one ChiantiIonization per charge state Z0 = 0, …, Z − 1: the direct ionization and the excitation-autoionization of the CHIANTI database, version 11.0.2 (the VERSION file of its SolarSoft mirror, https://soho.nascom.nasa.gov/solarsoft/packages/chianti/dbase/VERSION, read on 2026-10-13), from its files <ion>.diparams and <ion>.easplom, unchanged since K. P. Dere, Astron. Astrophys. 466, 771 (2007), doi:10.1051/0004-6361:20066728. Dere fits measured cross sections where they exist, mostly for the neutral and singly charged ions, and his distorted-wave calculations with the Flexible Atomic Code otherwise; the final errors of his Tables 1 to 28 have the median 10 % and the mean 13 % (his Sect. 3.31). The sources of the ions of the table, from his Sect. 3:

  • H I: the measurements of Shah et al. (1987), error 7 %. Deuterium and tritium take the parameters of hydrogen.
  • Ne I, Ar I and Ar II among others: fits to measurements (Ar I to Straub et al. 1995 and five other sets).
  • Ne IX, Ne X, Ar XVII, Ar XVIII (the one- and two-electron ions): Dere's text takes the relativistic distorted-wave fits of Fontes et al. (1999) for these, which ChiantiPy evaluates instead of the files; the table holds the parameters of the files, as fiasco evaluates them. Against the relativistic distorted-wave calculations of R. Bao and B. Li (At. Data Nucl. Data Tables 159, 101663 (2024)), the files of Ar¹⁶⁺ and Ar¹⁷⁺ give 1.24 times their cross section at 5 times the threshold and 1.35 and 1.38 times it at 10 times.
  • the other ions: the calculations, with the excitation-autoionization of Ne VII, Ne VIII, Ar III to Ar VIII, Ar XV and Ar XVI.

The parameters are in the data file data/chianti_ionization.toml, which scripts/generate_chianti_ionization.jl writes from the files of CHIANTI.

source