ViennaSHE 1.3.0
Free open-source semiconductor device simulator using spherical harmonics expansions techniques.
assemble_common.hpp
Go to the documentation of this file.
1#ifndef VIENNASHE_SHE_ASSEMBLE_COMMON_HPP
2#define VIENNASHE_SHE_ASSEMBLE_COMMON_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
19// viennagrid
20#include "viennagrid/mesh/mesh.hpp"
21#include "viennagrid/algorithm/norm.hpp"
22#include "viennagrid/algorithm/volume.hpp"
23#include "viennagrid/algorithm/voronoi.hpp"
24
25// viennashe
26#include "viennashe/config.hpp"
29
33
35
38
40
41#include "viennashe/log/log.hpp"
43
44
49namespace viennashe
50{
51 namespace she
52 {
53 namespace detail
54 {
55 template <typename PotentialAccessorType,
56 typename ConfigType, typename TagType>
57 std::vector<long> get_potential_indices(PotentialAccessorType const & potential_index,
58 viennagrid::element<TagType, ConfigType> const & elem)
59 {
60 std::vector<long> ret;
61 for (std::size_t i=0; i<viennagrid::vertices(elem).size(); ++i)
62 {
63 if (potential_index(viennagrid::vertices(elem)[i]) >= 0)
64 ret.push_back(potential_index(viennagrid::vertices(elem)[i]));
65 }
66 return ret;
67 }
68
69 template <typename PotentialAccessorType,
70 typename ConfigType>
71 std::vector<long> get_potential_indices(PotentialAccessorType const & potential_index,
72 viennagrid::element<viennagrid::vertex_tag, ConfigType> const & elem)
73 {
74 std::vector<long> ret;
75 if (potential_index(elem) >= 0)
76 ret.push_back(potential_index(elem));
77 return ret;
78 }
79
80
81 template <typename T>
82 bool is_odd_assembly(T const &, T const &)
83 {
84 return false;
85 }
86
87 template <typename T, typename U>
88 bool is_odd_assembly(T const &, U const &)
89 {
90 return true;
91 }
92
97 template <typename MeshT, typename CellT>
98 double cell_connection_length(MeshT const &, CellT const &, CellT const &)
99 {
100 assert(bool("Logic error: cell_connection_length() for cell called!"));
101 return 1.0;
102 }
103
108 template <typename MeshT, typename FacetT, typename CellT>
109 double cell_connection_length(MeshT const & mesh, FacetT const & facet, CellT const &)
110 {
111 typedef typename viennagrid::result_of::const_coboundary_range<MeshT, FacetT, CellT>::type CellOnFacetContainer;
112 typedef typename viennagrid::result_of::iterator<CellOnFacetContainer>::type CellOnFacetIterator;
113
114 CellOnFacetContainer cells_on_facet(mesh, viennagrid::handle(mesh, facet));
115
116 if (cells_on_facet.size() < 2)
117 {
118 assert(bool("Logic error: cell_connection_length() called for facet on boundary!"));
119 }
120
121 CellOnFacetIterator cofit = cells_on_facet.begin();
122 CellT const & c1 = *cofit;
123 ++cofit;
124 CellT const & c2 = *cofit;
125
126 return viennagrid::norm_2(viennagrid::centroid(c1) - viennagrid::centroid(c2));
127 }
128
129 template <typename DeviceType>
130 bool has_contact_potential(DeviceType const & device, typename viennagrid::result_of::cell<typename DeviceType::mesh_type>::type const & c)
131 {
133 }
134
135 template <typename DeviceType, typename ElementType>
136 bool has_contact_potential(DeviceType const &, ElementType const &)
137 {
138 return false;
139 }
140
141 }
142
143
152 template <typename DispersionRelation>
153 double integral_Z_over_hk(double energy_minus, double energy_plus, DispersionRelation const & dispersion)
154 {
155 if (energy_plus < energy_minus)
156 {
157 std::stringstream ss;
158 ss << "Invalid interval in integral_Z_over_hk(): [" << energy_minus << ", " << energy_plus << "]";
160 }
161
162 return 0.5 * ( dispersion.density_of_states(energy_plus) * dispersion.velocity(energy_plus)
163 - dispersion.density_of_states(energy_minus) * dispersion.velocity(energy_minus) );
164 }
165
166
176 template <typename MatrixType, typename CellType, typename PolarityTag>
177 MatrixType coupling_matrix_in_direction_impl(MatrixType const & m1, MatrixType const & m2, MatrixType const & m3,
178 CellType const & c1, CellType const & c2, PolarityTag const & polarity,
179 viennagrid::cartesian_cs<1>)
180 {
181 typedef typename viennagrid::result_of::point<CellType>::type PointType;
182
183 (void)m2; (void)m3; //prevent unused variable warnings
184 PointType p3 = viennagrid::centroid(c2) - viennagrid::centroid(c1);
185 p3 /= viennagrid::norm(p3); //normalize p3
186
187 if (!p3[0])
188 {
189 log::error() << " Vertices equal! ";
190 log::debug<log_coupling_matrix_in_direction>() << c1 << std::endl;
191 log::debug<log_coupling_matrix_in_direction>() << c2 << std::endl;
192 throw coupled_vertices_equal_exception("Equal vertices encountered!");
193 }
194
195 //log::debug<log_coupling_matrix_in_direction>() << "Coefficients: " << p3;
198
199 MatrixType result = m1 * (p3[0] * sqrt(m_d / m_t));
200 return result;
201 }
202
203
213 template <typename MatrixType, typename CellType, typename PolarityTag>
214 MatrixType coupling_matrix_in_direction_impl(MatrixType const & m1, MatrixType const & m2, MatrixType const & m3,
215 CellType const & c1, CellType const & c2, PolarityTag const & polarity,
216 viennagrid::cartesian_cs<2>)
217 {
218 typedef typename viennagrid::result_of::point<CellType>::type PointType;
219
220 (void)m3; //prevent unused variable warnings
221 PointType p3 = viennagrid::centroid(c2) - viennagrid::centroid(c1);
222 p3 /= viennagrid::norm(p3); //normalize p3
223
224 if (!p3[0] && !p3[1])
225 {
226 log::error() << " Vertices equal! ";
227 log::debug<log_coupling_matrix_in_direction>() << c1 << std::endl;
228 log::debug<log_coupling_matrix_in_direction>() << c2 << std::endl;
230 }
231
232 //log::debug<log_coupling_matrix_in_direction>() << "Coefficients: " << p3;
235
236 MatrixType result = m1 * (p3[0] * sqrt(m_d / m_t));
237 result += m2 * (p3[1] * sqrt(m_d / m_t));
238 return result;
239 }
240
241
251 template <typename MatrixType, typename CellType, typename PolarityTag>
252 MatrixType coupling_matrix_in_direction_impl(MatrixType const & m1, MatrixType const & m2, MatrixType const & m3,
253 CellType const & c1, CellType const & c2, PolarityTag const & polarity,
254 viennagrid::cartesian_cs<3>)
255 {
256 typedef typename viennagrid::result_of::point<CellType>::type PointType;
257
258 PointType p3 = viennagrid::centroid(c2) - viennagrid::centroid(c1);
259 p3 /= viennagrid::norm(p3); //normalize p3
260
264
265 MatrixType result = m1 * (p3[0] * sqrt(m_d / m_t));
266 result += m2 * (p3[1] * sqrt(m_d / m_t));
267 result += m3 * (p3[2] * sqrt(m_d / m_l));
268
269 return result;
270 }
271
272
273
283 template <typename MatrixType, typename VertexType, typename PolarityTag>
284 MatrixType coupling_matrix_in_direction(MatrixType const & m1, MatrixType const & m2, MatrixType const & m3,
285 VertexType const & v1, VertexType const & v2, PolarityTag const & polarity)
286 {
287 typedef typename viennagrid::result_of::point<VertexType>::type PointType;
288 typedef typename viennagrid::result_of::coordinate_system<PointType>::type CoordinateSystemTag;
289 return coupling_matrix_in_direction_impl(m1, m2, m3, v1, v2, polarity, CoordinateSystemTag()); //using ViennaGrid vertices here
290 }
291
292
301 template <typename MatrixType, typename VectorType, typename IteratorType>
302 void write_boundary(VectorType & rhs,
303 std::size_t row_start,
304 double prefactor,
305 MatrixType const & coupling_matrix,
306 IteratorType row_iter)
307 {
308 assert(prefactor == prefactor && bool("Writing nan to boundary!"));
309
310 //this function is called only for odd rows (more precisely: odd spherical harmonics) and even cols (more precisely: even spherical harmonics)
311 //thus: lowest even-order harmonic is unequal to zero and goes to right hand side
312 std::size_t row = row_start;
313 for (; row_iter.valid(); ++row_iter)
314 {
315 rhs[row] -= prefactor * coupling_matrix(std::size_t(*row_iter), 0);
316 ++row;
317 }
318
319 }
320
321
322
324 template <typename DeviceType, typename SHEQuantity, typename FacetType>
325 double lower_kinetic_energy_at_facet(DeviceType const & device,
326 SHEQuantity const & quan,
327 FacetType const & facet,
328 std::size_t index_H)
329 {
330 typedef typename DeviceType::mesh_type MeshType;
331
332 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
333
334 typedef typename viennagrid::result_of::const_coboundary_range<MeshType, FacetType, CellType>::type CellOnFacetContainer;
335 typedef typename viennagrid::result_of::iterator<CellOnFacetContainer>::type CellOnFacetIterator;
336
337 CellOnFacetContainer cells_on_facet(device.mesh(), viennagrid::handle(device.mesh(), facet));
338
339 CellOnFacetIterator cofit = cells_on_facet.begin();
340 CellType const & c1 = *cofit;
341 ++cofit;
342
343 if (cofit == cells_on_facet.end())
344 return quan.get_kinetic_energy(c1, index_H);
345
346 CellType const & c2 = *cofit;
347
348 if (index_H == 0)
349 return quan.get_kinetic_energy(c1, index_H) + quan.get_kinetic_energy(c2, index_H) / 2.0;
350
351 return ( quan.get_kinetic_energy(c1, index_H - 1) + quan.get_kinetic_energy(c2, index_H - 1)
352 + quan.get_kinetic_energy(c1, index_H) + quan.get_kinetic_energy(c2, index_H)
353 ) / 4.0;
354 }
355
356
358 template <typename DeviceType, typename SHEQuantity, typename FacetType>
359 double upper_kinetic_energy_at_facet(DeviceType const & device,
360 SHEQuantity const & quan,
361 FacetType const & facet,
362 std::size_t index_H)
363 {
364 typedef typename DeviceType::mesh_type MeshType;
365
366 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
367
368 typedef typename viennagrid::result_of::const_coboundary_range<MeshType, FacetType, CellType>::type CellOnFacetContainer;
369 typedef typename viennagrid::result_of::iterator<CellOnFacetContainer>::type CellOnFacetIterator;
370
371 CellOnFacetContainer cells_on_facet(device.mesh(), viennagrid::handle(device.mesh(), facet));
372
373 CellOnFacetIterator cofit = cells_on_facet.begin();
374 CellType const & c1 = *cofit;
375 ++cofit;
376
377 if (cofit == cells_on_facet.end())
378 return quan.get_kinetic_energy(c1, index_H);
379
380 CellType const & c2 = *cofit;
381
382 if (index_H == quan.get_value_H_size() - 1)
383 return quan.get_kinetic_energy(c1, index_H) + quan.get_kinetic_energy(c2, index_H) / 2.0;
384
385 return ( quan.get_kinetic_energy(c1, index_H + 1) + quan.get_kinetic_energy(c2, index_H + 1)
386 + quan.get_kinetic_energy(c1, index_H) + quan.get_kinetic_energy(c2, index_H)
387 ) / 4.0;
388 }
389
391 {
392 public:
393
394 template <typename SHEQuantity, typename CellType>
395 double operator()(SHEQuantity const & quan,
396 CellType const & c1,
397 CellType const & c2,
398 std::size_t index_H) const
399 {
400 const double distance = viennagrid::norm_2(viennagrid::centroid(c1) - viennagrid::centroid(c2));
401
402 const double ekin_v2 = quan.get_kinetic_energy(c2, index_H);
403 const double ekin_v1 = quan.get_kinetic_energy(c1, index_H);
404
405 return (ekin_v2 - ekin_v1) / distance; //Note: This works for electrons and holes
406 }
407
408 };
409
411 template <typename SHEQuantity, typename ElementType>
412 double box_height(SHEQuantity const & quan,
413 ElementType const & elem,
414 std::size_t index_H)
415 {
416 //NOTE: Don't try to be clever by being more accurate.
417 // If the box height on the band edge is not taken uniform, carrier conservation is lost!
418 double energy_height = 0;
419
420 if (index_H > 0)
421 energy_height = std::fabs(quan.get_kinetic_energy(elem, index_H) - quan.get_kinetic_energy(elem, index_H-1));
422
423 if (index_H < quan.get_value_H_size() - 1)
424 energy_height += std::fabs(quan.get_kinetic_energy(elem, index_H+1) - quan.get_kinetic_energy(elem, index_H));
425
426 return energy_height / 2.0;
427 }
428
429
431 template <typename SHEQuantity,
432 typename CellFacetType>
433 double averaged_density_of_states(SHEQuantity const & quan,
435 CellFacetType const & cell_facet,
436 std::size_t index_H)
437 {
438
439 // Apply some simple averaging over the box center, the upper and the lower energy at the box boundary. Can (should?) be replaced by better integration routines.
440 double Z = dispersion.density_of_states(quan.get_kinetic_energy(cell_facet, index_H));
441 double num_contributions = 1;
442
443 if (index_H < quan.get_value_H_size() - 1)
444 {
445 //evaluate density of states at upper boundary of the box:
446 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
447 + quan.get_kinetic_energy(cell_facet, index_H + 1)) / 2.0;
448 Z += dispersion.density_of_states(kin_energy);
449 ++num_contributions;
450 }
451 if (index_H > 0)
452 {
453 //evaluate density of states at lower boundary of the box:
454 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
455 + quan.get_kinetic_energy(cell_facet, index_H - 1)) / 2.0;
456 Z += dispersion.density_of_states(kin_energy);
457 ++num_contributions;
458 }
459 Z /= num_contributions;
460
461 return Z;
462 }
463
464
475 template <typename SHEQuantity,
476 typename CellFacetType>
477 double integral_v(SHEQuantity const & quan,
479 CellFacetType const & cell_facet,
480 std::size_t index_H)
481 {
482
483 // Apply some simple averaging over the box center, the upper and the lower energy at the box boundary. Can (should?) be replaced by better integration routines.
484 double v = dispersion.velocity(quan.get_kinetic_energy(cell_facet, index_H));
485 double num_contributions = 1;
486
487 if (index_H < quan.get_value_H_size() - 1)
488 {
489 //evaluate density of states at upper boundary of the box:
490 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
491 + quan.get_kinetic_energy(cell_facet, index_H + 1)) / 2.0;
492 v += dispersion.velocity(kin_energy);
493 ++num_contributions;
494 }
495 if (index_H > 0)
496 {
497 //evaluate density of states at lower boundary of the box:
498 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
499 + quan.get_kinetic_energy(cell_facet, index_H - 1)) / 2.0;
500 v += dispersion.velocity(kin_energy);
501 ++num_contributions;
502 }
503 v /= num_contributions;
504
505 return v * box_height(quan, cell_facet, index_H);
506
507 }
508
519 template <typename SHEQuantity,
520 typename CellFacetType>
521 double integral_vZ(SHEQuantity const & quan,
523 CellFacetType const & cell_facet,
524 std::size_t index_H)
525 {
526
527 // Apply some simple averaging over the box center, the upper and the lower energy at the box boundary. Can (should?) be replaced by better integration routines.
528 double kin_energy_center = quan.get_kinetic_energy(cell_facet, index_H);
529 double vZ = dispersion.velocity(kin_energy_center) * dispersion.density_of_states(kin_energy_center);
530 double num_contributions = 1;
531
532 if (index_H < quan.get_value_H_size() - 1)
533 {
534 //evaluate density of states at upper boundary of the box:
535 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
536 + quan.get_kinetic_energy(cell_facet, index_H + 1)) / 2.0;
537 vZ += dispersion.velocity(kin_energy) * dispersion.density_of_states(kin_energy);
538 ++num_contributions;
539 }
540 if (index_H > 0)
541 {
542 //evaluate density of states at lower boundary of the box:
543 double kin_energy = (quan.get_kinetic_energy(cell_facet, index_H)
544 + quan.get_kinetic_energy(cell_facet, index_H - 1)) / 2.0;
545 vZ += dispersion.velocity(kin_energy) * dispersion.density_of_states(kin_energy);
546 ++num_contributions;
547 }
548 vZ /= num_contributions;
549
550 return vZ * box_height(quan, cell_facet, index_H);
551 }
552
554 template <typename SHEQuantity, typename CellType>
555 double averaged_kinetic_energy(SHEQuantity const & quan,
556 CellType const & c,
557 std::size_t index_H)
558 {
559 double kin_energy = 0.0;
560
561 kin_energy = quan.get_kinetic_energy(c, index_H);
562
563 if (kin_energy <= 0.0)
564 {
565 if (index_H < quan.get_value_H_size() - 1)
566 return quan.get_kinetic_energy(c, index_H + 1) / 2.0;
567 }
568
569 return kin_energy;
570 }
571
572
573 } //namespace she
574} //namespace viennashe
575
576#endif
577
Writes a possibly scaled block matrix to the system matrix. Includes similar functionality for comput...
bool has_contact_potential(cell_type const &c) const
Returns true if a contact potential has been set for the respective vertex.
Definition: device.hpp:577
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
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
double velocity(double ekin, double theta=0, double phi=0) const
Returns the velocity as a function of kinetic energy (and angles, eventually)
Definition: dispersion.hpp:82
Exception for the case that a vertex has a coupling with itself.
Definition: exception.hpp:44
double operator()(SHEQuantity const &quan, CellType const &c1, CellType const &c2, std::size_t index_H) const
Exception for the case that neither electrons nor holes are selected for the simulation.
Definition: exception.hpp:149
The SHE configuration class is defined here.
Contains the dispersion relations for different materials and different polarities.
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.
Miscellaneous utilities.
logger< true > error()
Used to log errors. The logging level is logERROR.
Definition: log.hpp:301
VectorType::value_type norm_2(VectorType const &v)
Definition: linalg_util.hpp:37
std::vector< long > get_potential_indices(PotentialAccessorType const &potential_index, viennagrid::element< TagType, ConfigType > const &elem)
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....
bool has_contact_potential(DeviceType const &device, typename viennagrid::result_of::cell< typename DeviceType::mesh_type >::type const &c)
double integral_Z_over_hk(double energy_minus, double energy_plus, DispersionRelation const &dispersion)
Computes \int_a^b Z/(hbar * k) dE, where a and b are the energy boundaries, Z is the density of state...
double integral_v(SHEQuantity const &quan, viennashe::config::dispersion_relation_type const &dispersion, CellFacetType const &cell_facet, std::size_t index_H)
Computes \int v dH using a Simpson rule, where v is velocity.
double lower_kinetic_energy_at_facet(DeviceType const &device, SHEQuantity const &quan, FacetType const &facet, std::size_t index_H)
Returns the lower kinetic energy for the discretization box associated with the edge 'edge'.
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....
MatrixType coupling_matrix_in_direction(MatrixType const &m1, MatrixType const &m2, MatrixType const &m3, VertexType const &v1, VertexType const &v2, PolarityTag const &polarity)
Returns dot(M, n), where M=(m1, m2, m3) is the vector of coupling matrices, and n = v2 - v1 is the di...
MatrixType coupling_matrix_in_direction_impl(MatrixType const &m1, MatrixType const &m2, MatrixType const &m3, CellType const &c1, CellType const &c2, PolarityTag const &polarity, viennagrid::cartesian_cs< 1 >)
Returns dot(M, n), where M=(m1, m2, m3) is the vector of coupling matrices, and n = v2 - v1 is the di...
double integral_vZ(SHEQuantity const &quan, viennashe::config::dispersion_relation_type const &dispersion, CellFacetType const &cell_facet, std::size_t index_H)
Computes \int v Z dH using a Simpson rule, where v is velocity and Z is the density of states.
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)...
void write_boundary(VectorType &rhs, std::size_t row_start, double prefactor, MatrixType const &coupling_matrix, IteratorType row_iter)
Write Dirichlet boundary conditions to right hand side.
double upper_kinetic_energy_at_facet(DeviceType const &device, SHEQuantity const &quan, FacetType const &facet, std::size_t index_H)
Returns the upper kinetic energy of the discretization box for the edge 'edge'.
double averaged_kinetic_energy(SHEQuantity const &quan, CellType const &c, std::size_t index_H)
Returns the averaged kinetic energy in the box centered at a vertex v with total energy index index_H...
The main ViennaSHE namespace. All functionality resides inside this namespace.
Definition: accessors.hpp:40
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.
Implementation of spherical harmonics plus helper functions.
static double longitudinal_effective_mass(viennashe::carrier_type_id ctype)
Definition: all.hpp:66
static double transverse_effective_mass(viennashe::carrier_type_id ctype)
Definition: all.hpp:74
static double dos_effective_mass(viennashe::carrier_type_id ctype)
Definition: all.hpp:58
Defines the log keys used within the viennashe::she namespace.