ViennaSHE 1.3.0
Free open-source semiconductor device simulator using spherical harmonics expansions techniques.
simulator.hpp
Go to the documentation of this file.
1#ifndef VIENNASHE_SHE_SIMULATOR_HPP
2#define VIENNASHE_SHE_SIMULATOR_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// viennashe
19#include "viennashe/forwards.h"
20#include "viennashe/device.hpp"
24
25#include "viennashe/mapping.hpp"
26
27#include "viennashe/log/log.hpp"
28#include "viennashe/log_keys.h"
29
31
46
51
52// HDE
54
59namespace viennashe
60{
61
62 namespace detail
63 {
64
73 template<typename DeviceT, typename QuantityT>
74 void set_unknown_for_material(DeviceT const & device, QuantityT & quan, materials::checker const & material_check)
75 {
76 typedef typename DeviceT::mesh_type MeshType;
77
78 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
79 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
80
81 CellContainer cells(device.mesh());
82 for (CellIterator cit = cells.begin();
83 cit != cells.end();
84 ++cit)
85 {
86 if (!material_check(device.get_material(*cit)))
87 continue;
88
89 quan.set_unknown_mask(*cit, true);
90 } // for cells
91 } // set_unknown_for_material
92
93 template<typename DeviceT, typename QuantityT>
94 void set_she_unknown(DeviceT const & device, QuantityT & quan)
95 {
96 typedef typename DeviceT::mesh_type MeshType;
97
98 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
99
100 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
101 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
102
103 CellContainer cells(device.mesh());
104 for (CellIterator cit = cells.begin();
105 cit != cells.end();
106 ++cit)
107 {
108 quan.set_unknown_mask(*cit, false); //initialize
109
110 if (viennashe::materials::is_semiconductor(device.get_material(*cit))) // Semiconductor: good
111 quan.set_unknown_mask(*cit, true);
112 else if (viennashe::materials::is_conductor(device.get_material(*cit))) // Conductor: Set unknown if connected to semiconductor
113 {
114 CellType const * semi_cell = viennashe::util::get_connected_semiconductor_cell(device, *cit);
115
116 if (semi_cell)
117 quan.set_unknown_mask(*cit, true);
118 }
119 } // for cells
120 } // set_unknown_for_material
121
122
133 template<typename DeviceT, typename QuantityT, typename BoundaryValueAccessorT>
134 void set_boundary_for_material(DeviceT const & device,
135 QuantityT & quan,
136 materials::checker const & material_check,
137 BoundaryValueAccessorT boundary_value_accessor,
138 boundary_type_id bnd_id)
139 {
140 typedef typename DeviceT::mesh_type MeshType;
141
142 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
143 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
144
145 CellContainer cells(device.mesh());
146 for (CellIterator cit = cells.begin();
147 cit != cells.end();
148 ++cit)
149 {
150 if (!material_check(device.get_material(*cit)))
151 continue;
152
153 typename BoundaryValueAccessorT::value_type bnd_value = boundary_value_accessor(*cit);
154 quan.set_boundary_type(*cit, bnd_id);
155 quan.set_boundary_value(*cit, bnd_value);
156 quan.set_value(*cit, bnd_value);
157 } // for cells
158 } // set_boundary_for_material
159
160
170 template<typename DeviceT, typename CellT, typename FacetT>
171 void set_boundary_for_material(DeviceT const & device,
173 materials::checker const & material_check,
174 boundary_type_id bnd_id)
175 {
176 typedef typename DeviceT::mesh_type MeshType;
177
178 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
179 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
180
181 CellContainer cells(device.mesh());
182 for (CellIterator cit = cells.begin();
183 cit != cells.end();
184 ++cit)
185 {
186 if (!material_check(device.get_material(*cit)))
187 continue;
188
189 quan.set_boundary_type(*cit, bnd_id);
190 }
191 }
192
199 template<typename DeviceT, typename QuantityT, typename BoundaryValueAccessorT>
200 void set_initial_guess(DeviceT const & device,
201 QuantityT & quan,
202 BoundaryValueAccessorT const & initial_guess_accessor)
203 {
204 typedef typename DeviceT::mesh_type MeshType;
205
206 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
207 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
208
209 CellContainer cells(device.mesh());
210 for (CellIterator cit = cells.begin();
211 cit != cells.end();
212 ++cit)
213 {
214 quan.set_value(*cit, initial_guess_accessor(*cit));
215 }
216 }
217
218 } // namespace detail
219
220
221 // TODO: Think about whether this is a useful way of dealing with temperature. Doesn't seem right.
222 template<typename DeviceT, typename UnknownQuantityListT>
223 void transfer_quantity_to_device(DeviceT & device, UnknownQuantityListT const & quantities, viennashe::config const & conf)
224 {
225 typedef typename DeviceT::mesh_type MeshType;
226
227 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
228 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
229
230 //
231 // Transfer lattice temperature
232 if (conf.with_hde())
233 {
234 CellContainer cells(device.mesh());
235 for (CellIterator cit = cells.begin();
236 cit != cells.end();
237 ++cit)
238 {
239 const double TL = quantities.get_unknown_quantity(viennashe::quantity::lattice_temperature()).get_value(*cit);
241 }
242 }
243
244 //
245 // Insert transfer for other quantities here
246 //
247
248 } // transfer_quantity_to_device
249
256 template<typename UnknownQuantityT, typename VectorType>
257 double get_relative_update_norm(UnknownQuantityT const & spatial_quan, VectorType const & x)
258 {
259
260 double norm = 0;
261 for ( std::size_t j = 0; j < x.size(); ++j)
262 {
263 if (spatial_quan.get_name() == viennashe::quantity::potential())
264 {
265 norm += x.at(j) * x.at(j);
266 }
267 else
268 {
269 const double current_val = spatial_quan.values().at(j);
270 const double zw = (std::abs(current_val) > 0) ? (x.at(j) / current_val) : ( x.at(j) );
271 norm += zw * zw ;
272 }
273 }
274 return sqrt(norm);
275 } // get_relative_update_norm
276
285 template<typename DeviceT, typename CellT, typename FacetT>
286 double get_relative_update_norm(DeviceT const & device,
288 viennashe::unknown_quantity<CellT> const & spatial_quan,
289 viennashe::config const & conf)
290 {
291 typedef typename DeviceT::mesh_type MeshType;
292
293 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
294 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
295
297
299
300 double norm = 0;
301
302 // Set bandedge shift on vertices and edges:
303 CellContainer cells(device.mesh());
304 for (CellIterator cit = cells.begin();
305 cit != cells.end();
306 ++cit)
307 {
308 const double new_value = density_wrapper(*cit);
309 const double old_value = spatial_quan.get_value(*cit);
310 double zw = 0;
311 if (std::fabs(old_value) > 0)
312 zw = (new_value - old_value) / old_value;
313 else
314 zw = new_value;
315
316 norm += zw * zw;
317 }
318
319 return std::sqrt(norm);
320 } // get_relative_update_norm
321
322
323
330 template<typename DeviceT, typename TimeStepQuantitiesT, typename VectorT>
332 TimeStepQuantitiesT const & unknown_quantities,
333 VectorT const & x)
334 {
335 typedef typename DeviceT::mesh_type MeshType;
336
337 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
338 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
339
340 typedef typename TimeStepQuantitiesT::UnknownQuantityType UnknownQuantityType;
341
342 MeshType const & mesh = device.mesh();
343 CellContainer cells(mesh);
344
345 double potential_update_norm = 0;
346
347 for (std::size_t i=0; i<unknown_quantities.unknown_quantities().size(); ++i)
348 {
349 UnknownQuantityType const & current_quan = unknown_quantities.unknown_quantities()[i];
350
351 if (current_quan.get_name() != viennashe::quantity::potential())
352 continue;
353
354 for (CellIterator cit = cells.begin();
355 cit != cells.end();
356 ++cit)
357 {
358 long idx = current_quan.get_unknown_index(*cit);
359 if (idx >= 0)
360 potential_update_norm += x[std::size_t(idx)] * x[std::size_t(idx)];
361 }
362 }
363
364 return std::sqrt(potential_update_norm);
365 } // get_potential_update_norm_newton
366
373 template<typename DeviceT, typename UnknownQuantityT, typename VectorT>
374 double get_total_update_norm_newton(DeviceT const & device,
375 UnknownQuantityT const & unknown_quantities_,
376 VectorT const & x)
377 {
378 (void)device; (void)unknown_quantities_;
379 double total_update_norm = 0;
380
381 for ( std::size_t j = 0; j < x.size(); ++j)
382 total_update_norm += x.at(j) * x.at(j);
383
384 return total_update_norm;
385 } // get_total_update_norm_newton
386
394 template<typename DeviceT, typename TimeStepQuantitiesT, typename VectorType>
395 void update_quantities(DeviceT const & device,
396 viennashe::config const & conf,
397 TimeStepQuantitiesT & unknown_quantities,
398 VectorType const & x)
399 {
400 typedef typename DeviceT::mesh_type MeshType;
401
402 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
403 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
404
405 typedef typename TimeStepQuantitiesT::UnknownQuantityType UnknownQuantityType;
406
407 MeshType const & mesh = device.mesh();
408 CellContainer cells(mesh);
409
410 //
411 // Step 1: Prepare update and current value arrays (split up into the various quantities)
412 //
413 for (std::size_t i=0; i<unknown_quantities.unknown_quantities().size(); ++i)
414 {
415 UnknownQuantityType & current_quan = unknown_quantities.unknown_quantities()[i];
416
417 double norm_inf_update = 0;
418 double norm_inf_current = 0;
419
420 for (CellIterator cit = cells.begin();
421 cit != cells.end();
422 ++cit)
423 {
424 long idx = current_quan.get_unknown_index(*cit);
425 if (idx >= 0)
426 {
427 norm_inf_current = std::max(norm_inf_current, current_quan.get_value(*cit));
428 norm_inf_update = std::max(norm_inf_update, x[std::size_t(idx)]);
429 }
430 }
431
432 double norm_rel_update = norm_inf_current > 0 ? norm_inf_update / norm_inf_current : 1.0;
433
434 if (norm_inf_update == 0.0
435 //|| norm_rel_update < 1e-14 //no significant update at all, so move on to next quantity
436 )
437 continue;
438
439 double delta = 0.1;
440
441 std::size_t damping_iter_max = 10;
442 double alpha = 1.0;
443
444 for (std::size_t damping_iter = 0; damping_iter < damping_iter_max; ++damping_iter)
445 {
446 if (damping_iter == 0) //check whether a full Newton step is okay
447 alpha = 1.0;
448 else if (damping_iter == 1) //use logarithmic damping, cf. PhD thesis by Simlinger or Fischer
449 alpha = (1.0 + delta * std::log(norm_rel_update)) / (1.0 + delta * (norm_rel_update - 1.0));
450 else
451 alpha = conf.nonlinear_solver().damping() * alpha;
452
453 double update_norm = 0.0;
454 double current_norm = 0.0;
455
456 bool do_reject = false;
457
458 // Check to see whether new quantity makes sense:
459 for (CellIterator cit = cells.begin();
460 cit != cells.end();
461 ++cit)
462 {
463 long index = current_quan.get_unknown_index(*cit);
464 if (index >= 0)
465 {
466 double update_value = alpha * x[std::size_t(index)];
467 double current_value = current_quan.get_value(*cit);
468
469 update_norm += update_value * update_value;
470 current_norm += current_value * current_value;
471
472 if ( std::abs(current_value) != 0.0
473 && std::abs(current_value) < 0.5 * std::abs(alpha * update_value)
474 )
475 do_reject = true;
476
477 if (current_quan.get_logarithmic_damping() && current_value + update_value <= 0)
478 do_reject = true;
479 }
480 }
481
482 if (update_norm > current_norm)
483 do_reject = true;
484
485 // Don't accept updated variables if update is too large
486 if ( (damping_iter < damping_iter_max - 1) //last iterate is always accepted
487 && do_reject)
488 {
489 continue;
490 }
491
492 // write new values:
493 for (CellIterator cit = cells.begin();
494 cit != cells.end();
495 ++cit)
496 {
497 long index = current_quan.get_unknown_index(*cit);
498 if (index >= 0)
499 {
500 double update_value = alpha * x[std::size_t(index)];
501 double current_value = current_quan.get_value(*cit);
502
503 current_quan.set_value(*cit, current_value + update_value);
504 }
505 }
506 break;
507
508 } //for damping_iter
509
510
511 } //for quantities
512 }
513
523 template<typename DeviceT, typename CellT, typename FacetT, typename VectorType>
524 void update_quantity(DeviceT const & device,
527 viennashe::config const & conf,
528 VectorType const & x, bool force_no_damping = false)
529 {
530 typedef typename DeviceT::mesh_type MeshType;
531
532 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
533 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
534
535 typedef typename viennagrid::result_of::const_facet_range<MeshType>::type FacetContainer;
536 typedef typename viennagrid::result_of::iterator<FacetContainer>::type FacetIterator;
537
539
541
542 density_wrapper_type density_wrapper(conf, quan);
543
544 // Set bandedge shift on vertices and edges:
545 CellContainer cells(device.mesh());
546 for (CellIterator cit = cells.begin();
547 cit != cells.end();
548 ++cit)
549 {
550 for (std::size_t index_H = 0; index_H < quan.get_value_H_size(); ++index_H)
551 {
552 long index = quan.get_unknown_index(*cit, index_H);
553 if (index >= 0)
554 quan.set_values(*cit, index_H, &(x[static_cast<std::size_t>(index)]));
555 }
556
557 // write density:
558 if (force_no_damping)
559 {
560 spatial_quan.set_value(*cit, density_wrapper(*cit));
561 }
562 else
563 {
564 const double alpha = conf.nonlinear_solver().damping();
565 double new_value = density_wrapper(*cit);
566 const double current_value = spatial_quan.get_value(*cit);
567 if (spatial_quan.get_logarithmic_damping())
568 {
569 if (new_value < current_value * 1e-10) // prevent problems with fractional powers of negative numbers by limiting the update
570 new_value = current_value * 1e-10;
571
572 const double a1 = std::pow(current_value, 1.0 - alpha);
573 const double a2 = std::pow(new_value, alpha);
574
575 spatial_quan.set_value(*cit, a1 * a2);
576 }
577 else
578 {
579 spatial_quan.set_value(*cit, current_value * (1.0 - alpha) + new_value * alpha);
580 }
581 }
582 } // for vertices
583
584 FacetContainer facets(device.mesh());
585 for (FacetIterator fit = facets.begin();
586 fit != facets.end();
587 ++fit)
588 {
589 for (std::size_t index_H = 0; index_H < quan.get_value_H_size(); ++index_H)
590 {
591 long index = quan.get_unknown_index(*fit, index_H);
592 if (index >= 0)
593 quan.set_values(*fit, index_H, &(x[static_cast<std::size_t>(index)]));
594 }
595 }
596
597 } // update_quantity
598
606 template<typename DeviceT, typename VertexT, typename VectorType>
607 void update_quantity(DeviceT const & device,
609 double alpha,
610 VectorType const & x)
611 {
612 typedef typename DeviceT::mesh_type MeshType;
613
614 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
615 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
616
617 double update_norm = 0;
618 CellContainer cells(device.mesh());
619 for (CellIterator cit = cells.begin();
620 cit != cells.end();
621 ++cit)
622 {
623 double current_value = unknown_quantity.get_value(*cit);
624 double new_value = current_value;
625
626 if (unknown_quantity.get_boundary_type(*cit) == BOUNDARY_DIRICHLET) //boundary value
627 new_value = unknown_quantity.get_boundary_value(*cit);
628 else if (unknown_quantity.get_unknown_mask(*cit)) // interior value
629 new_value = current_value + x[std::size_t(unknown_quantity.get_unknown_index(*cit))];
630
631 update_norm += std::abs(new_value - current_value);
632
634 {
635 if (new_value < current_value * 1e-10) // prevent problems with fractional powers of negative numbers by limiting the update
636 {
637 new_value = current_value * 1e-10;
638 }
639
640 unknown_quantity.set_value(*cit, std::pow(current_value, 1.0 - alpha) * std::pow(new_value, alpha));
641 }
642 else
643 {
644 unknown_quantity.set_value(*cit, current_value * (1.0 - alpha) + new_value * alpha);
645 }
646 }
647
648 //log::info<log_simulator>() << "Norm of update (undamped): " << update_norm << std::endl;
649 } // update_quantity
650
659 template<typename DeviceT, typename VertexT, typename VectorType>
660 void update_quantity(DeviceT const & device,
662 viennashe::config const & conf,
663 VectorType const & x, bool force_no_damping = false)
664 {
665 if (force_no_damping) update_quantity(device, unknown_quantity, 1.0, x);
667 }
668
673 template <typename DeviceType>
675 {
676 private:
678
680 typedef std::vector<double> VectorType;
681
682 typedef typename DeviceType::mesh_type MeshType;
683
684 typedef typename viennagrid::result_of::vertex<MeshType>::type VertexType;
685 typedef typename viennagrid::result_of::edge<MeshType>::type EdgeType;
686 typedef typename viennagrid::result_of::cell<MeshType>::type CellType;
687
688 typedef typename viennagrid::result_of::const_cell_range<MeshType>::type CellContainer;
689 typedef typename viennagrid::result_of::iterator<CellContainer>::type CellIterator;
690
691 public:
692
693 typedef DeviceType device_type;
694
696
700
702
705
707
711
712
718 simulator(DeviceType & device, viennashe::config const & conf = viennashe::config()) : p_device_(&device), config_(conf)
719 {
720 quantities_history_.push_back(SHETimeStepQuantitiesT());
721
722 // Step 1: Doping must be available on cells
723 //setup_doping_on_vertices(device);
724
725 if(conf.setup_insulator_distances())
726 {
727 throw std::runtime_error("TODO: setup_insulator_distances()");
728 //setup_insulator_distances(device);
729 }
730
731 // ensure doping in vicinity of contact is constant
733
734 // push quantities
735 // (note that by default all values are 'known' default values, so one has to specify the unknown regions later):
736 std::size_t cell_num = viennagrid::cells(device.mesh()).size();
737 std::size_t facet_num = viennagrid::facets(device.mesh()).size();
738
739 // electrostatic potential:
744
745 // electron and hole densities:
746 contact_carrier_density_accessor<DeviceType> contact_carrier_density_holes(device, HOLE_TYPE_ID, true);
747 contact_carrier_density_accessor<DeviceType> contact_carrier_density_electrons(device, ELECTRON_TYPE_ID, true);
749 if (conf.with_electrons())
750 {
751 if (conf.get_electron_equation() == EQUATION_CONTINUITY) //enable standard DD instead of SHE
753 }
754 detail::set_boundary_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_CONDUCTOR_ID), contact_carrier_density_electrons, BOUNDARY_DIRICHLET);
755
756 detail::set_initial_guess(device, quantities().unknown_quantities().back(), contact_carrier_density_electrons);
757
758 quantities().unknown_quantities().back().set_logarithmic_damping(true);
759
761 if (conf.with_holes())
762 {
763 if (conf.get_hole_equation() == EQUATION_CONTINUITY) // enable standard DD instead of SHE
765 }
766 detail::set_boundary_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_CONDUCTOR_ID), contact_carrier_density_holes, BOUNDARY_DIRICHLET);
767
768
769 detail::set_initial_guess(device, quantities().unknown_quantities().back(), contact_carrier_density_holes);
770
771 quantities().unknown_quantities().back().set_logarithmic_damping(true);
772
773 // Density gradient: Correction potential for ELECTRONS
775 if (conf.quantum_correction()) detail::set_unknown_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_SEMICONDUCTOR_ID));
776 // BND Dirichlet with gamma=0 at conductors
778 // BND for insulators
779 if (conf.density_gradient(viennashe::ELECTRON_TYPE_ID).boundary_type() == viennashe::BOUNDARY_DIRICHLET)
780 {
781 if (conf.quantum_correction())
782 viennashe::log::info<viennashe::log_simulator>() << "* simulator(): Density Gradient (electrons) ... Insulator boundary condition is DIRICHLET " << std::endl;
783 const double dg_bnd_value = conf.density_gradient(viennashe::ELECTRON_TYPE_ID).dirichlet_boundary_value();
785 }
786 else
787 {
788 if (conf.quantum_correction())
789 viennashe::log::info<viennashe::log_simulator>() << "* simulator(): Density Gradient (electrons) ... Insulator boundary condition is ROBIN " << std::endl;
790 robin_boundary_coefficients<double> robin_coeffs_n = conf.density_gradient(viennashe::ELECTRON_TYPE_ID).robin_coeffs();
792 }
793
794 // Density gradient: Correction potential for HOLES
796 if (conf.quantum_correction()) detail::set_unknown_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_SEMICONDUCTOR_ID));
797 // BND Dirichlet with gamma=0 at conductors
799 // BND for insulators
800 if (conf.density_gradient(viennashe::HOLE_TYPE_ID).boundary_type() == viennashe::BOUNDARY_DIRICHLET)
801 {
802 if (conf.quantum_correction())
803 viennashe::log::info<viennashe::log_simulator>() << "* simulator(): Density Gradient (holes) ... Insulator boundary condition is DIRICHLET " << std::endl;
804 const double dg_bnd_value = conf.density_gradient(viennashe::HOLE_TYPE_ID).dirichlet_boundary_value();
806 }
807 else
808 {
809 if (conf.quantum_correction())
810 viennashe::log::info<viennashe::log_simulator>() << "* simulator(): Density Gradient (holes) ... Insulator boundary condition is ROBIN " << std::endl;
811 robin_boundary_coefficients<double> robin_coeffs_p = conf.density_gradient(viennashe::HOLE_TYPE_ID).robin_coeffs();
813 }
814
815 // Temperature
817 if (conf.with_hde())
821 // initial guess already set above
822
823 //
824 // Traps
825// quantities().unknown_quantities().push_back(UnknownQuantityType(viennashe::quantity::trap_occupancy(), viennashe::EQUATION_SRH_TRAPPING, cell_num, 0.0));
826// if (conf.with_traps())
827// {
828// // At the moment we only support bulk SRH traps
829// detail::set_unknown_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_SEMICONDUCTOR_ID));
830// }
831// detail::set_boundary_for_material(device, quantities().unknown_quantities().back(), materials::checker(MATERIAL_CONDUCTOR_ID),
832// constant_accessor<double>(0.0), BOUNDARY_DIRICHLET);
833
835
836
837 // SHE: electron distribution function
839 quantities().unknown_she_quantities().back().resize(cell_num, facet_num);
840 if (conf.with_electrons())
841 {
842 if (conf.get_electron_equation() == EQUATION_SHE) //enable standard DD instead of SHE
843 detail::set_she_unknown(device, quantities().unknown_she_quantities().back());
844 }
846 quantities().unknown_quantities().back().set_logarithmic_damping(true);
847
848 // SHE: hole distribution function
850 quantities().unknown_she_quantities().back().resize(cell_num, facet_num);
851 if (conf.with_holes())
852 {
853 if (conf.get_hole_equation() == EQUATION_SHE) //enable standard DD instead of SHE
854 detail::set_she_unknown(device, quantities().unknown_she_quantities().back());
855 }
857 }
858
865 template <typename QuantityAccessorT>
866 void set_initial_guess(std::string quan_name, QuantityAccessorT const & quan_acc)
867 {
868 CellContainer cells(device().mesh());
869
870 transfer_provided_quantities(quan_acc, quantities().get_unknown_quantity(quan_name), cells);
871 }
872
873
877 void run()
878 {
880
883
884 //
885 // Run nonlinear iterations
886 //
887
888 double initial_residual_norm = 0;
889
890 SHETimeStepQuantitiesT transferred_quantities;
891
892 bool update_no_damping = false;
893 bool break_after_update = false;
894
895 for (std::size_t nonlinear_iter = 1; nonlinear_iter <= this->config().nonlinear_solver().max_iters(); ++nonlinear_iter)
896 {
897 viennashe::util::timer stopwatch,stopwatch2;
898 stopwatch.start();
899
900 // If this is the last iteration set update_no_damping
901 if (nonlinear_iter == this->config().nonlinear_solver().max_iters())
902 update_no_damping = true;
903
904 //
905 // write kinetic energy to device:
906 //
907 //log::debug<viennashe::she::log_she_solver>() << "* simulator(): Writing center of band gap to device..." << std::endl;
908 viennashe::she::setup_energies(this->device(), this->quantities(), this->config());
909
910 //
911 // distribute SHE expansion orders over device
912 //
913 //log::debug<viennashe::she::log_she_solver>() << "* simulator(): Writing expansion orders to device (" << config().max_expansion_order() << ")... " << std::endl;
915
916 //
917 // write boundary conditions:
918 //
919 //log::debug<viennashe::she::log_she_solver>() << "* simulator(): Writing boundary conditions to device..." << std::endl;
921
922 //
923 // Mapping of unknowns:
924 //
925 viennashe::map_info_type map_info = viennashe::create_mapping(this->device(), this->quantities(), this->config());
926
927 transferred_quantities = quantities(); //transfer kinetic energy and other information related to the (x,H)-space
928 if ( quantities_history_.size() > 1 )
929 {
930 transferred_quantities.get_unknown_quantity(viennashe::quantity::potential()) = quantities_history_.at(quantities_history_.size()-2).get_unknown_quantity(viennashe::quantity::potential());
931 viennashe::she::transfer_to_new_h_space(device(), quantities_history_.at(quantities_history_.size()-2), transferred_quantities, this->config());
932
933 // push every nonlinear iteration state:
934 //quantities_history_.at(quantities_history_.size()-2) = transferred_quantities;
935 }
936
937 //
938 // Transfer device based quantities back to the device
939 //
941
942
943 if (nonlinear_iter == 1)
944 {
945 for (std::size_t i = 0; i < this->quantities().unknown_quantities().size(); ++i)
946 {
947 log::info() << "* run(): Number of unknowns for quantity '" << this->quantities().unknown_quantities()[i].get_name() << "': "
948 << map_info[this->quantities().unknown_quantities()[i].get_name()].first << std::endl;
949 }
950 }
951
952 // Log summary output
954 {
955 if (nonlinear_iter == 1)
956 {
957 //
958 // Write convergence table header:
959 //
960 if (use_newton)
961 log::info<log_simulator>() << "* run(): -- Newton scheme --" << std::endl;
962 else
963 log::info<log_simulator>() << "* run(): -- Gummel scheme --" << std::endl;
964
965 log::info<log_simulator>() << std::setw(5) << " Iter" << " | ";
966
968 log::info<log_simulator>() << std::setw(9) << "#f_even^e" << " | " << std::setw(8) << "#f_odd^e" << " | ";
969 if (map_info[viennashe::quantity::hole_distribution_function()].first > 0)
970 log::info<log_simulator>() << std::setw(8) << "#f_even^h" << " | " << std::setw(8) << "#f_odd^h" << " | ";
971
972 log::info<log_simulator>() << std::setw(9) << "ResidNorm" << " | "
973 << std::setw(9) << "|d_pot|_2" << " | "
974 << std::setw(9) << "TotUpdate" << " | "
975 << std::setw(8) << "Time (s)" << std::endl;
976 }
977
978 log::info<log_simulator>() << std::setw(5) << nonlinear_iter << " | ";
979
981 log::info<log_simulator>() << std::setw(9) << viennashe::util::format_number_to_width(map_info[viennashe::quantity::electron_distribution_function()].first, 7) << " | "
983 if (map_info[viennashe::quantity::hole_distribution_function()].first > 0)
984 log::info<log_simulator>() << std::setw(9) << viennashe::util::format_number_to_width(map_info[viennashe::quantity::hole_distribution_function()].first, 7) << " | "
985 << std::setw(8) << viennashe::util::format_number_to_width(map_info[viennashe::quantity::hole_distribution_function()].second, 7) << " | ";
986
987 std::cout << std::flush ;
988 }
989
990 //
991 // Nonlinear methods (Newton, Gummel)
992 //
993
994 double potential_norm_increment = 0;
995 double current_residual_norm = 0;
996 double total_update_norm = 0;
997
998 if (use_newton)
999 {
1000 std::size_t total_number_of_unknowns = 0;
1001 for (viennashe::map_info_type::const_iterator it = map_info.begin(); it != map_info.end(); ++it)
1002 total_number_of_unknowns += (it->second).first + (it->second).second; //sum of unknowns on vertices and edges
1003 stopwatch2.start();
1004 MatrixType A(total_number_of_unknowns, total_number_of_unknowns);
1005 VectorType b(total_number_of_unknowns);
1006
1007 // assemble spatial quantities:
1008 for (std::size_t i = 0; i < this->quantities().unknown_quantities().size(); ++i)
1009 viennashe::assemble(device(), this->quantities(), this->config(), this->quantities().unknown_quantities()[i], A, b);
1010
1011 // assemble SHE quantities:
1012 for (std::size_t i = 0; i < this->quantities().unknown_she_quantities().size(); ++i)
1013 viennashe::she::assemble(this->device(), transferred_quantities, this->quantities(), this->config(), this->quantities().unknown_she_quantities()[i], A, b,
1014 (quantities_history_.size() > 1), nonlinear_iter > 1);
1015 //log::info<log_simulator>() << " Assemble TIME:" << std::fixed << std::setprecision(3) << std::setw(8) << stopwatch.get() << std::endl;
1016 //VectorType x = viennashe::she::solve(A, b, map_info); //TODO: use this!
1017 VectorType x = this->solve(A, b, true);
1018
1019 current_residual_norm += viennashe::math::norm_2(b);
1020
1021 total_update_norm = viennashe::get_total_update_norm_newton(this->device(), this->quantities(), x);
1022 potential_norm_increment = viennashe::get_potential_update_norm_newton(this->device(), this->quantities(), x);
1023
1024 viennashe::update_quantities(this->device(), this->config(), this->quantities(), x);
1025 }
1026 else // Gummel iteration
1027 {
1028 // spatial quantities:
1029 for (std::size_t i=0; i < this->quantities().unknown_quantities().size(); ++i)
1030 {
1031 std::size_t number_of_unknowns = map_info[quantities().unknown_quantities()[i].get_name()].first;
1032 if (number_of_unknowns == 0)
1033 continue;
1034 stopwatch2.start();
1035 // System for this quantity only:
1036 MatrixType A(number_of_unknowns, number_of_unknowns);
1037 VectorType b(number_of_unknowns);
1038
1039 viennashe::assemble(this->device(), this->quantities(), this->config(), this->quantities().unknown_quantities()[i], A, b);
1040 //log::info<log_simulator>() << " Assemble TIME:" << std::fixed << std::setprecision(3) << std::setw(8) << stopwatch.get() << std::endl;
1041
1042 VectorType x = solve(A, b);
1043
1044 if (this->quantities().unknown_quantities()[i].get_name() == viennashe::quantity::potential())
1045 {
1046 potential_norm_increment = viennashe::math::norm_2(x);
1047 }
1048 current_residual_norm += viennashe::math::norm_2(b);
1049
1050 total_update_norm += viennashe::get_relative_update_norm(this->quantities().unknown_quantities()[i], x);
1051
1052 viennashe::update_quantity(this->device(), this->quantities().unknown_quantities()[i], this->config(), x, update_no_damping);
1053 }
1054
1055 // SHE quantities:
1056 for (std::size_t i = 0; i < quantities().unknown_she_quantities().size(); ++i)
1057 {
1058 typename map_info_type::value_type::second_type quan_unknowns = map_info[this->quantities().unknown_she_quantities()[i].get_name()];
1059 std::size_t number_of_unknowns = quan_unknowns.first + quan_unknowns.second;
1060 if (number_of_unknowns == 0)
1061 continue;
1062 stopwatch2.start();
1063 // System for this quantity only:
1064 MatrixType A(number_of_unknowns, number_of_unknowns);
1065 VectorType b(number_of_unknowns);
1066
1067 viennashe::she::assemble(device(), transferred_quantities, this->quantities(), this->config(),
1068 this->quantities().unknown_she_quantities()[i], A, b,
1069 (quantities_history_.size() > 1), nonlinear_iter > 1);
1070 //log::info<log_simulator>() << " Assemble TIME:" << std::fixed << std::setprecision(3) << std::setw(8) << stopwatch.get() << std::endl;
1071
1072 VectorType x = viennashe::she::solve(this->device(), this->quantities().unknown_she_quantities()[i], this->config(), A, b, quan_unknowns.first);
1073
1074 current_residual_norm += viennashe::math::norm_2(b);
1075
1076
1077 // TODO: The following is a rather ugly dispatch:
1078 if (quantities().unknown_she_quantities()[i].get_name() == viennashe::quantity::electron_distribution_function())
1079 {
1080 total_update_norm += viennashe::get_relative_update_norm(device(), quantities().unknown_she_quantities()[i],
1081 quantities().get_unknown_quantity(viennashe::quantity::electron_density()), config());
1082
1083 viennashe::update_quantity(device(), quantities().unknown_she_quantities()[i],
1084 quantities().get_unknown_quantity(viennashe::quantity::electron_density()),
1085 config(), x, update_no_damping);
1086 }
1087 else
1088 {
1089 total_update_norm += viennashe::get_relative_update_norm(device(), quantities().unknown_she_quantities()[i],
1090 quantities().get_unknown_quantity(viennashe::quantity::hole_density()), config());
1091
1092 viennashe::update_quantity(device(), quantities().unknown_she_quantities()[i],
1093 quantities().get_unknown_quantity(viennashe::quantity::hole_density()),
1094 config(), x, update_no_damping);
1095 }
1096 } // for each SHE quantity
1097 }
1098
1099 // Print norms:
1100 log::info<log_simulator>() << std::scientific << std::setprecision(3) << std::setw(9) << current_residual_norm << " | "
1101 << std::scientific << std::setprecision(3) << std::setw(9) << potential_norm_increment << " | "
1102 << std::scientific << std::setprecision(3) << std::setw(9) << total_update_norm << " | "
1103 << std::fixed << std::setprecision(3) << std::setw(8) << stopwatch.get() << std::endl;
1104
1105 // push every nonlinear iteration state:
1106 //quantities_history_.push_back(quantities());
1107
1108 if (break_after_update)
1109 break;
1110
1111 if (!current_residual_norm) //equilibrium case: no iterations required
1112 break;
1113
1114 // Check for convergence:
1115 if (nonlinear_iter == 1)
1116 {
1117 initial_residual_norm = current_residual_norm;
1118 }
1119 else if (current_residual_norm / initial_residual_norm < 1e-10)
1120 {
1121 update_no_damping = true;
1122 break_after_update = true;
1123 //break;
1124 }
1125 else if (potential_norm_increment < this->config().nonlinear_solver().tolerance())
1126 {
1127 update_no_damping = true;
1128 break_after_update = true;
1129 //break;
1130 }
1131
1132 } //for nonlinear iterations
1133
1134
1135 }
1136
1137
1139 SHETimeStepQuantitiesT const & quantities() const { return quantities_history_.back(); }
1140
1142 SHETimeStepQuantitiesT & quantities() { return quantities_history_.back(); }
1143
1147 {
1148 return she_df_type(config(), this->quantities().carrier_distribution_function(ctype));
1149 }
1150
1153 {
1154 return edf_type(config(), this->quantities().carrier_distribution_function(ctype));
1155 }
1156
1159 {
1160 return generalized_edf_type(config(), this->quantities().carrier_distribution_function(ctype));
1161 }
1162
1168 {
1169 return quantities().potential();
1170 }
1171
1174 {
1175 return quantities().electron_density();
1176 }
1177
1180 {
1181 return quantities().hole_density();
1182 }
1183
1185 //trap_occupancy_type trap_occupancy() const
1186 //{
1187 // return quantities().trap_occupancy();
1188 //}
1189
1194 SHETimeStepQuantitiesT const & quantity_history(std::size_t index) const { return quantities_history_.at(index); }
1195
1196 std::size_t quantity_history_size() const { return quantities_history_.size(); }
1197
1200 {
1201 quantities_history_.push_back(quantities());
1203 }
1204
1205 // the following is for compatibility reasons:
1208
1210 viennashe::config const & config() const { return config_; }
1211 viennashe::config & config() { return config_; }
1212
1213 DeviceType const & device() const { return *p_device_; }
1214 DeviceType & device() { return *p_device_; }
1215
1216 private:
1217
1219 template <typename QuantitySrcT,
1220 typename QuantityDestT,
1221 typename ElementContainer>
1222 void transfer_provided_quantities(QuantitySrcT const & src_quantity,
1223 QuantityDestT & dest_quantity,
1224 ElementContainer const & elements)
1225 {
1226 // Usage example: Transfer initial guess to SHE index space
1227
1228 typedef typename ElementContainer::iterator ElementIterator;
1229 for (ElementIterator elit = elements.begin();
1230 elit != elements.end();
1231 ++elit)
1232 {
1233 dest_quantity.set_value(*elit, src_quantity.at(*elit));
1234 }
1235
1236 } // transfer_provided_quantities
1237
1238
1245 template <typename MatrixType>
1246 VectorType solve(MatrixType & matrix, VectorType & rhs, VectorType & rescaling_vector)
1247 {
1248 if (this->config_.linear_solver().scale())
1249 {
1250 // Step 1: Rescale unknowns:
1251 typedef typename MatrixType::row_type RowType;
1252 typedef typename MatrixType::iterator2 AlongRowIterator;
1253
1254 for (std::size_t i=0; i<matrix.size1(); ++i)
1255 {
1256 RowType & row_i = matrix.row(i);
1257 for (AlongRowIterator iter = row_i.begin();
1258 iter != row_i.end();
1259 ++iter)
1260 {
1261 iter->second *= rescaling_vector[iter->first];
1262 }
1263 }
1264 }
1265
1266 // Step 2: Normalize matrix rows:
1267 VectorType scale_factors = viennashe::math::row_normalize_system(matrix, rhs);
1268
1269 // Step 3: Solve
1270 VectorType update = viennashe::solvers::solve(matrix, rhs, config().linear_solver());
1271 // Step 4: Check convergence:
1272 double lin_sol_res = viennashe::math::norm_2(viennashe::math::subtract(viennashe::math::prod(matrix, update), rhs));
1273 lin_sol_res /= viennashe::math::norm_2(rhs);
1274
1275 if (lin_sol_res > 1e-3)
1276 log::warning() << "Warning: Linear solver shows only mild convergence! Residual: " << lin_sol_res << std::endl;
1277 if (lin_sol_res > 1)
1278 {
1279 log::error() << "ERROR: Linear solver failed to converge properly! Residual: " << lin_sol_res << std::endl;
1280 //log::debug() << rhs << std::endl;
1281 throw viennashe::solver_failed_exception("Linear solver failed to converge properly!");
1282 }
1283 for (std::size_t i=0; i<update.size(); ++i)
1284 update[i] *= scale_factors[i];
1285 if (this->config_.linear_solver().scale())
1286 {
1287 // Step 4: Revert unknown scaling:
1288 for (std::size_t i=0; i<update.size(); ++i)
1289 update[i] *= rescaling_vector[i];
1290 }
1291
1292 return update;
1293 } // solve
1294
1301 template <typename MatrixType>
1302 VectorType solve(MatrixType & matrix, VectorType & rhs, bool is_newton = false)
1303 {
1304 VectorType rescaling_vector(rhs.size());
1305 if (this->config_.linear_solver().scale())
1306 {
1307 std::fill(rescaling_vector.begin(), rescaling_vector.end(), 1.0);
1308
1309 if (is_newton)
1310 {
1311 MeshType const & mesh = device().mesh();
1312 CellContainer cells(mesh);
1313
1314 for (std::size_t i=0; i<quantities().unknown_quantities().size(); ++i)
1315 {
1316 UnknownQuantityType & current_quan = quantities().unknown_quantities()[i];
1317
1318 double scaling_factor = 1.0;
1319 if ( current_quan.get_name() == viennashe::quantity::electron_density()
1320 || current_quan.get_name() == viennashe::quantity::hole_density())
1321 scaling_factor = 1e16;
1322
1323
1324 for (CellIterator cit = cells.begin();
1325 cit != cells.end();
1326 ++cit)
1327 {
1328 long idx = current_quan.get_unknown_index(*cit);
1329 if (idx >= 0)
1330 rescaling_vector[static_cast<std::size_t>(idx)] = scaling_factor;
1331 }
1332 }
1333 }
1334 }
1335
1336 return solve(matrix, rhs, rescaling_vector);
1337 }
1338
1339 // Transferred from solver.hpp
1340 /*
1341 double compute_newton_damping_factor(DeviceType const & device, VectorType const & she_update, VectorType const & n_old, VectorType const & p_old)
1342 {
1343 //VectorType
1344 VectorType she_result_new(she_result_);
1345 for (std::size_t i=0; i<she_result_.size(); ++i)
1346 she_result_new[i] += she_update[i];
1347
1348 VectorType n_new(n_old);
1349 if (conf_.with_electrons())
1350 compute_carrier_density_vector(device, quantities(), quantities().electron_distribution_function(), conf_.dispersion_relation(viennashe::ELECTRON_TYPE_ID), n_new);
1351
1352 VectorType p_new(p_old);
1353 if (conf_.with_holes())
1354 compute_carrier_density_vector(device, quantities(), quantities().hole_distribution_function(), conf_.dispersion_relation(viennashe::HOLE_TYPE_ID), p_new);
1355
1356
1357 // compute relative norm of update:
1358 double norm_inf_update = 0;
1359 double norm_inf_current = 0;
1360
1361 for (std::size_t i=0; i<n_new.size(); ++i)
1362 {
1363 norm_inf_update = std::max(std::abs(norm_inf_update), std::abs(n_new[i]));
1364 norm_inf_current = std::max(std::abs(norm_inf_current), std::abs(n_old[i]));
1365 }
1366
1367 double norm_rel_update = norm_inf_update / norm_inf_current;
1368
1369 double delta = 0.1;
1370 return (1.0 + delta * std::log(norm_rel_update)) / (1.0 + delta * (norm_rel_update - 1.0));
1371
1372
1373 std::size_t damping_iter_max = 10;
1374 double alpha = 1.0;
1375
1376 for (std::size_t damping_iter = 0; damping_iter < damping_iter_max; ++damping_iter)
1377 {
1378 for (std::size_t i=0; i<she_result_.size(); ++i)
1379 she_result_new[i] = she_result_[i] + alpha * she_update[i];
1380
1381 if (damping_iter == 0) //check whether a full Newton step is okay
1382 {
1383 alpha = 1.0;
1384 }
1385 else if (damping_iter == 1) //use logarithmic damping, cf. PhD thesis by Simlinger or Fischer
1386 {
1387 alpha = (1.0 + delta * std::log(norm_rel_update)) / (1.0 + delta * (norm_rel_update - 1.0));
1388 }
1389 else
1390 {
1391 alpha = 0.3 * alpha;
1392 }
1393
1394 double update_norm = 0.0;
1395 double current_norm = 0.0;
1396
1397 bool do_reject = false;
1398
1399 if (conf_.with_electrons())
1400 compute_carrier_density_vector(device, quantities(), quantities().electron_distribution_function(), conf_.dispersion_relation(viennashe::ELECTRON_TYPE_ID), n_new);
1401
1402 if (conf_.with_holes())
1403 compute_carrier_density_vector(device, quantities(), quantities().hole_distribution_function(), conf_.dispersion_relation(viennashe::HOLE_TYPE_ID), p_new);
1404
1405 VectorType n_update = n_old;
1406 for (std::size_t i=0; i<n_update.size(); ++i)
1407 n_update[i] -= n_new[i];
1408
1409
1410 for (std::size_t i=0; i<n_new.size(); ++i)
1411 {
1412 update_norm += alpha * alpha * n_update[i] * n_update[i];
1413 current_norm += n_old[i] * n_old[i];
1414
1415 if ( std::abs(n_old[i]) != 0.0
1416 && std::abs(n_old[i]) < std::abs(alpha * n_update[i])
1417 )
1418 do_reject = true;
1419
1420 }
1421
1422 if (update_norm > current_norm)
1423 do_reject = true;
1424
1425 // Don't accept updated variables if update is too large
1426 if ( (damping_iter < damping_iter_max - 1) //last iterate is always accepted
1427 && do_reject)
1428 {
1429 continue;
1430 }
1431
1432 break;
1433 } //for damping_iter
1434
1435 return alpha;
1436 } */
1437
1438
1439
1441 /*template <typename IndexAccessor, typename DamperType>
1442 VectorType compute_next_iterate(DeviceType const & device,
1443 VectorType const & update, VectorType const & current_guess, IndexAccessor const & index_accessor,
1444 DamperType const & damper, bool strict_positivity_required)
1445 {
1446 //typedef typename DeviceType::mesh_type MeshType;
1447 typedef typename viennagrid::result_of::const_ncell_range<MeshType, 0>::type VertexContainer;
1448 typedef typename viennagrid::result_of::iterator<VertexContainer>::type VertexIterator;
1449
1450 MeshType const & mesh = device.mesh();
1451
1452 VectorType next_iterate(current_guess.size());
1453
1454 // compute relative norm of update:
1455 double norm_inf_update = 0;
1456 double norm_inf_current = 0;
1457
1458 VertexContainer vertices = viennagrid::ncells<0>(mesh);
1459 for (VertexIterator vit = vertices.begin();
1460 vit != vertices.end();
1461 ++vit)
1462 {
1463 long index = index_accessor(*vit);
1464 if (index >= 0)
1465 {
1466 norm_inf_update = std::max(std::abs(norm_inf_update), std::abs(update[index]));
1467 norm_inf_current = std::max(std::abs(norm_inf_current), std::abs(current_guess[index]));
1468 }
1469 }
1470
1471 double norm_rel_update = norm_inf_update / norm_inf_current;
1472
1473 double delta = 0.1;
1474
1475 std::size_t damping_iter_max = 10;
1476 double alpha = 1.0;
1477
1478 for (std::size_t damping_iter = 0; damping_iter < damping_iter_max; ++damping_iter)
1479 {
1480 next_iterate.clear();
1481 next_iterate.resize(pot_n_p_.size());
1482
1483 if (damping_iter == 0) //check whether a full Newton step is okay
1484 {
1485 alpha = 1.0;
1486 }
1487 else if (damping_iter == 1) //use logarithmic damping, cf. PhD thesis by Simlinger or Fischer
1488 {
1489 if ( viennashe::util::is_Inf(norm_rel_update) ||
1490 viennashe::util::is_NaN(norm_rel_update)) { alpha = damper(alpha); } // limes norm_rel_update -> inf = 1.0
1491 else { alpha = (1.0 + delta * std::log(norm_rel_update)) / (1.0 + delta * (norm_rel_update - 1.0)); }
1492 }
1493 else
1494 {
1495 alpha = damper(alpha);
1496 }
1497
1498 double update_norm = 0.0;
1499 double current_norm = 0.0;
1500
1501 bool do_reject = false;
1502
1503 // Compute updated potential, electron density and hole density
1504 VertexContainer vertices = viennagrid::ncells<0>(mesh);
1505 for (VertexIterator vit = vertices.begin();
1506 vit != vertices.end();
1507 ++vit)
1508 {
1509 long index = index_accessor(*vit);
1510 if (index > -1)
1511 {
1512 next_iterate[index] = current_guess[index] + alpha * update[index];
1513 update_norm += alpha * alpha * update[index] * update[index];
1514 current_norm += current_guess[index] * current_guess[index];
1515
1516 if ( std::abs(current_guess[index]) != 0.0
1517 && std::abs(current_guess[index]) < 0.5 * std::abs(alpha * update[index])
1518 )
1519 do_reject = true;
1520
1521 if (strict_positivity_required && next_iterate[index] <= 0)
1522 do_reject = true;
1523 }
1524 }
1525
1526 if (update_norm > current_norm)
1527 do_reject = true;
1528
1529 // Don't accept updated variables if update is too large
1530 if ( (damping_iter < damping_iter_max - 1) //last iterate is always accepted
1531 && do_reject)
1532 {
1533 continue;
1534 }
1535
1536 break;
1537 } //for update
1538
1539 return next_iterate;
1540 } */
1541
1542 DeviceType * p_device_;
1543 viennashe::config config_;
1544
1545 std::vector<SHETimeStepQuantitiesT> quantities_history_;
1546
1547 }; //simulator
1548
1549} //namespace viennashe
1550
1551#endif
Dimension-independent assembling routines for the Drift-diffusion model using a Newton scheme or Gumm...
Generic assembly of traps is implemented here.
Writes the SHE boundary conditions to the mesh.
Provides an accessor for the carrier density.
Defines a set of checker functors for micro-tests within ViennaSHE.
Accessor for obtaining the Dirichlet boundary condition for the Poisson equation in the device (conta...
Definition: accessors.hpp:206
Accessor for retrieving the built-in potential in the device.
Definition: accessors.hpp:161
The main SHE configuration class. To be adjusted by the user for his/her needs.
Definition: config.hpp:124
bool with_hde() const
Definition: config.hpp:502
nonlinear_solver_config_type & nonlinear_solver()
Returns the configuration object for the nonlinear solver.
Definition: config.hpp:495
linear_solver_config_type & linear_solver()
Returns the configuration object for the linear solver.
Definition: config.hpp:485
Common representation of any quantity associated with objects of a certain type.
An accessor which returns a constant value independent of the object passed.
Definition: accessors.hpp:91
void set_lattice_temperature(double new_value)
Sets the homogeneous temperature of the device.
Definition: device.hpp:234
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
Defines the physical properties of a device, e.g. doping. This is the implementation for 2d and highe...
Definition: device.hpp:818
Returns the lattice temperature on the device.
Definition: accessors.hpp:107
Simple checker class for checking whether a certain material is of a given type (conductor,...
Definition: all.hpp:184
An accessor for the carrier density in the device.
An accessor for the carrier density in the device by reference
A convenience wrapper for accessing the energy distribution function on each vertex of the device.
A convenience wrapper for accessing the generalized energy distribution function (f * Z,...
A convenience wrapper for accessing the distribution function coefficients over the device....
The main SHE simulator controller class. Acts as an accessor for all SHE quantities needed for the si...
UnknownSHEQuantityListType & unknown_she_quantities()
ResultQuantityType electron_density() const
void setup_trap_unkown_indices(DeviceType const &device)
UnknownQuantityType & get_unknown_quantity(std::string quantity_name)
Returns a reference to the unkown quantity identified by its name.
std::deque< UnknownQuantityType > & unknown_quantities()
ResultQuantityType hole_density() const
General representation of any solver quantity defined on two different element types (e....
long get_unknown_index(AssociatedT1 const &elem, std::size_t index_H) const
void set_values(AssociatedT1 const &elem, std::size_t index_H, ValueT const *values)
void set_boundary_type(AssociatedT1 const &elem, boundary_type_id value)
Class for self-consistent SHE simulations.
Definition: simulator.hpp:675
viennashe::config const & config() const
Returns the config object used by the simulator controller.
Definition: simulator.hpp:1210
UnknownQuantityType unknown_quantity_type
Definition: simulator.hpp:704
unknown_quantity< CellType > UnknownQuantityType
Definition: simulator.hpp:703
generalized_edf_type generalized_edf(carrier_type_id ctype) const
Returns a wrapper for the evaluation of the generalized energy distribution function (f * Z,...
Definition: simulator.hpp:1158
const_quantity< CellType > ResultQuantityType
Definition: simulator.hpp:706
viennashe::she::timestep_quantities< DeviceType > SHETimeStepQuantitiesT
Definition: simulator.hpp:695
ResultQuantityType dg_pot_p() const
Definition: simulator.hpp:1207
SHETimeStepQuantitiesT const & quantity_history(std::size_t index) const
Returns a wrapper for the trap occupancy, which can be evaluated in every vertex of the mesh.
Definition: simulator.hpp:1194
she_df_type she_df(carrier_type_id ctype) const
Returns a wrapper for the distribution function for electrons and holes (whatever is computed by the ...
Definition: simulator.hpp:1146
DeviceType const & device() const
Definition: simulator.hpp:1213
ResultQuantityType hole_density_type
Definition: simulator.hpp:710
DeviceType device_type
Definition: simulator.hpp:693
SHETimeStepQuantitiesT::generalized_edf_type generalized_edf_type
Definition: simulator.hpp:699
void run()
Launches the solver. Uses the built-in potential as initial guess for the potential and the doping co...
Definition: simulator.hpp:877
electron_density_type electron_density() const
Returns a wrapper for the electron density, which can be evaluated in every vertex of the mesh.
Definition: simulator.hpp:1173
simulator(DeviceType &device, viennashe::config const &conf=viennashe::config())
Constructs the self-consistent simulator object.
Definition: simulator.hpp:718
ResultQuantityType electron_density_type
Definition: simulator.hpp:709
SHETimeStepQuantitiesT const & quantities() const
Returns the controller object. Const version.
Definition: simulator.hpp:1139
void set_initial_guess(std::string quan_name, QuantityAccessorT const &quan_acc)
Transfers the inital guess for the given quantity.
Definition: simulator.hpp:866
SHETimeStepQuantitiesT::edf_type edf_type
Definition: simulator.hpp:698
SHETimeStepQuantitiesT & quantities()
Returns the controller object. Non-const version.
Definition: simulator.hpp:1142
viennashe::config & config()
Definition: simulator.hpp:1211
SHETimeStepQuantitiesT::UnknownSHEQuantityType she_quantity_type
Definition: simulator.hpp:701
SHETimeStepQuantitiesT::she_df_type she_df_type
Definition: simulator.hpp:697
DeviceType & device()
Definition: simulator.hpp:1214
edf_type edf(carrier_type_id ctype) const
Returns a wrapper for the evaluation of the energy distribution function (i.e. the isotropic part of ...
Definition: simulator.hpp:1152
void advance_in_time()
Cycles the quantities. Moves (copy) the current quantities to the history and empties the current qua...
Definition: simulator.hpp:1199
ResultQuantityType dg_pot_n() const
Definition: simulator.hpp:1206
ResultQuantityType potential_type
Definition: simulator.hpp:708
potential_type potential() const
Returns a wrapper for the potential, which can be evaluated in every vertex of the mesh.
Definition: simulator.hpp:1167
std::size_t quantity_history_size() const
Definition: simulator.hpp:1196
hole_density_type hole_density() const
Returns a wrapper for the hole density, which can be evaluated in every vertex of the mesh
Definition: simulator.hpp:1179
Exception thrown in the case that an equation solver failed.
Definition: exception.hpp:140
void scale(bool value)
Controls the scaling of unkowns. Give "false" to disable unkown scaling.
Definition: config.hpp:68
std::size_t max_iters() const
Returns the maximum number of nonlinear solver iterations.
Definition: config.hpp:317
double damping() const
Returns the damping for the nonlinear solver.
Definition: config.hpp:328
long id() const
Returns the current linear solver ID.
Definition: config.hpp:262
bool get_unknown_mask(associated_type const &elem) const
ValueT get_value(associated_type const &elem) const
ValueT get_boundary_value(associated_type const &elem) const
long get_unknown_index(associated_type const &elem) const
boundary_type_id get_boundary_type(associated_type const &elem) const
void set_value(associated_type const &elem, ValueT value)
A simple timer class.
Definition: timer.hpp:74
void start()
Starts the timer.
Definition: timer.hpp:80
double get() const
Returns the number of seconds elapsed.
Definition: timer.hpp:88
Contains the definition of a device class independent of the actual macroscopic model to be solved.
Provides a convenience wrapper for accessing the distribution function coefficients over the device.
Implements the elimination and the recovery of odd-order unknowns from the discrete system of equatio...
Provides all routines for distributing unknown indices (dofs) over the device. Includes expansion ord...
Contains forward declarations and definition of small classes that must be defined at an early stage.
Implementation of various utilities related to linear algebra.
Provides the linear solvers for SHE.
A logging facility providing fine-grained control over logging in ViennaSHE.
Distributes the unknown indices ('mapping') for the Poisson equation and the continuity equations.
Miscellaneous utilities.
void set_unknown_for_material(DeviceT const &device, QuantityT &quan, materials::checker const &material_check)
Sets the unknown masks per material.
Definition: simulator.hpp:74
void smooth_doping_at_contacts(DeviceT &d)
Utility function for smoothing the doping next to contacts.
void set_she_unknown(DeviceT const &device, QuantityT &quan)
Definition: simulator.hpp:94
void set_initial_guess(DeviceT const &device, QuantityT &quan, BoundaryValueAccessorT const &initial_guess_accessor)
Sets the initial guess per quantity and device.
Definition: simulator.hpp:200
void set_boundary_for_material(DeviceT const &device, QuantityT &quan, materials::checker const &material_check, BoundaryValueAccessorT boundary_value_accessor, boundary_type_id bnd_id)
Sets the boundary condition (type and value) per vertex depending on the material.
Definition: simulator.hpp:134
logger< true > error()
Used to log errors. The logging level is logERROR.
Definition: log.hpp:301
logger< true > warning()
Used to log warnings. The logging level is logWARNING.
Definition: log.hpp:305
logger< true > info()
Used to log infos. The logging level is logINFO.
Definition: log.hpp:307
bool is_semiconductor(long material_id)
Convenience function for checking whether the supplied material ID refers to a semiconductor.
Definition: all.hpp:156
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
VectorType subtract(VectorType const &x, VectorType const &y)
Helper routine for the generic vector subtraction z = x - y;.
Definition: linalg_util.hpp:51
VectorType prod(sparse_matrix< NumericT > const &system_matrix, VectorType const &x)
Computes A * x for a sparse A and a vector x.
VectorT row_normalize_system(sparse_matrix< NumericT > &system_matrix, VectorT &rhs)
Normalizes an equation system such that all diagonal entries are non-negative, and such that all 2-no...
std::string lattice_temperature()
std::string electron_distribution_function()
std::string density_gradient_hole_correction()
std::string hole_distribution_function()
std::string density_gradient_electron_correction()
std::string electron_density()
void transfer_to_new_h_space(DeviceType const &device, viennashe::she::timestep_quantities< DeviceType > const &old_quantities, viennashe::she::timestep_quantities< DeviceType > &new_quantities, viennashe::config const &conf)
Interface transferring a solution given by 'old_solution' on some other (old) grid to the new grid.
void assemble(DeviceType &device, TimeStepQuantitiesT &old_quantities, TimeStepQuantitiesT &quantities, viennashe::config const &conf, viennashe::she::unknown_she_quantity< VertexT, EdgeT > const &quan, MatrixType &A, VectorType &b, bool use_timedependence, bool quan_valid)
void setup_energies(DeviceT const &device, viennashe::she::unknown_she_quantity< VertexT, EdgeT > &quan, viennashe::config const &conf, viennashe::unknown_quantity< VertexT > const &potential, viennashe::unknown_quantity< VertexT > const &quantum_correction)
Computes the kinetic energy over the device as obtained from the provided potential.
void write_boundary_conditions(DeviceType const &device, viennashe::she::unknown_she_quantity< VertexT, EdgeT > &quan, viennashe::config const &conf)
Writes boundary conditions for SHE to the device. Stores the result using ViennaData.
void distribute_expansion_orders(DeviceType &device, viennashe::she::unknown_she_quantity< VertexT, EdgeT > &quan, viennashe::config const &conf)
Assigns the various expansion orders to the device. This is the public interface.
VectorType solve(DeviceType const &device, SHEQuantity const &quan, viennashe::config const &configuration, MatrixType &full_matrix, VectorType &full_rhs, std::size_t reduced_unknowns)
Public interface for solving the provided system of discretized SHE equations.
std::vector< double > solve(viennashe::math::sparse_matrix< double > &A, std::vector< double > const &b, linear_solver_config const &config)
Public interface for solving a system of linear equations represented using a sparse matrix.
std::string format_number_to_width(T number, int width)
Returns the formatted number as string with the given length, where spaces are used as padding charac...
Definition: misc.hpp:372
CellT const * get_connected_semiconductor_cell(DeviceT const &device, 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:216
The main ViennaSHE namespace. All functionality resides inside this namespace.
Definition: accessors.hpp:40
std::map< std::string, std::pair< std::size_t, std::size_t > > map_info_type
Information on the number of unknowns per quantity.
Definition: forwards.h:101
void update_quantities(DeviceT const &device, viennashe::config const &conf, TimeStepQuantitiesT &unknown_quantities, VectorType const &x)
Updater for Newton.
Definition: simulator.hpp:395
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
double get_potential_update_norm_newton(DeviceT const &device, TimeStepQuantitiesT const &unknown_quantities, VectorT const &x)
Potential update calculator for Newton.
Definition: simulator.hpp:331
@ EQUATION_SHE
Definition: forwards.h:118
@ EQUATION_DENSITY_GRADIENT
Definition: forwards.h:119
@ EQUATION_POISSON_HEAT
Definition: forwards.h:120
@ EQUATION_POISSON_DD
Definition: forwards.h:116
@ EQUATION_CONTINUITY
Definition: forwards.h:117
void transfer_quantity_to_device(DeviceT &device, UnknownQuantityListT const &quantities, viennashe::config const &conf)
Definition: simulator.hpp:223
void update_quantity(DeviceT const &device, viennashe::she::unknown_she_quantity< CellT, FacetT > &quan, viennashe::unknown_quantity< CellT > &spatial_quan, viennashe::config const &conf, VectorType const &x, bool force_no_damping=false)
Updater for SHE quantities when using Gummel iteration.
Definition: simulator.hpp:524
long create_mapping(DeviceType const &device, viennashe::unknown_quantity< VertexT > &quantity, long start_index=0)
Maps unkown indices in the system matrix to the given unknown spatial quantity.
Definition: mapping.hpp:53
boundary_type_id
An enumeration of all boundary conditions ViennaSHE supports.
Definition: forwards.h:125
@ BOUNDARY_DIRICHLET
Definition: forwards.h:127
@ BOUNDARY_ROBIN
Definition: forwards.h:129
double get_relative_update_norm(UnknownQuantityT const &spatial_quan, VectorType const &x)
Returns the relative update norm (L_2 ; realtive to the current value). Except for the potential.
Definition: simulator.hpp:257
double get_total_update_norm_newton(DeviceT const &device, UnknownQuantityT const &unknown_quantities_, VectorT const &x)
Total update calculator for Newton. Needs to be called before update_quantities!
Definition: simulator.hpp:374
@ MATERIAL_SEMICONDUCTOR_ID
Definition: forwards.h:137
@ MATERIAL_NO_CONDUCTOR_ID
Definition: forwards.h:136
@ MATERIAL_CONDUCTOR_ID
Definition: forwards.h:135
@ MATERIAL_INSULATOR_ID
Definition: forwards.h:139
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
Writes the kinetic energies to each nodes in the (x,H)-domain.
Provides the exceptions used inside the viennashe::she namespace.
File to gather all scattering operators.
Defines a generic simulator quantity for use within the solvers of ViennaSHE.
Defines a set of setup routines and checkers for simulators.
Routines for ensuring a constant doping along contacts.
Returns the carrier density at contacts modelled as thermal baths (used by DD and SHE)
Definition: accessors.hpp:317
@ newton_nonlinear_solver
Gummel iteration.
Definition: config.hpp:244
Contains a timer class. Contains one windows implementation and one *NIX implementation.
A container of all quantities defined for a certain timestep t.
Interpolation of a solution defined in one H-space to another H-space (required by Newton's method)
Defines the log keys used within the main viennashe:: namespace.
Defines the log keys used within the viennashe::she namespace.