Îto-KMC plasma model
Warning
This section is not very well documented. The following featurse are definitely missing:
Use of hybrid models (PPC-fluid specifications)
How the physics time step is restricted
Underlying model
Plasma transport
The Îto-KMC model uses an Îto solver for some of the species, i.e. the particles drift and diffuse according to
where \(\mathbf{X}\) is the particle position and \(\mathbf{V}\) and \(D\) is the particle drift velocity and diffusion coefficients, respectively. These are obtained by interpolating the macroscopic drift velocity and diffusion coefficients to the particle position. Further details regarding the Îto method are given in ItoSolver.
Not all species in the Îto-KMC model need to be defined by particle solvers, as some species can be tracked by more conventional advection-diffusion-reaction solvers, see CdrSolver for discretization details.
Photoionization
Photoionization in the Îto-KMC model is done using discrete photons. These are generated and advanced in every time step, and interact with the plasma through user-defined photo-reactions. The underlying solver is the discrete Monte Carlo photon transport solver, see Monte Carlo solver.
Field interaction
The Îto-KMC model uses an electrostatic approximation where the field is obtained by solving the Poisson equation for the potential. See Electrostatic solver for further details.
Chemistry
Kinetic Monte Carlo (KMC) is used within grid cells for resolving the plasma chemistry. The algorithmic concepts are given in Kinetic Monte Carlo.
Algorithms
Time stepping
The Îto-KMC model has adaptive time step controls and no fundamental CFL limitation. Currently, the time step can be restricted through several means:
A physics-based time step, which is proportional to the non-critical time step from the KMC integrator.
A particle advective, diffusive, or advective-diffusive CFL limitation, both upper and lower bounds.
A CFL-condition on the convection-diffusion-reaction solvers.
A limit that restricts the time step to a factor proportional to the dielectric relaxation time.
Hard limits, placing upper and lower bounds on the time step.
In addition, the user can specify the maximum permitted growth or reduction in the time step.
These limits are given by the following input variables:
ItoKMCGodunovStepper.min_particle_advection_cfl = 0.0 ## Advective time step CFL restriction
ItoKMCGodunovStepper.max_particle_advection_cfl = 1.0 ## Advective time step CFL restriction
ItoKMCGodunovStepper.min_particle_diffusion_cfl = 0.0 ## Diffusive time step CFL restriction
ItoKMCGodunovStepper.max_particle_diffusion_cfl = 1.E99 ## Diffusive time step CFL restriction
ItoKMCGodunovStepper.min_particle_advection_diffusion_cfl = 0.0 ## Advection-diffusion time step CFL restriction
ItoKMCGodunovStepper.max_particle_advection_diffusion_cfl = 1.E99 ## Advection-diffusion time step CFL restriction
ItoKMCGodunovStepper.fluid_advection_diffusion_cfl = 0.5 ## Advection-diffusion time step CFL restriction
ItoKMCGodunovStepper.relax_dt_factor = 100.0 ## Relaxation time step restriction.
ItoKMCGodunovStepper.min_dt = 0.0 ## Minimum permitted time step
ItoKMCGodunovStepper.max_dt = 1.E99 ## Maximum permitted time step
ItoKMCGodunovStepper.max_growth_dt = 1.E99 ## Maximum permitted time step increase (dt * factor)
ItoKMCGodunovStepper.max_shrink_dt = 1.E99 ## Maximum permissible time step reduction (dt/factor)
ItoKMCGodunovStepper.extend_conductivity = true ## Permit particles to live outside the EB to avoid bad gradients near EB
ItoKMCGodunovStepper.cond_filter_num = 0 ## Number of filterings for conductivity
Particle placement
The KMC algorithm resolves the number of particles that are generated or lost within each grid cell, leaving substantial freedom in how one distributes new particles (or remove older ones). We currently support three methods for placing new particles:
Place all particles on the cell centroid.
Randomly distribute the new particles within the grid cell.
Compute an upstream position within the grid cell and randomly distribute the particles in the downstream region.
The methods each have their advantages and disadvantages.
Placing all new particles on the cell centroid has the advantage that there will be no spurious space charge effects arising in the grid cell.
Randomly distributing the new particles has the advantage that it generates secondary particles more evenly over the reaction region.
Both these methods (centroid and random) are, however, sources of numerical diffusion since the secondary particles are potentially placed in the wake of the primary particles (which is non-physical).
The downstream method circumvents this source of numerical diffusion by only placing secondary particles in the downstream region of some user-defined species (typically the electrons).
See JSON interface for instructions on how to assign the particle placement method.
Spatial filtering
It is possible to apply filtering of the space-charge density prior to the field advance in order to reduce the impact of discrete particle noise. Filters are applied as follows:
where \(\alpha\) is a filtering factor and \(s\) a stride. Users can apply this filtering by adjusting the following input options:
ItoKMCGodunovStepper.rho_filter_num = 0 # Number of filterings for the space-density
ItoKMCGodunovStepper.rho_filter_max_stride = 1 # Maximum stride for filter
ItoKMCGodunovStepper.rho_filter_alpha = 0.5 # Filtering factor (0.5 is a bilinear filter)
Warning
Spatial filtering of the space-charge density is a work-in-progress which may yield unexpected results. Bugs may or may not be present. Users should exercise caution when using this feature.
Reaction network
Particle management
0D chemistry
The user input interface to the Îto-KMC model consists of a zero-dimensional plasma kinetics interface called ItoKMCPhysics.
This interface consists of the following main functionalities:
User-defined species, and which solver types (Îto or CDR) to use when tracking them.
User-defined initial particles, reaction kinetics, and photoionization processes.
ItoKMCPhysics
The complete C++ interface specification is given below.
Because the interface is fairly extensive, chombo-discharge also supplies a JSON-based implementation called ItoKMCJSON (see JSON interface) for defining these things through file input.
The difference between ItoKMCPhysics and its implementation ItoKMCJSON is that the JSON-based implementation class only implements a subset of potential features supported by ItoKMCPhysics.
Show/hide full 0D interface
/* chombo-discharge
* Copyright © 2021 SINTEF Energy Research.
* Please refer to Copyright.txt and LICENSE in the chombo-discharge root directory.
*/
/*!
@file CD_ItoKMCPhysics.H
@brief Main file for describing Ito-based plasma physics
@author Robert Marskar
*/
#ifndef CD_ItoKMCPhysics_H
#define CD_ItoKMCPhysics_H
// Std includes
#include <memory>
#include <vector>
// Chombo includes
#include <RealVect.H>
#include <RefCountedPtr.H>
#include <List.H>
// Our includes
#include <CD_ItoSpecies.H>
#include <CD_CdrSpecies.H>
#include <CD_RtSpecies.H>
#include <CD_Photon.H>
#include <CD_ItoParticle.H>
#include <CD_PointParticle.H>
#include <CD_ItoKMCPhotoReaction.H>
#include <CD_ItoKMCSurfaceReactionSet.H>
#include <CD_KMCDualState.H>
#include <CD_KMCDualStateReaction.H>
#include <CD_KMCSolver.H>
#include <CD_NamespaceHeader.H>
namespace Physics {
namespace ItoKMC {
/*!
@brief Numerical representation of the KMC state. Can be floating-point or integer type
*/
using FPR = Real;
/*!
@brief KMC state used in the Kinetic Monte Carlo advancement
*/
using KMCState = KMCDualState<FPR>;
/*!
@brief KMC reaction used in the Kinetic Monte Carlo advancement
*/
using KMCReaction = KMCDualStateReaction<KMCState, FPR>;
/*!
@brief KMC solverl type used in the Kinetic Monte carlo advancement
*/
using KMCSolverType = KMCSolver<KMCReaction, KMCState, FPR>;
/*!
@brief Basic function for diffusing a particle
*/
using DiffusionFunction = std::function<RealVect(const ItoParticle& p, const Real& a_dt)>;
/*!
@brief Map to species type
@details This is just for distinguishing between species that are treated with an Ito or CDR formalism
*/
enum SpeciesType
{
Ito,
CDR
};
/*!
@brief Base class for interaction between Kinetic Monte Carlo and Ito-based plasma solvers.
@details When using this class, the user should populate m_kmcReactions with the appropriate reactions AND implement routines
for computing the mobility/diffusion coefficeints.
*/
class ItoKMCPhysics
{
public:
/*!
@brief Constructor. Does nothing.
*/
inline ItoKMCPhysics() noexcept;
/*!
@brief Destructor. Does nothing.
*/
inline virtual ~ItoKMCPhysics() noexcept;
/*!
@brief Define the KMC solver and state
*/
inline void
defineKMC() const noexcept;
/*!
@brief Kill the KMC solver
*/
inline void
killKMC() const noexcept;
/*!
@brief Get the neutral density at a position in space
@param[in] a_pos Physical position
@return Neutral density
*/
virtual Real
getNeutralDensity(const RealVect a_pos) const noexcept = 0;
/*!
@brief Compute Townsend ionization coefficient
@param[in] a_E Electric field magnitude
@param[in] a_x Physical coordinate
@return Should return the Townsend ionization coefficient.
*/
virtual Real
computeAlpha(const Real a_E, const RealVect a_x) const = 0;
/*!
@brief Compute Townsend attachment coefficient
@param[in] a_E Electric field magnitude
@param[in] a_x Physical coordinate
@return Should return the Townsend attachment coefficient.
*/
virtual Real
computeEta(const Real a_E, const RealVect a_x) const = 0;
/*!
@brief Get all particle drift-diffusion species
@return m_itoSpecies
*/
const Vector<RefCountedPtr<ItoSpecies>>&
getItoSpecies() const;
/*!
@brief Get all fluid drift-diffusion species
@return m_cdrSpecies
*/
const Vector<RefCountedPtr<CdrSpecies>>&
getCdrSpecies() const;
/*!
@brief Get all photon species
@return m_rtSpecies
*/
const Vector<RefCountedPtr<RtSpecies>>&
getRtSpecies() const;
/*!
@brief Get diffusion functions
*/
const Vector<DiffusionFunction>&
getItoDiffusionFunctions() const noexcept;
/*!
@brief Get number of plot variables
*/
virtual Vector<std::string>
getPlotVariableNames() const noexcept;
/*!
@brief Get plot variables
@param[in] a_E Electric field
@param[in] a_pos Physical position
@param[in] a_phi Plasma species densities
@param[in] a_gradPhi Density gradients for plasma species.
@param[in] a_dx Grid resolution
@param[in] a_kappa Cut-cell volume fraction
*/
virtual Vector<Real>
getPlotVariables(const RealVect a_E,
const RealVect a_pos,
const Vector<Real>& a_phi,
const Vector<RealVect>& a_gradPhi,
const Real a_dx,
const Real a_kappa) const noexcept;
/*!
@brief Get number of plot variables
*/
virtual int
getNumberOfPlotVariables() const noexcept;
/*!
@brief Return number of Ito solvers.
*/
inline int
getNumItoSpecies() const;
/*!
@brief Return number of CDR solvers.
*/
inline int
getNumCdrSpecies() const;
/*!
@brief Return total number of plasma species.
*/
inline int
getNumPlasmaSpecies() const;
/*!
@brief Return number of RTE solvers.
*/
inline int
getNumPhotonSpecies() const;
/*!
@brief Return true/false if physics model needs species gradients.
*/
virtual bool
needGradients() const noexcept;
/*!
@brief Get the internal mapping between plasma species and Ito solvers
*/
inline const std::map<int, std::pair<SpeciesType, int>>&
getSpeciesMap() const noexcept;
/*!
@brief Parse run-time options
*/
inline virtual void
parseRuntimeOptions() noexcept;
/*!
@brief Set initial surface charge. Default is 0, override if you want.
@param[in] a_time Simulation time
@param[in] a_pos Physical coordinate
*/
inline virtual Real
initialSigma(const Real a_time, const RealVect a_pos) const;
/*!
@brief Compute the Ito solver mobilities.
@param[in] a_time Time
@param[in] a_pos Position
@param[in] a_E Electric field
@return Must return a vector of non-negative mobility coefficients for the plasma species
*/
virtual Vector<Real>
computeMobilities(const Real a_time, const RealVect a_pos, const RealVect a_E) const noexcept = 0;
/*!
@brief Compute the Ito solver diffusion coefficients
@param[in] a_time Time
@param[in] a_pos Position
@param[in] a_E Electric field
@return Must return a vector of non-negative diffusion coefficients for the plasma species
*/
virtual Vector<Real>
computeDiffusionCoefficients(const Real a_time, const RealVect a_pos, const RealVect a_E) const noexcept = 0;
/*!
@brief Resolve secondary emission at the EB.
@details Routine is here to handle charge injection, secondary emission etc.
@param[out] a_secondaryParticles Outgoing plasma species particles.
@param[out] a_cdrFluxes CDR fluxes for CDR species.
@param[out] a_secondaryPhotons Photons injected through the EB.
@param[in] a_primaryParticles Particles that left the computational domain through the EB.
@param[in] a_cdrFluxesExtrap Extrapolated CDR fluxes.
@param[in] a_primaryPhotons Photons that left the computational domain through the EB.
@param[in] a_newNumParticles Total number of particles in the cut-cell AFTER the transport step.
@param[in] a_oldNumParticles Total number of particles in the cut-cell BEFORE the transport step.
@param[in] a_electricField Electric field.
@param[in] a_physicalCellCenter Physical position of the cell center.
@param[in] a_cellCentroid Cell centroid relative to the cell center (not multiplied by dx).
@param[in] a_bndryCentroid EB face centroid relative to the cell center (not multiplied by dx).
@param[in] a_bndryNormal Cut-cell normal vector.
@param[in] a_bndryArea Cut-cell boundary area - not multiplied by dx (2D) or dx^2 (3D).
@param[in] a_dx Grid resolution on this level.
@param[in] a_dt Time step.
@param[in] a_isDielectric Dielectric or electrode.
@param[in] a_matIndex Material index (taken from computationalGeometry).
*/
virtual void
secondaryEmissionEB(Vector<List<ItoParticle>>& a_secondaryParticles,
Vector<Real>& a_cdrFluxes,
Vector<List<Photon>>& a_secondaryPhotons,
const Vector<List<ItoParticle>>& a_primaryParticles,
const Vector<Real>& a_cdrFluxesExtrap,
const Vector<List<Photon>>& a_primaryPhotons,
const RealVect& a_E,
const RealVect& a_physicalCellCenter,
const RealVect& a_cellCentroid,
const RealVect& a_bndryCentroid,
const RealVect& a_bndryNormal,
const Real a_bndryArea,
const Real a_dx,
const Real a_dt,
const bool a_isDielectric,
const int a_matIndex) const noexcept = 0;
/*!
@brief Advance the reaction network using the KMC algorithm.
@param[inout] a_numParticles Number of physical particles
@param[out] a_numNewPhotons Number of new physical photons to generate (of each type)
@param[out] a_physicsDt Reasonable KMC time step computed at end of integration with eps = 1
@param[in] a_phi Plasma species densities.
@param[in] a_gradPhi Plasma species density gradients.
@param[in] a_dt Time step
@param[in] a_E Electric field
@param[in] a_pos Physical position
@param[in] a_dx Grid resolution
@param[in] a_kappa Cut-cell volume fraction.
*/
inline void
advanceKMC(Vector<FPR>& a_numParticles,
Vector<FPR>& a_numNewPhotons,
Real& a_physicsDt,
const Vector<Real>& a_phi,
const Vector<RealVect>& a_gradPhi,
const Real a_dt,
const RealVect a_E,
const RealVect a_pos,
const Real a_dx,
const Real a_kappa) const;
/*!
@brief Reconcile the number of particles.
@details This will add/remove particles and potentially also adjust the particle weights.
@param[inout] a_particles Computational particles
@param[in] a_newNumParticles New number of particles (i.e., after the KMC advance)
@param[in] a_oldNumParticles Previous number of particles (i.e., before the KMC advance)
@param[in] a_electricField Electric field in cell
@param[in] a_cellPos Cell center position
@param[in] a_centroidPos Cell centroid position
@param[in] a_loCorner Low corner of minimum box enclosing the cut-cell
@param[in] a_hiCorner High corner of minimum box enclosing the cut-cell
@param[in] a_bndryCentroid Cut-cell boundary centroid
@param[in] a_bndryNormal Cut-cell normal (pointing into the domain)
@param[in] a_dx Grid resolution
@param[in] a_kappa Cut-cell volume fraction.
@note Public because this is called by ItoKMCStepper
*/
inline void
reconcileParticles(Vector<List<ItoParticle>*>& a_particles,
const Vector<FPR>& a_newNumParticles,
const Vector<FPR>& a_oldNumParticles,
const RealVect a_electricField,
const RealVect a_cellPos,
const RealVect a_centroidPos,
const RealVect a_lo,
const RealVect a_hi,
const RealVect a_bndryCentroid,
const RealVect a_bndryNormal,
const Real a_dx,
const Real a_kappa) const noexcept;
/*!
@brief Generate new photons.
@details This will add photons
@param[in] a_newPhotons New photons
@param[in] a_numNewPhotons Number of physical photons to be generated.
@param[in] a_cellPos Cell center position
@param[in] a_centroidPos Cell centroid position
@param[in] a_loCorner Low corner of minimum box enclosing the cut-cell
@param[in] a_hiCorner High corner of minimum box enclosing the cut-cell
@param[in] a_bndryCentroid Cut-cell boundary centroid
@param[in] a_bndryNormal Cut-cell normal (pointing into the domain)
@param[in] a_dx Grid resolution
@param[in] a_kappa Cut-cell volume fraction.
@note Public because this is called by ItoKMCStepper
*/
inline void
reconcilePhotons(Vector<List<Photon>*>& a_newPhotons,
const Vector<FPR>& a_numNewPhotons,
const RealVect a_cellPos,
const RealVect a_centroidPos,
const RealVect a_lo,
const RealVect a_hi,
const RealVect a_bndryCentroid,
const RealVect a_bndryNormal,
const Real a_dx,
const Real a_kappa) const noexcept;
/*!
@brief Reconcile photoionization reactions.
@param[inout] a_itoPrticles Particle products placed in Ito solvers.
@param[inout] a_cdrParticles Particle products placed in CDR solvers.
@param[in] a_absorbedPhotons Photons absorbed on the mesh.
@details This runs through the photo-reactions and associates photo-ionization products.
*/
inline void
reconcilePhotoionization(Vector<List<ItoParticle>*>& a_itoParticles,
Vector<List<PointParticle>*>& a_cdrParticles,
const Vector<List<Photon>*>& a_absorbedPhotons) const noexcept;
protected:
/*!
@brief Enum for switching between KMC algorithms
@details 'SSA' is the Gillespie algorithm, 'Tau' is tau-leaping and 'Hybrid' is the Cao et. al. algorithm.
*/
enum class Algorithm
{
SSA,
ExplicitEuler,
Midpoint,
PRC,
ImplicitEuler,
HybridExplicitEuler,
HybridMidpoint,
HybridPRC,
HybridImplicitEuler
};
/*!
@brief Enum for switching between various particle placement algorithms
*/
enum class ParticlePlacement
{
Random,
Centroid,
Downstream
};
/*!
@brief Algorithm to use for KMC advance
*/
Algorithm m_algorithm;
/*!
@brief Particle placement algorithm
*/
ParticlePlacement m_particlePlacement;
/*!
@brief Map for associating a plasma species with an Ito solver or CDR solver.
*/
std::map<int, std::pair<SpeciesType, int>> m_speciesMap;
/*!
@brief Class name. Used for options parsing
*/
std::string m_className;
/*!
@brief Turn on/off debugging
*/
bool m_debug;
/*!
@brief Is defined or not
*/
bool m_isDefined;
/*!
@brief If true, increment onto existing particles rather than creating new ones
*/
bool m_incrementNewParticles;
/*!
@brief Is the KMC solver defined or not.
*/
static thread_local bool m_hasKMCSolver;
/*!
@brief Kinetic Monte Carlo solver used in advanceReactionNetwork.
*/
static thread_local KMCSolverType m_kmcSolver;
/*!
@brief KMC state used in advanceReactionNetwork.
*/
static thread_local KMCState m_kmcState;
/*!
@brief KMC reactions used in advanceReactionNetowkr
@note This is set up via setupKMC in order toi ensure OpenMP thread safety when calling advanceReactionNetwork. The vector
is later depopulated in killKMC().
*/
static thread_local std::vector<std::shared_ptr<const KMCReaction>> m_kmcReactionsThreadLocal;
/*!
@brief List of reactions for the KMC solver
*/
std::vector<KMCReaction> m_kmcReactions;
/*!
@brief List of photoionization reactions
*/
std::vector<ItoKMCPhotoReaction> m_photoReactions;
/*!
@brief List of reactions that are a part of the time step limitation.
*/
std::vector<Real> m_reactiveDtFactors;
/*!
@brief Random number generators for photoionization pathways.
@details The first index is the photon index, i.e. entry in m_photonSpecies. The second index in the
map is an RNG generator for selecting photo-reactions, and a map which associates the returned
reaction from the RNG generator with an index in m_photoReactions.
*/
std::map<int, std::pair<std::discrete_distribution<int>, std::map<int, int>>> m_photoPathways;
/*!
@brief Surface reactions.
*/
ItoKMCSurfaceReactionSet m_surfaceReactions;
/*!
@brief Diffusion functions for the various Ito species
*/
Vector<DiffusionFunction> m_itoDiffusionFunctions;
/*!
@brief List of solver-tracked particle drift-diffusion species.
*/
Vector<RefCountedPtr<ItoSpecies>> m_itoSpecies;
/*!
@brief List of solver-tracked fluid drift-diffusion species.
*/
Vector<RefCountedPtr<CdrSpecies>> m_cdrSpecies;
/*!
@brief List of solver-tracked photon species.
*/
Vector<RefCountedPtr<RtSpecies>> m_rtSpecies;
/*!
@brief An internal integer describing which species is the "ionizing" species.
@details This is used by the particle reconciliation routine when only placing secondary particles
in the downstream region of the primary particles.
*/
int m_downstreamSpecies;
/*!
@brief Maximum new number of particles generated by the chemistry advance
*/
int m_maxNewParticles;
/*!
@brief Maximum new number of photons generated by the chemistry advance
*/
int m_maxNewPhotons;
/*!
@brief Solver setting for the Cao et. al algorithm.
@details Determines critical reactions. A reaction is critical if it is m_Ncrit firings away from depleting a reactant.
*/
int m_Ncrit;
/*!
@brief Solver setting for the Cao et. al algorithm.
@details Maximum number of SSA steps to run when switching into SSA-based advancement for non-critical reactions.
*/
int m_NSSA;
/*!
@brief Maximum number of iterations for implicit KMC-leaping algorithms
*/
int m_maxIter;
/*!
@brief Solver setting for the Cao et. al. algorithm.
@details Equal to the maximum permitted change in the relative propensity for non-critical reactions
*/
Real m_SSAlim;
/*!
@brief Solver setting for the Cao et. al. algorithm.
@details Equal to the maximum permitted change in the relative propensity for non-critical reactions
*/
Real m_eps;
/*!
@brief Exit tolerance for implicit KMC-leaping algorithms
*/
Real m_exitTol;
/*!
@brief Define method -- defines all the internal machinery
*/
inline void
define() noexcept;
/*!
@brief Build internal representation of how we distinguish the Ito and CDR solvers
@note This should ALWAYS be called after initializing the species since ItoKMCStepper will rely on it.
*/
inline void
defineSpeciesMap() noexcept;
/*!
@brief Define pathways for photo-reactions
*/
inline void
definePhotoPathways() noexcept;
/*!
@brief Parse the maximum number of particles generated per cell
*/
inline void
parsePPC() noexcept;
/*!
@brief Parse the maximum number of particles generated per cell
*/
inline void
parseDebug() noexcept;
/*!
@brief Parse reaction algorithm
*/
inline void
parseAlgorithm() noexcept;
/*!
@brief Update reaction rates
@param[out] a_kmcReactions Reaction rates to be set.
@param[in] a_E Electric field
@param[in] a_pos Physical position
@param[in] a_phi Plasma species densities
@param[in] a_gradPhi Density gradients for plasma species.
@param[in] a_dt Time step
@param[in] a_dx Grid resolution
@param[in] a_kappa Cut-cell volume fraction
@note Must be implemented by the user.
*/
virtual void
updateReactionRates(std::vector<std::shared_ptr<const KMCReaction>>& a_kmcReactions,
const RealVect a_E,
const RealVect a_pos,
const Vector<Real>& a_phi,
const Vector<RealVect>& a_gradPhi,
const Real a_dt,
const Real a_dx,
const Real a_kappa) const noexcept = 0;
/*!
@brief Remove particles from the input list.
@details This will remove weight from the input particles if we can. Otherwise we remove full particles.
@param[inout] a_particles List of (super-)particles to remove from.
@param[in] a_numToRemove Number of physical particles to remove from the input list
*/
inline void
removeParticles(List<ItoParticle>& a_particles, const long long a_numToRemove) const;
/*!
@brief Compute the upstream position in a grid cell. Returns false if an upstream position was undefinable.
@details This routine will compute the position of the "most upstream" particle in the input list.
For all particles we assume that they advect in the direction of Z*E, where Z is the charge number.
This routine also computes the minimum bounding box (in unit coordinates) that encloses the downstreamn
region within the grid cell.
@param[out] a_pos Upstream position.
@param[out] a_lo Minimum coordinate of the downstream region (relative to the unit cell)
@param[out] a_hi Maximum coordinate of the downstream region (relative to the unit cell)
@param[in] a_Z Particle charge number
@param[in] a_particles Particles where we look for an upstream position.
@param[in] a_electricField Electric field vector
@param[in] a_cellPos Physical location of the cell center
@param[in] a_dx Grid resolution
*/
inline bool
computeUpstreamPosition(RealVect& a_pos,
RealVect& a_lo,
RealVect& a_hi,
const int& a_Z,
const List<ItoParticle>& a_particles,
const RealVect& a_electricField,
const RealVect& a_cellPos,
const Real& a_dx) const noexcept;
/*!
@brief No diffusion function for a particle
@param[in] a_particle Particle to diffuse
@param[in] a_dt Time step
@return Returns the zero vector.
*/
inline RealVect
noDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept;
/*!
@brief Isotropic diffusion function for a particle
@param[in] a_particle Particle to diffuse
@param[in] a_dt Time step
@return Returns sqrt(2 * D * a_dt) * N(0,1), where N(0,1) is a normal distribution
*/
inline RealVect
isotropicDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept;
/*!
@brief Quasi-isotropic diffusion function for a particle which does not permit backward diffusion.
@param[in] a_particle Particle to diffuse
@param[in] a_dt Time step
@return Same as isotropic diffusion, but ensures that the particle cannot diffuse backward
*/
inline RealVect
forwardIsotropicDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept;
};
} // namespace ItoKMC
} // namespace Physics
#include <CD_NamespaceFooter.H>
#include <CD_ItoKMCPhysicsImplem.H>
#endif
Species definitions
Species are defined either as input species for CDR solvers (see CdrSolver) or Îto solvers (see ItoSolver).
It is sufficient to populate the ItoKMCPhysics species vectors m_cdrSpecies and m_itoSpecies if one only wants to initialize the solvers.
See ItoKMCPhysics for their C++ definition.
In addition to actually populating the vectors, users will typically also include initial conditions for the species.
The interfaces permit initial particles and density functions for both both solver types.
Additionally, one must define all species associated with radiative transfer solvers.
Plasma reactions
Plasma reactions in the Îto-KMC model are represented stoichiometrically as
where \(c\) is the KMC rate coefficient, i.e. not the rate coefficient from the reaction rate equation. An arbitrary number of reactions is supported, but currently the reaction mechanisms are limited to:
The left-hand side must consist only of species that are tracked by a CDR or Îto solver.
The right-hand side can consist of species tracked by a CDR solver, an Îto solver, or a radiative transfer solver.
These rules apply only to the internal C++ interface; implementations of this interface will typically also allow species defined as ‘’background species’’, i.e. species that are not tracked by a solver. In that case, the interface implementation must absorb the background species contribution into the rate constant.
In the KMC algorithm, one operates with chemical propensities rather than rate coefficients. For example, the chemical propensity \(a_1\) for a reaction \(A + B \xrightarrow{c_1}\varnothing\) is
and from the macroscopic limit \(X_A \gg 1\), \(X_B \gg 1\) one may in fact also derive that \(c_1 = k_1/\Delta V\) where \(k_1\) is the rate coefficient from the reaction rate equation, i.e. where \(\partial_t n_A = -k_1n_An_B\). Note that if the reaction was \(A + A\xrightarrow{c_2}\varnothing\) then the propensity is
since there are \(\frac{1}{2}X_A\left(X_A-1\right)\) distinct pairs of particles of type \(A\). Likewise, the fluid rate coefficient would be \(k_2 = c_2\Delta V/2\).
The distinction between KMC and fluid rates is an important one; the reaction representation used in the Îto-KMC model only operates with the KMC rates \(c_j\), and it is us to the user to ensure that these are consistent with the fluid limit.
Internally, these reactions are implemented through the dual state KMC implementation, see Kinetic Monte Carlo.
During the reaction advance the user only needs to update the \(c_j\) coefficients (typically done via an interface implementation); the calculation of the propensity is automatic and follows the standard KMC rules (e.g., the KMC solver accounts for the number of distinct pairs of particles).
This must be done in the routine updateReactionRates(...), see ItoKMCPhysics for the complete specification.
Photoionization
Photo-reactions are also represented stoichiometrically as
where \(\xi\) is the photo-reaction efficiency, i.e. the probability that the photon \(\gamma\) causes a reaction when it is absorbed on the mesh. Currently, photons can only be generated through plasma reactions, see Plasma reactions, and we do not permit reactions between the photon and plasma species.
Important
We currently only support constant photoionization probabilities \(\xi\).
ItoKMCPhysics adds some flexibility when dealing with photo-reactions as it permits pre-evaluation of photoionization probabilities.
That is, when generating photons through reactions of the type \(A + B \rightarrow \gamma\), one may scale the reaction by the photoionization probability \(\xi\) so that only ionizing photons are generated and then write the photo-reaction as \(\gamma \xrightarrow{\xi=1} A + B + \ldots\).
However, this process becomes more complicated when dealing with multiple photo-reaction pathways, e.g.,
where \(\xi_1\) and \(\xi_2\) are the probabilities that the photon \(\gamma\) triggers the reaction. If only a single physical photon is absorbed, then one must stochastically determine which pathway is triggered. There are two ways of doing this:
Pre-scale the photon generation by \(\xi_1 + \xi_2\), and then determine the pathway through the relative probabilities \(\xi_1/\left(\xi_1+\xi_2\right)\) and \(\xi_2/\left(\xi_1+\xi_2\right)\).
Do not scale the photon generation reaction and determine the pathway through the absolute probabilities \(\xi_1\) and \(\xi_2\). This can imply a large computational cost since one will have to track all photons that are generated.
The former method is normally the preferred way as it can lead to reductions in computational complexity and particle noise, but for flexibility ItoKMCPhysics supports both of these.
The latter method can be of relevance if users wants precise descriptions of photons that trigger both photoionizing reactions and surface reactions (e.g., secondary electron emission).
An alternative is to split the photon type \(\gamma\) into volumetrically absorbed and surface absorbed photon species \(\gamma_v\) and \(\gamma_s\). In this case one may pre-scale the photon-generating reactions by the photo-reaction probabilities \(\xi\) (for volume absorption) and \(\zeta\) (for surface absorption) as follows:
Volumetric and surface absorption is then treated independently
This type of pre-evaluation of the photo-reaction pathways is sensible in a statistical sense, but loses meaning if only a single photon is involved.
Surface reactions
Warning
Surface reactions are supported by ItoKMCPhysics but not implemented in the JSON interface (yet).
Transport coefficients
Species mobilities and diffusion coefficients should be computed just as they are done in the fluid approximation.
The ItoKMCPhysics interface requires implementations of two functions that define the coefficients as functions of \(\mu = \mu\left(t,\mathbf{x}, \mathbf{E}\right)\),
and likewise for the diffusion coefficients.
Note that these functions should return the fluid coefficients.
Important
There is currently no support for computing \(\mu\) as a function of the species densities (e.g., the electron density), but this only requires modest extensions of the Îto-KMC module.
Time step calculation
The time step calculation in ItoKMCPhysics is based on the approximation of a reasonable time step as
where \(\nu_{ri}\) is the state change in species \(i\) due to firing of exactly one reaction of type \(r\). By default, the above is evaluated for all reactions, but the user can specify a subset of reactions for which the above constraint is applied.
Caveats
There are some caveats with using the time step limitation given above. For example, if there are no electrons in the simulation region, the above method will (correctly) return a very large time step based on the behavior of the other species. But one may very have detachment of electrons enabled, and the combination of a detachment event and a large time step is quite unfortunate. Because of this, the above constraint is also applied on proxy-states of \(\vec{X}\) that have been completely exhausted through critical reactions, which provides a much more reasonable time step.
A second factor involved in the time step calculation above is that one may have regions where \(X_i\) is very small, but where \(\sum_r \nu_{ri} a_r\) is very high. Outside of electron avalanching regions, detachment may completely dominate the time step calculation and cause a much smaller time step than necessary. Our resolution to this behavior is to permit the user to manually specify which reactions are important when computing the time step, see Time step calculation.
JSON interface
The JSON-based chemistry interface (called ItoKMCJSON) simplifies the definition of the plasma kinetics and initial conditions by permitting specifications through a JSON file.
In addition, the JSON specification will permit the definition of background species that are not tracked as solvers (but that simply exist as a density function).
The JSON interface thus provides a simple entry point for users that have no interest in defining the chemistry from scratch.
Several mandatory fields are required when specifying the plasma kinetics, such as:
Gas pressure, temperature, and number density.
Background species (if any).
Townsend coefficients.
Plasma species and their initial conditions, which solver to use when tracking them.
Photon species and their absorption lengths.
Plasma reactions.
Photoionization reactions.
These specifications follow a pre-defined JSON format which is discussed in detail below.
Tip
While JSON files do not formally support comments, the JSON submodule used by chombo-discharge allows them.
Gas law
The JSON gas law represents a loose definition of the gas pressure, temperature, and number density.
Multiple gas laws can be defined in the JSON file, and the user can then select which one to use.
This is defined within a JSON entry gas and a subentry law.
In the gas/law field one must specify an id field which indicates which gas law to use.
One may also set an optional field plot to true/false if one wants to include the pressure, temperature, and density in the HDF5 output files.
Important
Gas parameters are specified in SI units.
Each user-defined gas law exists as a separate entry in the gas/law field, and the minimum requirements for these are that they contain a type specifier.
Currently, the supported types are
ideal(for constant ideal gas)
An example JSON specification is given below.
Example gas law specification
{
"gas" : {
// Specify which gas law we use. The user can define multiple gas laws and then later specify which one to use.
"law" : {
"id" : "my_ideal_gas", // Specify which gas law we use.
"plot" : true, // Turn on/off plotting.
"my_ideal_gas" : {
// Definition for an ideal gas law. Temperature is always in Kelvin the pressure is in Pascal. The neutral density
// is derived using an ideal gas law.
"type" : "ideal",
"temperature" : 300,
"pressure" : 1E5
}
}
}
}
Ideal gas
When specifying that the gas law type is ideal, the user must further specify the temperature and pressure of the gas.
Example ideal gas specification
{
"gas" : {
"law" : {
"id" : "my_ideal_gas", // Specify which gas law we use.
"my_ideal_gas" : {
"type" : "ideal", // Ideal gas law type
"temperature" : 300, // Temperature must be in Kelvin
"pressure" : 1.0 // Pressure must be in bar
},
}
}
}
Background species
Background species \(i\) are defined as continuous background densities \(N_i\) given by
where \(\chi_i\left(\mathbf{x}\right)\) is the molar fraction of species \(i\) (typically, but not necessarily, a constant).
When specifying background species, the user must include an array of background species in the gas JSON field.
For example,
{
"gas" : {
"background species" : [
{
// First species goes here
},
{
// Second species goes here
}
]
}
}
The order of appearance of the background species does not matter.
Name
The species name is specified by including an id field, e.g.
{
"gas" : {
"background species" : [
{
"id": "O2"
}
]
}
}
Molar fraction
The molar fraction can be specified in various forms by including a field molar fraction.
This field also requires the user to specify the type of molar fraction.
In principle, the molar fraction can have a positional dependency.
Warning
There’s no internal renormalization of the molar fractions input by the user. Internal inconsitencies will occur if the user supplies inputs molar fractions that sum to a number different than one.
Various checks will be enabled if the user compiles in debug-mode (see Installation), but these checks are not guaranteed to catch all cases.
Constant
To specify a constant molar fraction, set the type specifier to constant and specify the value.
An example JSON specification is
{
"gas" : {
"background species" : [
{
"id": "O2" // Species name
"molar fraction": { // Molar fraction specification
"type": "constant", // Constant molar fraction
"value": 0.2 // Molar fraction value
}
}
]
}
}
Tabulated versus height
The molar fraction can be set as a tabulated value versus one of the Cartesian coordinate axis by setting the type specifier to table vs height.
The input data should be tabulated in column form, e.g.
# height molar fraction
0 0.1
1 0.1
2 0.1
The file parser (see LookupTable1D) will ignore the header file if it starts with a hashtag (#). Various other inputs are then also required:
fileFile name containing the height vs. molar fraction data (required).axisCartesian axis to associate with the height coordinate (required).height columnColumn in data file containing the height (optional, defaults to 0).molar fraction columnColumn in data file containing the molar fraction (optional, defaults to 1).height scaleScaling of height column (optional).fraction scaleScaling (optional).min heightTruncate data to minimum height (optional, applied after scaling).max heightTruncate data to maximum height (optional, applied after scaling).num pointsNumber of points to keep in table representation (optional, defaults to 1000).spacingSpacing table representation (optional, defaults to “linear”.dumpOption for dumping tabulated data to file.
An example JSON specification is
{
"gas" : {
"background species" : [
{
"id": "O2" // Species name
"molar fraction": { // Molar fraction specification
"type": "table vs height", // Constant molar fraction
"file" : "O2_molar_fraction.dat", // File name containing molar fraction
"dump" : "debug_O2_fraction.dat", // Optional debugging hook for dumping table to file
"axis" : "y", // Axis which represents the "height"
"height column" : 0, // Optional specification of column containing the height data (defaults to 0)
"molar fraction column" : 1, // Optional specification of column containing the height data (defaults to 1)
"height scale" : 1.0, // Optional scaling of height column
"fraction scale" : 1.0, // Optional scaling of molar fraction column
"min height" : 0.0, // Optional truncation of minimum height kept in internal table (occurs after scaling)
"max height" : 2.0, // Optional truncation of maximum height kept in internal table (occurs after scaling)
"num points" : 100, // Optional specification of number of data points kept in internal table (defaults to 1000)
"spacing" : "linear" // Optional specification of table representation. Can be 'linear' or 'exponential' (defaults to linear)
}
}
]
}
}
Plotting
Background species densities can be included in HDF5 plots by including an optional field plot.
For example
{
"gas" : {
"background species" : [
{
"id": "O2" // Species name
"molar fraction": { // Molar fraction specification
"type": "constant", // Constant molar fraction
"value": 0.2 // Molar fraction value
},
"plot": true // Plot number density in HDF5 or not
}
]
}
}
Townsend coefficients
Townsend ionization and attachment coefficients \(\alpha\) and \(\eta\) must be specified, and have two usages:
Flagging cells for refinement (users can override this).
Potential usage in plasma reactions (which requires particular care, see Warnings and caveats.
These are specified by including JSON entries alpha and eta, e.g.
{
"alpha": {
// alpha-coefficient specification goes here
},
"eta" : {
// eta-coefficient specification goes here
}
}
There are various way of specifying these, as discussed below:
Automatic
In auto-mode the Townsend coefficients for the electrons is automatically derived from the user-specified list of reactions. E.g., the Townsend ionization coefficient is computed as
where \(\sum k\) is the sum of all ionizing reactions, also incorporating the neutral density. The advantage of this approach is that one may choose to discard some of the reactions from e.g. BOLSIG+ output but without recomputing the Townsend coefficients.
To automatically set Townsend coefficients, set type to auto and specify the species that is involved, e.g.
{
"alpha": {
"type": "auto",
"species": "e"
}
}
The advantage of using auto-mode for the coefficients is that one automatically ensures consistency between the user-specified list of reactions and the
Constant
To set a constant Townsend coefficient, set type to constant and then specify the value, e.g.
{
"alpha": {
"type": "constant",
"value": 1E5
}
}
Tabulated vs \(E/N\)
To set the coefficient as functions \(f = f\left(E/N\right)\), set the type specifier to table vs E/N and specify the following fields:
fileFor specifying the file containing the input data, which must be organized as column data, e.g.# E/N alpha/N 0 1E5 100 1E5 200 1E5
headerFor specifying where in the file one starts reading data. This is an optional argument intended for use with BOLSIG+ output data where the user specifies that the data is contained in the lines below the specified header.E/N columnFor setting which column in the data file contains the values of \(E/N\) (optional, defaults to 0).alpha/N columnFor setting which column in the data file contains the values of \(\alpha/N\). Note that this should be replaced byeta/N columnwhen parsing the attachment coefficient. This is an optional argument that defaults to 1.scale E/NOptional scaling of the column containing the \(E/N\) data.scale alpha/NOptional scaling of the column containing the \(\alpha/N\) data.min E/NOptional truncation of the table representation of the data (applied after scaling).max E/NOptional truncation of the table representation of the data (applied after scaling).num pointsNumber of data points in internal table representation (optional, defaults to 1000).spacingSpacing in internal table representation. Can belinearorexponential. This is an optional argument that defaults toexponential.dumpA string specifier for writing the table representation of \(E/N, \alpha/N\) to file.
An example JSON specification that uses a BOLSIG+ output file for parsing the data is
{
"alpha": {
"type" : "table vs E/N", // Specify that we read in alpha/N vs E/N
"file" : "bolsig_air.dat", // File containing the data
"dump" : "debug_alpha.dat", // Optional dump of internalized table to file. Useful for debugging.
"header" : "E/N (Td)\tTownsend ioniz. coef. alpha/N (m2)", // Optional argument. Contains line immediately preceding the data to be read.
"E/N column" : 0, // Optional specification of column containing E/N (defaults to 0)
"alpha/N column" : 1, // Optional specification of column containing alpha/N (defaults to 1)
"min E/N" : 1.0, // Optional truncation of minium E/N kept when resampling the table (occurs after scaling)
"max E/N" : 1000.0, // Optional truncation of maximum E/N kept when resampling the table (happens after scaling)
"num points" : 1000, // Optional number of points kept when resamplnig the table (defaults to 1000)
"spacing" : "exponential", // Optional spcification of table representation. Defaults to 'exponential' but can also be 'linear'
"scale E/N" : 1.0, // Optional scaling of the column containing E/N
"scale alpha/N" : 1.0 // Optional scaling
}
}
Plotting
To include the Townsend coefficients as mesh variables in HDF5 files, include the plot specifier, e.g.
{
"alpha": {
"type": "auto",
"species": "e",
"plot": true
}
}
Plasma species
Plasma species are species that are tracked using either a CDR or Îto solver. The user must specify the following information:
An ID/name for the species.
The species’ charge number.
The solver type, i.e. whether or not it is tracked by a particle or fluid solver.
Whether or not the species is mobile/diffusive. If the species is mobile/diffusive, the mobility/diffusion coefficient must also be specified.
Optionally, the species temperature. If not specified, the temperature will be set to the background gas temperature.
Initial particles for the solvers.
Basic definition
Species are defined by an array of entries in a plasma species JSON field.
Each species must specify the following fields
id(string) For setting the species name.Z(integer) For setting the species’ charge number.solver(string) For specifying whether or not the species is tracked by a CDR or Îto solver. Acceptable values are cdr and ito.mobile(boolean) For specifying whether or not the species is mobile.mobile(boolean) For specifying whether or not the species is diffusive.
An example specification is
"plasma species" :
[
// List of plasma species that are tracked. This is an array of species
// with various identifiers, some of which are always required (id, Z, type, mobile, diffusive) and
// others which are secondary requirements.
{
"id": "e", // Species ID
"Z" : -1, // Charge number
"solver" : "ito", // Solver type. Either 'ito' or 'cdr'
"mobile" : true, // Mobile or not
"diffusive" : true // Diffusive or not
},
{
// Definition of O2+ plasma species.
"id": "O2+", // Species ID
"Z" : 1, // Charge number
"solver" : "cdr", // CDR solver.
"mobile" : false, // Not mobile.
"diffusive" : false // Not diffusive
}
]
Note that the order of appearance of the various species is irrelevant. However, if a species is specified as mobile/diffusive, the user must also specify the corresponding transport coefficients.
Mobility and diffusion coefficients
Mobility and diffusion coefficients are specified by including fields mobility and diffusion that further specifies how the transport coefficients are calculated.
Note that the input to these fields should be fluid transport coefficients.
These fields can be specified in various forms, as shown below.
Constant
To set a constant coefficient, set the type specifier to constant and then assign the value.
For example,
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : -1, // Charge number
"solver" : "ito", // Solver type. Either 'ito' or 'cdr'
"mobile" : true, // Mobile or not
"diffusive" : true // Diffusive or not,
"mobility": {
"type": "constant", // Set constant mobility
"value": 0.02
},
"diffusion": {
"type": "constant", // Set constant diffusion coefficient
"value": 2E-4
}
}
]
Constant \(*N\)
To set a coefficient that is constant vs \(N\), set the type specifier to constant mu*N or constant D*N and then assign the value.
For example,
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : -1, // Charge number
"solver" : "ito", // Solver type. Either 'ito' or 'cdr'
"mobile" : true, // Mobile or not
"diffusive" : true // Diffusive or not,
"mobility": {
"type": "constant mu*N", // Set mu*N to a constant
"value": 1E24
},
"diffusion": {
"type": "constant D*N", // Set D*N to a constant
"value": 5E24
}
}
]
Table vs \(E/N\)
To set the transport coefficients as functions \(f = f\left(E/N\right)\), set the type specifier to table vs E/N and specify the following fields:
fileFor specifying the file containing the input data, which must be organized as column data, e.g.# E/N mu/N 0 1E5 100 1E5 200 1E5
headerFor specifying where in the file one starts reading data. This is an optional argument intended for use with BOLSIG+ output data where the user specifies that the data is contained in the lines below the specified header.E/N columnFor setting which column in the data file contains the values of \(E/N\) (optional, defaults to 0).mu*N columnFor setting which column in the data file contains the values of \(\mu N\) (or alternatively \(DN\) for the diffusion coefficient). This is an optional argument that defaults to 1.E/N scaleOptional scaling of the column containing the \(E/N\) data.mu*N scaleOptional scaling of the column containing the \(\mu N\) data (or alternatively \(DN\) for the diffusion coefficient).min E/NOptional truncation of the table representation of the data (applied after scaling).max E/NOptional truncation of the table representation of the data (applied after scaling).num pointsNumber of data points in internal table representation (optional, defaults to 1000).spacingSpacing in internal table representation. Can belinearorexponential. This is an optional argument that defaults toexponential.dumpA string specifier for writing the table representation of \(E/N, \mu N\) to file.
An example JSON specification that uses a BOLSIG+ output file for parsing the data for the mobility is
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : -1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : true, // Mobile
"diffusive" : false, // Not diffusive
"mobility" : {
"type" : "table vs E/N", // Specification of tabulated mobility lookup method
"file" : "bolsig_air.dat", // File containg the mobility data
"dump" : "debug_mobility.dat", // Optional argument for dumping table to disk (useful for debugging)
"header" : "E/N (Td)\tMobility *N (1/m/V/s)", // Line immediately preceding the colum data
"E/N column" : 0, // Column containing E/N
"mu*N column" : 1, // Column containing mu*N
"min E/N" : 1, // Minimum E/N kept when resampling table
"max E/N" : 2E6, // Maximum E/N kept when resampling table
"points" : 1000, // Number of grid points kept when resampling the table
"spacing" : "exponential", // Grid point spacing when resampling the table
"E/N scale" : 1.0, // Optional argument for scaling mobility coefficient
"mu*N scale" : 1.0 // Optional argument for scaling the E/N column
}
}
]
Tip
The parser for the diffusion coefficient is analogous; simply replace mu*N by D*N.
Parallel diffusion
ItoKMCJSON can limit diffusion against the electric field (or strictly speaking, the particle drift direction).
The species specification permits a flag diffusion model that defauls to isotropic diffusion.
However, it is possible to set this to other types of diffusion models.
Currently, the available options are
isotropic, which sets an isotropic diffusion model.forward isotropic, which uses isotropic diffusion but does not permit diffusion against the drift direction (this component is set to zero).
The code block below shows an example:
"plasma species" :
[
{
"id": "e",
"Z" : -1,
"solver" : "ito",
"mobile" : true,
"diffusive" : true,
"diffusion model": "forward isotropic"
}
]
Warning
Changing the diffusion model to anything other than isotropic can have unintended physical side effects. Although this functionality can enhance stability in e.g. cathode sheaths, it can have other unintended consequences.
Temperature
The species temperature is only parametrically attached to the species (i.e., not solved for), and can be specified by the user.
Note that the temperature is mostly relevant for transport coefficients (e.g., reaction rates).
The temperature can be specified through the temperature field for each species, and various forms of specifying this is available.
Important
If the specifies temperature is not specified by the user, it will be set equal to the background gas temperature.
Background gas
To set the temperature equal to the background gas temperature, set the type specifier field to gas.
For example:
"plasma species" :
[
{
"id": "O2+", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"temperature": { // Specify temperature
"type": "gas" // Temperature same as gas
}
}
]
Constant
To set a constant temperature, set the type specifier to constant and then specify the temperature.
For example:
"plasma species" :
[
{
"id": "O2+", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"temperature": { // Specify temperature
"type": "constant", // Constant temperature
"value": 300 // Temperature in Kelvin
}
}
]
Table vs \(E/N\)
To set the species temperature as a function \(T = T\left(E/N\right)\), set the type specifier to table vs E/N and specify the following fields:
fileFor specifying the file containing the input data, which must be organized as column data versus the mean energy, e.g.# E/N energy (eV) 0 1 100 2 200 3
headerFor specifying where in the file one starts reading data. This is an optional argument intended for use with BOLSIG+ output data where the user specifies that the data is contained in the lines below the specified header.E/N columnFor setting which column in the data file contains the values of \(E/N\) (optional, defaults to 0).eV columnFor setting which column in the data file contains the energy vs \(E/N\). This is an optional argument that defaults to 1.E/N scaleOptional scaling of the column containing the \(E/N\) data.eV scaleOptional scaling of the column containing the energy (in eV).min E/NOptional truncation of the table representation of the data (applied after scaling).max E/NOptional truncation of the table representation of the data (applied after scaling).num pointsNumber of data points in internal table representation (optional, defaults to 1000).spacingSpacing in internal table representation. Can belinearorexponential. This is an optional argument that defaults toexponential.dumpA string specifier for writing the table representation of \(E/N, eV\) to file.
An example JSON specification that uses a BOLSIG+ output file for parsing the data for the mobility is
"plasma species" :
[
{
"id": "O2+", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : true, // Not mobile
"diffusive" : false, // Not diffusive
"temperature": { // Specification of temperature
"type": "table vs E/N", // Tabulated
"file": "bolsig_air.dat", // File name
"dump": "debug_temperature.dat", // Dump to file
"header" : "E/N (Td)\tMean energy (eV)", // Header preceding data
"E/N column" : 0, // Column containing E/N
"eV column" : 1, // Column containing the energy
"min E/N" : 10, // Truncation of table
"max E/N" : 2E6, // Truncation of table
"E/N scale": 1.0, // Scaling of input data
"eV scale": 1.0, // Scaling of input data
"spacing" : "exponential", // Table spacing
"points" : 1000 // Number of points in table
}
}
]
Initial particles
Initial particles for species are added by including an initial particles field.
The field consists of an array of JSON entries, where each array entry represents various ways of adding particles.
Important
The initial particles field is incrementing, each new entry will add additional particles.
For example, to add two particles with particle weights of 1, one may specify
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"single particle": { // Single physical particle at (0,0,0)
"position": [0, 0, 0],
"weight": 1.0
}
},
{
"single particle": { // Single physical particle at (0,1,0)
"position": [0, 1, 0],
"weight": 1.0
}
}
]
}
]
The various supported ways of additional initial particles are discussed below.
Important
If a solver is specified as a CDR solver, the initial particles are deposited as densities on the mesh using an NGP scheme.
Single particle
Single particles are placed by including a single particle JSON entry.
The user must specify
positionThe particle position.weightThe particle weight.
For example:
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"single particle": { // Single physical particle at (0,0,0)
"position": [0, 0, 0],
"weight": 1.0
}
}
]
}
]
Uniform distribution
Particles can be randomly drawn from a uniform distribution (a rectangular box in 2D/3D) by including a uniform distribution.
The user must specify
low cornerThe lower left corner of the box.high cornerThe lower left corner of the box.num particlesNumber of computational particles that are drawn.weightParticle weights.
For example:
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"uniform distribution" : { // Particles inside a box
"low corner" : [ -0.04, 0, 0 ], // Lower-left physical corner of sampling region
"high corner" : [ 0.04, 0.04, 0 ], // Upper-right physical corner of sampling region
"num particles": 1000, // Number of computational particles
"weight" : 1 // Particle weights
}
}
]
}
]
Sphere distribution
Particles can be randomly drawn inside a circle (sphere in 3D) by including a sphere distribution.
The user must specify
centerThe sphere center.radiusThe sphere radius.num particlesNumber of computational particles that are drawn.weightParticle weights.
For example:
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"sphere distribution" : { // Particles inside sphere
"center" : [ 0, 7.5E-3, 0 ], // Sphere center
"radius" : 1E-3, // Sphere radius
"num particles": 1000, // Number of computational particles
"weight" : 1 // Particle weights
}
}
]
}
]
Gaussian distribution
Particles can be randomly drawn from a Gaussian distribution by including a gaussian distribution.
The user must specify
centerThe sphere center.radiusThe sphere radius.num particlesNumber of computational particles that are drawn.weightParticle weights.
For example:
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"gaussian distribution" : { // Particles drawn from a gaussian
"center" : [ 0, 7.5E-3, 0 ], // Center
"radius" : 1E-3, // Radius
"num particles": 1000, // Number of computational particles
"weight" : 1 // Particle weights
}
}
]
}
]
List/file
Particles can be read from a file by including a list specifier.
The particles must be organized as rows containing the coordinate positions and weights, e.g.
# x y z w
0 0 0 1
The user must specify
fileFile name.xColumn containing the \(x\) coordinates (optional, defaults to 0).yColumn containing the \(y\) coordinates (optional, defaults to 1).zColumn containing the \(z\) coordinates (optional, defaults to 2).wColumn containing the particle weight (optional, defaults to 3).
For example:
"plasma species" :
[
{
"id": "e", // Species ID
"Z" : 1, // Charge number
"solver" : "ito", // Solver type.
"mobile" : false, // Not mobile
"diffusive" : false, // Not diffusive
"initial particles": [ // Initial particles
{
"list": {
"file": "initial_particles.dat", // File containing the particles
"x column": 0,
"y column": 1,
"z column": 2,
"w column": 3
}
}
]
}
]
Initial densities
One may include an initial density by setting the initial density parameter.
For example:
"plasma species" :
[
{
"id": "e",
"Z" : 1,
"solver" : "cdr",
"mobile" : true,
"diffusive" : true,
"initial density": 1E10
}
]
If the species is defined as a particle species, computational particles will be generated within each grid so that the density is approximately as specified.
Photon species
Photon species are species that are tracked using a radiative transfer solver.
These are defined by an array of entries in a photon species JSON entry, and must define the following information:
An ID/name for the species.
The absorption coefficient for the photon type.
Basic definition
A basic definition of a single photon species is
"photon species":
[
{
"id": "Y", // Photon species id. Must be unique
"kappa": { // Specification of absorption coefficient.
"type": "constant", // Specify constant absorption coefficient
"value": 1E4 // Value of the absorption coefficeint
}
}
]
Multiple photon species are added by appending with more entries, e.g.
"photon species":
[
{
"id": "Y1", // Photon species id. Must be unique
"kappa": { // Specification of absorption coefficient.
"type": "constant", // Specify constant absorption coefficient
"value": 1E4 // Value of the absorption coefficeint
}
},
{
"id": "Y2", // Photon species id. Must be unique
"kappa": { // Specification of absorption coefficient.
"type": "constant", // Specify constant absorption coefficient
"value": 1E4 // Value of the absorption coefficeint
}
}
]
Absorption coefficient
Constant
In order to set a constant absorption coefficient, set type to constant and then specify the value field.
For example:
"photon species":
[
{
"id": "Y",
"kappa": {
"type": "constant",
"value": 1E4
}
}
]
stochastic A
The stochastic A specifier computes a random absorption length from the expression
where \(p_i\) is the partial pressure of some species \(i\), and \(p_i\chi_{\textrm{min}}\) and \(p_i\chi_{\textrm{max}}\) are minimum and maximum absorption lengths on the frequency interval \(f\in[f_1,f_2]\).
The user must specify \(\chi_{\textrm{min}}\), \(\chi_{\textrm{max}}\), \(f_1\), \(f_2\), and the background species used when computing \(p_i\).
This is done through fields f1, f_2, chi min, chi max, and neutral.
For example:
"photon species":
[
{
"id": "Y",
"kappa": {
"type": "stochastic A",
"f1": 2.925E15,
"f2": 3.059E15,
"chi min": 2.625E-2,
"chi max": 1.5,
"neutral": "O2"
}
}
]
stochastic B
The stochastic B specifier computes a random absorption length from the expression
where \(p_i\) is the partial pressure of some species \(i\), and \(p_i\chi_{\textrm{min}}\) and \(p_i\chi_{\textrm{max}}\) are minimum and maximum absorption lengths, and \(u\) is a random number between 0 and 1.
Note that that this is just a simpler way of using the stochastic A specifier above.
The user must specify \(\chi_{\textrm{min}}\), \(\chi_{\textrm{max}}\), and the background species used when computing \(p_i\).
This is done through fields chi min, chi max, and neutral.
For example:
"photon species":
[
{
"id": "Y",
"kappa": {
"type": "stochastic B",
"chi min": 2.625E-2,
"chi max": 1.5,
"neutral": "O2"
}
}
]
Plasma reactions
Plasma reactions are always specified stoichiometrically in the form \(S_A + S_B + \ldots \xrightarrow{k} S_C + S_D + \ldots\). The user must supply the following information:
Species involved on the left-hand and right-hand side of the reaction.
How to compute the reaction rate.
Optionally specify gradient corrections to the reaction.
Optionally specify whether or not the reaction rate will be written to file.
Important
The JSON interface expects that the reaction rate \(k\) is the same as in the reaction rate equation. Internally, the rate is converted to a format that is consistent with the KMC algorithm.
Plasma reactions should be entered in the JSON file using an array (the order of reactions does not matter).
Basic reaction example specifiers
"plasma reactions": [
{
// First reaction goes here,
},
{
// Next reaction goes here,
}
]
Reaction specifiers
Each reaction must include an entry reaction in the list of reactions.
This entry must consist of a single string with a left-hand and right-hand sides separated by ->.
For example:
"plasma reactions": [
{
"reaction": "e + N2 -> e + e + N2+"
}
]
which is equivalent to the reaction \(\text{e} + \text{N}_2 \rightarrow \text{e} + \text{e} + \text{N}_2^+\).
Each species must be separated by whitespace and a addition operator.
For example, the specification A + B + C will be interpreted as a reaction \(\text{A} + \text{B} + \text{C}\) but the specification A+B + C will be interpreted as a reaction between one species A+B and another species C .
Internally, both the left- and right-hand sides are checked against background and plasma species. The program will perform a run-time exit if the species are not defined in these lists. Note that the right-hand side can additonal contain photon species.
Tip
Products not not defined as a species can be enclosed by parenthesis ( and ).
The reaction string generally expects all species on each side of the reaction to be defined.
It is, however, occasionally useful to include products which are not defined.
For example, one may with to include the reaction \(\text{e} + \text{O}_2^+ \rightarrow \text{O} +\text{O}\) but not actually track the species \(\text{O}\).
The reaction string can then be defined as the string e + O2+ -> but as it might be difficult for users to actually remember the full reaction, we may also enter it as e + O2+ -> (O) + (O).
Rate calculation
Reaction rates can be specified using multiple formats (constant, tabulated, function-based, etc).
To specify how the rate is computed, one must specify the type keyword in the JSON entry.
Constant
To use a constant reaction rate, the type specifier must be set to constant and the reaction rate must be specified through the value keyword.
A JSON specification is e.g.
"plasma reactions": [
{
"reaction": "e + N2 -> e + e + N2+" // Specify reaction
"type": "constant", // Reaction rate is constant
"value": 1.E-30 // Reaction rate
}
]
Function vs E/N
Some rates given as a function \(k = k\left(E/N\right)\) are supported, which are outlined below.
Important
In the below function-based rates, \(E/N\) indicates the electric field in units of Townsend.
function E/N exp A
This specification is equivalent to a fluid rate
The user must specify the constants \(c_i\) in the JSON file. An example specification is
"plasma reactions": [
{
"reaction": "A + B -> " // Example reaction string.
"type": "function E/N exp A", // Function based rate.
"c1": 1.0, // c1-coefficient
"c2": 1.0, // c2-coefficient
"c3": 1.0, // c3-coefficient
"c4": 1.0, // c4-coefficient
"c5": 1.0 // c5-coefficient
}
]
Temperature-dependent
Some rates can be given as functions \(k = k(T)\) where \(T\) is some temperature.
function T A
This specification is equivalent to a fluid rate
Mandatory input variables are \(c_1, c_2\), and the specification of the species corresponding to \(T_i\). This can correspond to one of the background species. An example specification is
"plasma reactions": [
{
"reaction": "A + B -> " // Example reaction string.
"type": "function T A", // Function based rate.
"c1": 1.0, // c1-coefficient
"c2": 1.0, // c2-coefficient
"T": "A" // Which species temperature
}
]
function T1T2 A
This specification is equivalent to a fluid rate
Mandatory input variables are \(c_1, c_2\), and the specification of the species corresponding to \(T_1\) and \(T_2\). This can correspond to one of the background species. An example specification is
"plasma reactions": [
{
"reaction": "A + B -> " // Example reaction string.
"type": "function T1T2 A", // Function based rate.
"c1": 1.0, // c1-coefficient
"c2": 1.0, // c2-coefficient
"T1": "A", // Which species temperature for T1
"T2": "B" // Which species temperature for T2
}
]
Townsend rates
Reaction rates can be specified to be proportional to \(\alpha\left|\mathbf{v}_i\right|\) where \(\alpha\) is the Townsend ionization coefficient and \(\left|\mathbf{v}_i\right|\) is the drift velocity for some species \(i\). This type of reaction is normally encountered when using simplified chemistry, e.g.
which is representative of the reaction \(S_i \rightarrow S_i + S_i\). To specify a Townsend rate constant, one can use the following:
alpha*vfor setting the rate constant proportional to the Townsend ionization rate.eta*vfor setting the rate constant proportional to the Townsend attachment rate.
One must also specify which species is associated with \(\left|\mathbf{v}\right|\) by specifying a species flag. A complete JSON specification is
{
"plasma reactions":
[
{
"reaction": "e -> e + e + M+", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e" // Species for v
}
]
}
Warning
When using the Townsend coefficients for computing the rates, one should normally not include any neutrals on the left hand side of the reaction.
The reason for this is that the Townsend coefficients \(\alpha\) and \(\eta\) already incorporate the neutral density.
By specifying e.g. a reaction string \(\text{e} + \text{N}_2 \rightarrow \text{e} + \text{e} + \text{N}_2^+\) together with the alpha*v or eta*v specifiers, one will end up multiplying in the neutral density twice, which will lead to an incorrect rate.
Tabulated vs E/N
Scaling
Reactions can be scaled by including a scale field in the JSON entry.
This will scale the reaction coefficient by the input factor, e.g. modify the rate constant as
where \(\nu\) is the scaling factor. This is useful when scaling reactions from different units, or for completely turning off some input reactions. An example JSON specification is
"plasma reactions":
[
{
"reaction": "e -> e + e + M+", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"scale": 0.0 // Scaling factor
}
]
Tip
If one turns off a reaction by setting scale to zero, the KMC algorithm will still use the reaction but no reactants/products are consumed/produced.
Efficiency
Reactions efficiencies can be modified in the same way as one do with the scale field, e.g. modify the rate constant as
where \(\nu\) is the reaction efficiency. Specifications of the efficiency can be achieved in the forms discussed below.
Constant efficiency
An example JSON specification for a constant efficiency is:
"plasma reactions":
[
{
"reaction": "e -> e + e + M+",
"efficiency": 0.5
}
]
Efficiency vs E/N
An example JSON specification for an efficiency computed versus \(E/N\) is
"plasma reactions":
[
{
"reaction": "e -> e + e + M+",
"efficiency vs E/N": "efficiencies.dat"
}
]
where the file efficiencies.dat must contain two-column data containing values of \(E/N\) along the first column and efficiencies along the second column.
An example file is e.g.
0 0
100 0.1
200 0.3
500 0.8
1000 1.0
This data is then internally convered to a uniformly spaced lookup table (see LookupTable1D).
Efficiency vs E
An example JSON specification for an efficiency computed versus \(E\) is
"plasma reactions":
[
{
"reaction": "e -> e + e + M+",
"efficiency vs E": "efficiencies.dat"
}
]
where the file efficiencies.dat must contain two-column data containing values of \(E\) along the first column and efficiencies along the second column.
This method follows the same as efficiency vs E/N where the data in the input file is put in a lookup table.
Quenching
Reaction quenching can be achieved in the following forms:
Pressure-based
The reaction rate can modified by a factor \(p_q/\left[p_q + p\left(\mathbf{x}\right)\right]\) where \(p_q\) is a quenching pressure and \(p\left(\mathbf{x}\right)\) is the gas pressure. This will modify the rate as
An example JSON specification is
"plasma reactions":
[
{
"reaction": "e + N2 -> e + N2 + Y", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"quenching pressure": 4000.0 // Quenching pressure
}
]
Important
The quenching pressure must be specified in units of Pascal.
Rate-based
The reaction rate can be modified by a factor \(k_r/\left[k_r + k_p + k_q\right]\). The intention behind this scaling is that reaction \(r\) occurs only if it is not predissociated (by rate \(k_p\)) or quenched (by rate \(k_q\)). Such processes can occur, for example, in excited molecules. This will modifiy the rate constant as
where the user will specifiy \(k_r\), \(k_p\), and \(k_q/N\).
The JSON specification must contain quenching rates, for example:
"plasma reactions":
[
{
"reaction": "e + N2 -> e + N2 + Y", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"quenching rates": { // Specify relevant rates
"kr": 1E9,
"kp": 1E9,
"kq/N": 0.0
}
}
]
Gradient correction
In LFA-based models it is frequently convenient to include energy-corrections to the ionization rate. In this case one modifies the rate as
To include this correction one may include a specifier gradient correction in the JSON entry, in which case one must also enter the species \(n_i\).
A JSON specification that includes this
"plasma reactions":
[
{
"reaction": "e -> e + e + M+", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"gradient correction": "e" // Specify gradient correction using species "e"
}
]
This is equivalent to a source term
One can recognize this term as a regular electron impact ionization source term (typically written as \(\alpha \mu n_e E\)). With the gradient correction, the ionization source term is essentially computed using the full electron flux, i.e., including the diffusive electron flux. Note that the full electron flux has a preferential direction, and the physical interpretation of this direction is that if there is net diffusion against the electric field, electrons lose energy and the impact ionization source term is correspondingly lower.
Understanding reaction rates
The JSON specification takes fluid rates as input to the KMC algorithm. Note that subsequent application of multiple scaling factors are multiplicative. For example, the specification
"plasma reactions":
[
{
"reaction": "e -> e + e + M+", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"gradient correction": "e" // Specify gradient correction using species "e"
"quenching pressure": 4000, // Quenching pressure
"efficiency": 0.1 // Reaction efficiency
}
]
is equivalent to the rate constant
Internally, conversions between the fluid rate \(k\) and the KMC rate \(c\) are made. The conversion factors depend on the reaction order, and ensure consistency between the KMC and fluid formulations.
Plotting rates
Reaction rates can be plotted by including an optional plot specifier.
If the specifier included, the reaction rates will be included in the HDF5 output.
The user can also include a description string which will be used when plotting the reaction.
The following two specifiers can be included:
plot(true/false) for plotting the reaction.description(string) for naming the reaction in the HDF5 output.
If the plot description is left out, the reaction string will be used as a variable name in the plot file. A JSON specification that includes these
"plasma reactions":
[
{
"reaction": "e -> e + e + M+", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"plot": true, // Plot this reaction
"description": "Townsend ionization" // Variable name in HDF5
}
]
Printing rates
ItoKMCJSON can print the plasma reaction rates (including photon-generating ones) to file.
This is done through an optional input argument print_rates in the input file (not the JSON specification).
The following options are supported:
ItoKMCJSON.print_rates = true
ItoKMCJSON.print_rates_minEN = 1
ItoKMCJSON.print_rates_maxEN = 1000
ItoKMCJSON.print_rates_num_points = 200
ItoKMCJSON.print_rates_spacing = exponential
ItoKMCJSON.print_rates_filename = fluid_rates.dat
ItoKMCJSON.print_rates_pos = 0 0 0
Setting ItoKMCJSON.print_rates to true in the input file will write all reaction rates as column data \(E/N, k(E/N)\).
Here, \(k\) indicates the fluid rate, so for a reaction \(A + B + C \xrightarrow{k}\ldots\) it will include the rate \(k\).
Reactions are ordered identical to the order of the reactions in the JSON specification.
This feature is mostly used for debugging or development efforts.
Time step calculation
The user can manually specify which reactions are to be used when computing a chemistry time step \(\Delta t\), as discussed in Time step calculation.
To enable a time step calculation, specify the include_dt_calc flag in the reaction specifier, as shown below:
"plasma reactions":
[
{
"reaction": "e -> e + e + M+",
"type": "alpha*v",
"species": "e",
"include_dt_calc": true
}
]
Tip
It is normally sufficient to enable \(\Delta t\) calculations for the ionizing reactions.
Particle placement
Specification of secondary particle placement is done through the JSON file by specifying the particle placement field.
Currently, new particles may be placed on the centroid, uniformly distributed in the grid cell, or placed randomly in the downstream region of some user-defined species.
The method is specified through the method specifier, which must either be centroid, random, or downstream.
If specifying downstream, one must also include a species specifier (which must correspond to one of the plasma species).
The following three specifies are all valid:
"particle placement":
{
"method": "centroid"
}
"particle placement":
{
"method": "random"
}
"particle placement":
{
"method": "downstream",
"species": "e"
}
Photoionization
Photoionization reactions are specified by including a photoionization array of JSON entries that specify each reaction.
The left-hand side of the reaction must contain a photon species (plasma and background species can be present but will be ignored), and the right-hand side can consist of any species except other photon species.
Each entry must contain a reaction string specifier and optionally also an efficiency (which defaults to 1).
Assume that photons are generated through the reaction
"plasma reactions":
[
{
"reaction": "e + N2 -> e + N2 + Y", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"quenching pressure": 4000.0 // Quenching
"efficiency": 0.6 // Excitation events per ionization event
"scale": 0.1 // Photoionization events per absorbed photon
}
]
where we assume an excitation efficiency of \(0.6\) and photoionization efficiency of \(0.1\). An example photoionization specification is then
"photoionization":
[
{
"reaction": "Y + O2 -> e + O2+"
}
]
Note that the efficiency of this reaction is 1, i.e. the photoionization probabilities was pre-evaluated. As discussed in ItoKMCPhysics, we can perform late evaluation of the photoionization probability by specifically including a efficiency. In this case we modify the above into
"plasma reactions":
[
{
"reaction": "e + N2 -> e + N2 + Y", // Reaction string
"type": "alpha*v", // Rate is alpha*v
"species": "e", // Species for v,
"quenching pressure": 4000.0 // Quenching
"efficiency": 0.6 // Excitation events per ionization event
}
],
"photoionization":
[
{
"reaction": "Y + O2 -> e + O2+",
"efficiency": 0.1
},
{
"reaction": "Y + O2 -> (null)",
"efficiency": 0.9
}
]
where a null-absorption model has been added for the photon absorption. When multiple pathways are specified this way, and they have probabilities \(\xi_1, \xi_2, \ldots\), the reaction is stochastically determined from a discrete distribution with relative probabilities \(p_i = \xi_i/(\xi_1 + \xi_2+\ldots)\).
Important
The null-reaction model is not automatically added when using late evaluation of the photoionization probability.
Secondary emission
Secondary emission is supported for particles, including photons, but not for fluid species.
Specification of secondary emission is done separately on dielectrics and electrodes by specifying JSON entries electrode emission and dielectric emission.
The internal specification for these are identical, so all examples below use dielectric emission.
Secondary emission reactions are specified similarly to photoionization reactions.
However, the left-hand side must consist of a single species (photon or particle), while the right-hand side can consist of multiple species.
Wildcards @ are supported, as is the (null) species which enables null-reactions, as further discussed below.
An example JSON specification that enables secondary electron emission due to photons and ion impact is
"dielectric emission":
[
{
"reaction": "Y -> e",
"efficiency": 0.1
},
{
"reaction": "Y -> (null)",
"efficiency": 0.9
}
]
The reaction field specifies which reaction is triggered: The left hand side is the primary (outgoing) species and the left hand side must contain the secondary emissions.
The left-hand side can consist of either photons (e.g., Y) or particle (e.g., O2+) species.
The efficiency field specifies the efficiency of the reaction.
When multiple reactions are specified, we randomly sample the reaction according to a discrete distribution with probabilities
where \(\nu_i\) are the efficiencies.
Note that the efficiencies do not need to sum to one, and if only a single reaction is specified the efficiency specifier has not real effect.
The above reactions include the null reaction in order to ensure that the correct secondary emission probability is reached, where the (null) specifier implies that no secondary emission takes place.
In the above example the probability of secondary electron emission is 0.1, while the probability of a null-reaction (outgoing particle is absorbed without any associated emission) is 0.9.
The above example can be compressed by using a wildcard and an efficiencies array as follows:
"dielectric emission":
[
{
"reaction": "Y -> @",
"@": ["e", "(null)"],
"efficiencies": [1,9]
}
]
where for the sake of demonstration the efficiencies are set to 1 and 9 (rather than 0.1 and 0.9). This has no effect on the probabilities \(p(i)\) given above.
Field emission
Field emission for a specified species is supported by including a field emission specifier.
Currently supported expressions are:
Fowler-Nordheim emission:
\[\begin{split}J(E) &= \frac{a F^2}{\phi}\exp\left(-v(f) b \phi^{3/2} F\right),\\ F &= \left(\beta E\right)\times 10^9,\\ a &= 1.541434\times 10^{-6}\,\text{AeV}/\text{V}^2, \\ b &= 6.830890\,\text{eV}^{-3/2}\text{V}\text{nm}^{-1},\\ v(f) &= 1 - f + \left(1/6\right)f\log(f),\\ f &\approx 1.439964\,\text{eV}^2\text{V}^{-1}\text{nm}\times \frac{F}{\phi^2}.\end{split}\]Here, \(\phi\) is the work function (in electron volts) and \(\beta\) is an empirical factor that describes local field amplifications. Note that the above expression gives \(J\) in units of \(\text{A}/\text{nm}^2\).
Schottky emission:
\[\begin{split}J(E) &= \lambda A_0 T^2\exp\left(-\frac{\left(\phi-\Delta\phi\right)q_\text{e}}{k_{\text{B}}T}\right), \\ F &= \beta E, \\ A_0 &\approx 1.201736\times 10^6\,\text{A}\text{m}^{-2}\text{K}^{-2}, \\ \Delta \phi &= \sqrt{\frac{q_\text{e} F}{4\pi\epsilon_0}}.\end{split}\]Here, \(T\) is the cathode temperature and \(\phi\) is the work function (in electron volts). The value \(\lambda\) is typically around \(0.5\). As for the Fowler-Nordheim equation, a factor that describes local field amplifications is given in \(\beta\).
Warning
The expressions above both use a correction factor \(\beta\) that describes local field amplifications.
Note, however, that the number of emitted electrons is proportional to the area of the emitter, and there are no current models in chombo-discharge that can compute this effective area.
Below, we show an example that includes both Schottky and Fowler-Nordheim emission
"field emission":
[
{
"species": "e",
"surface": "electrode",
"type": "fowler-nordheim",
"work": 5.0,
"beta": 1
},
{
"species": "e",
"surface": "electrode",
"type": "schottky",
"lambda": 0.5
"temperature": 300,
"work": 5.0,
"beta": 1,
}
]
Warnings and caveats
Higher-order reactions
Usually, many rate coefficients depend on the output of other software (e.g., BOLSIG+) and the scaling of rate coefficients is not immediately obvious. This is particularly the case for three-body reactions with BOLSIG+ that may require scaling before running the Boltzmann solver (by scaling the input cross sections), or after running the Boltzmann solver, in which case the rate coefficients themselves might require scaling. In any case the user should investigate the cross-section file that BOLSIG+ uses, and figure out the required scaling.
Important
For two-body reactions, e.g. \(A + B\rightarrow \varnothing\) the rate coefficient must be specified in units of \(\textrm{m}^3\textrm{s}^{-1}\), while for three-body reactions \(A + B + C\rightarrow\varnothing\) the rate coefficient must have units of \(\textrm{m}^6\textrm{s}^{-1}\).
For three-body reactions the units given by BOLSIG+ in the output file may or may not be incorrect (depending on whether or not the user scaled the cross sections).
Townsend coefficients
Townsend coefficients are not fundamentally required for specifying the reactions, but as with the higher-order reactions some of the output rates for three-body reactions might be inconsistently represented in the BOLSIG+ output files. For example, some care might be required when using the Townsend attachment coefficient for air when the reaction \(\textrm{e} + \textrm{O}_2 + \textrm{O}_2\rightarrow \textrm{O}_2^- + \textrm{O}_2\) is included because the rate constant might require proper scaling after running the Boltzmann solver, but this scaling is invisible to the BOLSIG+’s calculation of the attachment coefficient \(\eta/N\).
Warning
The JSON interface does not guard against inconsitencies in the user-provided chemistry, and provision of inconsistent \(\eta/N\) and attachment reaction rates are quite possible.
Tips and tricks
As with fluid drift-diffusion models, numerical instabilities can also occur due to unbounded growth in the plasma density. This is a process which has been linked both to the local field approximation and also to the presence of numerical diffusion. Simulations that fail to stabilize, i.e., where the field strength diverges, may benefit from the following stabilizing features:
Turn off parallel diffusion.
The JSON interface class permits various diffusion models, some of which turn off backward diffusion completely. Note that this also modifies the amount of physical diffusion in this direction.
Use gradient corrections.
As discussed earlier, using a gradient correction can help limit non-physical ionization due to backwards-diffusing electrons.
Use downstream particle placement.
Because the KMC algorithm solves for the number of particles in a grid cell, distributing new particles uniformly over a grid cell can lead to numerical diffusion where secondary electrons are placed in the wake of primary electrons. Using downstream particle placement often leads to slightly more stable simulations.
Describe the primary species using an Ito solver.
Similar to the point above, using a fluid solver for the ions may lead to upstream placement of the resulting positive charge.
Use kd-tree particle merging.
Currently particle merging strategies are reinitialization and merging based on bounding volume hierarchies. If using reinitialization, new particles can be generated in the wake of old ones and can thus upset the charge distribution and cause numerical backwards diffusion (e.g., of electrons).
Numerically limit reaction rates.
It is possible to specify that reaction rates will be numerically limited so that the rate does not exceed a specified threshold of \(\Delta t^{-1}\). This is done through the JSON interface by setting
limit max k*dtto some value. Note that this changes the physics of the model, but usually enhances stability at larger time steps. An example is given below:
"plasma reactions": [
{
"reaction": "e + N2 -> e + e + N2+"
"type": "constant",
"value": 1.E-12,
"limit max k*dt" : 2.0
}
]
This will limit the rate such that \(k \left[\text[N]_2\right]\Delta t = 2\). I.e., all background species are first absorbed into the rate calculation before the rate is limited. We point out that limiting is not possible if both species on the left hand side are solver variables.
Important
The above features have been implemented in order to push the algorithm towards coarser grids and larger time steps. It is essential that the user checks that the model converges when these features are applied.
Example programs
Example programs that use the Îto-KMC module are given in
$DISCHARGE_HOME/Exec/Examples/ItoKMC/AirBasicfor a basic streamer discharge in atmospheric air.$DISCHARGE_HOME/Exec/Examples/ItoKMC/AirDBDfor a streamer discharge over a dielectric.
Setting up a new problem
New problems that use the ItoKMC physics model are best set up by using the Python tools provided with the module.
A full description is available in the README.md file contained in the folder:
# Physics/ItoKMC
This physics module solves the Ito-KMC formulation for electric discharges.
The folders contain:
* **PlasmaModels** Implementations of various plasma models.
* **TimeSteppers** Implementations of the various time integration algorithms.
* **CellTaggers** Implementations of various cell taggers.
## Setting up a new application
To set up a new problem, use the Python script. For example:
```shell
python setup.py -base_dir=/home/foo/MyApplications -app_name=MyPlasmaModel -geometry=Vessel -physics=ItoKMCJSON
```
To install within chombo-discharge:
```shell
python setup.py -base_dir=$DISCHARGE_HOME/MyApplications -app_name=MyPlasmaModel -geometry=Vessel -physics=ItoKMCJSON
```
The application will then be installed to $DISCHARGE_HOME/MyApplications/MyPlasmaApplication.
The user will need to modify the geometry and set the initial conditions through the inputs file.
## Modifying the application
Users are free to modify this application, e.g. adding new initial conditions and flow fields.
To see the list of available options type
cd $DISCHARGE_HOME/Physics/ItoKMC
python setup.py --help