Example #1
0
class dielectric:
    """
    Benchmark that times the performance of funcions from
    Physics/dielectric package
    """
    B = 2 * u.T
    species = ['e', 'D+']
    n = [1e18 * u.m**-3, 1e18 * u.m**-3]
    omega = 3.7e9 * (2 * pi) * (u.rad / u.s)
    T = 30 * 11600 * u.K
    particle = 'Ne'
    z_mean = 8 * u.dimensionless_unscaled
    vTh = parameters.thermal_speed(T, particle, method="most_probable")
    kWave = omega / vTh
    n_permittivity_1D_Maxwellian = 1e18 * u.cm**-3

    def setup(self):
        pass

    def time_cold_plasma_permittivity_SDP(self):
        permittivity = S, D, P = cold_plasma_permittivity_SDP(
            dielectric.B, dielectric.species, dielectric.n, dielectric.omega)

    def time_cold_plasma_permittivity_LRP(self):
        permittivity = S, D, P = cold_plasma_permittivity_LRP(
            dielectric.B, dielectric.species, dielectric.n, dielectric.omega)

    def time_permittivity_1D_Maxwellian(self):
        permittivity_1D_Maxwellian(dielectric.omega, dielectric.kWave,
                                   dielectric.T,
                                   dielectric.n_permittivity_1D_Maxwellian,
                                   dielectric.particle, dielectric.z_mean)
