예제 #1
0
def test_weighted_averages_of_particles(
    particle_multiplicities: Dict[ParticleLike, int],
    use_rms_charge,
    use_rms_mass,
):
    """
    Compare the mass and charge of the average particle for two |ParticleList|
    instances.

    The first |ParticleList| contains repeated particles.

    The second |ParticleList| contains only one of each kind of particle
    present in the first list, with the number of each particle recorded
    in a separate array.

    The unweighted averages of the first |ParticleList| should equal the
    weighted averages of the second |ParticleList|, with the number of
    each particle provided as the abundances.
    """
    all_particles = ParticleList([])
    for particle, multiplicity in particle_multiplicities.items():
        all_particles.extend(ParticleList(multiplicity * [particle]))

    unique_particles = ParticleList(particle_multiplicities.keys())
    number_of_each_particle = list(particle_multiplicities.values())

    unweighted_mean_of_all_particles = all_particles.average_particle(
        use_rms_charge=use_rms_charge,
        use_rms_mass=use_rms_mass,
    )

    weighted_mean_of_unique_particles = unique_particles.average_particle(
        use_rms_charge=use_rms_charge,
        use_rms_mass=use_rms_mass,
        abundances=number_of_each_particle,
    )

    assert u.isclose(
        unweighted_mean_of_all_particles.mass,
        weighted_mean_of_unique_particles.mass,
        rtol=1e-14,
        equal_nan=True,
    )

    assert u.isclose(
        unweighted_mean_of_all_particles.charge,
        weighted_mean_of_unique_particles.charge,
        rtol=1e-14,
        equal_nan=True,
    )

    if len(unique_particles) == 1 and isinstance(unique_particles[0],
                                                 Particle):
        assert isinstance(unweighted_mean_of_all_particles, Particle)
        assert isinstance(weighted_mean_of_unique_particles, Particle)
예제 #2
0
def test_mean_particle():
    """
    Test that ``ParticleList.average_particle()`` returns a particle with
    the mean mass and mean charge of a |ParticleList|.
    """
    massless_uncharged_particle = CustomParticle(mass=0 * u.kg, charge=0 * u.C)
    particle_list = ParticleList([proton, electron, alpha, massless_uncharged_particle])
    expected_mass = (proton.mass + electron.mass + alpha.mass) / 4
    expected_charge = (proton.charge + electron.charge + alpha.charge) / 4
    average_particle = particle_list.average_particle()
    assert u.isclose(average_particle.mass, expected_mass, rtol=1e-14)
    assert u.isclose(average_particle.charge, expected_charge, rtol=1e-14)
예제 #3
0
def test_weighted_mean_particle():
    """
    Test that ``ParticleList.average_particle()`` returns a particle with
    the weighted mean.
    """
    custom_proton = CustomParticle(mass=proton.mass, charge=proton.charge)
    particle_list = ParticleList([proton, electron, alpha, custom_proton])
    abundances = [1, 2, 0, 1]
    expected_mass = (proton.mass + electron.mass) / 2
    expected_charge = 0 * u.C
    average_particle = particle_list.average_particle(abundances=abundances)
    assert u.isclose(average_particle.mass, expected_mass, rtol=1e-14)
    assert u.isclose(average_particle.charge, expected_charge, rtol=1e-14)
예제 #4
0
def test_comparison_to_equivalent_particle_list(physical_property, use_rms):
    """
    Test that `IonizationState.average_ion` gives consistent results with
    `ParticleList.average_particle` when the ratios of different particles
    is the same between the `IonizationState` and the `ParticleList`.
    """
    particles = ParticleList(2 * ["He-4 0+"] + 3 * ["He-4 1+"] + 5 * ["He-4 2+"])
    ionization_state = IonizationState("He-4", [0.2, 0.3, 0.5])
    kwargs = {f"use_rms_{physical_property}": True}
    expected_average_particle = particles.average_particle(**kwargs)
    expected_average_quantity = getattr(expected_average_particle, physical_property)
    actual_average_particle = ionization_state.average_ion(**kwargs)
    actual_average_quantity = getattr(actual_average_particle, physical_property)
    assert_quantity_allclose(actual_average_quantity, expected_average_quantity)
