예제 #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 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,
        )