Example #1
0
def test_upper_hybrid_frequency():
    r"""Test the upper_hybrid_frequency function in frequencies.py."""

    omega_uh = upper_hybrid_frequency(B, n_e=n_e)
    omega_uh_hz = upper_hybrid_frequency(B, n_e=n_e, to_hz=True)
    omega_ce = gyrofrequency(B, "e-")
    omega_pe = plasma_frequency(n=n_e, particle="e-")
    assert omega_ce.unit.is_equivalent(u.rad / u.s)
    assert omega_pe.unit.is_equivalent(u.rad / u.s)
    assert omega_uh.unit.is_equivalent(u.rad / u.s)
    assert omega_uh_hz.unit.is_equivalent(u.Hz)
    left_hand_side = omega_uh**2
    right_hand_side = omega_ce**2 + omega_pe**2
    assert np.isclose(left_hand_side.value, right_hand_side.value)

    assert np.isclose(omega_uh_hz.value, 69385868857.90918)

    with pytest.raises(ValueError):
        upper_hybrid_frequency(5 * u.T, n_e=-1 * u.m**-3)

    with pytest.warns(u.UnitsWarning):
        assert upper_hybrid_frequency(1.2, 1.3) == upper_hybrid_frequency(
            1.2 * u.T, 1.3 * u.m**-3)

    with pytest.warns(u.UnitsWarning):
        assert upper_hybrid_frequency(1.4 * u.T,
                                      1.3) == upper_hybrid_frequency(
                                          1.4, 1.3 * u.m**-3)

    assert_can_handle_nparray(upper_hybrid_frequency)
Example #2
0
def test_lower_hybrid_frequency():
    r"""Test the lower_hybrid_frequency function in frequencies.py."""

    ion = "He-4 1+"
    omega_ci = gyrofrequency(B, particle=ion)
    omega_pi = plasma_frequency(n=n_i, particle=ion)
    omega_ce = gyrofrequency(B, "e-")
    omega_lh = lower_hybrid_frequency(B, n_i=n_i, ion=ion)
    omega_lh_hz = lower_hybrid_frequency(B, n_i=n_i, ion=ion, to_hz=True)
    assert omega_ci.unit.is_equivalent(u.rad / u.s)
    assert omega_pi.unit.is_equivalent(u.rad / u.s)
    assert omega_ce.unit.is_equivalent(u.rad / u.s)
    assert omega_lh.unit.is_equivalent(u.rad / u.s)
    left_hand_side = omega_lh**-2
    right_hand_side = (1 / (omega_ci**2 + omega_pi**2) +
                       omega_ci**-1 * omega_ce**-1)
    assert np.isclose(left_hand_side.value, right_hand_side.value)

    assert np.isclose(omega_lh_hz.value, 299878691.3223296)

    with pytest.raises(ValueError):
        lower_hybrid_frequency(0.2 * u.T, n_i=5e19 * u.m**-3, ion="asdfasd")

    with pytest.raises(ValueError):
        lower_hybrid_frequency(0.2 * u.T, n_i=-5e19 * u.m**-3, ion="asdfasd")

    with pytest.raises(ValueError):
        lower_hybrid_frequency(np.nan * u.T,
                               n_i=-5e19 * u.m**-3,
                               ion="asdfasd")

    with pytest.warns(u.UnitsWarning):
        assert lower_hybrid_frequency(1.3, 1e19,
                                      "p+") == lower_hybrid_frequency(
                                          1.3 * u.T, 1e19 * u.m**-3, "p+")
    assert_can_handle_nparray(lower_hybrid_frequency)
Example #3
0
    def test_proton_electron_plasma(self):
        """
        Test proton-electron plasma against the (approximate)
        analytical formulas
        """
        B = 1 * u.T
        n = [1, 1] * 1 / u.m ** 3
        omega = 1 * u.rad / u.s
        omega_ce = gyrofrequency(B, particle="e", signed=True)
        omega_pe = plasma_frequency(n[0], particle="e")
        omega_cp = abs(omega_ce) / 1860
        omega_pp = omega_pe / 43

        S_analytical = (
            1
            - omega_pe ** 2 / (omega ** 2 - omega_ce ** 2)
            - omega_pp ** 2 / (omega ** 2 - omega_cp ** 2)
        )

        D_analytical = +omega_ce / omega * omega_pe ** 2 / (
            omega ** 2 - omega_ce ** 2
        ) + omega_cp / omega * omega_pp ** 2 / (omega ** 2 - omega_cp ** 2)

        P_analytical = 1 - (omega_pe ** 2 + omega_pp ** 2) / omega ** 2

        species = ["e", "p"]
        S, D, P = tuple_result = cold_plasma_permittivity_SDP(B, species, n, omega)

        assert tuple_result.sum is S
        assert tuple_result.difference is D
        assert tuple_result.plasma is P
        assert isinstance(tuple_result, StixTensorElements)

        assert np.isclose(S, S_analytical)
        assert np.isclose(D, D_analytical)
        assert np.isclose(P, P_analytical)

        L, R, P = rotating_tuple_result = cold_plasma_permittivity_LRP(
            B, species, n, omega
        )
        assert rotating_tuple_result.left is L
        assert rotating_tuple_result.right is R
        assert rotating_tuple_result.plasma is P
        assert isinstance(rotating_tuple_result, RotatingTensorElements)