예제 #5
0
def test_root_mean_square_particle(use_rms_charge, use_rms_mass):
    """
    Test that ``ParticleList.average_particle`` returns the mean or root
    mean square of the charge and mass, as appropriate.
    """

    particle_list = ParticleList(["p+", "e-"])
    average_particle = particle_list.average_particle(
        use_rms_charge=use_rms_charge, use_rms_mass=use_rms_mass
    )

    expected_average_charge = (1 if use_rms_charge else 0) * proton.charge
    assert u.isclose(average_particle.charge, expected_average_charge, rtol=1e-14)

    if use_rms_mass:
        expected_average_mass = np.sqrt((proton.mass**2 + electron.mass**2) / 2)
    else:
        expected_average_mass = (proton.mass + electron.mass) / 2

    assert u.isclose(average_particle.mass, expected_average_mass, atol=1e-35 * u.kg)
예제 #6
0
    def average_ion(
        self,
        *,
        include_neutrals: bool = True,
        use_rms_charge: bool = False,
        use_rms_mass: bool = False,
    ) -> CustomParticle:
        """
        Return a |CustomParticle| representing the mean particle
        included across all ionization states.

        By default, this method will use the weighted mean to calculate
        the properties of the |CustomParticle|, where the weights for
        each ionic level is given by its ionic fraction multiplied by
        the abundance of the base element or isotope. If
        ``use_rms_charge`` or ``use_rms_mass`` is `True`, then this
        method will return the root mean square of the charge or mass,
        respectively.

        Parameters
        ----------
        include_neutrals : `bool`, optional, keyword-only
            If `True`, include neutrals when calculating the mean values
            of the different particles.  If `False`, exclude neutrals.
            Defaults to `True`.

        use_rms_charge : `bool`, optional, keyword-only
            If `True`, use the root mean square charge instead of the
            mean charge. Defaults to `False`.

        use_rms_mass : `bool`, optional, keyword-only
            If `True`, use the root mean square mass instead of the mean
            mass. Defaults to `False`.

        Raises
        ------
        `~plasmapy.particles.exceptions.ParticleError`
            If the abundance of any of the elements or isotopes is not
            defined and the |IonizationStateCollection| instance includes
            more than one element or isotope.

        Returns
        -------
        ~plasmapy.particles.particle_class.CustomParticle

        Examples
        --------
        >>> states = IonizationStateCollection(
        ...     {"H": [0.1, 0.9], "He": [0, 0.1, 0.9]},
        ...     abundances={"H": 1, "He": 0.1}
        ... )
        >>> states.average_ion()
        CustomParticle(mass=2.12498...e-27 kg, charge=1.5876...e-19 C)
        >>> states.average_ion(include_neutrals=False, use_rms_charge=True, use_rms_mass=True)
        CustomParticle(mass=2.633...e-27 kg, charge=1.805...e-19 C)
        """
        min_charge = 0 if include_neutrals else 1

        all_particles = ParticleList()
        all_abundances = []

        for base_particle in self.base_particles:

            ionization_state = self[base_particle]
            ionic_levels = ionization_state.to_list()[min_charge:]
            all_particles.extend(ionic_levels)

            base_particle_abundance = self.abundances[base_particle]

            if np.isnan(base_particle_abundance):
                if len(self) == 1:
                    base_particle_abundance = 1
                else:
                    raise ParticleError(
                        "Unable to provide an average particle without abundances."
                    )

            ionic_fractions = ionization_state.ionic_fractions[min_charge:]
            ionic_abundances = base_particle_abundance * ionic_fractions
            all_abundances.extend(ionic_abundances)

        return all_particles.average_particle(
            use_rms_charge=use_rms_charge,
            use_rms_mass=use_rms_mass,
            abundances=all_abundances,
        )