Example #2
0
def spectral_density(
    wavelengths: u.nm,
    probe_wavelength: u.nm,
    n: u.m**-3,
    Te: u.K,
    Ti: u.K,
    efract: np.ndarray = None,
    ifract: np.ndarray = None,
    ion_species: Union[str, List[str], Particle, List[Particle]] = "H+",
    electron_vel: u.m / u.s = None,
    ion_vel: u.m / u.s = None,
    probe_vec=np.array([1, 0, 0]),
    scatter_vec=np.array([0, 1, 0]),
) -> Tuple[Union[np.floating, np.ndarray], np.ndarray]:
    r"""
    Calculate the spectral density function for Thomson scattering of a
    probe laser beam by a multi-species Maxwellian plasma.

    This function calculates the spectral density function for Thomson
    scattering of a probe laser beam by a plasma consisting of one or more ion
    species and a one or more thermal electron populations (the entire plasma
    is assumed to be quasi-neutral)

    .. math::
        S(k,\omega) = \sum_e \frac{2\pi}{k}
        \bigg |1 - \frac{\chi_e}{\epsilon} \bigg |^2
        f_{e0,e} \bigg (\frac{\omega}{k} \bigg ) +
        \sum_i \frac{2\pi Z_i}{k}
        \bigg |\frac{\chi_e}{\epsilon} \bigg |^2 f_{i0,i}
        \bigg ( \frac{\omega}{k} \bigg )

    where :math:`\chi_e` is the electron component susceptibility of the
    plasma and :math:`\epsilon = 1 + \sum_e \chi_e + \sum_i \chi_i` is the total
    plasma dielectric  function (with :math:`\chi_i` being the ion component
    of the susceptibility), :math:`Z_i` is the charge of each ion, :math:`k`
    is the scattering wavenumber, :math:`\omega` is the scattering frequency,
    and :math:`f_{e0,e}` and :math:`f_{i0,i}` are the electron and ion velocity
    distribution functions respectively. In this function the electron and ion
    velocity distribution functions are assumed to be Maxwellian, making this
    function equivalent to Eq. 3.4.6 in `Sheffield`_.

    Parameters
    ----------

    wavelengths : `~astropy.units.Quantity`
        Array of wavelengths over which the spectral density function
        will be calculated. (convertible to nm)

    probe_wavelength : `~astropy.units.Quantity`
        Wavelength of the probe laser. (convertible to nm)

    n : `~astropy.units.Quantity`
        Mean (0th order) density of all plasma components combined.
        (convertible to cm^-3.)

    Te : `~astropy.units.Quantity`, shape (Ne, )
        Temperature of each electron component. Shape (Ne, ) must be equal to the
        number of electron components Ne. (in K or convertible to eV)

    Ti : `~astropy.units.Quantity`, shape (Ni, )
        Temperature of each ion component. Shape (Ni, ) must be equal to the
        number of ion components Ni. (in K or convertible to eV)

    efract : array_like, shape (Ne, ), optional
        An array-like object where each element represents the fraction (or ratio)
        of the electron component number density to the total electron number density.
        Must sum to 1.0. Default is a single electron component.

    ifract : array_like, shape (Ni, ), optional
        An array-like object where each element represents the fraction (or ratio)
        of the ion component number density to the total ion number density.
        Must sum to 1.0. Default is a single ion species.

    ion_species : str or `~plasmapy.particles.Particle`, shape (Ni, ), optional
        A list or single instance of `~plasmapy.particles.Particle`, or strings
        convertible to `~plasmapy.particles.Particle`. Default is `'H+'`
        corresponding to a single species of hydrogen ions.

    electron_vel : `~astropy.units.Quantity`, shape (Ne, 3), optional
        Velocity of each electron component in the rest frame. (convertible to m/s)
        Defaults to a stationary plasma [0, 0, 0] m/s.

    ion_vel : `~astropy.units.Quantity`, shape (Ni, 3), optional
        Velocity vectors for each electron population in the rest frame
        (convertible to m/s) Defaults zero drift
        for all specified ion species.

    probe_vec : float `~numpy.ndarray`, shape (3, )
        Unit vector in the direction of the probe laser. Defaults to
        [1, 0, 0].

    scatter_vec : float `~numpy.ndarray`, shape (3, )
        Unit vector pointing from the scattering volume to the detector.
        Defaults to [0, 1, 0] which, along with the default `probe_vec`,
        corresponds to a 90 degree scattering angle geometry.

    Returns
    -------
    alpha : float
        Mean scattering parameter, where `alpha` > 1 corresponds to collective
        scattering and `alpha` < 1 indicates non-collective scattering. The
        scattering parameter is calculated based on the total plasma density n.

    Skw : `~astropy.units.Quantity`
        Computed spectral density function over the input `wavelengths` array
        with units of s/rad.

    Notes
    -----

    For details, see "Plasma Scattering of Electromagnetic Radiation" by
    Sheffield et al. `ISBN 978\\-0123748775`_. This code is a modified version
    of the program described therein.

    For a concise summary of the relevant physics, see Chapter 5 of Derek
    Schaeffer's thesis, DOI: `10.5281/zenodo.3766933`_.

    .. _`ISBN 978\\-0123748775`: https://www.sciencedirect.com/book/9780123748775/plasma-scattering-of-electromagnetic-radiation
    .. _`10.5281/zenodo.3766933`: https://doi.org/10.5281/zenodo.3766933
    .. _`Sheffield`: https://doi.org/10.1016/B978-0-12-374877-5.00003-8
    """
    if efract is None:
        efract = np.ones(1)
    else:
        efract = np.asarray(efract, dtype=np.float64)

    if ifract is None:
        ifract = np.ones(1)
    else:
        ifract = np.asarray(ifract, dtype=np.float64)

    # If electon velocity is not specified, create an array corresponding
    # to zero drift
    if electron_vel is None:
        electron_vel = np.zeros([efract.size, 3]) * u.m / u.s

    # If ion drift velocity is not specified, create an array corresponding
    # to zero drift
    if ion_vel is None:
        ion_vel = np.zeros([ifract.size, 3]) * u.m / u.s

    # Condition ion_species
    if isinstance(ion_species, (str, Particle)):
        ion_species = [ion_species]
    if len(ion_species) == 0:
        raise ValueError("At least one ion species needs to be defined.")
    for ii, ion in enumerate(ion_species):
        if isinstance(ion, Particle):
            continue
        ion_species[ii] = Particle(ion)

    # Condition Te
    if Te.size == 1:
        # If a single quantity is given, put it in an array so it's iterable
        # If Te.size != len(efract), assume same temp. for all species
        Te = np.repeat(Te, len(efract))
    elif Te.size != len(efract):
        raise ValueError(f"Got {Te.size} electron temperatures and expected "
                         f"{len(efract)}.")

    # Condition Ti
    if Ti.size == 1:
        # If a single quantity is given, put it in an array so it's iterable
        # If Ti.size != len(ion_species), assume same temp. for all species
        Ti = [Ti.value] * len(ion_species) * Ti.unit
    elif Ti.size != len(ion_species):
        raise ValueError(f"Got {Ti.size} ion temperatures and expected "
                         f"{len(ion_species)}.")

    # Make sure the sizes of ion_species, ifract, ion_vel, and Ti all match
    if ((len(ion_species) != ifract.size) or (ion_vel.shape[0] != ifract.size)
            or (Ti.size != ifract.size)):
        raise ValueError(
            f"Inconsistent number of species in ifract ({ifract}), "
            f"ion_species ({len(ion_species)}), Ti ({Ti.size}), "
            f"and/or ion_vel ({ion_vel.shape[0]}).")

    # Make sure the sizes of efract, electron_vel, and Te all match
    if (electron_vel.shape[0] != efract.size) or (Te.size != efract.size):
        raise ValueError(
            f"Inconsistent number of electron populations in efract ({efract.size}), "
            f"Te ({Te.size}), or electron velocity ({electron_vel.shape[0]}).")

    # Ensure unit vectors are normalized
    probe_vec = probe_vec / np.linalg.norm(probe_vec)
    scatter_vec = scatter_vec / np.linalg.norm(scatter_vec)

    # Define some constants
    C = const.c.si  # speed of light

    # Calculate plasma parameters
    vTe = thermal_speed(Te, particle="e-")
    vTi, ion_z = [], []
    for T, ion in zip(Ti, ion_species):
        vTi.append(thermal_speed(T, particle=ion).value)
        ion_z.append(ion.integer_charge * u.dimensionless_unscaled)
    vTi = vTi * vTe.unit
    zbar = np.sum(ifract * ion_z)
    ne = efract * n
    ni = ifract * n / zbar  # ne/zbar = sum(ni)
    # wpe is calculated for the entire plasma (all electron populations combined)
    wpe = plasma_frequency(n=n, particle="e-")

    # Convert wavelengths to angular frequencies (electromagnetic waves, so
    # phase speed is c)
    ws = (2 * np.pi * u.rad * C / wavelengths).to(u.rad / u.s)
    wl = (2 * np.pi * u.rad * C / probe_wavelength).to(u.rad / u.s)

    # Compute the frequency shift (required by energy conservation)
    w = ws - wl

    # Compute the wavenumbers in the plasma
    # See Sheffield Sec. 1.8.1 and Eqs. 5.4.1 and 5.4.2
    ks = np.sqrt(ws**2 - wpe**2) / C
    kl = np.sqrt(wl**2 - wpe**2) / C

    # Compute the wavenumber shift (required by momentum conservation)
    scattering_angle = np.arccos(np.dot(probe_vec, scatter_vec))
    # Eq. 1.7.10 in Sheffield
    k = np.sqrt(ks**2 + kl**2 - 2 * ks * kl * np.cos(scattering_angle))
    # Normal vector along k
    k_vec = (scatter_vec - probe_vec) * u.dimensionless_unscaled

    # Compute Doppler-shifted frequencies for both the ions and electrons
    # Matmul is simultaneously conducting dot product over all wavelengths
    # and ion components
    w_e = w - np.matmul(electron_vel, np.outer(k, k_vec).T)
    w_i = w - np.matmul(ion_vel, np.outer(k, k_vec).T)

    # Compute the scattering parameter alpha
    # expressed here using the fact that v_th/w_p = root(2) * Debye length
    alpha = np.sqrt(2) * wpe / np.outer(k, vTe)

    # Calculate the normalized phase velocities (Sec. 3.4.2 in Sheffield)
    xe = (np.outer(1 / vTe, 1 / k) * w_e).to(u.dimensionless_unscaled)
    xi = (np.outer(1 / vTi, 1 / k) * w_i).to(u.dimensionless_unscaled)

    # Calculate the susceptibilities

    chiE = np.zeros([efract.size, w.size], dtype=np.complex128)
    for i, fract in enumerate(efract):
        chiE[i, :] = permittivity_1D_Maxwellian(w_e[i, :], k, Te[i], ne[i],
                                                "e-")

    # Treatment of multiple species is an extension of the discussion in
    # Sheffield Sec. 5.1
    chiI = np.zeros([ifract.size, w.size], dtype=np.complex128)
    for i, ion in enumerate(ion_species):
        chiI[i, :] = permittivity_1D_Maxwellian(w_i[i, :],
                                                k,
                                                Ti[i],
                                                ni[i],
                                                ion,
                                                z_mean=ion_z[i])

    # Calculate the longitudinal dielectric function
    epsilon = 1 + np.sum(chiE, axis=0) + np.sum(chiI, axis=0)

    econtr = np.zeros([efract.size, w.size], dtype=np.complex128) * u.s / u.rad
    for m in range(efract.size):
        econtr[m, :] = efract[m] * (2 * np.sqrt(np.pi) / k / vTe[m] * np.power(
            np.abs(1 - np.sum(chiE, axis=0) / epsilon), 2) *
                                    np.exp(-xe[m, :]**2))

    icontr = np.zeros([ifract.size, w.size], dtype=np.complex128) * u.s / u.rad
    for m in range(ifract.size):
        icontr[m, :] = ifract[m] * (
            2 * np.sqrt(np.pi) * ion_z[m] / k / vTi[m] *
            np.power(np.abs(np.sum(chiE, axis=0) / epsilon), 2) *
            np.exp(-xi[m, :]**2))

    # Recast as real: imaginary part is already zero
    Skw = np.real(np.sum(econtr, axis=0) + np.sum(icontr, axis=0))

    return np.mean(alpha), Skw
