ViennaSHE 1.3.0
Free open-source semiconductor device simulator using spherical harmonics expansions techniques.
assemble.hpp
Go to the documentation of this file.
1#ifndef VIENNASHE_DD_ASSEMBLE_HPP
2#define VIENNASHE_DD_ASSEMBLE_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
14
15 http://viennashe.sourceforge.net/
16
17 License: MIT (X11), see file LICENSE in the base directory
18=============================================================================== */
19
20// std
21#include <iostream>
22#include <fstream>
23#include <vector>
24
25// viennagrid
26#include "viennagrid/mesh/mesh.hpp"
27#include "viennagrid/mesh/coboundary_iteration.hpp"
28#include "viennagrid/algorithm/volume.hpp"
29
30// viennashe
31#include "viennashe/forwards.h"
32#include "viennashe/device.hpp"
42
43#include "viennashe/log/log.hpp"
44#include "viennashe/log_keys.h"
45
47
51
53
54
59namespace viennashe
60{
61
62 namespace detail
63 {
75 template <typename CellType, typename SpatialUnknownT, typename SHEUnknownT, typename MatrixType>
77 CellType const & cell,
78 SpatialUnknownT const & np_density,
79 SHEUnknownT const & f_np,
80 MatrixType & A, std::size_t row_index, double box_volume)
81 {
82 const double polarity = (f_np.get_carrier_type_id() == ELECTRON_TYPE_ID) ? -1.0 : 1.0;
83 equation_id equ_id = (f_np.get_carrier_type_id() == ELECTRON_TYPE_ID) ? conf.get_electron_equation() : conf.get_hole_equation();
84
85 if (equ_id == EQUATION_CONTINUITY) //Drift-Diffusion: Coupling term -|q|*n for electrons, |q|*p for holes
86 {
87 const long np_index = np_density.get_unknown_index(cell); // This is a hack and needs to be resolved with ViennaMath!
88 if (np_index >= 0) //here is a Dirichlet boundary condition or no simulation region
89 A(row_index, std::size_t(np_index)) = polarity * viennashe::physics::constants::q * box_volume;
90 }
91 else //SHE: -|q| * integral_{energies} f_n(E) * Z(E) dE for electrons, |q| * integral_{energies} f_p(E) * Z(E) dE for holes
92 {
93 // matrix entries (note: the following piece of code pretty much resembles the one in postproc/carrier_density.hpp - which is required to be consistent)
94 for (std::size_t index_H = 1; index_H < f_np.get_value_H_size() - 1; ++index_H)
95 {
96 long col_index = f_np.get_unknown_index(cell, index_H);
97 if (col_index >= 0)
98 {
99 double energy_mid = f_np.get_kinetic_energy(cell, index_H);
100 double energy_upper = (f_np.get_kinetic_energy(cell, index_H + 1) + energy_mid) / 2.0;
101 double Z = viennashe::she::averaged_density_of_states(f_np, conf.dispersion_relation(f_np.get_carrier_type_id()), cell, index_H);
102
103 if (energy_upper <= 0.0) //nothing to do if in band gap
104 continue;
105
106 double box_height = viennashe::she::box_height(f_np, cell, index_H);
107
108 // Check Y_{0,0} !!
109 A(row_index, std::size_t(col_index)) = polarity
111 * box_volume
112 * Z * conf.dispersion_relation(f_np.get_carrier_type_id()).symmetry_factor() * box_height; // Check Y_{0,0} !!
113 }
114 } // for index_H
115 }
116 }
117
126 template <typename DeviceType, typename CellType, typename SpatialUnknownT, typename SHEUnknownT>
128 CellType const & cell,
129 SpatialUnknownT const & np_density,
130 SHEUnknownT const & f_np)
131 {
132 const equation_id equ_id = (f_np.get_carrier_type_id() == ELECTRON_TYPE_ID) ? conf.get_electron_equation() : conf.get_hole_equation();
134
135 if (equ_id == EQUATION_CONTINUITY || !with_full_newton)
136 {
137 return np_density.get_value(cell);
138 }
139 else
140 {
142 return density_wrapper(cell);
143 }
144 }
145
146 }
147
162 template <typename DeviceType,
163 typename MatrixType,
164 typename VectorType>
165 void assemble_poisson(DeviceType const & device,
167 viennashe::config const & conf,
168 MatrixType & A,
169 VectorType & b)
170 {
171 typedef typename DeviceType::mesh_type MeshType;
172
173 typedef typename viennagrid::result_of::point<MeshType>::type PointType;
174 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
175
176 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
177 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
178
179 typedef typename viennagrid::result_of::const_facet_range<CellType>::type FacetOnCellContainer;
180 typedef typename viennagrid::result_of::iterator<FacetOnCellContainer>::type FacetOnCellIterator;
181
184
185 //
186 // Poisson equation: + eps * laplace psi - |q| * (n - p - doping) = 0
187 // Linearisation (Gummel): + eps * laplace dpsi - q * (n+p)/VT * dpsi = - eps * laplace psi + q * (n - p - doping)
188 // Note that the term q * (n+p)/VT is for Gummel damping
189 //
190
191 MeshType const & mesh = device.mesh();
192
194
195 // Set up filters:
198
199 // Quantity lookup:
200 SpatialUnknownType const & potential = quantities.get_unknown_quantity(viennashe::quantity::potential());
201 SpatialUnknownType const & n_density = quantities.get_unknown_quantity(viennashe::quantity::electron_density());
202 SpatialUnknownType const & p_density = quantities.get_unknown_quantity(viennashe::quantity::hole_density());
203 SHEUnknownType const & f_n = quantities.electron_distribution_function();
204 SHEUnknownType const & f_p = quantities.hole_distribution_function();
205
206 CellContainer cells(mesh);
207 for (CellIterator cit = cells.begin();
208 cit != cells.end();
209 ++cit)
210 {
211
212 const long row_index2 = potential.get_unknown_index(*cit);
213 if (row_index2 < 0) //here is a Dirichlet boundary condition
214 continue;
215 const std::size_t row_index = std::size_t(row_index2);
216
217 PointType centroid_cell = viennagrid::centroid(*cit);
218
219 const double potential_center = potential.get_value(*cit);
220 const double permittivity_center = permittivity(*cit);
221
222
223 b[row_index] = 0;
224 A(row_index, row_index) = 0;
225
226 //
227 // eps * laplace psi
228 //
229 FacetOnCellContainer facets(*cit);
230 for (FacetOnCellIterator focit = facets.begin();
231 focit != facets.end();
232 ++focit)
233 {
234 CellType const *other_cell_ptr = util::get_other_cell_of_facet(mesh, *focit, *cit);
235
236 if (!other_cell_ptr) continue; //Facet is on the boundary of the simulation domain -> homogeneous Neumann conditions
237
238 PointType centroid_other_cell = viennagrid::centroid(*other_cell_ptr);
239 PointType cell_connection = centroid_other_cell - centroid_cell;
240 PointType cell_connection_normalized = cell_connection / viennagrid::norm(cell_connection);
241 PointType facet_unit_normal = viennashe::util::outer_cell_normal_at_facet(*cit, *focit);
242
243 const long col_index = potential.get_unknown_index(*other_cell_ptr);
244 const double connection_len = viennagrid::norm_2(cell_connection);
245 const double weighted_interface_area = viennagrid::volume(*focit) * viennagrid::inner_prod(facet_unit_normal, cell_connection_normalized);
246 const double potential_outer = potential.get_value(*other_cell_ptr);
247
248 // off-diagonal contribution
249 if (col_index >= 0)
250 {
251 PointType facet_center = viennagrid::centroid(*focit); //TODO: Use intersection of facet plane with connection
252 double connection_in_cell = viennagrid::norm(facet_center - centroid_cell);
253 double connection_in_other_cell = viennagrid::norm(facet_center - centroid_other_cell);
254 const double permittivity_mean = (connection_in_cell + connection_in_other_cell) /
255 (connection_in_cell/permittivity_center + connection_in_other_cell/permittivity(*other_cell_ptr));
256
257 A(row_index, std::size_t(col_index)) = permittivity_mean * weighted_interface_area / connection_len;
258 A(row_index, row_index) -= permittivity_mean * weighted_interface_area / connection_len;
259 b[row_index] -= permittivity_mean * weighted_interface_area * (potential_outer - potential_center) / connection_len;
260 }
261 else if (potential.get_boundary_type(*other_cell_ptr) == BOUNDARY_DIRICHLET)
262 {
263 A(row_index, row_index) -= permittivity_center * weighted_interface_area / connection_len;
264 b[row_index] -= permittivity_center * weighted_interface_area / connection_len * (potential.get_boundary_value(*other_cell_ptr) - potential_center);
265 }
266 } //for facets
267
268 const double cell_volume = viennagrid::volume(*cit);
269
270 const double value_n = detail::get_carrier_density_for_poisson<DeviceType>(conf, *cit, n_density, f_n);
271 const double value_p = detail::get_carrier_density_for_poisson<DeviceType>(conf, *cit, p_density, f_p);
272
273 //
274 // Gummel-Damping: extra volume term q * (n + p) / V_T
275 //
276 if (!with_full_newton && viennashe::materials::is_semiconductor(device.get_material(*cit)))
277 {
279
280 A(row_index, row_index) -= cell_volume * viennashe::physics::constants::q * (value_n + value_p) / VT;
281 }
282
283 //
284 // - |q| * (n - p - doping)
285 //
286
287 // carrier contribution to Jacobian matrix - |q| * n + |q| * p
288 if (with_full_newton)
289 {
290 if (conf.with_electrons())
291 detail::assemble_poisson_carrier_coupling(conf, *cit, n_density, f_n, A, row_index, cell_volume);
292 if (conf.with_holes())
293 detail::assemble_poisson_carrier_coupling(conf, *cit, p_density, f_p, A, row_index, cell_volume);
294 }
295
296
297 // residual contribution
298 const double doping_n = device.get_doping_n(*cit);
299 const double doping_p = device.get_doping_p(*cit);
300
302 {
303 b[row_index] += cell_volume * viennashe::physics::constants::q * (value_n - doping_n - value_p + doping_p);
304 // *Fixed* charges
305 b[row_index] += fixed_charge(*cit) * cell_volume; // Charge has to be in As
306 }
307
308 if (conf.with_traps() && conf.with_trap_selfconsistency()
309 && viennashe::materials::is_semiconductor(device.get_material(*cit))) // *Trapped* charges
310 {
311 typedef typename DeviceType::trap_level_container_type trap_level_container_type;
312 typedef typename trap_level_container_type::const_iterator trap_iterator_type;
313
314 trap_level_container_type const & traps = device.get_trap_levels(*cit);
315
316 const std::size_t num_trap_unknowns = quantities.num_trap_unknown_indices(*cit);
317
318 if (num_trap_unknowns != traps.size())
319 throw viennashe::invalid_value_exception("The number of traps configured in the device does not match the number of unknowns for traps!", static_cast<double>(num_trap_unknowns));
320
321 if (num_trap_unknowns != 0)
322 {
323 std::size_t index = 0;
324 for (trap_iterator_type tit = traps.begin(); tit != traps.end(); ++tit, ++index)
325 {
326 const double occupancy = quantities.trap_occupancy(*cit, index);
327 // Charge has to be in As => sign * |q| * f_T * N_T * V_i
328 b[row_index] += tit->charge_sign() * viennashe::physics::constants::q * occupancy * tit->density() * cell_volume ;
329 } // for trap levels
330 }
331
332 } // trapped charges
333
334
335 } //for cells
336
337 } //assemble_poisson
338
339
340
350 template <typename DeviceType,
351 typename MatrixType,
352 typename VectorType>
353 void assemble_dd(DeviceType const & device,
355 viennashe::config const & conf,
356 carrier_type_id ctype,
357 MatrixType & A,
358 VectorType & b)
359 {
360 typedef typename DeviceType::mesh_type MeshType;
361
362 typedef typename viennagrid::result_of::point<MeshType>::type PointType;
363 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
364
365 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
366 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
367
368 typedef typename viennagrid::result_of::const_facet_range<CellType>::type FacetOnCellContainer;
369 typedef typename viennagrid::result_of::iterator<FacetOnCellContainer>::type FacetOnCellIterator;
370
372 typedef typename viennashe::contact_carrier_density_accessor<DeviceType> bnd_carrier_accessor;
373
374 MeshType const & mesh = device.mesh();
375
376 bnd_carrier_accessor bnd_carrier_density(device, ctype);
377
379
380 // Quantity lookup:
381 SpatialUnknownType const & potential = quantities.get_unknown_quantity(viennashe::quantity::potential());
382 SpatialUnknownType const & carrier_density = (ctype == ELECTRON_TYPE_ID) ? quantities.get_unknown_quantity(viennashe::quantity::electron_density())
384 SpatialUnknownType const & quantum_corr = (ctype == ELECTRON_TYPE_ID) ? quantities.get_unknown_quantity(viennashe::quantity::density_gradient_electron_correction())
386
387 double mobility = 1.0; //[KR] TODO: Add better mobility model here. Doesn't really matter as long as there is no recombination...
388
389 scharfetter_gummel flux_approximator(ctype);
390 scharfetter_gummel_dVi flux_approximator_dVi(ctype);
391 scharfetter_gummel_dVj flux_approximator_dVj(ctype);
392
393 CellContainer cells(mesh);
394 for (CellIterator cit = cells.begin();
395 cit != cells.end();
396 ++cit)
397 {
398 //log::debug<log_continuity_solver>() << "* electron_solver::assemble(): Iterating over vertex: " << vit->id() << std::endl;
399
400 const long row_index2 = carrier_density.get_unknown_index(*cit);
401 if (row_index2 < 0)
402 continue;
403 const std::size_t row_index = std::size_t(row_index2);
404
405 //get potential in box center:
406 const long pot_index_center = potential.get_unknown_index(*cit);
407 double potential_center = potential.get_value(*cit);
408
409 if (conf.with_quantum_correction())
410 potential_center += quantum_corr.get_value(*cit);
411
412 const double carrier_center = carrier_density.get_value(*cit);
413
414 PointType centroid_cell = viennagrid::centroid(*cit);
415
416 A(row_index, row_index) = 0.0; //make sure that there is no bogus in the diagonal
417 b[row_index] = 0.0;
418
419 FacetOnCellContainer facets(*cit);
420 for (FacetOnCellIterator focit = facets.begin();
421 focit != facets.end();
422 ++focit)
423 {
424 CellType const *other_cell_ptr = util::get_other_cell_of_facet(mesh, *focit, *cit);
425
426 if (!other_cell_ptr) continue;
427
428 const double T = 0.5 * (device.get_lattice_temperature(*cit) + device.get_lattice_temperature(*other_cell_ptr));
429
430 if ( (carrier_density.get_unknown_mask(*other_cell_ptr) || carrier_density.get_boundary_type(*other_cell_ptr) == BOUNDARY_DIRICHLET) )
431 {
432 PointType centroid_other_cell = viennagrid::centroid(*other_cell_ptr);
433 PointType cell_connection = centroid_other_cell - centroid_cell;
434 PointType cell_connection_normalized = cell_connection / viennagrid::norm(cell_connection);
435 PointType facet_unit_normal = viennashe::util::outer_cell_normal_at_facet(*cit, *focit);
436
437 const double connection_len = viennagrid::norm_2(centroid_cell - centroid_other_cell);
438 const double weighted_interface_area = viennagrid::volume(*focit) * viennagrid::inner_prod(facet_unit_normal, cell_connection_normalized);
439 double potential_outer = potential.get_value(*other_cell_ptr);
440 if (conf.with_quantum_correction())
441 potential_outer += quantum_corr.get_value(*other_cell_ptr);
442
443 const long pot_index_other = potential.get_unknown_index(*other_cell_ptr);
444
445 //
446 // div() part of continuity equation:
447 //
448 const long np_col_index = carrier_density.get_unknown_index(*other_cell_ptr);
449 if (np_col_index > -1) // no bc
450 {
451 A(row_index, std::size_t(np_col_index)) = weighted_interface_area * flux_approximator(0.0, 1.0,
452 potential_center, potential_outer,
453 connection_len, mobility, T);
454 }
455
456 A(row_index, row_index) += weighted_interface_area * flux_approximator(1.0, 0.0,
457 potential_center, potential_outer,
458 connection_len, mobility, T);
459
460 //
461 // Residual contribution
462 //
463 const double carrier_outer = viennashe::materials::is_conductor(device.get_material(*other_cell_ptr))
464 ? bnd_carrier_density(*cit, device.get_lattice_temperature(*other_cell_ptr))
465 : carrier_density.get_value(*other_cell_ptr);
466
467 b[row_index] -= weighted_interface_area * flux_approximator(carrier_center, carrier_outer,
468 potential_center, potential_outer,
469 connection_len, mobility, T);
470
471 if (!with_full_newton) //Gummel-type iteration ignores cross-couplings
472 continue;
473
474 //
475 // NEWTON-SOLVER: couplings for Jacobi matrix
476 //
477
478 // DG contributions
479 if (conf.with_quantum_correction())
480 {
481 const long corrpot_center_index = quantum_corr.get_unknown_index(*cit);
482 const long corrpot_outer_index = quantum_corr.get_unknown_index(*other_cell_ptr);
483 if (corrpot_center_index >= 0)
484 {
485 const double gamma_outer = quantum_corr.get_value(*other_cell_ptr);
486 const double gamma_center = quantum_corr.get_value(*cit);
487 A(row_index, std::size_t(corrpot_center_index)) = weighted_interface_area *
488 flux_approximator_dVj(carrier_center, carrier_outer,
489 gamma_center, gamma_outer,
490 connection_len, mobility, T);
491 }
492 if (corrpot_outer_index >= 0)
493 {
494 const double gamma_outer = quantum_corr.get_value(*other_cell_ptr);
495 const double gamma_center = quantum_corr.get_value(*cit);
496 A(row_index, std::size_t(corrpot_center_index)) = weighted_interface_area *
497 flux_approximator_dVj(carrier_center, carrier_outer,
498 gamma_center, gamma_outer,
499 connection_len, mobility, T);
500 }
501 }
502
503 // off-diagonal contribution
504 if (pot_index_other >= 0)
505 {
506 A(row_index, std::size_t(pot_index_other)) = weighted_interface_area *
507 flux_approximator_dVj(carrier_center, carrier_outer,
508 potential_center, potential_outer,
509 connection_len, mobility, T);
510 }
511
512 // 'diagonal' contribution
513 A(row_index, std::size_t(pot_index_center)) += weighted_interface_area *
514 flux_approximator_dVi(carrier_center, carrier_outer,
515 potential_center, potential_outer,
516 connection_len, mobility, T);
517 }
518
519 } //for edges
520
521 } //for vertices
522 }
523
524
525 namespace detail
526 {
538 inline double density_gradient_flux(double E, double pot_i, double gamma_i, double T_i, double pot_j, double gamma_j, double T_j)
539 {
540 const double kB = viennashe::physics::constants::kB;
541 const double q = viennashe::physics::constants::q;
542 const double kbT_i = (kB * T_i) * 2.0;
543 const double kbT_j = (kB * T_j) * 2.0;
544 return ( (q * (pot_j + gamma_j) - E) / kbT_j) - ((q * (pot_i + gamma_i) - E) / kbT_i);
545 }
546 }
547
558 template <typename DeviceType, typename MatrixType, typename VectorType>
559 void assemble_density_gradient(DeviceType const & device,
561 viennashe::config const & conf,
562 carrier_type_id ctype,
563 MatrixType & A,
564 VectorType & b)
565 {
566 typedef typename DeviceType::mesh_type MeshType;
567
568 typedef typename viennagrid::result_of::point<MeshType>::type PointType;
569 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
570
571 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
572 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
573
574 typedef typename viennagrid::result_of::const_facet_range<CellType>::type FacetOnCellContainer;
575 typedef typename viennagrid::result_of::iterator<FacetOnCellContainer>::type FacetOnCellIterator;
576
578
579 MeshType const & mesh = device.mesh();
580
582
583 // Quantity lookup:
584 SpatialUnknownType const & potential = quantities.get_unknown_quantity(viennashe::quantity::potential());
585 SpatialUnknownType const & quantum_corr = (ctype == ELECTRON_TYPE_ID) ? quantities.get_unknown_quantity(viennashe::quantity::density_gradient_electron_correction())
587
588 const double kB = viennashe::physics::constants::kB;
589 const double q = viennashe::physics::constants::q;
590 const double lambda = conf.density_gradient(ctype).lambda();
591
593 const double coeff_b = (viennashe::physics::constants::hbar * viennashe::physics::constants::hbar) / (6.0 * lambda * m0 * q);
594
595 const double nominal_band_edge = viennashe::physics::get_band_edge(ctype);
596
597 CellContainer cells(mesh);
598 for (CellIterator cit = cells.begin();
599 cit != cells.end();
600 ++cit)
601 {
602 const long row_index2 = quantum_corr.get_unknown_index(*cit);
603 if ( row_index2 < 0 ) //here is a Dirichlet boundary condition
604 continue;
605 const std::size_t row_index = std::size_t(row_index2);
606
607 const double potential_center = potential.get_value(*cit);
608 const double gamma_center = quantum_corr.get_value(*cit);
609
610 // Reset matrix and rhs for the current index
611 A(row_index, row_index) = 0.0;
612 b[row_index] = 0.0;
613
614 PointType centroid_cell = viennagrid::centroid(*cit);
615 const double box_volume = viennagrid::volume(*cit);
616
617 FacetOnCellContainer facets(*cit);
618 for (FacetOnCellIterator focit = facets.begin();
619 focit != facets.end();
620 ++focit)
621 {
622 CellType const *other_cell_ptr = util::get_other_cell_of_facet(mesh, *focit, *cit);
623
624 if (!other_cell_ptr) continue;
625
626 const double T = 0.5 * (device.get_lattice_temperature(*cit) + device.get_lattice_temperature(*other_cell_ptr));
627
628 PointType centroid_other_cell = viennagrid::centroid(*other_cell_ptr);
629 PointType cell_connection = centroid_other_cell - centroid_cell;
630 PointType cell_connection_normalized = cell_connection / viennagrid::norm(cell_connection);
631 PointType facet_unit_normal = viennashe::util::outer_cell_normal_at_facet(*cit, *focit);
632
633 const double connection_len = viennagrid::norm_2(centroid_cell - centroid_other_cell);
634 const double weighted_interface_area = viennagrid::volume(*focit) * viennagrid::inner_prod(facet_unit_normal, cell_connection_normalized);
635
636 const long col_index = quantum_corr.get_unknown_index(*other_cell_ptr);
637
638 const double c_ij = coeff_b * (weighted_interface_area / connection_len);
639 const double potential_outer = potential.get_value(*other_cell_ptr);
640
641 // off-diagonal contribution
642 if ( col_index >= 0 )
643 {
644 const double gamma_outer = quantum_corr.get_value(*other_cell_ptr);
645
646 A(row_index, std::size_t(col_index)) -= c_ij * q / (kB * T * 2.0);
647 A(row_index, row_index) += c_ij * q / (kB * T * 2.0);
648
649 b[row_index] += c_ij * detail::density_gradient_flux(nominal_band_edge, potential_center, gamma_center, T, potential_outer, gamma_outer, T);
650 }
651 else if ( quantum_corr.get_boundary_type(*other_cell_ptr) == BOUNDARY_DIRICHLET)
652 {
653 const double gamma_outer = quantum_corr.get_boundary_value(*other_cell_ptr);
654
655 A(row_index, row_index) += c_ij * q / (kB * T * 2.0);
656
657 b[row_index] += c_ij * detail::density_gradient_flux(nominal_band_edge, potential_center, gamma_center, T, potential_outer, gamma_outer, T);
658 }
659
660 //
661 // CHECK THIS ...
662 if ( with_full_newton ) //Gummel-type iteration ignores cross-couplings
663 {
664 const long pot_index_outer = potential.get_unknown_index(*other_cell_ptr);
665 const long pot_index_center = potential.get_unknown_index(*cit);
666
667 // off-diagonal contribution
668 if ( pot_index_outer >= 0 )
669 A(row_index, std::size_t(pot_index_outer)) -= c_ij * q / (kB * T * 2.0);
670
671 // 'diagonal' contribution
672 A(row_index, std::size_t(pot_index_center)) += c_ij * q / (kB * T * 2.0);
673 }
674
675 if ( quantum_corr.get_boundary_type(*other_cell_ptr) == BOUNDARY_ROBIN )
676 {
677 // Get boundary coefficients
678 robin_boundary_coefficients<double> robin_coeffs = quantum_corr.get_boundary_values(*other_cell_ptr);
679 const double alpha = robin_coeffs.alpha;
680 const double beta = robin_coeffs.beta;
681
682 // Apply Robin-condition
683 A(row_index, row_index) -= alpha * weighted_interface_area ;
684 b[row_index] += (beta + alpha * gamma_center) * weighted_interface_area;
685 }
686 } //for facets
687
688 // volume contributions
689 A(row_index, row_index) += box_volume;
690 b[row_index] -= box_volume * gamma_center;
691
692 } //for vertices
693
694 }
695
704 template <typename DeviceType, typename MatrixType, typename VectorType>
705 void assemble_heat(DeviceType const & device,
707 viennashe::config const & conf,
708 MatrixType & A,
709 VectorType & b)
710 {
711 typedef typename viennashe::she::timestep_quantities<DeviceType> QuantitiesType;
712 typedef typename DeviceType::mesh_type MeshType;
713
714 typedef typename viennagrid::result_of::point<MeshType>::type PointType;
715 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
716
717 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
718 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
719
720 typedef typename viennagrid::result_of::const_facet_range<CellType>::type FacetOnCellContainer;
721 typedef typename viennagrid::result_of::iterator<FacetOnCellContainer>::type FacetOnCellIterator;
722
724
725 //
726 // Poisson equation for heat: + kappa * laplace T = Q
727 //
728
729 MeshType const & mesh = device.mesh();
730
733
734 // Quantity lookup:
736
737 CellContainer cells(mesh);
738 for (CellIterator cit = cells.begin();
739 cit != cells.end();
740 ++cit)
741 {
742
743 const long row_index2 = lattice_temperature.get_unknown_index(*cit);
744 if (row_index2 < 0) //here is a Dirichlet boundary condition
745 continue;
746 const std::size_t row_index = std::size_t(row_index2);
747
748 PointType centroid_cell = viennagrid::centroid(*cit);
749
750 const double T_center = lattice_temperature.get_value(*cit);
751
752 const double kappa_center = diffusivity(*cit, T_center);
753
754 b[row_index] = 0;
755 A(row_index, row_index) = 0;
756
757 //
758 // K * laplace T
759 //
760 FacetOnCellContainer facets(*cit);
761 for (FacetOnCellIterator focit = facets.begin();
762 focit != facets.end();
763 ++focit)
764 {
765 CellType const *other_cell_ptr = util::get_other_cell_of_facet(mesh, *focit, *cit);
766
767 if (!other_cell_ptr) continue;
768
769 const long col_index = lattice_temperature.get_unknown_index(*other_cell_ptr);
770
771 PointType centroid_other_cell = viennagrid::centroid(*other_cell_ptr);
772 PointType cell_connection = centroid_other_cell - centroid_cell;
773 PointType cell_connection_normalized = cell_connection / viennagrid::norm(cell_connection);
774 PointType facet_unit_normal = viennashe::util::outer_cell_normal_at_facet(*cit, *focit);
775
776 const double connection_len = viennagrid::norm_2(centroid_cell - centroid_other_cell);
777 const double weighted_interface_area = viennagrid::volume(*focit) * viennagrid::inner_prod(facet_unit_normal, cell_connection_normalized);
778
779 const double T_outer = lattice_temperature.get_value(*other_cell_ptr);
780
781 // off-diagonal contribution
782 if (col_index >= 0)
783 {
784 PointType facet_center = viennagrid::centroid(*focit); //TODO: Use intersection of facet plane with connection
785 const double connection_in_cell = viennagrid::norm(facet_center - centroid_cell);
786 const double connection_in_other_cell = viennagrid::norm(facet_center - centroid_other_cell);
787 const double kappa_mean = (connection_in_cell + connection_in_other_cell) /
788 (connection_in_cell/kappa_center + connection_in_other_cell/diffusivity(*other_cell_ptr, T_outer));
789
790 A(row_index, std::size_t(col_index)) = kappa_mean * weighted_interface_area / connection_len;
791 A(row_index, row_index) -= kappa_mean * weighted_interface_area / connection_len;
792
793 b[row_index] -= kappa_mean * weighted_interface_area * (T_outer - T_center) / connection_len;
794 }
795 else if (lattice_temperature.get_boundary_type(*other_cell_ptr) == BOUNDARY_DIRICHLET)
796 {
797 A(row_index, row_index) -= kappa_center * weighted_interface_area / connection_len;
798
799 b[row_index] -= kappa_center * weighted_interface_area / connection_len * (lattice_temperature.get_boundary_value(*other_cell_ptr) - T_center);
800 }
801
802 } //for facets
803
804 const double volume = viennagrid::volume(*cit);
805
806 // residual contribution
807 b[row_index] += volume * quan_power_density(*cit); // / kappa_center
808
809 } //for vertices
810
811 } //assemble_poisson
812
825 template <typename DeviceType,
826 typename TimeStepQuantitiesT,
827 typename VertexT,
828 typename MatrixType,
829 typename VectorType>
830 void assemble( DeviceType const & device,
831 TimeStepQuantitiesT & quantities,
832 viennashe::config const & conf,
834 MatrixType & A,
835 VectorType & b)
836 {
837 //
838 // Improve this by using ViennaMath to dispatch
839 //
840
842 assemble_poisson(device, quantities, conf, A, b);
844 assemble_dd(device, quantities, conf, ELECTRON_TYPE_ID, A, b);
845 else if (quan.get_name() == viennashe::quantity::hole_density())
846 assemble_dd(device, quantities, conf, HOLE_TYPE_ID, A, b);
848 assemble_density_gradient(device, quantities, conf, ELECTRON_TYPE_ID, A, b);
850 assemble_density_gradient(device, quantities, conf, HOLE_TYPE_ID, A, b);
852 assemble_heat(device, quantities, conf, A, b);
853 else
855 }
856
857} //namespace viennashe
858
859#endif
Contains the definition of per-device accessors (read-only!) for various quantities.
Generic assembly of traps is implemented here.
Implementation of the Bernoulli function.
Defines a set of checker functors for micro-tests within ViennaSHE.
Exception thrown in the case that an equation assembler cannot be found.
Definition: exception.hpp:147
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
equation_id get_electron_equation() const
Definition: config.hpp:230
bool with_holes() const
Returns true if holes are considered in the simulation.
Definition: config.hpp:234
equation_id get_hole_equation() const
Definition: config.hpp:238
bool with_traps() const
Returns true if traps are considered in the simulation.
Definition: config.hpp:243
nonlinear_solver_config_type & nonlinear_solver()
Returns the configuration object for the nonlinear solver.
Definition: config.hpp:495
bool with_trap_selfconsistency() const
Returns true if traps are considered self-consistently in the simulation.
Definition: config.hpp:247
bool with_electrons() const
Returns true if electrons are considered in the simulation.
Definition: config.hpp:226
detail::density_gradient_config const & density_gradient(viennashe::carrier_type_id ctype) const
Definition: config.hpp:519
bool with_quantum_correction() const
Definition: config.hpp:512
double get_lattice_temperature(cell_type const &c) const
Returns the lattice temperature on a cell.
Definition: device.hpp:255
double get_doping_p(cell_type const &c) const
Returns the donator doping (in m^-3) in the specified cell.
Definition: device.hpp:393
long get_material(cell_type const &elem) const
Returns the material id of the provided cell.
Definition: device.hpp:502
MeshT const & mesh() const
Returns the underlying mesh.
Definition: device.hpp:145
double get_doping_n(cell_type const &c) const
Returns the donator doping (in m^-3) in the specified cell.
Definition: device.hpp:320
trap_level_container_type const & get_trap_levels(cell_type const &cell) const
Returns all the trap levels defined for the provided cell.
Definition: device.hpp:615
Defines the physical properties of a device, e.g. doping. This is the implementation for 2d and highe...
Definition: device.hpp:818
Accessor to get the diffusivity. Used in the assembly of the heat diffusion equation.
Definition: accessors.hpp:295
Accessor for fixed charges.
Definition: accessors.hpp:273
Power density accessor. Used to get the power density in the assembly of the heat diffusion equation.
Exception for the case that an invalid value (depends on the method called) is encountered.
Definition: exception.hpp:76
Accessor for obtaining the permittivity in the device.
Definition: accessors.hpp:254
Generic dispatcher for partial derivatives of the Scharfetter-Gummel discretization of electron and h...
Generic dispatcher for Scharfetter-Gummel discretization of electron and hold fluxes.
Dispatcher for Scharfetter-Gummel discretization of electron and hold fluxes.
An accessor for the carrier density in the device.
The main SHE simulator controller class. Acts as an accessor for all SHE quantities needed for the si...
UnknownSHEQuantityType const & electron_distribution_function() const
UnknownQuantityType & get_unknown_quantity(std::string quantity_name)
Returns a reference to the unkown quantity identified by its name.
UnknownSHEQuantityType const & hole_distribution_function() const
double trap_occupancy(CellType const &c, std::size_t inner_index) const
std::size_t num_trap_unknown_indices(CellType const &c) const
Returns the number of inner unknown indices (aka. degree of freedom - dof) for traps associated with ...
long id() const
Returns the current linear solver ID.
Definition: config.hpp:262
Contains the definition of a device class independent of the actual macroscopic model to be solved.
Helper routines for projecting normal components of a vector-valued quantity defined on edges to vert...
Defines several filter functors for the device. A filter functor returns true if the supplied argumen...
Contains forward declarations and definition of small classes that must be defined at an early stage.
Implementation of various utilities related to linear algebra.
A logging facility providing fine-grained control over logging in ViennaSHE.
A very simple material database. Needs to be replaced by something more versatile soon.
Miscellaneous utilities.
void assemble_poisson_carrier_coupling(viennashe::config const &conf, CellType const &cell, SpatialUnknownT const &np_density, SHEUnknownT const &f_np, MatrixType &A, std::size_t row_index, double box_volume)
Assembles, per cell, the carrier concentration coupling on the RHS of Poisson's equation.
Definition: assemble.hpp:76
double density_gradient_flux(double E, double pot_i, double gamma_i, double T_i, double pot_j, double gamma_j, double T_j)
Returns the quantum correction potential flux between two points (Density Gradient)
Definition: assemble.hpp:538
double get_carrier_density_for_poisson(viennashe::config const &conf, CellType const &cell, SpatialUnknownT const &np_density, SHEUnknownT const &f_np)
Returns the requested carrier concentration, calculated from a SHE or DD solution,...
Definition: assemble.hpp:127
bool is_semiconductor(long material_id)
Convenience function for checking whether the supplied material ID refers to a semiconductor.
Definition: all.hpp:156
double permittivity(long material_id)
Convenience function for returning the permittivity of the material identified by the ID provided.
Definition: all.hpp:214
double diffusivity(long material_id)
Definition: all.hpp:235
bool is_conductor(long material_id)
Convenience function for checking whether the supplied material ID refers to a metal.
Definition: all.hpp:147
VectorType::value_type norm_2(VectorType const &v)
Definition: linalg_util.hpp:37
double get_thermal_potential(double T)
Returns the thermal potential for the provided temperature.
Definition: physics.hpp:61
double get_band_edge(viennashe::carrier_type_id const &ctype)
Returns the band edge relative to the reference energy (mid-gap)
Definition: physics.hpp:98
std::string lattice_temperature()
std::string density_gradient_hole_correction()
std::string density_gradient_electron_correction()
std::string electron_density()
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....
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)...
viennagrid::result_of::point< viennagrid::element< CellTagT, WrappedConfigT > >::type outer_cell_normal_at_facet(viennagrid::element< CellTagT, WrappedConfigT > const &cell, viennagrid::element< FacetTagT, WrappedConfigT > const &facet)
Returns the unit outer normal of a facet with respect to the provided cell.
CellT const * get_other_cell_of_facet(MeshT const &mesh, FacetT const &facet, CellT const &cell)
Helper function returning a const-pointer to the 'second cell' of a facet, or NULL if there is no sec...
Definition: misc.hpp:196
The main ViennaSHE namespace. All functionality resides inside this namespace.
Definition: accessors.hpp:40
void assemble_poisson(DeviceType const &device, viennashe::she::timestep_quantities< DeviceType > const &quantities, viennashe::config const &conf, MatrixType &A, VectorType &b)
Assembles the potential block (i.e. the linear equations obtained from the discretization of the Pois...
Definition: assemble.hpp:165
carrier_type_id
Enumeration type for selecting the carrier type.
Definition: forwards.h:185
@ HOLE_TYPE_ID
Definition: forwards.h:188
@ ELECTRON_TYPE_ID
Definition: forwards.h:187
equation_id
An enumeration of all equation types ViennaSHE supports.
Definition: forwards.h:114
@ EQUATION_CONTINUITY
Definition: forwards.h:117
@ BOUNDARY_DIRICHLET
Definition: forwards.h:127
@ BOUNDARY_ROBIN
Definition: forwards.h:129
void assemble_density_gradient(DeviceType const &device, viennashe::she::timestep_quantities< DeviceType > const &quantities, viennashe::config const &conf, carrier_type_id ctype, MatrixType &A, VectorType &b)
Assembles the density gradient equation set for the given carrier type.
Definition: assemble.hpp:559
void assemble_heat(DeviceType const &device, viennashe::she::timestep_quantities< DeviceType > const &quantities, viennashe::config const &conf, MatrixType &A, VectorType &b)
Assembles the heat diffusion equation (HDE)
Definition: assemble.hpp:705
void assemble_dd(DeviceType const &device, viennashe::she::timestep_quantities< DeviceType > const &quantities, viennashe::config const &conf, carrier_type_id ctype, MatrixType &A, VectorType &b)
Definition: assemble.hpp:353
void assemble(DeviceType const &device, TimeStepQuantitiesT &quantities, viennashe::config const &conf, viennashe::unknown_quantity< VertexT > const &quan, MatrixType &A, VectorType &b)
Assemble a spatial quantity, where the equation is deduced from 'quan'.
Definition: assemble.hpp:830
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.
Scharfetter-Gummel stabilization of the continuity equations.
Returns the carrier density at contacts modelled as thermal baths (used by DD and SHE)
Definition: accessors.hpp:317
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
static const double mass_electron
Electron rest mass.
Definition: constants.hpp:42
@ newton_nonlinear_solver
Gummel iteration.
Definition: config.hpp:244
A container of all quantities defined for a certain timestep t.
Defines the log keys used within the main viennashe:: namespace.