The Particle Classes#

The Particle class is the base class for all particles, whether introduced discretely one by one or as a distribution. In reality, the Particle class is based on two intermediate classes: ParticleDistribution and ParticleInstances to instantiate particle distributions and particles directly, respectively.

The ParticleDistribution inherits from the Vapor class, with the following added attributes and methods:

ParticleDistribution attributes#

attribute

unit

default value

spacing

"linspace"

nbins

1e3

nparticles

1e5

volume

/ m^3

1e-6

cutoff

0.9999

gsigma

1.25

mode

m

1e-7

disttype

"lognormal"

ParticleDistribution methods#

ParticleDistribution.pre_radius()#

The pre_radius method uses the attributes cutoff, gsigma, and mode to determine the starting and ending radii according to the utility cutoff_radius.cut_rad. Here, cutoff refers to the fraction cutoff of the distribution (e.g. cutoff=0.9999 means taking only 99.99% of the lognormal distribution). Moreover, gsigma and mode are the lognormal parameters of the distribution, referring to the geometric standard deviation and mode (geometric mean) respectively. We use the scipy.stats.lognorm to construct the distribution in the pre_discretize method below.

Then, the spacing is used to create a vector of radii with nbins entries. The spacing attribute is a string type and for now, it can only be "linspace" or "logspace": using the numpy.linspace or numpy.logspace functions, respectively, to construct the vector.

Finally, the pre_radius method returns a radius vector with units of meter.

ParticleDistribution.pre_discretize()#

The pre_discretize method uses the result of the pre_radius() method above, disttype, gsigma, and mode attributes to produce a probability density function distribution based on the scipy.stats.lognorm (lognormal) function. This is done via the distribution_discretization utility.

ParticleDistribution.pre_distribution()#

The pre_distribution method simply constructs the distribution multiplying the product of pre_discretize() by nparticles and dividing by volume.

from particula.particle import ParticleDistribution
PDcutoff1 = ParticleDistribution()
PDcutoff2 = ParticleDistribution(
    cutoff=.99,
)

print("Starting radius from 99.99% cutoff is ", PDcutoff1.pre_radius().min())
print("Starting radius from 99% cutoff is ", PDcutoff2.pre_radius().min())
Starting radius from 99.99% cutoff is  4.1972292396604054e-08 meter
Starting radius from 99% cutoff is  5.628288472608393e-08 meter
from particula.particle import ParticleDistribution
PDspacing1 = ParticleDistribution()
PDspacing2 = ParticleDistribution(
    spacing="logspace",
)

print("First few radii from linspace spacing are ", PDspacing1.pre_radius()[:6])
print("First few radii from logspace spacing are ", PDspacing2.pre_radius()[:6])
First few radii from linspace spacing are  [4.1972292396604054e-08 4.216876899517281e-08 4.236524559374157e-08 4.256172219231033e-08 4.275819879087909e-08 4.295467538944785e-08] meter
First few radii from logspace spacing are  [4.1972292396604034e-08 4.204530615154216e-08 4.211844691904267e-08 4.2191714920053024e-08 4.2265110375904696e-08 4.2338633508314515e-08] meter
print("The units of the distribution (density units): ", PDspacing1.pre_distribution().u)
The units of the distribution (density units):  1 / meter ** 4

The ParticleInstances inherits from the ParticleDistribution class, with the following added attributes and methods:

ParticleInstances attributes#

attribute

unit

default value

particle_radius

m

None or ParticleDistribution.pre_radius()

particle_number

1 or ParticleDistribution.nparticles

particle_density

kg / m^3

1e3

shape_factor

1

volume_void

0

particle_charge

0

We note that the particle_radius attribute defaults to ParticleDistribution.pre_radius() if it is not given explicitly. Therefore, one could either provide a one or more radii via particle_radius or provide the parameters for a distribution as described above. The particle_number attribute defaults to ParticleDistribution.nparticles if the particle_radius attribute is not given explicitly; otherwise, it is set to 1 by default, but the user could provide a different value, for example, particle_radius=[1e-9, 2e-9] coupled with particle_number=[1, 2] would mean one particle of radius 1e-9 and two particles of radius 2e-9.

In any event, the attributes here have higher precedence than the attributes in ParticleDistribution. Below, we reaffirm the particle_distribution as well.

ParticleInstances methods#