Example #4
0
def gyroradius(
    B: u.T,
    particle: Particle,
    *,
    Vperp: u.m / u.s = np.nan * u.m / u.s,
    T_i: u.K = None,
    T: u.K = None,
) -> u.m:
    r"""Return the particle gyroradius.

    **Aliases:** `rc_`, `rhoc_`

    Parameters
    ----------
    B : `~astropy.units.Quantity`
        The magnetic field magnitude in units convertible to tesla.

    particle : `~plasmapy.particles.particle_class.Particle`
        Representation of the particle species (e.g., ``'p'`` for protons, ``'D+'``
        for deuterium, or ``'He-4 +1'`` for singly ionized helium-4).  If no
        charge state information is provided, then the particles are assumed
        to be singly charged.

    Vperp : `~astropy.units.Quantity`, optional, keyword-only
        The component of particle velocity that is perpendicular to the
        magnetic field in units convertible to meters per second.

    T : `~astropy.units.Quantity`, optional, keyword-only
        The particle temperature in units convertible to kelvin.

    T_i : `~astropy.units.Quantity`, optional, keyword-only
        The particle temperature in units convertible to kelvin.
        Note: Deprecated. Use ``T`` instead.

    Returns
    -------
    r_Li : `~astropy.units.Quantity`
        The particle gyroradius in units of meters.  This
        `~astropy.units.Quantity` will be based on either the
        perpendicular component of particle velocity as inputted, or
        the most probable speed for a particle within a Maxwellian
        distribution for the particle temperature.

    Raises
    ------
    `TypeError`
        The arguments are of an incorrect type.

    `~astropy.units.UnitConversionError`
        The arguments do not have appropriate units.

    `ValueError`
        If any argument contains invalid values.

    Warns
    -----
    : `~astropy.units.UnitsWarning`
        If units are not provided, SI units are assumed.

    Notes
    -----
    One but not both of ``Vperp`` and ``T`` must be inputted.

    If any of ``B``, ``Vperp``, or ``T`` is a number rather than a
    `~astropy.units.Quantity`, then SI units will be assumed and a
    warning will be raised.

    The particle gyroradius is also known as the particle Larmor
    radius and is given by

    .. math::
        r_{Li} = \frac{V_{\perp}}{ω_{ci}}

    where :math:`V_⟂` is the component of particle velocity that is
    perpendicular to the magnetic field and :math:`ω_{ci}` is the
    particle gyrofrequency.  If a temperature is provided, then
    :math:`V_⟂` will be the most probable thermal velocity of a
    particle at that temperature.

    Examples
    --------
    >>> from astropy import units as u
    >>> gyroradius(0.2*u.T, particle='p+', T=1e5*u.K)
    <Quantity 0.002120... m>
    >>> gyroradius(0.2*u.T, particle='p+', T=1e5*u.K)
    <Quantity 0.002120... m>
    >>> gyroradius(5*u.uG, particle='alpha', T=1*u.eV)
    <Quantity 288002.38... m>
    >>> gyroradius(400*u.G, particle='Fe+++', Vperp=1e7*u.m/u.s)
    <Quantity 48.23129... m>
    >>> gyroradius(B=0.01*u.T, particle='e-', T=1e6*u.K)
    <Quantity 0.003130... m>
    >>> gyroradius(0.01*u.T, 'e-', Vperp=1e6*u.m/u.s)
    <Quantity 0.000568... m>
    >>> gyroradius(0.2*u.T, 'e-', T=1e5*u.K)
    <Quantity 4.94949...e-05 m>
    >>> gyroradius(5*u.uG, 'e-', T=1*u.eV)
    <Quantity 6744.25... m>
    >>> gyroradius(400*u.G, 'e-', Vperp=1e7*u.m/u.s)
    <Quantity 0.001421... m>
    """

    # Backwards Compatibility and Deprecation check for keyword T_i
    if T_i is not None:
        warnings.warn(
            "Keyword T_i is deprecated, use T instead.",
            PlasmaPyFutureWarning,
        )
        if T is None:
            T = T_i
        else:
            raise ValueError(
                "Keywords T_i and T are both given.  T_i is deprecated, "
                "please use T only."
            )

    if T is None:
        T = np.nan * u.K

    isfinite_T = np.isfinite(T)
    isfinite_Vperp = np.isfinite(Vperp)

    # check 1: ensure either Vperp or T invalid, keeping in mind that
    # the underlying values of the astropy quantity may be numpy arrays
    if np.any(np.logical_and(isfinite_Vperp, isfinite_T)):
        raise ValueError(
            "Must give Vperp or T, but not both, as arguments to gyroradius"
        )

    # check 2: get Vperp as the thermal speed if is not already a valid input
    if np.isscalar(Vperp.value) and np.isscalar(
        T.value
    ):  # both T and Vperp are scalars
        # we know exactly one of them is nan from check 1
        if isfinite_T:
            # T is valid, so use it to determine Vperp
            Vperp = speeds.thermal_speed(T, particle=particle)
        # else: Vperp is already valid, do nothing
    elif np.isscalar(Vperp.value):  # only T is an array
        # this means either Vperp must be nan, or T must be an array of all nan,
        # or else we couldn't have gotten through check 1
        if isfinite_Vperp:
            # Vperp is valid, T is a vector that is all nan
            # uh...
            Vperp = np.repeat(Vperp, len(T))
        else:
            # normal case where Vperp is scalar nan and T is valid array
            Vperp = speeds.thermal_speed(T, particle=particle)
    elif np.isscalar(T.value):  # only Vperp is an array
        # this means either T must be nan, or V_perp must be an array of all nan,
        # or else we couldn't have gotten through check 1
        if isfinite_T:
            # T is valid, V_perp is an array of all nan
            # uh...
            Vperp = speeds.thermal_speed(np.repeat(T, len(Vperp)), particle=particle)
        # else: normal case where T is scalar nan and Vperp is already a valid
        # array so, do nothing
    else:  # both T and Vperp are arrays
        # we know all the elementwise combinations have one nan and one finite,
        # due to check 1 use the valid Vperps, and replace the others with those
        # calculated from T
        Vperp = Vperp.copy()  # avoid changing Vperp's value outside function
        Vperp[isfinite_T] = speeds.thermal_speed(T[isfinite_T], particle=particle)

    omega_ci = frequencies.gyrofrequency(B, particle)

    return np.abs(Vperp) / omega_ci
