particle_input

plasmapy.particles.decorators.particle_input(callable_: Callable[[...], Any] | None = None, *, require: str | Iterable[str] | None = None, any_of: str | Iterable[str] | None = None, exclude: str | Iterable[str] | None = None, allow_custom_particles: bool = True, allow_particle_lists: bool = True) Callable[[...], Any][source]

Convert particle-like arguments into particle objects.

When a callable is decorated with particle_input(), particle-like arguments that are appropriately annotated (i.e., with ParticleLike or ParticleListLike) will be converted into a Particle, CustomParticle, or ParticleList.

The parameters Z and mass_numb may be used to specify the charge number of an ion and mass number of an isotope, respectively, as long as Z and/or mass_numb are parameters of the callable and only one parameter is annotated with ParticleLike or ParticleListLike.

To indicate that None can be passed to a parameter, annotate it with ParticleLike | None or ParticleListLike | None.

If the particle representation does not satisfy any categorization criteria that have been provided, then particle_input() will raise an exception.

If the annotated parameter is named element, isotope, or ion, then particle_input() will raise an exception if the argument provided to the callable is not consistent with parameter.

Note

An annotated parameter named ion will accept neutral atoms and CustomParticle-like objects as long as the charge number is explicitly defined. To enforce that the particle be charged, provide require={"charged"} to particle_input().

Note

When both particle_input() and validate_quantities() are used to decorate a function, they may be used in either order. When using both particle_input() and validate_quantities() to decorate an instance method, particle_input() should be the outer decorator and validate_quantities() should be the inner decorator (see #2035).

import astropy.units as u
from plasmapy.particles import particle_input, ParticleLike
from plasmapy.utils.decorators.validators import validate_quantities


class SomeClass:
    @particle_input
    @validate_quantities
    def instance_method(self, particle: ParticleLike, B: u.Quantity[u.T]): ...

Note

When decorating a class method with particle_input(), classmethod should be the outer decorator and particle_input() should be the inner decorator, and the first argument (representing the class) must be named cls.

Parameters:
Return type:

callable

Raises:
  • TypeError – If the annotated argument is not particle-like; or if Z or mass_numb is not an integer.

  • InvalidParticleError – If the annotated argument does not correspond to a valid particle, allow_custom_particles is False and the argument corresponds to a CustomParticle, or allow_particle_lists is False and the argument corresponds to a ParticleList.

  • InvalidParticleError – If the decorated argument has charge and/or mass number information, and Z and/or mass_numb contain contradictory information.

  • InvalidElementError – If an annotated argument is named element, and the input does not correspond to an element, isotope, or ion.

  • InvalidIsotopeError – If an annotated argument is named isotope, and the input does not correspond to an isotope or an ion of an isotope.

  • InvalidIonError – If an annotated argument is named ion, and the input does not correspond to an ion.

  • ChargeError – If "charged" is in the require argument and the particle is not explicitly charged, or if any_of = {"charged", "uncharged"} and the particle does not have charge information associated with it.

  • ParticleError – If the returned particle(s) do not meet the categorization criteria specified through require, any_of, or exclude; or if none of the parameters of callable_ have been appropriately annotated.

  • UnitConversionError – If the annotated argument is a Quantity, but does not have a physical type of mass or charge.

Warns:

ParticleWarning – If decorated argument has charge and/or mass number information, and Z and/or mass_numb contain redundant information.

Notes

There are some known limitations to particle_input().

  • Particle categorization criteria are not yet applied to arguments that get converted into a ParticleList (see #2048).

  • This decorator is not compatible with setters (see #2507).

  • particle_input() has limited compatibility with positional-only, variadic positional, and variadic keyword arguments (see #2150).

  • When particle_input() and validate_quantities() are both used to decorate an instance method on a class, particle_input() must be the outside decorator (see #2035).

  • Because it dynamically changes arguments, functions decorated with particle_input() often do not work well with static type checkers like mypy. These errors may be silenced by commenting # type: ignore[union-attr] on a line of code, where union-attr is the name of the mypy error code.

Examples

The particle_input() decorator takes appropriately annotated particle-like or particle-list-like arguments that are provided to callable_, and converts them into a Particle, CustomParticle, or ParticleList object.

The following decorated function accepts a particle-like input and returns the corresponding particle object.

>>> from plasmapy.particles import particle_input, ParticleLike
>>> import astropy.units as u
>>> @particle_input
... def get_particle(particle: ParticleLike):
...     return particle
>>> get_particle("p+")
Particle("p+")
>>> get_particle(["p+", "e-"])
ParticleList(['p+', 'e-'])
>>> get_particle(1e-26 * u.kg)
CustomParticle(mass=1e-26 kg, charge=nan C)

To allow None to pass, use ParticleLike | None as the annotation.

>>> from typing import Optional
>>> @particle_input
... def get_particle_or_none(particle: ParticleLike | None):
...     return particle
>>> get_particle_or_none("p+")
Particle("p+")
>>> print(get_particle_or_none(None))
None

If the decorated callable has parameters named Z and/or mass_numb and exactly one annotated parameter, then Z may be used to provide the charge number and mass_numb may be used to provide the mass number. Making Z and mass_numb keyword-only reduces the chances of confusion or mistakes.

>>> @particle_input
... def make_particle(particle: ParticleLike, *, Z=None, mass_numb=None):
...     return particle
>>> make_particle("H", Z=0, mass_numb=3)
Particle("T 0+")

Instance methods can also be decorated with particle_input() as long as the first argument (representing the instance itself) is named self following standard convention.

>>> class SampleClass:
...     @particle_input
...     def __init__(self, particle: ParticleLike):
...         self.particle = particle
...
...     @particle_input
...     def return_particle(self, new_particle: ParticleLike):
...         return new_particle
>>> instance = SampleClass("α")
>>> instance.particle
Particle("He-4 2+")
>>> instance.return_particle("T+")
Particle("T 1+")

The allow_custom_particles and allow_particle_lists keyword arguments indicate whether callable_ should accept CustomParticle and ParticleList objects, respectively.

>>> @particle_input(allow_custom_particles=False, allow_particle_lists=False)
... def get_atomic_number(particle: ParticleLike):
...     return particle.atomic_number
>>> get_atomic_number("Fe")
26

The require, any_of, and exclude keyword arguments may be used to specify categorization criteria that a particle must be consistent with. For more details, please refer to the docstring for is_category.

>>> @particle_input(require="element", any_of={"charged", "uncharged"})
... def return_ionic_level(particle: ParticleLike):
...     return particle
>>> return_ionic_level("Fe-56 0+")
Particle("Fe-56 0+")

When the parameter is named element, isotope, or ion, then the corresponding argument must be consistent with the name. When the parameter is named ion, then the particle(s) may also be a neutral atom as long as the charge number is explicitly defined.

>>> @particle_input
... def mass_number(isotope: ParticleLike):
...     return isotope.mass_number
>>> mass_number("D")
2