ViennaSHE 1.3.0
Free open-source semiconductor device simulator using spherical harmonics expansions techniques.
assemble_scattering.hpp
Go to the documentation of this file.
1#ifndef VIENNASHE_SHE_ASSEMBLE_SCATTERING_HPP
2#define VIENNASHE_SHE_ASSEMBLE_SCATTERING_HPP
3
4/* ============================================================================
5 Copyright (c) 2011-2022, Institute for Microelectronics,
6 Institute for Analysis and Scientific Computing,
7 TU Wien.
8
9 -----------------
10 ViennaSHE - The Vienna Spherical Harmonics Expansion Boltzmann Solver
11 -----------------
12
13 http://viennashe.sourceforge.net/
14
15 License: MIT (X11), see file LICENSE in the base directory
16=============================================================================== */
17
18#include <cmath>
19
20// viennagrid
21#include "viennagrid/mesh/mesh.hpp"
22#include "viennagrid/algorithm/voronoi.hpp"
23
24// viennashe
28
32
35
40
41#include "viennashe/log/log.hpp"
43
44#include "scattering/all.hpp"
45
50namespace viennashe
51{
52 namespace she
53 {
54
64 template <typename SHEQuantity, typename ElementType>
65 long energy_index_H(SHEQuantity const & quan,
66 ElementType const & elem,
67 long index_H_hint,
68 double kinetic_energy)
69 {
70 long result_index = index_H_hint;
71
72 if ( kinetic_energy < quan.get_kinetic_energy(elem, std::size_t(result_index))
73 || kinetic_energy > quan.get_kinetic_energy(elem, std::size_t(result_index)) ) // this is an inelastic process, so we have to search
74 {
75 long increment = 1;
76
77 double diff = kinetic_energy - quan.get_kinetic_energy(elem, std::size_t(result_index));
78 double diff_init = diff;
79
80 if (diff < 0) //we need to search for a lower index
81 increment = -1;
82
83 while ( (result_index < static_cast<long>(quan.get_value_H_size() - 1)) && (result_index > 0)
84 && (diff * diff_init > 0) ) //sign hasn't changed yet
85 {
86 result_index += increment;
87 diff = kinetic_energy - quan.get_kinetic_energy(elem, std::size_t(result_index));
88 }
89
90 if (diff * diff_init > 0) //still no sign change, hence final scattering state is outside the considered range
91 return -1;
92
93 // Pick best approximation to final energy:
94 double error_1 = std::abs(diff);
95 double error_2 = error_1;
96 if ( (result_index - increment >= 0)
97 && (result_index - increment < static_cast<long>(quan.get_value_H_size())) )
98 error_2 = std::abs(kinetic_energy - quan.get_kinetic_energy(elem, std::size_t(result_index - increment)));
99
100 if (error_2 < error_1)
101 return result_index - increment; //going one step/increment back gives the best approximation
102 }
103
104 return result_index;
105 }
106
107
108 template <typename ScatterProcessesT,
109 typename DeviceType,
110 //typename TimeStepQuantitiesT,
111 typename SHEQuantity,
112 typename MatrixType,
113 typename VectorType,
114 typename ElementType,
115 typename CouplingMatrix>
116 void assemble_scattering_operator_on_box(ScatterProcessesT const & scatter_processes,
117 DeviceType const & device,
118 viennashe::config const & conf,
119 SHEQuantity const & quan,
120 MatrixType & A, VectorType & b,
121 ElementType const & elem, std::size_t index_H,
122 CouplingMatrix const & coupling_in_scatter,
123 CouplingMatrix const & coupling_out_scatter)
124 {
125 typedef typename viennagrid::result_of::point<typename DeviceType::mesh_type>::type PointType;
126
127 typedef scattering_base<DeviceType> ScatterProcessType;
128
129 //
130 // Step 1: Get standard quantities
131 //
133
134 const double pi = viennashe::math::constants::pi;
135
136 const long row_index = quan.get_unknown_index(elem, index_H);
137
138 if (row_index < 0)
139 return;
140
141 double kinetic_energy = quan.get_kinetic_energy(elem, index_H);
142
143 typedef typename ScatterProcessType::value_type scatter_descriptors_type;
144
145 const long expansion_order_mid = static_cast<long>(quan.get_expansion_order(elem, index_H));
146
147 bool odd_assembly = detail::is_odd_assembly(elem, viennagrid::cells(device.mesh())[0]);
148
149 double box_volume = viennagrid::volume(elem);
150 if (odd_assembly)
151 box_volume *= detail::cell_connection_length(device.mesh(), elem, viennagrid::cells(device.mesh())[0]) / static_cast<double>(PointType::dim);
152
153 //
154 // Now iterate over all scattering operators
155 //
156 for (typename ScatterProcessesT::const_iterator scatter_process_it = scatter_processes.begin();
157 scatter_process_it != scatter_processes.end();
158 ++scatter_process_it)
159 {
160 ScatterProcessType const & scatter_process = *(*scatter_process_it);
161
163 log::debug<log_assemble_scattering_operator>() << "* assemble_scattering_operator_on_box(): Assembling scattering process with ID " << scatter_process.id() << std::endl;
164
165 scatter_descriptors_type scatter_events = scatter_process(elem, kinetic_energy, quan.get_carrier_type_id());
166
167 //
168 // Part 2: Loop over all possible scattering events for this energy (in- and out-scattering)
169 //
170 for (std::size_t scatter_index = 0; scatter_index < scatter_events.size(); ++scatter_index)
171 {
172 const double initial_kinetic_energy = scatter_events[scatter_index].initial_energy();
173 const double final_kinetic_energy = scatter_events[scatter_index].final_energy();
174 const double rate = scatter_events[scatter_index].rate();
175 const double generation_rate = scatter_events[scatter_index].generation_rate();
176
177 // skip if no contribution
178 if (rate <= 0 && generation_rate <= 0) // inequality in order to avoid strict comparison with zero (triggers warnings)
179 continue;
180
181 long initial_state_index_H = energy_index_H(quan, elem, static_cast<long>(index_H), initial_kinetic_energy);
182 long final_state_index_H = energy_index_H(quan, elem, static_cast<long>(index_H), final_kinetic_energy);
183
184
185 // skip this process if one of the states is outside the simulation region
186 if ( (initial_state_index_H == -1) || (final_state_index_H == -1))
187 continue;
188
189 //
190 // Compute density of states at initial and final states
191 //
192 const double Z_initial = averaged_density_of_states(quan, conf.dispersion_relation(quan.get_carrier_type_id()), elem, static_cast<std::size_t>(initial_state_index_H));
193 const double Z_final = averaged_density_of_states(quan, conf.dispersion_relation(quan.get_carrier_type_id()), elem, static_cast<std::size_t>(final_state_index_H));
194
195 const double energy_height = std::min(box_height(quan, elem, static_cast<std::size_t>(initial_state_index_H)), // Note: box height near band edge might be smaller, reducing the active area.
196 box_height(quan, elem, static_cast<std::size_t>(final_state_index_H)));
197
198 const bool elastic_scattering_roundoff_error_prevention = (initial_state_index_H == final_state_index_H) && !odd_assembly;
199
200 //
201 // Out-Scattering
202 //
203 if (initial_kinetic_energy <= kinetic_energy && initial_kinetic_energy >= kinetic_energy)
204 {
205 const long col_index = quan.get_unknown_index(elem, static_cast<std::size_t>(final_state_index_H));
206
207 if (col_index >= 0) //only write contribution if final state is within simulation domain
208 {
210 double coefficient = 0;
211 switch (conf.she_discretization_type())
212 {
214 coefficient = 2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height;
215 break;
217 coefficient = 2.0 * pi * rate * Z_final * box_volume * energy_height;
218 break;
219 default:
220 throw std::runtime_error("assemble_scattering_operator_on_box(): Unknown SHE discretization type!");
221 }
223 static_cast<std::size_t>(row_index), static_cast<std::size_t>(row_index),
224 coefficient,
225 coupling_out_scatter,
226 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
227 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
228 elastic_scattering_roundoff_error_prevention
229 );
230
232 {
233 log::debug<log_assemble_scattering_operator>() << "* assemble_scattering_operator_on_box(): out-scattering contribution: " << std::endl;
234 log::debug<log_assemble_scattering_operator>() << " 2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height " << std::endl;
235 log::debug<log_assemble_scattering_operator>() << " at (" << row_index << ", " << row_index << ")" << std::endl;
236 log::debug<log_assemble_scattering_operator>() << " rate = " << rate << std::endl;
237 log::debug<log_assemble_scattering_operator>() << " Z_initial = " << Z_initial << std::endl;
238 log::debug<log_assemble_scattering_operator>() << " Z_final = " << Z_final << std::endl;
239 log::debug<log_assemble_scattering_operator>() << " box_volume = " << box_volume << std::endl;
240 log::debug<log_assemble_scattering_operator>() << " energy_height = " << energy_height << std::endl;
241 }
242
243 if (with_full_newton)
244 {
245 // We assume 'rate' to be independent of the potential, which is certainly true for phonon scattering, but also for most other processes.
246 // Also note that dQ/dphi is zero, hence no additional entries in the Jacobian matrix.
247
248 // residual contribution:
249 throw std::runtime_error("assemble_scattering_operator_on_box(): Newton not available!");
251 static_cast<std::size_t>(row_index),
252 2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height,
253 coupling_out_scatter,
254 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
255 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
256 quan.get_values(elem, index_H));
257 }
258
259 }
260 }
261
262
263 // Note: A scattering process with initial_kinetic_energy == final_kinetic_energy leads to both in- and out-scattering. Thus, do not try to merge the two if-statements above and below!
264
265 //
266 // In-Scattering
267 //
268 if (final_kinetic_energy <= kinetic_energy && final_kinetic_energy >= kinetic_energy) //in-scattering
269 {
270 const long col_index = quan.get_unknown_index(elem, static_cast<std::size_t>(initial_state_index_H));
271
272 if (col_index >= 0) //If final scatter state is invalid, no scattering considered!
273 {
275 double coefficient = 0;
276 switch (conf.she_discretization_type())
277 {
279 coefficient = -2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height
280 - generation_rate * Z_initial * box_volume * energy_height; // additional generation due to in-scattering
281 break;
283 coefficient = -2.0 * pi * rate * Z_final * box_volume * energy_height
284 - generation_rate * box_volume * energy_height; // additional generation due to in-scattering
285 break;
286 default:
287 throw std::runtime_error("assemble_scattering_operator_on_box(): Unknown SHE discretization type!");
288 }
290 static_cast<std::size_t>(row_index), static_cast<std::size_t>(col_index),
291 coefficient, // in-scattering
292 coupling_in_scatter,
293 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
294 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
295 elastic_scattering_roundoff_error_prevention
296 );
297
299 {
300 log::debug<log_assemble_scattering_operator>() << "* assemble_scattering_operator_on_box(): in-scattering contribution:" << std::endl;
301 log::debug<log_assemble_scattering_operator>() << " - 2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height" << std::endl;
302 log::debug<log_assemble_scattering_operator>() << " - generation_rate * Z_initial * box_volume * energy_height" << std::endl;
303 log::debug<log_assemble_scattering_operator>() << " at (" << row_index << ", " << col_index << ")" << std::endl;
304 log::debug<log_assemble_scattering_operator>() << " rate = " << rate << std::endl;
305 log::debug<log_assemble_scattering_operator>() << " Z_initial = " << Z_initial << std::endl;
306 log::debug<log_assemble_scattering_operator>() << " Z_final = " << Z_final << std::endl;
307 log::debug<log_assemble_scattering_operator>() << " box_volume = " << box_volume << std::endl;
308 log::debug<log_assemble_scattering_operator>() << " energy_height = " << energy_height << std::endl;
309 log::debug<log_assemble_scattering_operator>() << " generation_rate = " << generation_rate << std::endl;
310 }
311
312 if (with_full_newton)
313 {
314 // We assume 'rate' to be independent of the potential, which is certainly true for phonon scattering, but also for most other processes.
315 // Also note that dQ/dphi is zero, hence no additional entries in the Jacobian matrix.
316
317 // residual contribution:
318 throw std::runtime_error("assemble_scattering_operator_on_box(): Newton not available!");
320 static_cast<std::size_t>(row_index),
321 - 2.0 * pi * rate * Z_initial * Z_final * box_volume * energy_height
322 - generation_rate * Z_initial * box_volume * energy_height, // additional generation due to in-scattering
323 coupling_in_scatter,
324 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
325 viennashe::math::spherical_harmonics_iterator(expansion_order_mid, harmonics_it_id),
326 quan.get_values(elem, index_H));
327 } // with_full_newton
328
329 } // if col_index >= 0
330 } // in-scattering
331
332 } // for all scattering events (i.e. events within a process)
333
334 } //for all scattering processes
335 }
336
337
338 } //namespace she
339} //namespace viennashe
340
341#endif
Common routines for the assembly of SHE equations.
Writes a possibly scaled block matrix to the system matrix. Includes similar functionality for comput...
Defines a set of checker functors for micro-tests within ViennaSHE.
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
nonlinear_solver_config_type & nonlinear_solver()
Returns the configuration object for the nonlinear solver.
Definition: config.hpp:495
she_discretization_type_id she_discretization_type() const
Definition: config.hpp:360
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.
long id() const
Returns the current linear solver ID.
Definition: config.hpp:262
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.
Miscellaneous utilities.
harmonics_iteration_type
An enumeration of spherical harmonics types (all, even, odd) ViennaSHE supports.
Definition: forwards.h:159
@ EVEN_HARMONICS_ITERATION_ID
Definition: forwards.h:161
@ ODD_HARMONICS_ITERATION_ID
Definition: forwards.h:162
bool is_odd_assembly(T const &, T const &)
double cell_connection_length(MeshT const &, CellT const &, CellT const &)
Static dispatcher for computing the connection between two cells adjacent to a facet....
double averaged_density_of_states(SHEQuantity const &quan, viennashe::config::dispersion_relation_type const &dispersion, CellFacetType const &cell_facet, std::size_t index_H)
Returns the density of states around a vertex or an edge at total energy specified by index_H....
void assemble_scattering_operator_on_box(ScatterProcessesT const &scatter_processes, DeviceType const &device, viennashe::config const &conf, SHEQuantity const &quan, MatrixType &A, VectorType &b, ElementType const &elem, std::size_t index_H, CouplingMatrix const &coupling_in_scatter, CouplingMatrix const &coupling_out_scatter)
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)...
long energy_index_H(SHEQuantity const &quan, ElementType const &elem, long index_H_hint, double kinetic_energy)
Finds the closest H-index for the provided kinetic energy.
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,...
void subtract_folded_block_vector(VectorType &residual, std::size_t row_index, double prefactor, BlockMatrixType const &coupling_matrix, RowIndexIterator row_iter, ColumnIndexIterator const &col_iter_init, double const *fold_vector)
Writes a sub-matrix of a block matrix 'coupling_matrix' to the residual vector. The columns are 'fold...
The main ViennaSHE namespace. All functionality resides inside this namespace.
Definition: accessors.hpp:40
@ SHE_DISCRETIZATION_EVEN_ODD_ORDER_GENERALIZED_DF
Definition: forwards.h:146
@ SHE_DISCRETIZATION_EVEN_ODD_ORDER_DF
Definition: forwards.h:145
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.
File to gather all scattering operators.
Implementation of spherical harmonics plus helper functions.
static const double pi
Pi.
Definition: constants.hpp:34
@ newton_nonlinear_solver
Gummel iteration.
Definition: config.hpp:244
Defines the log keys used within the viennashe::she namespace.