Example #3
0
def test_gyroradius():
    r"""Test the gyroradius function in parameters.py."""

    assert gyroradius(B, "e-", T_i=T_e).unit.is_equivalent(u.m)

    assert gyroradius(B, "e-", Vperp=25 * u.m / u.s).unit.is_equivalent(u.m)

    Vperp = 1e6 * u.m / u.s
    Bmag = 1 * u.T
    omega_ce = gyrofrequency(Bmag, "e-")
    analytical_result = (Vperp / omega_ce).to(
        u.m, equivalencies=u.dimensionless_angles())
    assert gyroradius(Bmag, "e-", Vperp=Vperp) == analytical_result

    with pytest.raises(TypeError):
        gyroradius(u.T, "e-")

    with pytest.raises(u.UnitTypeError):
        gyroradius(5 * u.A, "e-", Vperp=8 * u.m / u.s)

    with pytest.raises(u.UnitTypeError):
        gyroradius(5 * u.T, "e-", Vperp=8 * u.m)

    with pytest.raises(ValueError):
        gyroradius(np.array([5, 6]) * u.T,
                   "e-",
                   Vperp=np.array([5, 6, 7]) * u.m / u.s)

    assert np.isnan(gyroradius(np.nan * u.T, "e-", Vperp=1 * u.m / u.s))

    with pytest.raises(ValueError):
        gyroradius(3.14159 * u.T, "e-", T_i=-1 * u.K)

    with pytest.warns(u.UnitsWarning):
        assert gyroradius(1.0, "e-",
                          Vperp=1.0) == gyroradius(1.0 * u.T,
                                                   "e-",
                                                   Vperp=1.0 * u.m / u.s)

    with pytest.warns(u.UnitsWarning):
        assert gyroradius(1.1, "e-", T_i=1.2) == gyroradius(1.1 * u.T,
                                                            "e-",
                                                            T_i=1.2 * u.K)

    with pytest.raises(ValueError):
        gyroradius(1.1 * u.T, "e-", Vperp=1 * u.m / u.s, T_i=1.2 * u.K)

    with pytest.raises(u.UnitTypeError):
        gyroradius(1.1 * u.T, "e-", Vperp=1.1 * u.m, T_i=1.2 * u.K)

    assert gyroradius(B, particle="p", T_i=T_i).unit.is_equivalent(u.m)

    assert gyroradius(B, particle="p",
                      Vperp=25 * u.m / u.s).unit.is_equivalent(u.m)

    # Case when Z=1 is assumed
    assert np.isclose(
        gyroradius(B, particle="p", T_i=T_i),
        gyroradius(B, particle="H+", T_i=T_i),
        atol=1e-6 * u.m,
    )

    gyroPos = gyroradius(B, particle="p", Vperp=V)
    gyroNeg = gyroradius(B, particle="p", Vperp=-V)
    assert gyroPos == gyroNeg

    Vperp = 1e6 * u.m / u.s
    Bmag = 1 * u.T
    omega_ci = gyrofrequency(Bmag, particle="p")
    analytical_result = (Vperp / omega_ci).to(
        u.m, equivalencies=u.dimensionless_angles())
    assert gyroradius(Bmag, particle="p", Vperp=Vperp) == analytical_result

    T2 = 1.2 * u.MK
    B2 = 123 * u.G
    particle2 = "alpha"
    Vperp2 = thermal_speed(T2, particle=particle2)
    gyro_by_vperp = gyroradius(B2, particle="alpha", Vperp=Vperp2)
    assert gyro_by_vperp == gyroradius(B2, particle="alpha", T_i=T2)

    explicit_positron_gyro = gyroradius(1 * u.T,
                                        particle="positron",
                                        T_i=1 * u.MK)
    assert explicit_positron_gyro == gyroradius(1 * u.T, "e-", T_i=1 * u.MK)

    with pytest.raises(TypeError):
        gyroradius(u.T, particle="p", Vperp=8 * u.m / u.s)

    with pytest.raises(ValueError):
        gyroradius(B, particle="p", T_i=-1 * u.K)

    with pytest.warns(u.UnitsWarning):
        gyro_without_units = gyroradius(1.0, particle="p", Vperp=1.0)
        gyro_with_units = gyroradius(1.0 * u.T,
                                     particle="p",
                                     Vperp=1.0 * u.m / u.s)
        assert gyro_without_units == gyro_with_units

    with pytest.warns(u.UnitsWarning):
        gyro_t_without_units = gyroradius(1.1, particle="p", T_i=1.2)
        gyro_t_with_units = gyroradius(1.1 * u.T, particle="p", T_i=1.2 * u.K)
        assert gyro_t_with_units == gyro_t_without_units

    with pytest.raises(ValueError):
        gyroradius(1.1 * u.T, particle="p", Vperp=1 * u.m / u.s, T_i=1.2 * u.K)

    with pytest.raises(u.UnitTypeError):
        gyroradius(1.1 * u.T, particle="p", Vperp=1.1 * u.m, T_i=1.2 * u.K)

    with pytest.raises(u.UnitTypeError):
        gyroradius(1.1 * u.T, particle="p", Vperp=1.2 * u.m, T_i=1.1 * u.K)