Example #5
0
def two_fluid(
    *,
    B: u.T,
    ion: Union[str, Particle],
    k: u.rad / u.m,
    n_i: u.m**-3,
    T_e: u.K,
    T_i: u.K,
    theta: u.rad,
    gamma_e: Union[float, int] = 1,
    gamma_i: Union[float, int] = 3,
    z_mean: Union[float, int] = None,
):
    r"""
    Using the solution provided by :cite:t:`bellan:2012`, calculate the
    analytical solution to the two fluid, low-frequency
    (:math:`\omega/kc \ll 1`) dispersion relation presented by
    :cite:t:`stringer:1963`.  This dispersion relation also assumes a
    uniform magnetic field :math:`\mathbf{B_o}`, no D.C. electric field
    :math:`\mathbf{E_o}=0`, and quasi-neutrality.  For more information
    see the **Notes** section below.

    Parameters
    ----------
    B : `~astropy.units.Quantity`
        The magnetic field magnitude in units convertible to T.
    ion : `str` or `~plasmapy.particles.particle_class.Particle`
        Representation of the ion species (e.g., ``'p'`` for protons,
        ``'D+'`` for deuterium, ``'He-4 +1'`` for singly ionized
        helium-4, etc.). If no charge state information is provided,
        then the ions are assumed to be singly ionized.
    k : `~astropy.units.Quantity`, single valued or 1-D array
        Wavenumber in units convertible to rad/m`.  Either single
        valued or 1-D array of length :math:`N`.
    n_i : `~astropy.units.Quantity`
        Ion number density in units convertible to m\ :sup:`-3`.
    T_e : `~astropy.units.Quantity`
        The electron temperature in units of K or eV.
    T_i : `~astropy.units.Quantity`
        The ion temperature in units of K or eV.
    theta : `~astropy.units.Quantity`, single valued or 1-D array
        The angle of propagation of the wave with respect to the
        magnetic field, :math:`\cos^{-1}(k_z / k)`, in units must be
        convertible to radians. Either single valued or 1-D array of
        size :math:`M`.
    gamma_e : `float` or `int`, optional
        The adiabatic index for electrons, which defaults to 1.  This
        value assumes that the electrons are able to equalize their
        temperature rapidly enough that the electrons are effectively
        isothermal.
    gamma_i : `float` or `int`, optional
        The adiabatic index for ions, which defaults to 3. This value
        assumes that ion motion has only one degree of freedom, namely
        along magnetic field lines.
    z_mean : `float` or int, optional
        The average ionization state (arithmetic mean) of the ``ion``
        composing the plasma.  Will override any charge state defined
        by argument ``ion``.

    Returns
    -------
    omega : Dict[str, `~astropy.units.Quantity`]
        A dictionary of computed wave frequencies in units rad/s.  The
        dictionary contains three keys: ``'fast_mode'`` for the fast
        mode, ``'alfven_mode'`` for the Alfvén mode, and
        ``'acoustic_mode'`` for the ion-acoustic mode.  The value for
        each key will be a :math:`N × M` array.

    Raises
    ------
    TypeError
        If applicable arguments are not instances of
        `~astropy.units.Quantity` or cannot be converted into one.

    TypeError
        If ``ion`` is not of type or convertible to
        `~plasmapy.particles.particle_class.Particle`.

    TypeError
        If ``gamma_e``, ``gamma_i``, or ``z_mean`` are not of type `int`
        or `float`.

    ~astropy.units.UnitTypeError
        If applicable arguments do not have units convertible to the
        expected units.

    ValueError
        If any of ``B``, ``k``, ``n_i``, ``T_e``, or ``T_i`` is negative.

    ValueError
        If ``k`` is negative or zero.

    ValueError
        If ``ion`` is not of category ion or element.

    ValueError
        If ``B``, ``n_i``, ``T_e``, or ``T_I`` are not single valued
        `astropy.units.Quantity` (i.e. an array).

    ValueError
        If ``k`` or ``theta`` are not single valued or a 1-D array.

    Warns
    -----
    : `~plasmapy.utils.exceptions.PhysicsWarning`
        When the computed wave frequencies violate the low-frequency
        (:math:`\omega/kc \ll 1`) assumption of the dispersion relation.

    Notes
    -----

    The complete dispersion equation presented by :cite:t:`stringer:1963`
    (equation 1 of :cite:t:`bellan:2012`) is:

    .. math::
        \left( \cos^2 \theta - Q \frac{\omega^2}{k^2 {v_A}^2} \right) &
        \left[
            \left( \cos^2 \theta - \frac{\omega^2}{k^2 {c_s}^2} \right)
            - Q \frac{\omega^2}{k^2 {v_A}^2} \left(
                1 - \frac{\omega^2}{k^2 {c_s}^2}
            \right)
        \right] \\
            &= \left(1 - \frac{\omega^2}{k^2 {c_s}^2} \right)
              \frac{\omega^2}{{\omega_{ci}}^2} \cos^2 \theta

    where

    .. math::
        Q &= 1 + k^2 c^2/{\omega_{pe}}^2 \\
        \cos \theta &= \frac{k_z}{k} \\
        \mathbf{B_o} &= B_{o} \mathbf{\hat{z}}

    :math:`\omega` is the wave frequency, :math:`k` is the wavenumber,
    :math:`v_A` is the Alfvén velocity, :math:`c_s` is the sound speed,
    :math:`\omega_{ci}` is the ion gyrofrequency, and
    :math:`\omega_{pe}` is the electron plasma frequency. This relation
    does additionally assume low-frequency waves
    :math:`\omega/kc \ll 1`, no D.C. electric field
    :math:`\mathbf{E_o}=0` and quasi-neutrality.

    Following section 5 of :cite:t:`bellan:2012` the exact roots of the
    above dispersion equation can be derived and expressed as one
    analytical solution (equation 38 of :cite:t:`bellan:2012`):

    .. math::
        \frac{\omega}{\omega_{ci}} = \sqrt{
            2 \Lambda \sqrt{-\frac{P}{3}} \cos\left(
                \frac{1}{3} \cos^{-1}\left(
                    \frac{3q}{2p} \sqrt{-\frac{3}{p}}
                \right)
                - \frac{2 \pi}{3}j
            \right)
            + \frac{\Lambda A}{3}
        }

    where :math:`j = 0` represents the fast mode, :math:`j = 1`
    represents the Alfvén mode, and :math:`j = 2` represents the
    acoustic mode.  Additionally,

    .. math::
        p &= \frac{3B-A^2}{3} \; , \; q = \frac{9AB-2A^3-27C}{27} \\
        A &= \frac{Q + Q^2 \beta + Q \alpha + \alpha \Lambda}{Q^2} \;
            , \; B = \alpha \frac{1 + 2 Q \beta + \Lambda \beta}{Q^2} \;
            , \; C = \frac{\alpha^2 \beta}{Q^2} \\
        \alpha &= \cos^2 \theta \;
            , \; \beta = \left( \frac{c_s}{v_A}\right)^2 \;
            , \; \Lambda = \left( \frac{k v_{A}}{\omega_{ci}}\right)^2

    Examples
    --------
    >>> from astropy import units as u
    >>> from plasmapy.dispersion.analytical import two_fluid
    >>> inputs = {
    ...     "k": 0.01 * u.rad / u.m,
    ...     "theta": 30 * u.deg,
    ...     "B": 8.3e-9 * u.T,
    ...     "n_i": 5e6 * u.m ** -3,
    ...     "T_e": 1.6e6 * u.K,
    ...     "T_i": 4.0e5 * u.K,
    ...     "ion": "p+",
    ... }
    >>> omegas = two_fluid(**inputs)
    >>> omegas
    {'fast_mode': <Quantity 1520.57... rad / s>,
     'alfven_mode': <Quantity 1261.75... rad / s>,
     'acoustic_mode': <Quantity 0.688152... rad / s>}

    >>> inputs = {
    ...     "k": [1e-7, 2e-7] * u.rad / u.m,
    ...     "theta": [10, 20] * u.deg,
    ...     "B": 8.3e-9 * u.T,
    ...     "n_i": 5e6 * u.m ** -3,
    ...     "T_e": 1.6e6 * u.K,
    ...     "T_i": 4.0e5 * u.K,
    ...     "ion": "He+",
    ... }
    >>> omegas = two_fluid(**inputs)
    >>> omegas['fast_mode']
    <Quantity [[0.00767..., 0.00779... ],
               [0.01534..., 0.01558...]] rad / s>
    """

    # validate argument ion
    if not isinstance(ion, Particle):
        try:
            ion = Particle(ion)
        except TypeError:
            raise TypeError(
                f"For argument 'ion' expected type {Particle} but got {type(ion)}."
            )
    if not ion.is_ion and not ion.is_category("element"):
        raise ValueError(
            "The particle passed for 'ion' must be an ion or element.")

    # validate z_mean
    if z_mean is None:
        try:
            z_mean = abs(ion.charge_number)
        except ChargeError:
            z_mean = 1
    elif isinstance(z_mean, (int, np.integer, float, np.floating)):
        z_mean = abs(z_mean)
    else:
        raise TypeError(
            f"Expected int or float for argument 'z_mean', but got {type(z_mean)}."
        )

    # validate arguments
    for arg_name in ("B", "n_i", "T_e", "T_i"):
        val = locals()[arg_name].squeeze()
        if val.shape != ():
            raise ValueError(
                f"Argument '{arg_name}' must a single value and not an array of "
                f"shape {val.shape}.")
        locals()[arg_name] = val

    # validate arguments
    for arg_name in ("gamma_e", "gamma_i"):
        if not isinstance(locals()[arg_name],
                          (int, np.integer, float, np.floating)):
            raise TypeError(
                f"Expected int or float for argument '{arg_name}', but got "
                f"{type(locals()[arg_name])}.")

    # validate argument k
    k = k.squeeze()
    if k.ndim not in [0, 1]:
        raise ValueError(
            f"Argument 'k' needs to be a single valued or 1D array astropy Quantity,"
            f" got array of shape {k.shape}.")
    if np.any(k <= 0):
        raise ValueError("Argument 'k' can not be a or have negative values.")

    # validate argument theta
    theta = theta.squeeze()
    if theta.ndim not in [0, 1]:
        raise ValueError(
            f"Argument 'theta' needs to be a single valued or 1D array astropy "
            f"Quantity, got array of shape {k.shape}.")

    # Calc needed plasma parameters
    n_e = z_mean * n_i
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=PhysicsWarning)
        c_s = ion_sound_speed(
            T_e=T_e,
            T_i=T_i,
            ion=ion,
            n_e=n_e,
            gamma_e=gamma_e,
            gamma_i=gamma_i,
            z_mean=z_mean,
        )
    v_A = Alfven_speed(B, n_i, ion=ion, z_mean=z_mean)
    omega_ci = gyrofrequency(B=B, particle=ion, signed=False, Z=z_mean)
    omega_pe = plasma_frequency(n=n_e, particle="e-")

    # Bellan2012JGR params equation 32
    alpha = np.cos(theta.value)**2
    beta = (c_s / v_A).to(u.dimensionless_unscaled).value**2
    alphav, kv = np.meshgrid(alpha, k.value)  # create grid
    Lambda = (kv * v_A.value / omega_ci.value)**2

    # Bellan2012JGR params equation 2
    Q = 1 + (kv * c.value / omega_pe.value)**2

    # Bellan2012JGR params equation 35
    A = ((1 + alphav) / Q) + beta + (alphav * Lambda / Q**2)
    B = alphav * (1 + 2 * Q * beta + Lambda * beta) / Q**2
    C = beta * (alphav / Q)**2

    # Bellan2012JGR params equation 36
    p = (3 * B - A**2) / 3
    q = (9 * A * B - 2 * A**3 - 27 * C) / 27

    # Bellan2012JGR params equation 38
    R = 2 * Lambda * np.emath.sqrt(-p / 3)
    S = 3 * q / (2 * p) * np.emath.sqrt(-3 / p)
    T = Lambda * A / 3
    omega = {}
    for ind, key in enumerate(("fast_mode", "alfven_mode", "acoustic_mode")):
        # The solution corresponding to equation 38
        w = omega_ci * np.emath.sqrt(
            R * np.cos(1 / 3 * np.emath.arccos(S) - 2 * np.pi / 3 * ind) + T)
        omega[key] = w.squeeze()

        # check for violation of dispersion relation assumptions
        # (i.e. low-frequency, w/kc << 0.1)
        wkc_max = np.max(w.value / (kv * c.value))
        if wkc_max > 0.1:
            warnings.warn(
                f"The {key} calculation produced a high-frequency wave (w/kc == "
                f"{wkc_max:.3f}), which violates the low-frequency (w/kc << 1) "
                f"assumption of the dispersion relation.",
                PhysicsWarning,
            )

    return omega
