def test_ionization_state_ion_input_error(): """ Test that `~plasmapy.particles.IonizationState` raises the appropriate exception when an ion is the base particle and ionic fractions are specified """ ion = "He 1+" unnecessary_ionic_fractions = [0.0, 0.0, 1.0] with pytest.raises(ParticleError): IonizationState(ion, ionic_fractions=unnecessary_ionic_fractions)
def setup_class(self): self.element = "H" self.valid_number_densities = u.Quantity([0.1, 0.2], unit=u.m**-3) self.expected_n_elem = np.sum(self.valid_number_densities) self.expected_ionic_fractions = (self.valid_number_densities / self.expected_n_elem) try: self.instance = IonizationState(self.element) except Exception: pytest.fail( "Unable to instantiate IonizationState with no ionic fractions." )
def test_nans(): """ Test that when no ionic fractions or temperature are inputted, the result is an array full of `~numpy.nan` of the right size. """ element = "He" nstates = atomic_number(element) + 1 instance = IonizationState(element) assert (len(instance.ionic_fractions) == nstates ), f"Incorrect number of ionization states for {element}" assert np.all([ np.isnan(instance.ionic_fractions) ]), ("The ionic fractions for IonizationState are not defaulting " "to numpy.nan when not set by user.")
def test_IonizationState_ionfracs_from_ion_input(ion): ionization_state = IonizationState(ion) ion_particle = Particle(ion) actual_ionic_fractions = ionization_state.ionic_fractions expected_ionic_fractions = np.zeros(ion_particle.atomic_number + 1) expected_ionic_fractions[ion_particle.integer_charge] = 1.0 if not np.allclose(expected_ionic_fractions, actual_ionic_fractions, atol=1e-16): pytest.fail( f"The returned ionic fraction for IonizationState({repr(ion)}) " f"should have entirely been in the Z = {ion_particle.integer_charge} " f"level, but was instead: {ionization_state.ionic_fractions}." )
def __next__(self): if self._element_index < len(self.base_particles): particle = self.base_particles[self._element_index] result = IonizationState( particle, self.ionic_fractions[particle], T_e=self.T_e, n_elem=np.sum(self.number_densities[particle]), tol=self.tol, ) self._element_index += 1 return result else: del self._element_index raise StopIteration
def test_IonizationState_base_particles_from_ion_input(ion): """ Test that supplying an ion to IonizationState will result in the base particle being the corresponding isotope or ion and that the ionic fraction of the corresponding charge level is 100%. """ ionization_state = IonizationState(ion) ion_particle = Particle(ion) expected_base_particle = ion_particle.isotope or ion_particle.element if expected_base_particle != ionization_state.base_particle: pytest.fail( f"The expected base particle was {expected_base_particle}, " f"but the returned base particle was {ionization_state.base_particle}. " )
def test_IonizationState_ionfracs_from_ion_input(ion): ionization_state = IonizationState(ion) ion_particle = Particle(ion) actual_ionic_fractions = ionization_state.ionic_fractions expected_ionic_fractions = np.zeros(ion_particle.atomic_number + 1) expected_ionic_fractions[ion_particle.charge_number] = 1.0 np.testing.assert_allclose( expected_ionic_fractions, actual_ionic_fractions, atol=1e-16, err_msg=f"The returned ionic fraction for IonizationState({repr(ion)}) " f"should have entirely been in the Z = {ion_particle.integer_charge} " f"level.", )
def test_exclude_neutrals_from_average_ion(): """ Test that the `IonizationState.average_ion` method returns a |CustomParticle| that does not include neutrals in the averaging when the ``include_neutrals`` keyword is `False`. """ base_particle = Particle("He-4") ionization_state_without_neutrals = IonizationState(base_particle, [0, 0.2, 0.8]) expected_average_ion = ionization_state_without_neutrals.average_ion() ionization_state_with_neutrals = IonizationState(base_particle, [0.50, 0.1, 0.4]) actual_average_ion = ionization_state_with_neutrals.average_ion( include_neutrals=False ) assert actual_average_ion == expected_average_ion
def __getitem__(self, *values) -> IonizationState: errmsg = f"Invalid indexing for IonizationStates instance: {values[0]}" one_input = not isinstance(values[0], tuple) two_inputs = len(values[0]) == 2 if not one_input and not two_inputs: raise IndexError(errmsg) try: arg1 = values[0] if one_input else values[0][0] int_charge = None if one_input else values[0][1] particle = arg1 if arg1 in self.base_particles else particle_symbol(arg1) if int_charge is None: return IonizationState( particle=particle, ionic_fractions=self.ionic_fractions[particle], T_e=self._pars["T_e"], n_elem=np.sum(self.number_densities[particle]), tol=self.tol, ) else: if not isinstance(int_charge, Integral): raise TypeError( f"{int_charge} is not a valid charge for {particle}." ) elif not 0 <= int_charge <= atomic_number(particle): raise ChargeError( f"{int_charge} is not a valid charge for {particle}." ) return State( integer_charge=int_charge, ionic_fraction=self.ionic_fractions[particle][int_charge], ionic_symbol=particle_symbol(particle, Z=int_charge), number_density=self.number_densities[particle][int_charge], ) except Exception as exc: raise IndexError(errmsg) from exc
def test_instantiation(self, test_name): """ Test that each IonizationState test case can be instantiated. """ self.instances[test_name] = IonizationState(**test_cases[test_name])
"element": "He", "atomic_number": 2, "Z_mean": 1.3, "Z_rms": 1.51657508881031, "n_e": 1.3e19 * u.m ** -3, "n_elem": 1e19 * u.m ** -3, "integer_charges": [0, 1, 2], "ionic_fractions": np.array([0.2, 0.3, 0.5]), "ionic_symbols": ["He-4 0+", "He-4 1+", "He-4 2+"], "_is_normalized()": True, "number_densities": np.array([2e18, 3e18, 5e18]) * u.m ** -3, "tol": 2e-14, "__str__()": "<IonizationState instance for He-4>", } instance = IonizationState(**kwargs) @pytest.mark.parametrize("key", expected_properties.keys()) def test_IonizationState_attributes(key): """ Test a specific case that the `IonizationState` attributes are working as expected. """ expected = expected_properties[key] actual = eval(f"instance.{key}") if isinstance(expected, u.Quantity): assert expected.unit == actual.unit, f"Unit mismatch for IonizationState.{key}" assert np.allclose( expected, actual, atol=1e-15 * expected.unit
def test_ionization_state_inequality_and_identity(): deuterium_states = IonizationState("D+", n_elem=1e20 * u.m**-3, T_e=10 * u.eV) tritium_states = IonizationState("T+", n_elem=1e20 * u.m**-3, T_e=10 * u.eV) assert deuterium_states != tritium_states
def test_setting_ionic_fractions(): instance = IonizationState("He") new_ionic_fractions = [0.2, 0.5, 0.3] instance.ionic_fractions = new_ionic_fractions assert np.allclose(instance.ionic_fractions, new_ionic_fractions)