Expand description
Event-by-event electron transport in a layered stack, on precomputed cross-section tables, with secondary electrons and surface barriers.
This is the low-energy electron Monte Carlo loop of Kieft and Bosch,
J. Phys. D: Appl. Phys. 41, 215310 (2008), doi:10.1088/0022-3727/41/21/215310
(the scheme on which Nebula is built): an electron at (r, direction, E)
in a layer flies a free path drawn from the total inverse mean free path
(the sum of the elastic and inelastic CrossSectionTable rates of the
layer at E), then picks the elastic or the inelastic channel with
probability proportional to the two rates, samples the polar angle θ or
the energy loss W from that channel’s inverse CDF, and updates its state.
An electron ends when E falls below the stopping threshold or it leaves
the target. The loop itself is written from the paper; the secondary
electron kinematics (crate::electron::secondary) and the potential step
(crate::electron::boundary) are ported from Nebula with attribution.
§Targets other than a stack
Transport<G> is generic over Geometry; Transport::new and
Transport::with_band_structures build the layered Transport<Stack>,
Transport::on_geometry and
Transport::on_geometry_with_band_structures any other geometry (for
example a crate::geometry::VoxelGrid). Tables, band parameters,
channels and thresholds are indexed by material
(Geometry::material_index), and ElectronState::layer is the
electron’s material index (the layer index, for a stack). The flight uses
Geometry::flight: a face between two regions of one material is not an
event and the free path is not redrawn there. A primary starts at
Geometry::entry_point. An electron leaving through a lateral face of a
voxel grid ends with Fate::Escaped(Face::Side), whatever the
EscapeRule. The Boundary::Interface indices are region indices.
The step barrier is applied to the x component of the direction, the
face normal of a stack only, so the on_geometry constructors accept
BoundaryModel::Transparent only. For a stack the results are those of
before the generalisation, bit for bit.
§Model
- Free flight.
s = -ln(1 - u) / (λ_el⁻¹ + λ_inel⁻¹),uuniform in[0, 1). Rates are interpolated linearly in energy on each table’s own grid and held constant beyond its ends. - Layer boundaries are crossed exactly. If the drawn path is longer than the distance to the layer face along the flight direction, the electron moves to the face (its depth is set to the face value exactly), meets the face (below) and redraws the path with the rates of the layer it is then in. This is exact because the free path is memoryless.
- Elastic collision.
θfrom the table, azimuth uniform on[0, 2π); the energy is unchanged (no recoil energy). - Inelastic collision.
Wfrom the table (clamped toE); the energy becomesE - W. WithSecondaryModel::Offthe flight direction is unchanged and nothing else happens. WithSecondaryModel::KieftBoschWis further clamped toE - E_F(the primary cannot end below the Fermi level, as in Nebula’s loss table), and the event may liberate a secondary electron (energyE_F + W - B, direction by the Ivanchenko method) and deflect the primary; seecrate::electron::secondary. The stopping threshold of every layer must then exceed its Fermi energy, or secondaries would multiply without end. - Insulator channels (opt-in per layer). A layer given
InsulatorChannelsthroughTransport::with_insulator_channelsadds up to three more rates to the total: Fröhlich LO-phonon emission and absorption, and polaron trapping (models and sources incrate::electron::phonon). A phonon event changes the energy by∓ħωand deflects the electron byθdrawn from the Fröhlich angular distribution, azimuth uniform. A trapped electron deposits all of its remaining energy where it is trapped and its history ends withFate::PolaronTrapped. Layers default to no insulator channel, which is the right choice for a metal; the per-layer choice is inLayerMetadata. - Inner-shell channels (opt-in per layer). A layer given
ShellChannelTables throughTransport::with_inner_shellsadds one rate per shellj, read from that shell’s table like the others; the layer’s inelastic table is then the valence channel. The construction is channel-first: the inelastic rate is the sum of the valence and shell rates, a channel is chosen with probability proportional to its rate (below), and the lossωis drawn from the chosen channel’s own inverse CDF. (The conditional choice ofcrate::electron::inelastic::ShellResolvedChannels::sample_channelbelongs with a loss drawn from the total DIIMFP and is not used.) A shell channel is closed (rate zero) when the electron cannot pay its binding energyB_j: whenE <= B_jwithout a secondary model andE - E_F <= B_jwith one. An open channel’sωis kept in[B_j, E]([B_j, E - E_F]with a secondary model), so the primary never ends below the Fermi level and the shell’s secondary energy is never negative. WithSecondaryModel::Offthe event only takesω; withSecondaryModel::KieftBoschit liberates an electron of energyE_F + ω - B_jand leavesB_j - E_Fin the solid (crate::electron::secondary, “Inner-shell events”). Layers default to no shell channel, which is exactly the valence-only transport. - Channel choice. With the rates in the fixed order elastic,
inelastic (valence), phonon emission, phonon absorption, polaron
trapping, then the inner shells in the order given, the channel is the
first whose cumulative rate exceeds
u · total; channels with zero rate are never chosen (a rounding overshoot falls to the last channel with a positive rate). A layer without shells sums and compares exactly the five rates of before. - Faces. With
BoundaryModel::Transparenta face changes nothing: the electron enters the next layer or leaves the target at its energy and direction. WithBoundaryModel::StepBarrierevery face is a step in the inner potential (vacuum outside the target hasU = 0), crossed with the quantum-mechanical transmission probability and refraction ofcrate::electron::boundary, or reflected specularly; an electron below the barrier is always reflected. Energies inside a layer are then measured from that layer’s band bottom and an escaped electron carries its vacuum energyE - U. The back face underEscapeRule::FrontOnlyabsorbs without a barrier. - Inverse CDFs between grid energies are interpolated linearly in energy between the two bracketing rows, at the same cumulative probability (a zero-rate bracketing row is ignored).
§Electrons of one history
A history starts with its primary. With BoundaryModel::StepBarrier the
primary is incident from vacuum on the front face, its Primary energy
is a vacuum energy, and it first meets the front-face step (it may be
reflected there, ending the history as Fate::Escaped through
Face::Front). Secondaries are pushed on a last-in, first-out stack and
each is transported in full, like the primary, once the electron before it
has ended; secondaries of secondaries are pushed on the same stack. The
history ends when the stack is empty.
An electron stops when its energy falls below the stopping threshold
of its layer: the cutoff, measured from the band bottom
(CutoffReference::BandBottom) or from the vacuum level
(CutoffReference::VacuumLevel, threshold U + cutoff, Nebula’s rule
in source/drivers/cpu/cpu_driver.inl at the commit named in
crate::electron::boundary). A secondary below the threshold of its
layer is not created; its energy is recorded as deposited.
§Energy reference of the inelastic table
The transport looks up both tables of a layer at the electron’s kinetic
energy E as it measures it: from the band bottom of the layer (with the
step barrier, the vacuum energy plus the inner potential U). The models
of crate::electron::inelastic have their own axis instead: T, the
energy above the model’s Fermi energy, with kinematics on
T' = T + E_F(model) and losses up to T (Shinotsuka et al. 2017, eqs.
(2)-(3), as in crate::electron::inelastic::penn). The table has to
bridge the two, and it does so when it is built
(crate::electron::inelastic::table, “Energy axis”):
- Band-bottom axis (
EnergyAxis::BandBottom, with the model’s Fermi energy set to the band’s): the row atEis the model’s atT = E - E_F, so its kinematics are onT' = Eand its losses stop atE - E_F. This is the convention of the cstool table compiler which builds Nebula’s tables (rows at the kinetic energyK, kinematics onK, losses belowK - F, the “Fermi correction” ofcompile_full_imfp_icdfincstool/dielectric_function/compile.py, Nebula-simulator/cstool commit0c739eb), andFis what cstool passes there,BandStructure::min_excitation_ev: the Fermi energy of a metal, the conduction-band bottom of an insulator. It is howlindhard runbuilds the table of every material with a band. The losses of such a table are below the clamp ofSecondaryModel::KieftBoschabove, which therefore does not act (testband_bottom_table_never_reaches_the_fermi_clamp,tests/electron_secondaries.rs). One exception remains, for either axis: between the last empty row of a table and its first row with losses the rate is interpolated from zero and the loss is drawn from that first row, so it can exceed what an electron below that row may lose; there the clamps (W <= E, andW <= E - E_Funder Kieft-Bosch) are still the bound. - Model axis (
EnergyAxis::ModelFermiLevel, the builder’s default, and whatlindhard runbuilds for a material without a band, with[electron.inelastic] fermi_energy_ev): the row read atEis the model’s atT = E. With the model’s Fermi energy 0 the kinematics are right (T' = E) but the losses reachE; in a layer with a band and the Kieft-Bosch model the clamp takes every loss beyondE - E_FtoE - E_F, leaving the primary at the Fermi level and giving the secondary its whole energy. Setting the model’s Fermi energy to the band’s on this axis countsE_Ftwice (T' = E + E_F). The testsinelastic_table_is_read_at_the_band_bottom_energyandlosses_beyond_the_fermi_level_are_clamped_to_itpin how the transport reads and clamps such a table.
Inner-shell channel tables
(crate::electron::inelastic::table::build_shell_channel_tables) are
built on the axis of the options they are given, like the valence table,
and are read the same way, at the band-bottom energy E. lindhard run
does not build them yet (no input, #156); a library caller picks the axis.
On top of the table, the transport applies the shell’s own limits on the
band-bottom axis: the channel is closed unless the largest allowed loss
(E, or E - E_F under the secondary model) exceeds B_j, and ω is
clamped into [B_j, that loss]. Near an edge, a table on the model
axis (losses up to E above the model’s Fermi energy) has losses above
E - E_F that this clamp moves down to E - E_F, as for valence losses;
a band-bottom table’s losses are already below E - E_F, apart from the
interpolation exception above.
Measured effect (#173). Until #241 lindhard run built every table
on the model axis with Fermi energy 0. With the single-pole Penn default,
the Al input of validation/experiments/se_yield.py (E_F = 11.66 eV)
then sent 78 to 82 % of the inelastic events of electrons 5 to 20 eV above
the Fermi level into the clamp (Al 400 eV, 200 histories). A diagnostic
table in the cstool convention (the model’s rows at T = E - E_F,
relabelled E, so on a grid shifted by E_F) removed every clamped
event and changed δ by a few per cent, against a factor of about 5 to
explain (1000 histories, seed 1): single pole Al 7.61 to 8.17 and Au 3.23
to 3.37 (800 eV); Mermin Al 1.34 to 1.26 (400 eV). With 200 histories:
single pole Cu 1.67 to 1.80, Mermin Au 2.62 to 2.58 and Cu 2.02 (800 eV).
The gap between the single pole and Mermin was the same in both. The axis
was therefore a real mismatch but not the cause of the δ overestimate of
#173; that is the single-pole model’s inelastic mean free path at low
energy (crate::electron::inelastic::penn, “Low energies”).
With the band-bottom table lindhard run builds since #241 (on the
input’s own grid), the same Al input at 400 eV (200 histories, seed 1, so
about ±0.1 to 0.2 in δ) has no clamped event in any energy bin, and δ is
6.44 against 6.89 with the table built as before. The commands are in
lindhard-cli/examples/inelastic_low_energy.rs (modes clamp and
legacy).
§Random draw order
The order of draws from the per-history stream is part of the contract (the
tests replay it). With the step barrier, the primary first draws one
uniform for its entry. Then, per flight with a nonzero total rate: one
uniform for the path. If a collision happens, one for the channel, then
θ and the azimuth (elastic), W (inelastic), or cos θ and the azimuth
(phonon emission or absorption) or nothing (polaron trapping). Under the
Kieft-Bosch secondary model an inelastic event that liberates an electron
then draws the secondary’s azimuth and, with the instantaneous momentum on,
two more uniforms. An inner-shell event draws one uniform for ω, then,
under the Kieft-Bosch model, the secondary’s azimuth and, with the
instantaneous momentum on, two more uniforms (a shell event always
liberates an electron). With no insulator or inner-shell channel switched
on, the draws are exactly those of a run without them. A path drawn for a flight that ends at a layer face is discarded;
with the step barrier, the face draws one uniform if the electron has the
normal energy to get over (and none if it is surely reflected). A flight
with zero total rate draws nothing. Secondaries continue on the same
stream, in stack order, after the electron before them ends.
§Reproducibility
Transport::run goes through crate::rng::run_particles: history i
uses the stream (seed, i), and chunk tallies are merged in chunk order,
so a tally whose ElectronTally::merge is order-faithful (a sum, or a
concatenation) is bit-identical at any thread count.
§Hooks
The ElectronTally trait mirrors crate::ion::bca::BcaTally: every
event hook has a no-op default. Per-electron hooks (step, elastic,
inelastic, secondary, interface, barrier, reflected, stopped,
escaped, absorbed, phonon, polaron_trapped) fire for the primary and
for every secondary;
ElectronTally::begin_secondary and ElectronTally::end_secondary
bracket each secondary, and ElectronTally::end_history comes last, with
the primary’s fate. The insulator channels report through
ElectronTally::phonon and ElectronTally::polaron_trapped. An
inner-shell event calls ElectronTally::inelastic like a valence one,
then ElectronTally::inner_shell with the shell, then (with a
secondary model) ElectronTally::secondary.
ElectronTally::table_lookup is a diagnostic hook, also with a no-op
default: it reports, per channel, whether each rate evaluation read the
table inside its energy grid or used the constant continuation beyond an
end (GridCoverage). It draws nothing and changes nothing, so a tally
that uses it sees the same histories as one that does not.
crate::tally::TableCoverageTally counts these evaluations. The rates
of inner-shell channel tables are not reported to it.
Re-exports§
pub use crate::geometry::Face;
Structs§
- Beam
Spread - Where a run’s primaries land and how they diverge: a finite lateral
profile about a landing point on the front face, and an angular spread
about the nominal
Primary::direction. The default is a point beam without divergence, which draws no random numbers, so a run with it is bit-identical to one made before this type existed. - Electron
State - The state of an electron after an event. Positions are
[x, y, z]in metres,xthe depth. - Inner
Shell Metadata - Identity of one inner-shell channel table of a layer, for
LayerMetadata. - Layer
Metadata - Identity of the tables used in one layer, for
RunMetadata. - Layer
Tables - The tables of one layer.
- Primary
- A primary electron: it starts on the front face (
x = 0,y = z = 0) in the first layer, or, withBoundaryModel::StepBarrier, just outside it in vacuum (thenenergy_evis the vacuum energy and the direction must point into the target). - RunMetadata
- What a run was configured with, for output metadata.
- Summary
Tally - A minimal summary tally: counts, path length and energy bookkeeping.
- Transport
- The electron transport engine: a stack, the tables of each layer, their band parameters (optional) and a configuration.
- Transport
Config - Run configuration, recorded in the
RunMetadata. - Transport
Run - The result of
Transport::run.
Enums§
- Beam
Profile - Lateral profile of the beam on the front face, about its landing point. Lengths are metres.
- Boundary
- A face an electron met: between two layers, or a face of the target.
- Boundary
Model - What a face does to an electron.
- Cutoff
Reference - Where the energy cutoff is measured from.
- Escape
Rule - What happens when an electron reaches a face of the target. Whether it can
get out at all is decided by the
BoundaryModel; an electron that does leave never returns. - Fate
- How a history (or one electron of it) ended.
- Grid
Coverage - Where an energy lies against the energy grid of one table, as reported to
ElectronTally::table_lookup. - Phonon
Event - Which way a Fröhlich LO-phonon event went.
- Table
Channel - One of the two cross-section tables of a layer (
LayerTables). - Transport
Error - Errors from building a
Transportor starting a run.
Traits§
- Electron
Tally - Event hooks called by
Transport. All default to no-ops exceptmerge. TheElectronStatepassed is the state after the event.