Example #6
0
def hollweg(
    *,
    B: u.T,
    ion: Union[str, Particle],
    k: u.rad / u.m,
    n_i: u.m**-3,
    T_e: u.K,
    T_i: u.K,
    theta: u.rad,
    gamma_e: Union[float, int] = 1,
    gamma_i: Union[float, int] = 3,
    z_mean: Union[float, int] = None,
):
    r"""
    Calculate the two fluid dispersion relation presented by
    :cite:t:`hollweg:1999`, and discussed by :cite:t:`bellan:2012`.
    This is a numberical solver of equation 3 in :cite:t:`bellan:2012`.
    See the **Notes** section below for additional details.

    Parameters
    ----------
    B : `~astropy.units.Quantity`
        The magnetic field magnitude in units convertible to T.
    ion : `str` or `~plasmapy.particles.particle_class.Particle`
        Representation of the ion species (e.g., ``'p'`` for protons,
        ``'D+'`` for deuterium, ``'He-4 +1'`` for singly ionized
        helium-4, etc.). If no charge state information is provided,
        then the ions are assumed to be singly ionized.
    k : `~astropy.units.Quantity`, single valued or 1-D array
        Wavenumber in units convertible to rad/m.  Either single
        valued or 1-D array of length :math:`N`.
    n_i : `~astropy.units.Quantity`
        Ion number density in units convertible to m\ :sup:`-3`.
    T_e : `~astropy.units.Quantity`
        The electron temperature in units of K or eV.
    T_i : `~astropy.units.Quantity`
        The ion temperature in units of K or eV.
    theta : `~astropy.units.Quantity`, single valued or 1-D array
        The angle of propagation of the wave with respect to the
        magnetic field, :math:`\cos^{-1}(k_z / k)`, in units convertible
        to radians.  Either single valued or 1-D array of size
        :math:`M`.
    gamma_e : `float` or `int`, optional
        The adiabatic index for electrons, which defaults to 1.  This
        value assumes that the electrons are able to equalize their
        temperature rapidly enough that the electrons are effectively
        isothermal.
    gamma_i : `float` or `int`, optional
        The adiabatic index for ions, which defaults to 3. This value
        assumes that ion motion has only one degree of freedom, namely
        along magnetic field lines.
    z_mean : `float` or int, optional
        The average ionization state (arithmetic mean) of the ``ion``
        composing the plasma.  Will override any charge state defined
        by argument ``ion``.

    Returns
    -------
    omega : Dict[str, `~astropy.units.Quantity`]
        A dictionary of computed wave frequencies in units rad/s.  The
        dictionary contains three keys: ``'fast_mode'`` for the fast
        mode, ``'alfven_mode'`` for the Alfvén mode, and
        ``'acoustic_mode'`` for the ion-acoustic mode.  The value for
        each key will be a :math:`N x M` array.

    Raises
    ------
    TypeError
        If applicable arguments are not instances of
        `~astropy.units.Quantity` or cannot be converted into one.

    TypeError
        If ``ion`` is not of type or convertible to
        `~plasmapy.particles.particle_class.Particle`.

    TypeError
        If ``gamma_e``, ``gamma_i``, or ``z_mean`` are not of type `int`
        or `float`.

    ~astropy.units.UnitTypeError
        If applicable arguments do not have units convertible to the
        expected units.

    ValueError
        If any of ``B``, ``k``, ``n_i``, ``T_e``, or ``T_i`` is
        negative.

    ValueError
        If ``k`` is negative or zero.

    ValueError
        If ``ion`` is not of category ion or element.

    ValueError
        If ``B``, ``n_i``, ``T_e``, or ``T_I`` are not single valued
        `astropy.units.Quantity` (i.e. an array).

    ValueError
        If ``k`` or ``theta`` are not single valued or a 1-D array.

    Warns
    -----
    : `~plasmapy.utils.exceptions.PhysicsWarning`
        When :math:`\omega / \omega_{\rm ci} > 0.1`, violation of the
        low-frequency assumption.

    : `~plasmapy.utils.exceptions.PhysicsWarning`
        When :math:`c_{\rm s} / v_{\rm A} > 0.1`, violation of low-β.

    : `~plasmapy.utils.exceptions.PhysicsWarning`
        When :math:`|θ - π/2| > 0.1`, violation of quasi-perpendicular
        propagation.

    Notes
    -----

    The dispersion relation presented in :cite:t:`hollweg:1999`
    (equation 3 in :cite:t:`bellan:2012`) is:

    .. math::
        \left( \frac{\omega^2}{k_{\rm z}^2 v_{\rm A}^2} - 1 \right) &
        \left[
            \omega^2 \left( \omega^2 - k^2 v_{\rm A}^2 \right)
            - \beta k^2 v_{\rm A}^2 \left(
                \omega^2 - k_{\rm z}^2 v_{\rm A}^2
            \right)
        \right] \\
        &= \omega^2 \left(\omega^2 - k^2 v_{\rm A}^2 \right) k_{\rm x}^2
        \left(
            \frac{c_{\rm s}^2}{\omega_{\rm ci}^2}
            - \frac{c^2}{\omega_{\rm pe}^2} \frac{\omega^2}{k_{\rm z}^2v_{\rm A}^2}
        \right)

    where

    .. math::
        \mathbf{B_o} &= B_{o} \mathbf{\hat{z}} \\
        \cos \theta &= \frac{k_z}{k} \\
        \mathbf{k} &= k_{\rm x} \hat{x} + k_{\rm z} \hat{z}

    :math:`\omega` is the wave frequency, :math:`k` is the wavenumber,
    :math:`v_{\rm A}` is the Alfvén velocity, :math:`c_{\rm s}` is the
    sound speed, :math:`\omega_{\rm ci}` is the ion gyrofrequency, and
    :math:`\omega_{\rm pe}` is the electron plasma frequency. In the
    derivation of this relation Hollweg assumed low-frequency waves
    :math:`\omega / \omega_{\rm ci} \ll 1`, no D.C. electric field
    :math:`\mathbf{E_o}=0`, and quasi-neutrality.

    :cite:t:`hollweg:1999` asserts this expression is valid for
    arbitrary :math:`c_{\rm s} / v_{\rm A}` (β) and
    :math:`k_{\rm z} / k` (θ).  Contrarily, :cite:t:`bellan:2012`
    states in §1.7 that due to the inconsistent retention of the
    :math:`\omega / \omega_{\rm ci} \ll 1` terms the expression can
    only be valid if both :math:`c_{\rm s} \ll v_{\rm A}` (low-β) and
    the wave propgation is nearly perpendicular to the magnetic field.

    This routine solves for ω for given :math:`k` values by numerically
    solving for the roots of the above expression.

    Examples
    --------
    >>> from astropy import units as u
    >>> from plasmapy.dispersion.numerical import hollweg_
    >>> inputs = {
    ...    "k": np.logspace(-7, -2, 2) * u.rad / u.m,
    ...    "theta": 88 * u.deg,
    ...    "n_i": 5 * u.cm ** -3,
    ...    "B": 2.2e-8 * u.T,
    ...    "T_e": 1.6e6 * u.K,
    ...    "T_i": 4.0e5 * u.K,
    ...    "ion": Particle("p+"),
    ... }
    >>> omegas = hollweg(**inputs)
    >>> omegas
    {'fast_mode': <Quantity [2.62911663e-02+0.j, 2.27876968e+03+0.j] rad / s>,
     'alfven_mode': <Quantity [7.48765909e-04+0.j, 2.13800404e+03+0.j] rad / s>,
     'acoustic_mode': <Quantity [0.00043295+0.j, 0.07358991+0.j] rad / s>}
    """

    # validate argument ion
    if not isinstance(ion, Particle):
        try:
            ion = Particle(ion)
        except TypeError:
            raise TypeError(
                f"For argument 'ion' expected type {Particle} but got {type(ion)}."
            )
    if not (ion.is_ion or ion.is_category("element")):
        raise ValueError(
            "The particle passed for 'ion' must be an ion or element.")

    # validate z_mean
    if z_mean is None:
        try:
            z_mean = abs(ion.charge_number)
        except ChargeError:
            z_mean = 1
    else:
        if not isinstance(z_mean, (int, np.integer, float, np.floating)):
            raise TypeError(
                f"Expected int or float for argument 'z_mean', but got {type(z_mean)}."
            )
        z_mean = abs(z_mean)

    # validate arguments
    for arg_name in ("B", "n_i", "T_e", "T_i"):
        val = locals()[arg_name].squeeze()
        if val.shape != ():
            raise ValueError(
                f"Argument '{arg_name}' must be single valued and not an array of "
                f"shape {val.shape}.")
        locals()[arg_name] = val

    # validate arguments
    for arg_name in ("gamma_e", "gamma_i"):
        if not isinstance(locals()[arg_name],
                          (int, np.integer, float, np.floating)):
            raise TypeError(
                f"Expected int or float for argument '{arg_name}', but got "
                f"{type(locals()[arg_name])}.")

    # validate argument k
    k = k.squeeze()
    if not (k.ndim == 0 or k.ndim == 1):
        raise ValueError(
            f"Argument 'k' needs to be single valued or a 1D array astropy Quantity,"
            f" got array of shape {k.shape}.")
    if np.any(k <= 0):
        raise ValueError("Argument 'k' can not be a or have negative values.")

    # validate argument theta
    theta = theta.squeeze()
    if theta.ndim not in (0, 1):
        raise ValueError(
            f"Argument 'theta' needs to be a single valued or 1D array astropy "
            f"Quantity, got array of shape {theta.shape}.")

    # Single k value case
    if np.isscalar(k.value):
        k = np.array([k.value]) * u.rad / u.m

    # Calc needed plasma parameters
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=PhysicsWarning)
        n_e = z_mean * n_i
        c_s = ion_sound_speed(
            T_e=T_e,
            T_i=T_i,
            ion=ion,
            n_e=n_e,
            gamma_e=gamma_e,
            gamma_i=gamma_i,
            z_mean=z_mean,
        ).value
        v_A = Alfven_speed(B, n_i, ion=ion, z_mean=z_mean).value
        omega_ci = gyrofrequency(B=B, particle=ion, signed=False,
                                 Z=z_mean).value
        omega_pe = plasma_frequency(n=n_e, particle="e-").value

    cs_vA = c_s / v_A
    thetav, kv = np.meshgrid(theta.value, k.value)

    # Parameters kx and kz
    kz = np.cos(thetav) * kv
    kx = np.sin(thetav) * kv

    # Define helpful parameters
    beta = (c_s / v_A)**2
    alpha_A = (kv * v_A)**2
    alpha_s = (kv * c_s)**2  # == alpha_A * beta
    sigma = (kz * v_A)**2
    D = (c_s / omega_ci)**2
    F = (c_si_unitless / omega_pe)**2

    # Polynomial coefficients: c3*x^3 + c2*x^2 + c1*x + c0 = 0
    c3 = F * kx**2 + 1
    c2 = -alpha_A * (1 + beta + F * kx**2) - sigma * (1 + D * kx**2)
    c1 = sigma * alpha_A * (1 + 2 * beta + D * kx**2)
    c0 = -alpha_s * sigma**2

    # Find roots to polynomial
    coefficients = np.array([c3, c2, c1, c0], ndmin=3)
    nroots = coefficients.shape[0] - 1  # 3
    nks = coefficients.shape[1]
    nthetas = coefficients.shape[2]
    roots = np.empty((nroots, nks, nthetas), dtype=np.complex128)
    for ii in range(nks):
        for jj in range(nthetas):
            roots[:, ii, jj] = np.roots(coefficients[:, ii, jj])

    roots = np.sqrt(roots)
    roots = np.sort(roots, axis=0)

    # Warn about NOT low-beta
    if c_s / v_A > 0.1:
        warnings.warn(
            f"This solver is valid in the low-beta regime, "
            f"c_s/v_A << 1 according to Bellan, 2012, Sec. 1.7 "
            f"(see documentation for DOI). A c_s/v_A value of {cs_vA:.2f} "
            f"was calculated which may affect the validity of the solution.",
            PhysicsWarning,
        )

    # Warn about theta not nearly perpendicular
    theta_diff_max = np.amax(np.abs(thetav - np.pi / 2))
    if theta_diff_max > 0.1:
        warnings.warn(
            f"This solver is valid in the regime where propagation is "
            f"nearly perpendicular to B according to Bellan, 2012, Sec. 1.7 "
            f"(see documentation for DOI). A |theta - pi/2| value of "
            f"{theta_diff_max:.2f} was calculated which may affect the "
            f"validity of the solution.",
            PhysicsWarning,
        )

    # dispersion relation is only valid in the regime w << w_ci
    w_max = np.max(roots)
    w_wci_max = w_max / omega_ci
    if w_wci_max > 0.1:
        warnings.warn(
            f"This solver is valid in the regime w/w_ci << 1.  A w "
            f"value of {w_max:.2f} and a w/w_ci value of "
            f"{w_wci_max:.2f} were calculated which may affect the "
            f"validity of the solution.",
            PhysicsWarning,
        )

    omegas = {
        "fast_mode": roots[2, :].squeeze() * u.rad / u.s,
        "alfven_mode": roots[1, :].squeeze() * u.rad / u.s,
        "acoustic_mode": roots[0, :].squeeze() * u.rad / u.s,
    }

    return omegas