Example #4
0
def test_thermal_speed():
    r"""Test the thermal_speed function in parameters.py"""
    assert thermal_speed(T_e, "e-").unit.is_equivalent(u.m / u.s)

    assert thermal_speed(T_e, "e-") > thermal_speed(T_e, "p")

    # The NRL Plasma Formulary uses a definition of the electron
    # thermal speed that differs by a factor of sqrt(2).
    assert np.isclose(thermal_speed(1 * u.MK, "e-").value, 5505694.743141063)

    with pytest.raises(u.UnitTypeError):
        thermal_speed(5 * u.m, "e-")

    with pytest.raises(ValueError):
        thermal_speed(-T_e, "e-")

    with pytest.warns(RelativityWarning):
        thermal_speed(1e9 * u.K, "e-")

    with pytest.raises(RelativityError):
        thermal_speed(5e19 * u.K, "e-")

    with pytest.warns(u.UnitsWarning):
        assert thermal_speed(1e5, "e-") == thermal_speed(1e5 * u.K, "e-")

    assert thermal_speed(T_i, particle="p").unit.is_equivalent(u.m / u.s)

    # The NRL Plasma Formulary uses a definition of the particle thermal
    # speed that differs by a factor of sqrt(2).
    assert np.isclose(
        thermal_speed(1 * u.MK, particle="p").si.value, 128486.56960876315)

    # Explicitly check all three modes and dimensionalities
    # ndim = 1
    assert np.isclose(
        thermal_speed(T_e, "e-", method="most_probable", ndim=1).si.value, 0.0)

    # Regression tests start here!
    assert np.isclose(
        thermal_speed(T_e, "e-", method="rms", ndim=1).si.value,
        3893114.2008620175)

    assert np.isclose(
        thermal_speed(T_e, "e-", method="mean_magnitude", ndim=1).si.value,
        3106255.714310189,
    )

    # ndim = 2
    assert np.isclose(
        thermal_speed(T_e, "e-", method="most_probable", ndim=2).si.value,
        3893114.2008620175,
    )

    assert np.isclose(
        thermal_speed(T_e, "e-", method="rms", ndim=2).si.value,
        5505694.902726359)

    assert np.isclose(
        thermal_speed(T_e, "e-", method="mean_magnitude", ndim=2).si.value,
        4879295.066124102,
    )

    # ndim = 3
    assert np.isclose(
        thermal_speed(T_e, "e-", method="most_probable", ndim=3).si.value,
        5505694.902726359,
    )

    assert np.isclose(
        thermal_speed(T_e, "e-", method="rms", ndim=3).si.value,
        6743071.595560921)

    assert np.isclose(
        thermal_speed(T_e, "e-", method="mean_magnitude", ndim=3).si.value,
        6212511.428620378,
    )

    # Case when Z=1 is assumed
    assert thermal_speed(T_i, particle="p") == thermal_speed(T_i,
                                                             particle="H-1+")

    assert thermal_speed(1 * u.MK,
                         particle="e+") == thermal_speed(1 * u.MK, "e-")

    with pytest.raises(u.UnitTypeError):
        thermal_speed(5 * u.m, particle="p")

    with pytest.raises(ValueError):
        thermal_speed(-T_e, particle="p")

    with pytest.warns(RelativityWarning):
        thermal_speed(1e11 * u.K, particle="p")

    with pytest.raises(RelativityError):
        thermal_speed(1e14 * u.K, particle="p")

    with pytest.raises(InvalidParticleError):
        thermal_speed(T_i, particle="asdfasd")

    with pytest.warns(u.UnitsWarning):
        assert thermal_speed(1e6, particle="p") == thermal_speed(1e6 * u.K,
                                                                 particle="p")

    assert np.isclose(
        thermal_speed(1e6 * u.K, "e-", method="mean_magnitude").si.value,
        6212510.3969422,
    )

    assert np.isclose(
        thermal_speed(1e6 * u.K, "e-", method="rms").si.value,
        6743070.475775486)

    # Test invalid method
    with pytest.raises(ValueError):
        thermal_speed(T_i, "e-", method="sadks")

    # Test invalid ndim
    with pytest.raises(ValueError):
        thermal_speed(T_i, "e-", ndim=4)

    assert_can_handle_nparray(thermal_speed)