ParticleInstances.particle_distribution()#

The particle_distribution method either returns the ParticleDistribution.pre_distribution() if the particle_radius attribute is not given explicitly, or it constructs the distribution dividing particle_number by particle_radius and dividing by volume.

Note: the idea of distribution density necessitates normalizing by the variable (in our case, it is the radius) and here we divide by radius to get the unit of 1 / m^3 / m (i.e. number concentration density).

ParticleInstances.particle_mass()#

The particle_mass method returns the mass of the particle by using the particle_radius, particle_density, shape_factor, and volume_void attributes.

ParticleInstances.knudsen_number()#

The knudsen_number method returns the knudsen number of the particle by using the particle_radius attribute as well as the mean_free_path() method.

ParticleInstances.slip_correction_factor()#

The slip_correction_factor method returns the slip correction factor of the particle by using the particle_radius attribute as well as the knudsen_number() method.

ParticleInstances.friction_factor()#

The friction_factor method returns the friction factor of the particle by using the particle_radius attribute as well as the dynamic_viscosity() and slip_correction_factor() methods.

from particula.particle import ParticleInstances
PD_dist = ParticleInstances()
PD_disc = ParticleInstances(
    particle_radius=[1e-9, 2e-9],
    particle_number=[1, 2]
)

print("The units of the 'continuous' distribution (density units): ", PD_dist.particle_distribution().u)
print("The units of the 'discrete' distribution (density units): ", PD_disc.particle_distribution().u)
print("First few points of continuous: ", PD_dist.particle_distribution()[:2])
print("First few points of discrete: ", PD_disc.particle_distribution()[:2])
The units of the 'continuous' distribution (density units):  1 / meter ** 4
The units of the 'discrete' distribution (density units):  1 / meter ** 4
First few points of continuous:  [2200235960693703.8 2375247794883328.5] / meter ** 4
First few points of discrete:  [999999999999999.9 999999999999999.9] / meter ** 4
print("Particle mass: ", PD_disc.particle_mass())
print("Friction factor: ", PD_disc.friction_factor())
Particle mass:  [4.188790204786392e-24 3.3510321638291136e-23] kilogram
Friction factor:  [3.1276392144839314e-15 1.244639390633872e-14] kilogram / second

The Particle inherits from the ParticleInstances class, with the following added attributes and methods:

The Particle attributes#

attribute

unit

default value

elementary_charge_value

C

constants.ELEMENTARY_CHARGE_VALUE

electric_permittivity

F / m

constants.ELECTRIC_PERMITTIVITY

boltzmann_constant

kg * m^2 / K / s^2

constants.BOLTZMANN_CONSTANT

coagulation_approximation

"hardsphere"

The Particle methods#

The following methods are available for the Particle class:

  • Particle._coag_prep()

  • Particle.reduced_mass()

  • Particle.reduced_friction_factor()

  • Particle.coulomb_potential_ratio()

  • Particle.coulomb_enhancement_kinetic_limit()

  • Particle.coulomb_enhancement_continuum_limit()

  • Particle.diffusive_knudsen_number()

  • Particle.dimensionless_coagulation()

  • Particle.coagulation()

They all rely on one underlying utility: the class DimensionlessCoagulation from dimensionless_coagulation, which is documented in the particula.util.dimensionless_coagulation notebook.

All these added methods rely on previously defined attributes and methods to construct particle–particle interaction (e.g. coagulation). Thus, each method takes an input argument particle which is an instance of the Particle class. If not explicitly provided, the particle argument defaults to self (itself).

from particula.particle import Particle

two_particles = Particle(
    particle_radius=[1e-9, 2e-9],
    particle_number=[1, 1]
)

another_particle = Particle(particle_radius=1e-8)
two_particles.coagulation()
Magnitude
[[8.835482747955866e-16 1.4836836107072172e-15]
[1.4836836107072172e-15 1.2356686651065113e-15]]
Unitsmeter3/second
another_particle.coagulation()
Magnitude
[[2.306525014899979e-15]]
Unitsmeter3/second
another_particle.coagulation(another_particle)
Magnitude
[[2.306525014899979e-15]]
Unitsmeter3/second
two_particles.coagulation(another_particle)
Magnitude
[[1.8143554037039256e-14]
[7.479664412223585e-15]]
Unitsmeter3/second