Example #7
0
def test_gyrofrequency():
    r"""Test the gyrofrequency function in frequencies.py."""

    assert gyrofrequency(B, "e-").unit.is_equivalent(u.rad / u.s)

    assert gyrofrequency(B, "e-", to_hz=True).unit.is_equivalent(u.Hz)

    assert np.isclose(gyrofrequency(1 * u.T, "e-").value, 175882008784.72018)

    assert np.isclose(gyrofrequency(2.4 * u.T, "e-").value, 422116821083.3284)

    assert np.isclose(
        gyrofrequency(1 * u.T, "e-", to_hz=True).value, 27992490076.528206)

    assert np.isclose(
        gyrofrequency(2.4 * u.T, "e-", signed=True).value, -422116821083.3284)

    assert np.isclose(gyrofrequency(1 * u.G, "e-").cgs.value,
                      1.76e7,
                      rtol=1e-3)

    with pytest.raises(TypeError):
        with pytest.warns(u.UnitsWarning):
            gyrofrequency(u.m, "e-")

    with pytest.raises(u.UnitTypeError):
        gyrofrequency(u.m * 1, "e-")

    assert np.isnan(gyrofrequency(B_nanarr, "e-")[-1])

    # The following is a test to check that equivalencies from astropy
    # are working.
    omega_ce = gyrofrequency(2.2 * u.T, "e-")
    f_ce = (omega_ce / (2 * np.pi)) / u.rad
    f_ce_use_equiv = omega_ce.to(u.Hz, equivalencies=[(u.cy / u.s, u.Hz)])
    assert np.isclose(f_ce.value, f_ce_use_equiv.value)

    with pytest.warns(u.UnitsWarning):
        assert gyrofrequency(5.0, "e-") == gyrofrequency(5.0 * u.T, "e-")

    assert gyrofrequency(B, particle=ion).unit.is_equivalent(u.rad / u.s)

    assert np.isclose(
        gyrofrequency(1 * u.T, particle="p").value, 95788335.834874)

    assert np.isclose(
        gyrofrequency(2.4 * u.T, particle="p").value, 229892006.00369796)

    assert np.isclose(gyrofrequency(1 * u.G, particle="p").cgs.value,
                      9.58e3,
                      rtol=2e-3)

    assert gyrofrequency(-5 * u.T, "p") == gyrofrequency(5 * u.T, "p")

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

    assert gyrofrequency(B, particle="e+") == gyrofrequency(B, "e-")

    with pytest.warns(u.UnitsWarning):
        gyrofrequency(8, "p")

    with pytest.raises(u.UnitTypeError):
        gyrofrequency(5 * u.m, "p")

    with pytest.raises(InvalidParticleError):
        gyrofrequency(8 * u.T, particle="asdfasd")

    with pytest.warns(u.UnitsWarning):
        # TODO this should be WARNS, not RAISES. and it's probably still raised
        assert gyrofrequency(5.0, "p") == gyrofrequency(5.0 * u.T, "p")

    gyrofrequency(1 * u.T, particle="p")
    # testing for user input Z
    testMeth1 = gyrofrequency(1 * u.T, particle="p", Z=0.8).si.value
    testTrue1 = 76630665.79318453
    errStr = f"gyrofrequency() gave {testMeth1}, should be {testTrue1}."
    assert np.isclose(testMeth1, testTrue1, atol=0.0, rtol=1e-5), errStr

    assert_can_handle_nparray(gyrofrequency, kwargs={"signed": True})

    assert_can_handle_nparray(gyrofrequency, kwargs={"signed": False})