Example #5
0
def permittivity_1D_Maxwellian(
    omega: u.rad / u.s,
    kWave: u.rad / u.m,
    T: u.K,
    n: u.m ** -3,
    particle,
    z_mean: u.dimensionless_unscaled = None,
) -> u.dimensionless_unscaled:
    r"""
    The classical dielectric permittivity for a 1D Maxwellian plasma. This
    function can calculate both the ion and electron permittivities. No
    additional effects are considered (e.g. magnetic fields, relativistic
    effects, strongly coupled regime, etc.)

    Parameters
    ----------
    omega : ~astropy.units.Quantity
        The frequency in rad/s of the electromagnetic wave propagating
        through the plasma.

    kWave : ~astropy.units.Quantity
        The corresponding wavenumber, in rad/m, of the electromagnetic wave
        propagating through the plasma. This is often modulated by the
        dispersion of the plasma or by relativistic effects. See em_wave.py
        for ways to calculate this.

    T : ~astropy.units.Quantity
        The plasma temperature - this can be either the electron or the ion
        temperature, but should be consistent with density and particle.

    n : ~astropy.units.Quantity
        The plasma density - this can be either the electron or the ion
        density, but should be consistent with temperature and particle.

    particle : str
        The plasma particle species.

    z_mean : str
        The average ionization of the plasma. This is only required for
        calculating the ion permittivity.

    Returns
    -------
    chi : ~astropy.units.Quantity
        The ion or the electron dielectric permittivity of the plasma.
        This is a dimensionless quantity.

    Notes
    -----
    The dielectric permittivities for a Maxwellian plasma are described
    by the following equations [1]_

    .. math::
        \chi_e(k, \omega) = - \frac{\alpha_e^2}{2} Z'(x_e)

        \chi_i(k, \omega) = - \frac{\alpha_i^2}{2}\frac{Z}{} Z'(x_i)

        \alpha = \frac{\omega_p}{k v_{Th}}

        x = \frac{\omega}{k v_{Th}}

    :math:`chi_e` and :math:`chi_i` are the electron and ion permittivities
    respectively. :math:`Z'` is the derivative of the plasma dispersion
    function. :math:`\alpha` is the scattering parameter which delineates
    the difference between the collective and non-collective Thomson
    scattering regimes. :math:`x` is the dimensionless phase velocity
    of the EM wave propagating through the plasma.

    References
    ----------
    .. [1] J. Sheffield, D. Froula, S. H. Glenzer, and N. C. Luhmann Jr,
       Plasma scattering of electromagnetic radiation: theory and measurement
       techniques. Chapter 5 Pg 106 (Academic press, 2010).

    Example
    -------
    >>> from astropy import units as u
    >>> from numpy import pi
    >>> from astropy.constants import c
    >>> T = 30 * 11600 * u.K
    >>> n = 1e18 * u.cm**-3
    >>> particle = 'Ne'
    >>> z_mean = 8 * u.dimensionless_unscaled
    >>> vTh = parameters.thermal_speed(T, particle, method="most_probable")
    >>> omega = 5.635e14 * 2 * pi * u.rad / u.s
    >>> kWave = omega / vTh
    >>> permittivity_1D_Maxwellian(omega, kWave, T, n, particle, z_mean)
    <Quantity -6.72809...e-08+5.76037...e-07j>
    """
    # thermal velocity
    vTh = parameters.thermal_speed(T=T, particle=particle, method="most_probable")
    # plasma frequency
    wp = parameters.plasma_frequency(n=n, particle=particle, z_mean=z_mean)
    # scattering parameter alpha.
    # explicitly removing factor of sqrt(2) to be consistent with Froula
    alpha = np.sqrt(2) * (wp / (kWave * vTh)).to(u.dimensionless_unscaled)
    # The dimensionless phase velocity of the propagating EM wave.
    zeta = (omega / (kWave * vTh)).to(u.dimensionless_unscaled)
    chi = alpha ** 2 * (-1 / 2) * plasma_dispersion_func_deriv(zeta.value)
    return chi
