def Z_mean(self) -> np.float64: """Return the mean integer charge""" if np.nan in self.ionic_fractions: raise ChargeError( "Z_mean cannot be found because no ionic fraction " f"information is available for {self.base_particle}.") return np.sum(self.ionic_fractions * np.arange(self.atomic_number + 1))
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 {base_particle}." ) elif not 0 <= int_charge <= atomic_number(particle): raise ChargeError( f"{int_charge} is not a valid charge for {base_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 __getitem__(self, value) -> State: """Return information for a single ionization level.""" if isinstance(value, slice): raise TypeError("IonizationState instances cannot be sliced.") if isinstance(value, Integral) and 0 <= value <= self.atomic_number: result = State( value, self.ionic_fractions[value], self.ionic_symbols[value], self.number_densities[value], ) else: if not isinstance(value, Particle): try: value = Particle(value) except InvalidParticleError as exc: raise InvalidParticleError( f"{value} is not a valid integer charge or " f"particle.") from exc same_element = value.element == self.element same_isotope = value.isotope == self.isotope has_charge_info = value.is_category( any_of=["charged", "uncharged"]) if same_element and same_isotope and has_charge_info: Z = value.integer_charge result = State( Z, self.ionic_fractions[Z], self.ionic_symbols[Z], self.number_densities[Z], ) else: if not same_element or not same_isotope: raise AtomicError("Inconsistent element or isotope.") elif not has_charge_info: raise ChargeError("No integer charge provided.") return result
def get_particle(argname, params, already_particle, funcname): argval, Z, mass_numb = params # Convert the argument to a Particle object if it is not # already one. if not already_particle: if not isinstance(argval, (numbers.Integral, str, tuple, list)): raise TypeError( f"The argument {argname} to {funcname} must be " f"a string, an integer or a tuple or list of them " f"corresponding to an atomic number, or a " f"Particle object.") try: particle = Particle(argval, Z=Z, mass_numb=mass_numb) except InvalidParticleError as e: raise InvalidParticleError(_particle_errmsg( argname, argval, Z, mass_numb, funcname)) from e # We will need to do the same error checks whether or not the # argument is already an instance of the Particle class. if already_particle: particle = argval # If the name of the argument annotated with Particle in the # decorated function is element, isotope, or ion; then this # decorator should raise the appropriate exception when the # particle ends up not being an element, isotope, or ion. cat_table = [ ('element', particle.element, InvalidElementError), ('isotope', particle.isotope, InvalidIsotopeError), ('ion', particle.ionic_symbol, InvalidIonError), ] for category_name, category_symbol, CategoryError in cat_table: if argname == category_name and not category_symbol: raise CategoryError( f"The argument {argname} = {repr(argval)} to " f"{funcname} does not correspond to a valid " f"{argname}.") # Some functions require that particles be charged, or # at least that particles have charge information. _integer_charge = particle._attributes['integer charge'] must_be_charged = 'charged' in require must_have_charge_info = set(any_of) == {'charged', 'uncharged'} uncharged = _integer_charge == 0 lacks_charge_info = _integer_charge is None if must_be_charged and (uncharged or must_have_charge_info): raise ChargeError(f"A charged particle is required for {funcname}.") if must_have_charge_info and lacks_charge_info: raise ChargeError(f"Charge information is required for {funcname}.") # Some functions require particles that belong to more complex # classification schemes. Again, be sure to provide a # maximally useful error message. if not particle.is_category(require=require, exclude=exclude, any_of=any_of): raise AtomicError( _category_errmsg(particle, require, exclude, any_of, funcname)) return particle