Example #8
0
def cold_plasma_permittivity_SDP(B: u.T, species, n, omega: u.rad / u.s):
    r"""
    Magnetized cold plasma dielectric permittivity tensor elements.

    Elements (S, D, P) are given in the "Stix" frame, i.e. with
    :math:`B ∥ \hat{z}` :cite:p:`stix:1992`.

    The :math:`\exp(-i ω t)` time-harmonic convention is assumed.

    Parameters
    ----------
    B : `~astropy.units.Quantity`
        Magnetic field magnitude in units convertible to tesla.

    species : `list` of `str`
        List of the plasma particle species,
        e.g.: ``['e', 'D+']`` or ``['e', 'D+', 'He+']``.

    n : `list` of `~astropy.units.Quantity`
        `list` of species density in units convertible to per cubic meter
        The order of the species densities should follow species.

    omega : `~astropy.units.Quantity`
        Electromagnetic wave frequency in rad/s.

    Returns
    -------
    sum : `~astropy.units.Quantity`
        S ("Sum") dielectric tensor element.

    difference : `~astropy.units.Quantity`
        D ("Difference") dielectric tensor element.

    plasma : `~astropy.units.Quantity`
        P ("Plasma") dielectric tensor element.

    Notes
    -----
    The dielectric permittivity tensor is expressed in the Stix frame with
    the :math:`\exp(-i ω t)` time-harmonic convention as
    :math:`ε = ε_0 A`, with :math:`A` being

    .. math::

        ε = ε_0 \left(\begin{matrix}  S & -i D & 0 \\
                              +i D & S & 0 \\
                              0 & 0 & P \end{matrix}\right)

    where:

    .. math::
        S = 1 - \sum_s \frac{ω_{p,s}^2}{ω^2 - Ω_{c,s}^2}

        D = \sum_s \frac{Ω_{c,s}}{ω}
            \frac{ω_{p,s}^2}{ω^2 - Ω_{c,s}^2}

        P = 1 - \sum_s \frac{ω_{p,s}^2}{ω^2}

    where :math:`ω_{p,s}` is the plasma frequency and
    :math:`Ω_{c,s}` is the signed version of the cyclotron frequency
    for the species :math:`s`.

    Examples
    --------
    >>> from astropy import units as u
    >>> from numpy import pi
    >>> 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)
    >>> permittivity = S, D, P = cold_plasma_permittivity_SDP(B, species, n, omega)
    >>> S
    <Quantity 1.02422...>
    >>> permittivity.sum   # namedtuple-style access
    <Quantity 1.02422...>
    >>> D
    <Quantity 0.39089...>
    >>> P
    <Quantity -4.8903...>
    """
    S, D, P = 1, 0, 1

    for s, n_s in zip(species, n):
        omega_c = gyrofrequency(B=B, particle=s, signed=True)
        omega_p = plasma_frequency(n=n_s, particle=s)

        S += -(omega_p**2) / (omega**2 - omega_c**2)
        D += omega_c / omega * omega_p**2 / (omega**2 - omega_c**2)
        P += -(omega_p**2) / omega**2
    return StixTensorElements(S, D, P)