Example #6
0
def Maxwellian_speed_3D(v,
                        T,
                        particle="e",
                        v_drift=0,
                        vTh=np.nan,
                        units="units"):
    r"""
    Probability distribution function of speed for a Maxwellian
    distribution in 3D.

    Return the probability density function for finding a particle with
    speed components `vx`, `vy`, and `vz` in m/s in an equilibrium
    plasma of temperature `T` which follows the 3D Maxwellian
    distribution function. This function assumes Cartesian coordinates.

    Parameters
    ----------
    v: ~astropy.units.Quantity
        The speed in units convertible to m/s.

    T: ~astropy.units.Quantity
        The temperature, preferably in Kelvin.

    particle: str, optional
        Representation of the particle species(e.g., `'p'` for protons, `'D+'`
        for deuterium, or `'He-4 +1'` for :math:`He_4^{+1}`
        (singly ionized helium-4)), which defaults to electrons.

    v_drift: ~astropy.units.Quantity
        The drift speed in units convertible to m/s.

    vTh: ~astropy.units.Quantity, optional
        Thermal velocity (most probable) in m/s. This is used for
        optimization purposes to avoid re-calculating vTh, for example
        when integrating over velocity-space.

    units: str, optional
        Selects whether to run function with units and unit checks (when
        equal to "units") or to run as unitless (when equal to "unitless").
        The unitless version is substantially faster for intensive
        computations.

    Returns
    -------
    f : ~astropy.units.Quantity
        Probability density in speed^-1, normalized so that:
        :math:`\iiint_{0}^{\infty} f(\vec{v}) d\vec{v} = 1`.

    Raises
    ------
    TypeError
        A parameter argument is not a `~astropy.units.Quantity` and
        cannot be converted into a `~astropy.units.Quantity`.

    ~astropy.units.UnitConversionError
        If the parameters is not in appropriate units.

    ValueError
        If the temperature is negative, or the particle mass or charge state
        cannot be found.

    Notes
    -----
    In 3D, the Maxwellian speed distribution function describing
    the distribution of particles with speed :math:`v` in a plasma with
    temperature :math:`T` is given by:

    .. math::

       f = 4 \pi v^{2} (\pi v_{Th}^2)^{-3/2} \exp(-v^{2} / v_{Th}^2)

    where :math:`v_{Th} = \sqrt{2 k_B T / m}` is the thermal speed.

    See also
    --------
    Maxwellian_speed_1D

    Example
    -------
    >>> from astropy import units as u
    >>> v=1 * u.m / u.s
    >>> Maxwellian_speed_3D(v=v, T=30000*u.K, particle='e', v_drift=0 * u.m / u.s)
    <Quantity 2.60235...e-18 s / m>

    """
    if v_drift != 0:
        raise NotImplementedError("Non-zero drift speed is work in progress.")
    if units == "units":
        # unit checks and conversions
        # checking velocity units
        v = v.to(u.m / u.s)
        # Catching case where drift velocity has default value, and
        # needs to be assigned units
        v_drift = _v_drift_units(v_drift)
        # convert temperature to Kelvins
        T = T.to(u.K, equivalencies=u.temperature_energy())
        if np.isnan(vTh):
            # get thermal velocity and thermal velocity squared
            vTh = parameters.thermal_speed(T,
                                           particle=particle,
                                           method="most_probable")
        elif not np.isnan(vTh):
            # check units of thermal velocity
            vTh = vTh.to(u.m / u.s)
    elif np.isnan(vTh) and units == "unitless":
        # assuming unitless temperature is in Kelvins
        vTh = (parameters.thermal_speed(T * u.K,
                                        particle=particle,
                                        method="most_probable")).si.value
    # getting square of thermal speed
    vThSq = vTh**2
    # get square of relative particle speed
    vSq = (v - v_drift)**2
    # calculating distribution function
    coeff1 = (np.pi * vThSq)**(-3 / 2)
    coeff2 = 4 * np.pi * vSq
    expTerm = np.exp(-vSq / vThSq)
    distFunc = coeff1 * coeff2 * expTerm
    if units == "units":
        return distFunc.to(u.s / u.m)
    elif units == "unitless":
        return distFunc
Example #7
0
def Maxwellian_speed_1D(v,
                        T,
                        particle="e",
                        v_drift=0,
                        vTh=np.nan,
                        units="units"):
    r"""
    Probability distribution function of speed for a Maxwellian distribution
    in 1D.

    Return the probability density function for finding a particle with
    speed `v` in m/s in an equilibrium plasma of temperature `T` which
    follows the Maxwellian distribution function.

    Parameters
    ----------
    v: ~astropy.units.Quantity
        The speed in units convertible to m/s.

    T: ~astropy.units.Quantity
        The temperature, preferably in Kelvin.

    particle: str, optional
        Representation of the particle species(e.g., `'p'` for protons, `'D+'`
        for deuterium, or `'He-4 +1'` for :math:`He_4^{+1}`
        (singly ionized helium-4)), which defaults to electrons.

    v_drift: ~astropy.units.Quantity
        The drift speed in units convertible to m/s.

    vTh: ~astropy.units.Quantity, optional
        Thermal velocity (most probable) in m/s. This is used for
        optimization purposes to avoid re-calculating vTh, for example
        when integrating over velocity-space.

    units: str, optional
        Selects whether to run function with units and unit checks (when
        equal to "units") or to run as unitless (when equal to "unitless").
        The unitless version is substantially faster for intensive
        computations.

    Returns
    -------
    f : ~astropy.units.Quantity
        Probability density in speed^-1, normalized so that
        :math:`\int_{0}^{\infty} f(v) dv = 1`.

    Raises
    ------
    TypeError
        The parameter arguments are not Quantities and
        cannot be converted into Quantities.

    ~astropy.units.UnitConversionError
        If the parameters is not in appropriate units.

    ValueError
        If the temperature is negative, or the particle mass or charge state
        cannot be found.

    Notes
    -----
    In one dimension, the Maxwellian speed distribution function describing
    the distribution of particles with speed v in a plasma with temperature T
    is given by:

    .. math::

       f(v) = 2 \frac{1}{(\pi v_{Th}^2)^{1/2}} \exp(-(v - V_{drift})^2 / v_{Th}^2 )

    where :math:`v_{Th} = \sqrt{2 k_B T / m}` is the thermal speed.

    Example
    -------
    >>> from astropy import units as u
    >>> v=1 * u.m / u.s
    >>> Maxwellian_speed_1D(v=v, T=30000 * u.K, particle='e', v_drift=0 * u.m / u.s)
    <Quantity 1.1832...e-06 s / m>

    """
    if units == "units":
        # unit checks and conversions
        # checking velocity units
        v = v.to(u.m / u.s)
        # Catching case where drift velocities have default values, they
        # need to be assigned units
        v_drift = _v_drift_units(v_drift)
        # convert temperature to Kelvins
        T = T.to(u.K, equivalencies=u.temperature_energy())
        if np.isnan(vTh):
            # get thermal velocity and thermal velocity squared
            vTh = parameters.thermal_speed(T,
                                           particle=particle,
                                           method="most_probable")
        elif not np.isnan(vTh):
            # check units of thermal velocity
            vTh = vTh.to(u.m / u.s)
    elif np.isnan(vTh) and units == "unitless":
        # assuming unitless temperature is in Kelvins
        vTh = (parameters.thermal_speed(T * u.K,
                                        particle=particle,
                                        method="most_probable")).si.value
    # Get thermal velocity squared
    vThSq = vTh**2
    # Get square of relative particle velocity
    vSq = (v - v_drift)**2
    # calculating distribution function
    coeff = 2 * (vThSq * np.pi)**(-1 / 2)
    expTerm = np.exp(-vSq / vThSq)
    distFunc = coeff * expTerm
    if units == "units":
        return distFunc.to(u.s / u.m)
    elif units == "unitless":
        return distFunc
