ViennaSHE 1.3.0
Free open-source semiconductor device simulator using spherical harmonics expansions techniques.
assemble_ee_scattering.hpp
Go to the documentation of this file.
1#ifndef VIENNASHE_SHE_ASSEMBLE_EE_SCATTERING_HPP
2#define VIENNASHE_SHE_ASSEMBLE_EE_SCATTERING_HPP
3/* ============================================================================
4 Copyright (c) 2011-2022, Institute for Microelectronics,
5 Institute for Analysis and Scientific Computing,
6 TU Wien.
7
8 -----------------
9 ViennaSHE - The Vienna Spherical Harmonics Expansion Boltzmann Solver
10 -----------------
11
12 http://viennashe.sourceforge.net/
13
14 License: MIT (X11), see file LICENSE in the base directory
15=============================================================================== */
16
17// viennagrid
18#include "viennagrid/mesh/mesh.hpp"
19
20// viennashe
24
28
30
33
39
40#include "viennashe/log/log.hpp"
43
44
49namespace viennashe
50{
51 namespace she
52 {
53
54 template <typename DispersionRelation>
55 double ee_scattering_rate(DispersionRelation const & dispersion, double kinetic_energy, double n, double T)
56 {
57 const double pi = viennashe::math::constants::pi;
58 const double kB = viennashe::physics::constants::kB;
59 const double hbar = viennashe::physics::constants::hbar;
60 const double q = viennashe::physics::constants::q;
61 const double eps_Si = viennashe::materials::si::permittivity();
62 double lambda_sq = 0.0;
63 double scattering_rate = 0.0;
64
65 const double prefactor = (2.0 * pi * n * q * q * q * q) / (hbar * eps_Si * eps_Si);
66
67 double norm_k = dispersion.norm_k(kinetic_energy);
68
69 if (norm_k <= 0.0) //avoid singularity in the following expression
70 return 0;
71
72 lambda_sq = (eps_Si * kB * T) / (q * q * n);
73 double a = 4.0 * lambda_sq * norm_k * norm_k;
74
75 scattering_rate = prefactor
76 * 0.5 * (std::log(1.0 + a) - (a / (1.0 + a)) ) / 4.0 / std::pow(norm_k, 4.0);
77
78 return scattering_rate;
79
80 }
81
82
83
94 template <typename DeviceType, typename SHEQuantityT, typename MatrixType, typename VectorType>
95 void assemble_ee_scattering(DeviceType const & device,
96 viennashe::config const & conf,
97 SHEQuantityT const & quan,
98 SHEQuantityT const & quan_old,
99 MatrixType & matrix, VectorType & rhs
100 )
101 {
102 typedef typename DeviceType::mesh_type MeshType;
103
104 typedef typename viennagrid::result_of::facet<MeshType>::type FacetType;
105 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
106
107 typedef typename viennagrid::result_of::const_facet_range<MeshType>::type FacetContainer;
108 typedef typename viennagrid::result_of::iterator<FacetContainer>::type FacetIterator;
109
110 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
111 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
112
113 typedef typename viennagrid::result_of::const_coboundary_range<MeshType, FacetType, CellType>::type CellOnFacetContainer;
114 typedef typename viennagrid::result_of::iterator<CellOnFacetContainer>::type CellOnFacetIterator;
115
116 typedef viennashe::math::sparse_matrix<double> CouplingMatrixType;
117
118
119 // We only assemble EE scattering for electrons
120 if ( quan_old.get_carrier_type_id() != viennashe::ELECTRON_TYPE_ID)
121 return;
122
123
124 (void)rhs; // avoid unused parameter warnings
125
126 MeshType const & mesh = device.mesh();
127
129
130 double damping = 1e-4; //Empirical fitting factor. Certainly requires more investigations for a general ee-scattering model.
131
132 //
133 // Create wrappers for quantities
134 //
135
137 density_n(conf, quan_old);
138
140
141 //
142 // Set up scatter matrices
143 //
144 const std::size_t L_max = static_cast<std::size_t>(conf.max_expansion_order());
145 const std::size_t num_harmonics = (L_max+1) * (L_max+1);
146 CouplingMatrixType scatter_op_in(num_harmonics, num_harmonics);
147 CouplingMatrixType scatter_op_out(num_harmonics, num_harmonics);
148
149 for (std::size_t i = 0; i < (L_max+1) * (L_max+1); ++i)
150 scatter_op_out(i,i) += 1.0;
151 scatter_op_in(0,0) += 1.0;
152
153 //
154 // Step 1: assemble on even nodes:
155 //
156 //log::debug<log_assemble_ee_scattering>() << "* assembleScatteringOperator(): Assembling on even nodes..." << std::endl;
157 CellContainer cells(mesh);
158 for (CellIterator cit = cells.begin();
159 cit != cells.end();
160 ++cit)
161 {
162 // Step 1: Get carrier density:
163 const double carrier_density = density_n(*cit); // we need the carrier density in a single valley
164 const double kinetic_energy_star = energy_n(*cit); // mean particle energy
165
166 //
167 // Iterate over relevant energies (at index_H == 0 there is no unknown...)
168 for (std::size_t index_H = 1; index_H < quan.get_value_H_size() - 1; ++index_H)
169 {
170
171 const long row_index = quan.get_unknown_index(*cit, index_H);
172
173 if (row_index < 0)
174 continue;
175
176 const double kinetic_energy = quan.get_kinetic_energy(*cit, index_H);
177
178 if (kinetic_energy <= 0.0)
179 continue;
180
181 if (carrier_density <= 0)
182 {
183 log::warn() << "* assemble_ee_scattering(): Warning: Carrier_density zero!" << std::endl;
184 }
185 if (kinetic_energy_star <= 0)
186 {
187 log::warn() << "* assemble_ee_scattering(): Warning: kinetic_energy_star zero or less ! kinetic_energy_star = " << kinetic_energy_star << std::endl;
188 }
189
190 const double box_volume = viennagrid::volume(*cit);
191 const long expansion_order_mid = static_cast<long>(quan.get_expansion_order(*cit, index_H));
192 const double energy_height = box_height(quan, *cit, index_H);
193
194
196 // Step 2: Determine E* and f(E*):
197 std::size_t Estar_index = 1;
198 for ( ; Estar_index < quan_old.get_value_H_size() - 1; ++Estar_index)
199 {
200 if (quan_old.get_kinetic_energy(*cit, Estar_index) > kinetic_energy_star)
201 break;
202 }
203
204 double f_Estar = quan_old.get_values(*cit, Estar_index)[0] / carrier_density; //normalize f such that \int f dk^3 = 1
205
206 { // account for DOS ... we now assemble for g = f*Z
207 const double dos_norm = dispersion_relation.density_of_states(quan_old.get_kinetic_energy(*cit, Estar_index));
208 if (dos_norm <= 0.0)
209 {
210 f_Estar = 0;
211 std::cout << "kinetic_energy = " << quan_old.get_kinetic_energy(*cit, Estar_index) << std::endl;
212 std::cout << "dos_norm = " << dos_norm << std::endl;
213 }
214 else f_Estar = f_Estar / dos_norm; // account for DOS ... we now assemble for g = f*Z
215 }
216
217 if (f_Estar <= 0)
218 {
219 log::warn() << "* assemble_ee_scattering(): Warning: f_Estar is smaller or equal to zero!" << std::endl;
220 }
221
222 // Step 3: Run an integration over energy to obtain scattering contributions:
223 double out_scattering_coeff = 0.0;
224 double kinetic_energy_max = quan.get_kinetic_energy(*cit, quan_old.get_value_H_size() - 2);
226 double prefactor = pow(1.0 / Y_00(0, 0), 3) / 2.0; //prefactor for the integrals
227
228 for (std::size_t index_H_prime = 1; index_H_prime < quan.get_value_H_size() - 1; ++index_H_prime)
229 {
230 const double kinetic_energy_prime = quan.get_kinetic_energy(*cit, index_H_prime);
231
232 if (kinetic_energy_prime <= 0)
233 continue;
234
235 //store E + E* - E' in a separte variable:
236 const double energy_argument = kinetic_energy + kinetic_energy_star - kinetic_energy_prime;
237 if (energy_argument < 0) //since E' increases monotonically, energy_argument will only get smaller -> break for-loop
238 break;
239
240 if (energy_argument > kinetic_energy_max)
241 continue;
242
243 const double c_ee = ee_scattering_rate(dispersion_relation,
244 std::abs(kinetic_energy - kinetic_energy_prime),
245 carrier_density,
247
248 //
249 // In-scattering part: Energies couple
250 //
251 double f_00_prime = (quan_old.get_values(*cit, index_H_prime)[0]) / carrier_density; //normalization to \int f dk^3 = 1
252 { // account for DOS ... we now assemble for g = f*Z
253 const double dos_norm = dispersion_relation.density_of_states(kinetic_energy_prime);
254 if (dos_norm <= 0.0) f_00_prime = 0;
255 else f_00_prime = f_00_prime / dos_norm;
256 }
257
258 // find unknown index for g_00(E + E* - E'):
259 std::size_t energy_index = 1;
260 for ( ; energy_index < quan.get_value_H_size() - 1; ++energy_index)
261 {
262 if (quan.get_kinetic_energy(*cit, energy_index) > energy_argument)
263 break;
264 }
265
266 if (energy_index == quan.get_value_H_size() - 2) //E + E* - E' is outside the simulation mesh
267 continue;
268
269 double in_scattering_coeff = prefactor
270 * c_ee
271 * dispersion_relation.density_of_states(kinetic_energy_prime)
272 * dispersion_relation.density_of_states(energy_argument)
273 * energy_height; // \int_E h(E) dE is replaced by \sum_Ei h(Ei) Delta(E_i)
274
275 // write to system matrix: (mind negative sign of in-scattering)
276 if (quan.get_unknown_index(*cit, energy_index) >= 0)
278 std::size_t(row_index), std::size_t(quan.get_unknown_index(*cit, energy_index)),
279 - damping * in_scattering_coeff * f_00_prime * box_volume * energy_height,
280 scatter_op_in,
283 );
284
285 //
286 // Out-scattering part:
287 //
288 out_scattering_coeff += prefactor
289 * c_ee
290 * dispersion_relation.density_of_states(kinetic_energy_prime)
291 * dispersion_relation.density_of_states(energy_argument)
292 * energy_height; // \int_E h(E) dE is replaced by \sum_Ei h(Ei) Delta(E_i)
293 }
294
295 //write out-scattering terms:
296 double coeff = damping * out_scattering_coeff * f_Estar * box_volume * energy_height;
297 if (coeff > 0)
299 std::size_t(row_index), std::size_t(row_index),
300 coeff,
301 scatter_op_out,
304 );
305 } //index_H
306 } //for vertices
307
308
309 //
310 // Step 2: assemble on odd nodes:
311 //
312 //log::debug<log_assemble_ee_scattering>() << "* assembleScatteringOperator(): Assembling on odd nodes..." << std::endl;
313 FacetContainer facets(mesh);
314 for (FacetIterator fit = facets.begin();
315 fit != facets.end();
316 ++fit)
317 {
318 CellOnFacetContainer cells_on_facet(device.mesh(), fit.handle());
319
320 if (cells_on_facet.size() < 2)
321 continue;
322
323 CellOnFacetIterator cofit = cells_on_facet.begin();
324 CellType const & c1 = *cofit;
325 ++cofit;
326 CellType const & c2 = *cofit;
327
328 double connection_len = viennagrid::norm_2(viennagrid::centroid(c1) - viennagrid::centroid(c2));
329
330 //
331 // Iterate over relevant energies (at index_H == 0 there is no unknown...)
332 for (std::size_t index_H = 1; index_H < quan.get_value_H_size() - 1; ++index_H)
333 {
334 const long row_index = quan.get_unknown_index(*fit, index_H);
335 if (row_index < 0)
336 continue;
337
338
339 const double box_volume = viennagrid::volume(*fit) * connection_len;
340
341 const long expansion_order_mid = static_cast<long>(quan.get_expansion_order(*fit, index_H));
342
343 //
344 // Compute density of states at initial and final states
345 //
346 const double kinetic_energy_at_edge = quan.get_kinetic_energy(*fit, index_H);
347 const double energy_height = box_height(quan, *fit, index_H);
348
350
351 //std::cout << density_n(v1) << " * " << density_n(v2) << std::endl;
352 // Step 1: Get carrier density:
353 double carrier_density = std::sqrt(density_n(c1) * density_n(c2));
354 if (carrier_density <= 0)
355 log::warn() << "* assemble_ee_scattering(): Warning: carrier_density zero!" << std::endl;
356
357 //std::cout << energy_n(v1) << " * " << energy_n(v2) << std::endl;
358 // Step 2: Determine E* and f(E*):
359 double kinetic_energy_star = (energy_n(c1) + energy_n(c2)) / 2.0; //mean particle energy
360 if (kinetic_energy_star <= 0)
361 log::warn() << "* assemble_ee_scattering(): Warning: kinetic_energy_star zero!" << std::endl;
362
363 std::size_t Estar_index = 1;
364 for ( ; Estar_index < quan_old.get_value_H_size(); ++Estar_index)
365 {
366 if (quan_old.get_kinetic_energy(*fit, Estar_index) > kinetic_energy_star)
367 break;
368 }
369
370
371
372 double f_Estar = 0;
373
374 if ( quan_old.get_unknown_mask(c1) ) f_Estar += quan_old.get_boundary_value(c1, Estar_index);
375 else f_Estar += quan_old.get_values(c1, Estar_index)[0];
376
377 if ( quan_old.get_unknown_mask(c2) ) f_Estar += quan_old.get_boundary_value(c2, Estar_index);
378 else f_Estar += quan_old.get_values(c2, Estar_index)[0];
379
380 f_Estar = f_Estar / (2.0 * carrier_density);
381
382 //f_Estar = ( quan_old.get_values(v1, Estar_index)[0]
383 // + quan_old.get_values(v2, Estar_index)[0] ) / (2.0 * carrier_density); //normalize f such that \int f dk^3 = 1
384
385 {
386 const double dos_norm = dispersion_relation.density_of_states(quan_old.get_kinetic_energy(*fit, Estar_index));
387 if (dos_norm <= 0.0) f_Estar = 0.0;
388 else f_Estar = f_Estar / dos_norm;
389 }
390
391 // Step 3: Run an integration over energy to obtain scattering contributions:
392 double kinetic_energy_max = quan.get_kinetic_energy(*fit, quan_old.get_value_H_size() - 2);
393 double out_scattering_coeff = 0.0;
395 double prefactor = pow(1.0 / Y_00(0, 0), 3); //prefactor for the integrals
396
397 for (std::size_t index_H_prime = 1; index_H_prime < quan.get_value_H_size() - 1; ++index_H_prime)
398 {
399 double kinetic_energy_prime = quan.get_kinetic_energy(*fit, index_H_prime);
400 if (kinetic_energy_prime < 0)
401 continue;
402
403 //store E + E* - E' in a separte variable:
404 const double energy_argument = kinetic_energy_at_edge + kinetic_energy_star - kinetic_energy_prime;
405 if (energy_argument < 0) //since E' increases monotonically, energy_argument will only get smaller -> break for-loop
406 break;
407
408 if (energy_argument > kinetic_energy_max)
409 continue;
410
411 const double c_ee = ee_scattering_rate( dispersion_relation,
412 std::abs(kinetic_energy_at_edge - kinetic_energy_prime),
413 carrier_density,
415 );
416
417 //
418 // In-scattering part: Nothing to be done, because couples only f_{0,0}
419 //
420
421 //
422 // Out-scattering part:
423 //
424 out_scattering_coeff += prefactor
425 * c_ee
426 * dispersion_relation.density_of_states(kinetic_energy_prime)
427 * dispersion_relation.density_of_states(energy_argument)
428 * energy_height; // \int_E h(E) dE is replaced by \sum_Ei h(Ei) Delta(E_i)
429 }
430
431 //write out-scattering terms:
432 const double coeff = damping * out_scattering_coeff * f_Estar * box_volume * energy_height;
433 if (coeff > 0)
435 std::size_t(row_index), std::size_t(row_index),
436 coeff,
437 scatter_op_out,
440 );
441
442 } //index_H
443 } //for edges
444
445
446
447 } // assemble_ee
448
449 } //namespace she
450} //namespace viennashe
451
452#endif
453
Generic assembly of the scattering operator(s) is implemented here.
Writes a possibly scaled block matrix to the system matrix. Includes similar functionality for comput...
Provides an accessor for the carrier density.
Provides an accessor for the average carrier energy.
The main SHE configuration class. To be adjusted by the user for his/her needs.
Definition: config.hpp:124
viennashe::physics::dispersion_proxy dispersion_relation(viennashe::carrier_type_id ctype) const
Returns the dispersion relation for electrons.
Definition: config.hpp:266
long max_expansion_order() const
Returns the current maximum expansion order.
Definition: config.hpp:369
double get_lattice_temperature(cell_type const &c) const
Returns the lattice temperature on a cell.
Definition: device.hpp:255
MeshT const & mesh() const
Returns the underlying mesh.
Definition: device.hpp:145
Defines the physical properties of a device, e.g. doping. This is the implementation for 2d and highe...
Definition: device.hpp:818
Iteration over all spherical harmonics indices up to order L, with increasing indices l and m.
A proxy object for a dispersion relation. Does NOT take ownership of the provided pointer!
Definition: dispersion.hpp:69
double density_of_states(double ekin, double theta=0, double phi=0) const
Returns the density of states as a function of kinetic energy (and angles, eventually)
Definition: dispersion.hpp:75
An accessor for the average carrier energy at each point inside the device.
An accessor for the carrier density in the device by reference
Provides a convenience wrapper for accessing the distribution function coefficients over the device.
Contains the dispersion relations for different materials and different polarities.
Defines several filter functors for the device. A filter functor returns true if the supplied argumen...
Provides the SHE coupling matrices and computes higher-order coupling matrices if required....
Implementation of numerical integration routines.
A logging facility providing fine-grained control over logging in ViennaSHE.
Provides a number of fundamental math constants.
logger< true > warn()
Used to log warnings. The logging level is logWARNING.
Definition: log.hpp:303
@ EVEN_HARMONICS_ITERATION_ID
Definition: forwards.h:161
@ ODD_HARMONICS_ITERATION_ID
Definition: forwards.h:162
VectorType::value_type norm_2(VectorType const &v)
Definition: linalg_util.hpp:37
void assemble_ee_scattering(DeviceType const &device, viennashe::config const &conf, SHEQuantityT const &quan, SHEQuantityT const &quan_old, MatrixType &matrix, VectorType &rhs)
Interface function for electron-electron scattering. Differs significantly from ac,...
double box_height(SHEQuantity const &quan, ElementType const &elem, std::size_t index_H)
Returns the height of the control box with respect to energy at the provided element (vertex or edge)...
double ee_scattering_rate(DispersionRelation const &dispersion, double kinetic_energy, double n, double T)
void add_block_matrix(SystemMatrixType &system_matrix, std::size_t row_index, std::size_t col_index, double prefactor, BlockMatrixType const &coupling_matrix, RowIndexIterator row_iter, ColumnIndexIterator const &col_iter_init, bool elastic_scattering_roundoff_error_prevention=false)
Writes a sub-matrix of a block matrix 'coupling_matrix' into the global system matrix,...
The main ViennaSHE namespace. All functionality resides inside this namespace.
Definition: accessors.hpp:40
@ ELECTRON_TYPE_ID
Definition: forwards.h:187
Provides a number of fundamental constants. All constants in SI units.
Returns a few helper routines for computing physical quantities. To be replaced in the future.
Provides the exceptions used inside the viennashe::she namespace.
File to gather all scattering operators.
Implementation of spherical harmonics plus helper functions.
static double permittivity()
Definition: all.hpp:53
static const double pi
Pi.
Definition: constants.hpp:34
static const double q
Elementary charge.
Definition: constants.hpp:44
static const double kB
Boltzmann constant.
Definition: constants.hpp:46
static const double hbar
Modified Planck constant.
Definition: constants.hpp:50
Defines the log keys used within the viennashe::she namespace.