Example #9
0
def cold_plasma_permittivity_LRP(B: u.T, species, n, omega: u.rad / u.s):
    r"""
    Magnetized cold plasma dielectric permittivity tensor elements.

    Elements (L, R, P) are given in the "rotating" basis, i.e. in the basis
    :math:`(\mathbf{u}_{+}, \mathbf{u}_{-}, \mathbf{u}_z)`,
    where the tensor is diagonal and with :math:`B ∥ z`\ .

    The :math:`\exp(-i ω t)` time-harmonic convention is assumed.

    Parameters
    ----------
    B : `~astropy.units.Quantity`
        Magnetic field magnitude in units convertible to tesla.

    species : `list` of `str`
        The plasma particle species (e.g.: ``['e', 'D+']`` or
        ``['e', 'D+', 'He+']``.

    n : `list` of `~astropy.units.Quantity`
        `list` of species density in units convertible to per cubic meter.
        The order of the species densities should follow species.

    omega : `~astropy.units.Quantity`
        Electromagnetic wave frequency in rad/s.

    Returns
    -------
    left : `~astropy.units.Quantity`
        L ("Left") Left-handed circularly polarization tensor element.

    right : `~astropy.units.Quantity`
        R ("Right") Right-handed circularly polarization tensor element.

    plasma : `~astropy.units.Quantity`
        P ("Plasma") dielectric tensor element.

    Notes
    -----
    In the rotating frame defined by
    :math:`(\mathbf{u}_{+}, \mathbf{u}_{-}, \mathbf{u}_z)`
    with :math:`\mathbf{u}_{\pm}=(\mathbf{u}_x \pm \mathbf{u}_y)/\sqrt{2}`,
    the dielectric tensor takes a diagonal form with elements L, R, P with:

    .. math::
        L = 1 - \sum_s
                \frac{ω_{p,s}^2}{ω\left(ω - Ω_{c,s}\right)}

        R = 1 - \sum_s
                \frac{ω_{p,s}^2}{ω\left(ω + Ω_{c,s}\right)}

        P = 1 - \sum_s \frac{ω_{p,s}^2}{ω^2}

    where :math:`ω_{p,s}` is the plasma frequency and
    :math:`Ω_{c,s}` is the signed version of the cyclotron frequency
    for the species :math:`s` :cite:p:`stix:1992`.

    Examples
    --------
    >>> from astropy import units as u
    >>> from numpy import pi
    >>> 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)
    >>> L, R, P = permittivity = cold_plasma_permittivity_LRP(B, species, n, omega)
    >>> L
    <Quantity 0.63333...>
    >>> permittivity.left    # namedtuple-style access
    <Quantity 0.63333...>
    >>> R
    <Quantity 1.41512...>
    >>> P
    <Quantity -4.8903...>
    """
    L, R, P = 1, 1, 1

    for s, n_s in zip(species, n):
        omega_c = gyrofrequency(B=B, particle=s, signed=True)
        omega_p = plasma_frequency(n=n_s, particle=s)

        L += -(omega_p**2) / (omega * (omega - omega_c))
        R += -(omega_p**2) / (omega * (omega + omega_c))
        P += -(omega_p**2) / omega**2
    return RotatingTensorElements(L, R, P)