Example #8
0
def Maxwellian_velocity_3D(
    vx,
    vy,
    vz,
    T,
    particle="e",
    vx_drift=0,
    vy_drift=0,
    vz_drift=0,
    vTh=np.nan,
    units="units",
):
    r"""
    Probability distribution function of velocity for a Maxwellian
    distribution in 3D.

    Return the probability density function for finding a particle with
    velocity components `vx`, `vy`, and `vz` in m/s in an equilibrium
    plasma of temperature `T` which follows the 3D Maxwellian distribution
    function. This function assumes Cartesian coordinates.

    Parameters
    ----------
    vx: ~astropy.units.Quantity
        The velocity in x-direction units convertible to m/s.

    vy: ~astropy.units.Quantity
        The velocity in y-direction units convertible to m/s.

    vz: ~astropy.units.Quantity
        The velocity in z-direction units convertible to m/s.

    T: ~astropy.units.Quantity
        The temperature, preferably in Kelvin.

    particle: str, optional
        Representation of the particle species (e.g., ``'p'`` for protons,
        ``'D+'`` for deuterium, or ``'He-4 +1'`` for :math:`He_4^{+1}`
        (singly ionized helium-4)), which defaults to electrons.

    vx_drift: ~astropy.units.Quantity, optional
        The drift velocity in x-direction units convertible to m/s.

    vy_drift: ~astropy.units.Quantity, optional
        The drift velocity in y-direction units convertible to m/s.

    vz_drift: ~astropy.units.Quantity, optional
        The drift velocity in z-direction units convertible to m/s.

    vTh: ~astropy.units.Quantity, optional
        Thermal velocity (most probable) in m/s. This is used for
        optimization purposes to avoid re-calculating `vTh`, for example
        when integrating over velocity-space.

    units: str, optional
        Selects whether to run function with units and unit checks (when
        equal to "units") or to run as unitless (when equal to "unitless").
        The unitless version is substantially faster for intensive
        computations.

    Returns
    -------
    f : ~astropy.units.Quantity
        Probability density in Velocity^-1, normalized so that
        :math:`\iiint_{0}^{\infty} f(\vec{v}) d\vec{v} = 1`.

    Raises
    ------
    TypeError
        A parameter argument is not a `~astropy.units.Quantity` and
        cannot be converted into a `~astropy.units.Quantity`.

    ~astropy.units.UnitConversionError
        If the parameters is not in appropriate units.

    ValueError
        If the temperature is negative, or the particle mass or charge state
        cannot be found.

    Notes
    -----
    In 3D, the Maxwellian speed distribution function describing
    the distribution of particles with speed :math:`v` in a plasma with
    temperature :math:`T` is given by:

    .. math::

        f = (\pi v_{Th}^2)^{-3/2} \exp \left [-(\vec{v} -
        \vec{V}_{drift})^2 / v_{Th}^2 \right ]

    where :math:`v_{Th} = \sqrt{2 k_B T / m}` is the thermal speed.

    See also
    --------
    Maxwellian_1D

    Example
    -------
    >>> from astropy import units as u
    >>> v=1 * u.m / u.s
    >>> Maxwellian_velocity_3D(vx=v,
    ... vy=v,
    ... vz=v,
    ... T=30000 * u.K,
    ... particle='e',
    ... vx_drift=0 * u.m / u.s,
    ... vy_drift=0 * u.m / u.s,
    ... vz_drift=0 * u.m / u.s)
    <Quantity 2.0708...e-19 s3 / m3>


    """
    if units == "units":
        # unit checks and conversions
        # checking velocity units
        vx = vx.to(u.m / u.s)
        vy = vy.to(u.m / u.s)
        vz = vz.to(u.m / u.s)
        # catching case where drift velocities have default values, they
        # need to be assigned units
        vx_drift = _v_drift_units(vx_drift)
        vy_drift = _v_drift_units(vy_drift)
        vz_drift = _v_drift_units(vz_drift)
        # convert temperature to Kelvins
        T = T.to(u.K, equivalencies=u.temperature_energy())
        if np.isnan(vTh):
            # get thermal velocity and thermal velocity squared
            vTh = parameters.thermal_speed(T,
                                           particle=particle,
                                           method="most_probable")
        elif not np.isnan(vTh):
            # check units of thermal velocity
            vTh = vTh.to(u.m / u.s)
    elif np.isnan(vTh) and units == "unitless":
        # assuming unitless temperature is in Kelvins
        vTh = parameters.thermal_speed(T * u.K,
                                       particle=particle,
                                       method="most_probable").si.value
    # accounting for thermal velocity in 3D
    vThSq = vTh**2
    # Get square of relative particle velocity
    vSq = (vx - vx_drift)**2 + (vy - vy_drift)**2 + (vz - vz_drift)**2
    # calculating distribution function
    coeff = (vThSq * np.pi)**(-3 / 2)
    expTerm = np.exp(-vSq / vThSq)
    distFunc = coeff * expTerm
    if units == "units":
        return distFunc.to((u.s / u.m)**3)
    elif units == "unitless":
        return distFunc