Example #10
0
def Hall_parameter(
    n: u.m**-3,
    T: u.K,
    B: u.T,
    ion: Particle,
    particle: Particle,
    coulomb_log=None,
    V=None,
    coulomb_log_method="classical",
):
    r"""
    Calculate the ``particle`` Hall parameter for a plasma.

    The Hall parameter for plasma species :math:`s` (``particle``) is given by:

    .. math::

        β_{s} = \frac{Ω_{c s}}{ν_{s s^{\prime}}}

    where :math:`Ω_{c s}` is the gyrofrequncy for plasma species :math:`s`
    (``particle``) and :math:`ν_{s s^{\prime}}` is the collision frequency
    between plasma species :math:`s` (``particle``) and species
    :math:`s^{\prime}` (``ion``).

    **Aliases:** `betaH_`

    Parameters
    ----------
    n : `~astropy.units.quantity.Quantity`
        The number density associated with ``particle``.

    T : `~astropy.units.quantity.Quantity`
        The temperature of associated with ``particle``.

    B : `~astropy.units.quantity.Quantity`
        The magnetic field.

    ion : `~plasmapy.particles.particle_class.Particle`
        The type of ion ``particle`` is colliding with.

    particle : `~plasmapy.particles.particle_class.Particle`
        The particle species for which the Hall parameter is calculated for.
        Representation of the particle species (e.g., ``'p'`` for protons,
        ``'D+'`` for deuterium, or ``'He-4 +1'`` for singly ionized helium-4).
        If no charge state information is provided, then the particles are
        assumed to be singly charged.

    coulomb_log : `float`, optional
        Preset value for the Coulomb logarithm. Used mostly for testing purposes.

    V : `~astropy.units.quantity.Quantity`
        The relative velocity between ``particle`` and ``ion``.  If not provided,
        then the ``particle`` thermal velocity is assumed
        (`~plasmapy.formulary.speeds.thermal_speed`).

    coulomb_log_method : `str`, optional
        The method by which to compute the Coulomb logarithm.
        The default method is the classical straight-line Landau-Spitzer
        method (``"classical"`` or ``"ls"``). The other 6 supported methods
        are ``"ls_min_interp"``, ``"ls_full_interp"``, ``"ls_clamp_mininterp"``,
        ``"hls_min_interp"``, ``"hls_max_interp"``, and ``"hls_full_interp"``.
        Please refer to the docstring of
        `~plasmapy.formulary.collisions.Coulomb_logarithm` for more
        information about these methods.

    See Also
    --------
    ~plasmapy.formulary.frequencies.gyrofrequency
    ~plasmapy.formulary.collisions.fundamental_electron_collision_freq
    ~plasmapy.formulary.collisions.fundamental_ion_collision_freq
    ~plasmapy.formulary.collisions.Coulomb_logarithm

    Returns
    -------
    `~astropy.units.quantity.Quantity`
        Hall parameter for ``particle``.

    Notes
    -----
    * For calculating the collision frequency
      `~plasmapy.formulary.collisions.fundamental_electron_collision_freq` is used
      when ``particle`` is an electron and
      `~plasmapy.formulary.collisions.fundamental_ion_collision_freq` when
      ``particle`` is an ion.
    * The collision frequencies are calculated assuming a slowly moving
      Maxwellian distribution.

    Examples
    --------
    >>> import astropy.units as u
    >>> import pytest
    >>> from plasmapy.utils.exceptions import RelativityWarning

    >>> Hall_parameter(1e10 * u.m**-3, 2.8e2 * u.eV, 2.3 * u.T, 'He-4 +1', 'e-')
    <Quantity 2.500...e+15>
    >>> with pytest.warns(RelativityWarning):
    ...     Hall_parameter(1e10 * u.m**-3, 5.8e3 * u.eV, 2.3 * u.T, 'He-4 +1', 'e-')
    <Quantity 2.11158...e+17>
    """
    from plasmapy.formulary.collisions import (
        fundamental_electron_collision_freq,
        fundamental_ion_collision_freq,
    )

    gyro_frequency = frequencies.gyrofrequency(B, particle)
    gyro_frequency = gyro_frequency / u.radian
    if Particle(particle).symbol == "e-":
        coll_rate = fundamental_electron_collision_freq(
            T, n, ion, coulomb_log, V, coulomb_log_method=coulomb_log_method)
    else:
        coll_rate = fundamental_ion_collision_freq(T, n, ion, coulomb_log, V)
    return gyro_frequency / coll_rate
Example #11
0
    if theta.ndim not in (0, 1):
        raise TypeError(
            "Argument 'theta' needs to be a single value or 1D array "
            f" astropy Quantity, got array of shape {theta.shape}.")
    elif np.isscalar(theta):
        theta = np.array([theta])

    # Generate mesh grid of w x theta
    w, theta = np.meshgrid(w, theta, indexing="ij")

    # Generate the plasma parameters needed
    wps = []
    wcs = []
    for par, dens in zip(species, densities):
        wps.append(plasma_frequency(n=dens * u.m**-3, particle=par).value)
        wcs.append(gyrofrequency(B=B, particle=par, signed=True).value)

    # Stix method implemented
    S = np.ones_like(w, dtype=np.float64)
    P = np.ones_like(S)
    D = np.zeros_like(S)
    for wc, wp in zip(wcs, wps):
        S -= (wp**2) / (w**2 - wc**2)
        P -= (wp / w)**2
        D += ((wp**2) / (w**2 - wc**2)) * (wc / w)

    R = S + D
    L = S - D

    # Generate coefficients to solve, a * k**4 + b * k**2 + c = 0
    a = (S * np.sin(theta)**2) + (P * np.cos(theta)**2)