Example #9
0
def test_thermal_speed():
    r"""Test the thermal_speed function in parameters.py"""
    assert thermal_speed(T_e).unit.is_equivalent(u.m / u.s)

    assert thermal_speed(T_e) > thermal_speed(T_e, 'p')

    # The NRL Plasma Formulary uses a definition of the electron
    # thermal speed that differs by a factor of sqrt(2).
    assert np.isclose(thermal_speed(1 * u.MK).value,
                      5505694.743141063)

    with pytest.raises(u.UnitConversionError):
        thermal_speed(5 * u.m)

    with pytest.raises(ValueError):
        thermal_speed(-T_e)

    with pytest.warns(RelativityWarning):
        thermal_speed(1e9 * u.K)

    with pytest.raises(RelativityError):
        thermal_speed(5e19 * u.K)

    with pytest.warns(u.UnitsWarning):
        assert thermal_speed(1e5) == thermal_speed(1e5 * u.K)

    assert thermal_speed(T_i, particle='p').unit.is_equivalent(u.m / u.s)

    # The NRL Plasma Formulary uses a definition of the particle thermal
    # speed that differs by a factor of sqrt(2).
    assert np.isclose(thermal_speed(1 * u.MK, particle='p').si.value,
                      128486.56960876315)

    # Case when Z=1 is assumed
    assert thermal_speed(T_i, particle='p') == thermal_speed(T_i, particle='H-1+')

    assert thermal_speed(1 * u.MK, particle='e+') == thermal_speed(1 * u.MK)

    with pytest.raises(u.UnitConversionError):
        thermal_speed(5 * u.m, particle='p')

    with pytest.raises(ValueError):
        thermal_speed(-T_e, particle='p')

    with pytest.warns(RelativityWarning):
        thermal_speed(1e11 * u.K, particle='p')

    with pytest.raises(RelativityError):
        thermal_speed(1e14 * u.K, particle='p')

    with pytest.raises(InvalidParticleError):
        thermal_speed(T_i, particle='asdfasd')

    with pytest.warns(u.UnitsWarning):
        assert thermal_speed(1e6, particle='p') == thermal_speed(1e6 * u.K, particle='p')

    assert np.isclose(thermal_speed(1e6 * u.K,
                                    method="mean_magnitude").si.value,
                      6212510.3969422)

    assert np.isclose(thermal_speed(1e6 * u.K, method="rms").si.value,
                      6743070.475775486)

    with pytest.raises(ValueError):
        thermal_speed(T_i, method="sadks")

    assert_can_handle_nparray(thermal_speed)
Example #10
0
 def time_thermal_speed(self):
     thermal_speed(1 * u.MK)
Example #11
0
def Maxwellian_1D(v, T, particle="e", v_drift=0, vTh=np.nan, units="units"):
    r"""
    Probability distribution function of velocity for a Maxwellian
    distribution in 1D.

    Returns the probability density function at the velocity ``v`` in m/s
    to find a particle ``particle`` in a plasma of temperature ``T``
    following the Maxwellian distribution function.

    Parameters
    ----------
    v : `~astropy.units.Quantity`
        The velocity in units convertible to m/s.

    T : `~astropy.units.Quantity`
        The temperature in kelvin.

    particle : `str`, optional
        Representation of the particle species(e.g., ``'p'`` for protons,
        ``'D+'`` for deuterium, or ``'He-4 +1'`` for singly ionized
        helium-4), which defaults to electrons.

    v_drift : `~astropy.units.Quantity`, optional
        The drift velocity in units convertible to m/s.

    vTh : `~astropy.units.Quantity`, optional
        Thermal velocity (most probable velocity) in m/s. This is used for
        optimization purposes to avoid re-calculating ``vTh``, for example
        when integrating over velocity-space.

    units : `str`, optional
        Selects whether to run function with units and unit checks (when
        equal to "units") or to run as unitless (when equal to "unitless").
        The unitless version is substantially faster for intensive
        computations.

    Returns
    -------
    f : `~astropy.units.Quantity`
        Probability density in units of velocity\ :sup:`-1`\ , normalized so that
        :math:`\int_{-∞}^{+∞} f(v) dv = 1`.

    Raises
    ------
    `TypeError`
        The parameter arguments are not Quantities and
        cannot be converted into Quantities.

    `~astropy.units.UnitConversionError`
        If the parameters are not in appropriate units.

    `ValueError`
        If the temperature is negative, or the particle mass or charge state
        cannot be found.

    Notes
    -----
    In one dimension, the Maxwellian distribution function for a particle of
    mass m, velocity v, a drift velocity V and with temperature T is:

    .. math::

        f = \sqrt{\frac{m}{2 \pi k_B T}} e^{-\frac{m}{2 k_B T} (v-V)^2}
        \equiv \frac{1}{\sqrt{\pi v_{Th}^2}} e^{-(v - v_{drift})^2 / v_{Th}^2}

    where :math:`v_{Th} = \sqrt{2 k_B T / m}` is the thermal speed

    Examples
    --------
    >>> from astropy import units as u
    >>> v=1*u.m/u.s
    >>> Maxwellian_1D(v=v, T=30000 * u.K, particle='e', v_drift=0 * u.m / u.s)
    <Quantity 5.9163...e-07 s / m>
    """

    if units == "units":
        # unit checks and conversions
        # checking velocity units
        v = v.to(u.m / u.s)
        # Catching case where drift velocities have default values, they
        # need to be assigned units
        v_drift = _v_drift_units(v_drift)
        # convert temperature to kelvin
        T = T.to(u.K, equivalencies=u.temperature_energy())
        if np.isnan(vTh):
            # get thermal velocity and thermal velocity squared
            vTh = parameters.thermal_speed(T, particle=particle, method="most_probable")
        elif not np.isnan(vTh):
            # check units of thermal velocity
            vTh = vTh.to(u.m / u.s)
    elif np.isnan(vTh) and units == "unitless":
        # assuming unitless temperature is in kelvin
        vTh = (
            parameters.thermal_speed(T * u.K, particle=particle, method="most_probable")
        ).si.value
    # Get thermal velocity squared
    vThSq = vTh ** 2
    # Get square of relative particle velocity
    vSq = (v - v_drift) ** 2
    # calculating distribution function
    coeff = (vThSq * np.pi) ** (-1 / 2)
    expTerm = np.exp(-vSq / vThSq)
    distFunc = coeff * expTerm
    if units == "units":
        return distFunc.to(u.s / u.m)
    elif units == "unitless":
        return distFunc