Coverage for tbkit/error_handling.py: 100%
1289 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-30 11:04 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-30 11:04 +0000
1import numpy as np
2import inspect
5ATOL = 1e-3 # matches the distance/angle tolerance used throughout tbkit.system
8###############################
9# GENERIC EXCEPTION HANDLING
10###############################
13def boolean(var, var_name):
14 '''
15 Check if *var* is a boolean.
17 Raises
18 ------
19 TypeError
20 Parameter *var* must be a bool.
21 '''
22 if not isinstance(var, bool):
23 raise TypeError('\n\nParameter {} must be a bool.\n'.format(var_name))
26def positive_int(var, var_name):
27 '''
28 Check if *var* is a positive integer.
30 Raises
31 ------
32 TypeError
33 Parameter *var* must be an integer.
34 ValueError
35 Parameter *var* must be a positive integer.
36 '''
37 if not isinstance(var, int):
38 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
39 if var < 1:
40 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name))
43def positive_int_lim(var, var_name, nmax):
44 '''
45 Check if *var* is a positive integer smaller than nmax.
47 Raises
48 ------
49 TypeError
50 Parameter *var* must be an integer.
51 ValueError
52 Parameter *var* must be a positive integer.
53 ValueError
54 Parameter *var* must be a positive integer
55 smaller than nmax.
56 '''
57 if not isinstance(var, int):
58 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
59 if var < 1:
60 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name))
61 if var > nmax:
62 raise ValueError('\n\nParameter {} must be a positive integer '
63 'smaller than {}.\n'.format(var_name, nmax))
66def real_number(var, var_name):
67 '''
68 Check if parameter *var* is a real number.
70 Raises
71 ------
72 TypeError
73 Parameter *var* must be a real number.
74 '''
75 if not isinstance(var, (int, float)):
76 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
79def positive_real(var, var_name):
80 '''
81 Check if parameter *var* is a positive number.
83 Raises
84 ------
85 TypeError
86 Parameter *var* must be a real number.
87 ValueError
88 Parameter *var* must be a positive number.
89 '''
90 if not isinstance(var, (int, float)):
91 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
92 if var <= 0:
93 raise ValueError('\n\nParameter {} must be a positive number.\n'.format(var_name))
96def positive_real_zero(var, var_name):
97 '''
98 Check if parameter *var* is a positive number or zero.
100 Raises
101 ------
102 TypeError
103 Parameter *var* must be a real number.
104 ValueError
105 Parameter *var* must be a positive number or zero.
106 '''
107 if not isinstance(var, (int, float)):
108 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
109 if var < 0:
110 raise ValueError('\n\nParameter {} must be a positive number or zero.\n'.format(var_name))
113def negative_real(var, var_name):
114 '''
115 Check if parameter *var* is a negative number.
117 Raises
118 ------
119 TypeError
120 Parameter *var* must be a real number.
121 ValueError
122 Parameter *var* must be a negative number.
123 '''
124 if not isinstance(var, (int, float)):
125 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
126 if var >= 0:
127 raise ValueError('\n\nParameter {} must be a negative number.\n'.format(var_name))
130def number(var, var_name):
131 '''
132 Check if parameter *var* is a number.
134 Raises
135 ------
136 TypeError
137 Parameter *var* must be a real or complex number.
138 '''
139 if not isinstance(var, (int, float, complex)):
140 raise TypeError('\n\nParameter {} must be a real or complex number.\n'.format(var_name))
143def value_params(required, params, func):
144 '''
145 Check that *params* holds every parameter that the value function *func*
146 needs (see *tbkit.values*).
148 Raises
149 ------
150 TypeError
151 The value function needs parameters missing from get_ham.
152 '''
153 missing = [name for name in required if name not in params]
154 if missing:
155 raise TypeError('\n\nThe value function {} needs the parameter(s) {}: '
156 'pass them to get_ham (or KSpace.set_params).\n'.format(
157 getattr(func, '__name__', func), ', '.join(missing)))
160def value_shape(val, size, spin, func):
161 '''
162 Check the values returned by the value function *func* for *size*
163 bonds or sites: a number or one per entry (or, if *spin*, also a 2x2
164 matrix or one per entry).
166 Raises
167 ------
168 ValueError
169 The value function must return a number or an array of one value per bond/site.
170 '''
171 shapes = [(), (size,)] + ([(2, 2), (size, 2, 2)] if spin else [])
172 if val.shape not in shapes:
173 raise ValueError('\n\nThe value function {} returned shape {}: it must return a number, '
174 'or one per bond/site (shape ({},)){}.\n'.format(
175 getattr(func, '__name__', func), val.shape, size,
176 ', or a 2x2 matrix (one per bond, shape ({}, 2, 2))'.format(size)
177 if spin else ''))
180def no_value_functions(model, method):
181 '''
182 Check that *model* has no value functions (callable hoppings or onsite
183 energies), which *method* cannot represent.
185 Raises
186 ------
187 ValueError
188 method does not support value functions.
189 '''
190 if getattr(model, '_hop_values', None) or getattr(model, '_onsite_values', None):
191 raise ValueError('\n\n{} does not support value functions (callable hoppings or '
192 'onsite energies): use plain numbers.\n'.format(method))
195def is_callable(var, var_name):
196 '''
197 Check if parameter *var* is callable.
199 Raises
200 ------
201 TypeError
202 Parameter *var* must be callable.
203 '''
204 if not callable(var):
205 raise TypeError('\n\nParameter {} must be callable.\n'.format(var_name))
208def larger(var1, var_name1, var2, var_name2):
209 '''
210 Check if *var1* larger than *val*.
212 Raises
213 ------
214 ValueError
215 Parameter *var1* larger than *var2*.
216 '''
217 if var1 >= var2:
218 raise ValueError('\n\n{} must be larger than {}.\n'
219 .format(var_name1, var_name2))
222def smaller(var1, var_name1, var2, var_name2):
223 '''
224 Check if *var1* smaller than *var2*.
226 Raises
227 ------
228 ValueError
229 Parameter *var1* must be smaller than *var2*.
230 '''
231 if var1 >= var2:
232 raise ValueError('\n\n{} must be smaller than {}.\n'
233 .format(var_name1, var_name2))
236def string(var, var_name):
237 '''
238 Check if parameter *var* is a string.
240 Raises
241 ------
242 TypeError
243 Parameter *var* must be a string.
244 '''
245 if var is None:
246 return
247 if not isinstance(var, str):
248 raise TypeError('\n\nParameter {} must be a string.\n'.format(var_name))
251def ndarray(var, var_name, length):
252 '''
253 Check if parameter *var* is a numpy array.
255 Raises
256 ------
257 TypeError
258 Parameter *var* must be a numpy ndarray.
259 ValueError
260 length array must be equal to length.
261 '''
262 if not isinstance(var, np.ndarray):
263 raise TypeError('\n\nParameter {} must be a numpy ndarray.\n'.format(var_name))
264 if len(var) != length:
265 raise ValueError('\n\nParameter {} must be a numpy ndarray of {}\n'
266 ''.format(var_name, length))
269def ndarray_null(var, var_name):
270 '''
271 Check if parameter *var* is not a null numpy array.
273 Raises
274 ------
275 ValueError
276 Parameter *var* must not be a null numpy ndarray.
277 '''
278 array_null = np.zeros(len(var))
279 if np.allclose(var, array_null):
280 raise ValueError('\n\nParameter {} must not be a null numpy ndarray.\n'.format(var_name))
283def ndarray_empty(var, var_name):
284 '''
285 Check if parameter *var* is not an empty numpy array.
287 Raises
288 ------
289 ValueError
290 Parameter *var* must not be an emptynumpy ndarray.
291 '''
292 if var.size == 0:
293 raise ValueError('\n\nParameter {} must not be an empty numpy ndarray.\n'.format(var_name))
296def list_tuple_2elem(var, var_name):
297 '''
298 Check if parameter *var* is a list/tuple with 2 elements.
300 Raises
301 ------
302 TypeError
303 Parameter *var* must be a list/tuple.
304 ValueError
305 Parameter *var* must contain 2 elements.
306 '''
307 if var is None:
308 return
309 if not isinstance(var, (list, tuple)):
310 raise TypeError('\n\nParameter {} must be a list/tuple.\n'.format(var_name))
311 if len(var) != 2:
312 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name))
315def tuple_2elem(var, var_name):
316 '''
317 Check if parameter *var* is a tuple with 2 elements.
319 Raises
320 ------
321 TypeError
322 Parameter *var* must be a tuple.
323 ValueError
324 Parameter *var* must contain 2 elements.
325 '''
326 if var is None:
327 return
328 if not isinstance(var, tuple):
329 raise TypeError('\n\nParameter {} must be a tuple.\n'.format(var_name))
330 if len(var) != 2:
331 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name))
334###############################
335# LATTICE EXCEPTION HANDLING
336###############################
339def lat(lat):
340 '''
341 Check if parameter is an instance of the *lattice*.
343 Raises
344 ------
345 TypeError
346 Parameter must be an instance of the class lattice.
347 '''
348 names = inspect.getmro(lat.__class__)
349 if str(names).find('lattice') == -1:
350 raise TypeError('\n\nParameter must be an instance of the class lattice.\n')
353def unit_cell(unit_cell):
354 '''
355 Check parameter *unit_cell*.
357 Raises
358 ------
359 TypeError
360 Parameter unit_cell must be a list.
361 KeyError
362 Dictionaries must contain the key "tag".
363 KeyError
364 Dictionaries must contain the key "r0".
365 TypeError
366 Key "tags" must contain a one-character string.
367 ValueError
368 Key "tags" must contain a one-character string.
369 ValueError
370 Key "r0" must contain be a list.
371 TypeError
372 Key "r0" must contain be a tuple.
373 ValueError
374 Key "r0" must contain a tuple of length two.
375 ValueError
376 Key "r0" must contain a tuple of two real numbers.
377 '''
378 if not isinstance(unit_cell, list):
379 raise TypeError('\n\nParameter unit_cell must be a list.\n')
380 for dic in unit_cell:
381 if 'tag' not in dic:
382 raise KeyError('\n\nDictionaries must contain the key "tag".\n')
383 if 'r0' not in dic:
384 raise KeyError('\n\nDictionaries must contain the key "r0".\n')
385 if not isinstance(dic['tag'], str):
386 raise TypeError('\n\nKey "tag" must contain a one-character string.\n')
387 if not len(dic['tag']) == 1:
388 raise ValueError('\n\nKey "tag" must be a one-character string.\n')
389 if not isinstance(dic['r0'], tuple):
390 raise TypeError('\n\nKey "r0" must be a tuple.\n')
391 if len(dic['r0']) not in (2, 3):
392 raise ValueError('\n\nKey "r0" must contain a tuple of length two (or three, '
393 'for a lattice in 3D space).\n')
394 if not all(isinstance(c, (int, float)) for c in dic['r0']):
395 raise ValueError('\n\nKey "r0" must contain a tuple of real numbers.\n')
396 if len({len(dic['r0']) for dic in unit_cell}) > 1:
397 raise ValueError('\n\nAll "r0" must have the same length.\n')
400def prim_vec(prim_vec):
401 '''
402 Check parameter *prim_vec*.
404 Raises
405 ------
406 TypeError
407 Parameter prim_vec must be a list.
408 ValueError
409 Parameter prim_vec must be a list
410 of length 1 for 1D lattices or length 2 fro 2D lattices.
411 TypeError
412 List elements must be tuples.
413 ValueError
414 List elements must be 1 or 2 tuples.
415 ValueError
416 Tuples must be of length 2.
417 ValueError
418 Tuples must containt real numbers.
419 ValueError
420 Norm of coor should be larger than 0.1.
421 '''
422 if not isinstance(prim_vec, list):
423 raise TypeError('\n\nParameter prim_vec must be a list.\n')
424 if len(prim_vec) not in (1, 2, 3):
425 raise ValueError('\n\nParameter prim_vec must be a list of length 1 '
426 'for 1D lattices, 2 for 2D lattices, or 3 for 3D lattices.\n')
427 for coor in prim_vec:
428 if not isinstance(coor, tuple):
429 raise TypeError('\n\nParameter prim_vec must contain tuples.\n')
430 if len(coor) not in (2, 3):
431 raise ValueError('\n\nParameter prim_vec must contain tuples of length 2 '
432 '(or 3, for a lattice in 3D space).\n')
433 if not all(isinstance(c, (int, float)) for c in coor):
434 raise ValueError('\n\nParameter prim_vec must contain tuples of '
435 'real numbers.\n')
436 if sum(c ** 2 for c in coor) < 0.1:
437 raise ValueError('\n\nEach primitive vector must have a norm larger '
438 'than 0.1.\n')
439 if len({len(coor) for coor in prim_vec}) > 1:
440 raise ValueError('\n\nAll primitive vectors must have the same length.\n')
441 if len(prim_vec) > len(prim_vec[0]):
442 raise ValueError('\n\nA lattice cannot have more primitive vectors than '
443 'space dimensions (use 3-tuples for a 3D lattice).\n')
446def independent(prim_vec):
447 '''
448 Check that the primitive vectors are linearly independent (needed for
449 the reciprocal lattice).
451 Raises
452 ------
453 ValueError
454 The primitive vectors must be linearly independent.
455 '''
456 if np.linalg.matrix_rank(np.array(prim_vec, dtype='f8'), tol=1e-8) != len(prim_vec):
457 raise ValueError('\n\nThe primitive vectors must be linearly independent.\n')
460def space_dim(unit_cell, prim_vec):
461 '''
462 Check that the unit cell positions and the primitive vectors live in the
463 same space (2D or 3D).
465 Raises
466 ------
467 ValueError
468 "r0" and prim_vec tuples must have the same length.
469 '''
470 if unit_cell and len(unit_cell[0]['r0']) != len(prim_vec[0]):
471 raise ValueError('\n\nThe "r0" positions and the primitive vectors must have '
472 'the same number of components.\n')
475def get_lattice(prim_vec, n1, n2, n3=1):
476 '''
477 Check method *get_lattice*.
479 Raises
480 ------
481 TypeError
482 Parameters n1, n2, n3 must be integers.
483 ValueError
484 Parameters n1, n2, n3 must be positive integers.
485 ValueError
486 n2 (n3) must be 1 with fewer than 2 (3) primitive vectors.
487 '''
488 positive_int(n1, 'n1')
489 positive_int(n2, 'n2')
490 positive_int(n3, 'n3')
491 if len(prim_vec) == 1 and n2 > 1:
492 raise ValueError('\n\nParameter n2 should be equal to 1\n'
493 'if one primitive vector is given\n')
494 if len(prim_vec) < 3 and n3 > 1:
495 raise ValueError('\n\nParameter n3 should be equal to 1\n'
496 'if fewer than three primitive vectors are given\n')
499def coor(coor, dtype=None):
500 '''
501 Check if *coor* is a structured array with
502 dtype=[('x', 'f8'), ('y', 'f8'), ('tag', 'U1')], or, for a lattice in 3D
503 space, dtype=[('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')]. If
504 *dtype* is given, *coor* must have exactly that dtype.
505 '''
506 dtypes = [np.dtype([('x', 'f8'), ('y', 'f8'), ('tag', 'U1')]),
507 np.dtype([('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')])]
508 if dtype is not None:
509 dtypes = [np.dtype(dtype)]
510 if coor.dtype not in dtypes:
511 raise TypeError('\n\nParameter coor dtype must be\n'
512 'dtype=[("x", "f8"), ("y", "f8"), ("tag", "U1")]\n'
513 '(with a ("z", "f8") field after "y" for a lattice in 3D space).\n')
516def coor_1d(coor):
517 '''
518 Check if *coor* is 1d (coor['y'] = cst).
520 Raises
521 ------
522 ValueError
523 *coor* must be 1d( coor['y'] = cst)..
524 '''
525 if not np.allclose(coor['y'][0], coor['y']):
526 raise ValueError('\n\ncoor["y"] must be constant.\n')
529def remove_sites(index, sites):
530 '''
531 Check method *remove_sites*.
533 Raises
534 ------
535 TypeError
536 Parameter index must be a list.
537 ValueError
538 Parameter index must be a list of integers.
539 ValueError
540 Indices must be between 0 and sites -1.
541 of integers between 0 and sites
542 '''
543 if not isinstance(index, list):
544 raise TypeError('\n\nParameter index must be a list.\n')
545 if not all(isinstance(i, int) for i in index):
546 raise ValueError('\n\nParameter index must be a list of integers.\n')
547 if not all(-1 < i < sites for i in index):
548 raise ValueError('\n\nElements of index must be between 0 and sites - 1.\n')
551def shift(shift):
552 '''
553 Check *shift_x* and *shift_y*.
555 Raises
556 ------
557 TypeError
558 Parameter delta must be a real number.
559 '''
560 if not isinstance(shift, (int, float)):
561 raise TypeError('\n\nParameter shift must be a real number.\n')
564def boundary_line(cx, cy, co):
565 '''
566 Check *boundary_line*.
568 Raises
569 ------
570 TypeError
571 Parameter cx must be a real number.
572 TypeError
573 Parameter cy must be a real number.
574 TypeError
575 Parameter co must be a real number.
576 '''
577 if not isinstance(cx, (int, float)):
578 raise TypeError('\n\nParameter cx must be a real number.\n')
579 if not isinstance(cy, (int, float)):
580 raise TypeError('\n\nParameter cy must be a real number.\n')
581 if not isinstance(co, (int, float)):
582 raise TypeError('\n\nParameter co must be a real number.\n')
585def space_3d(space_dim):
586 '''
587 Check that the lattice lives in 3D space.
589 Raises
590 ------
591 ValueError
592 This method requires a lattice in 3D space.
593 '''
594 if space_dim != 3:
595 raise ValueError('\n\nThis method requires a lattice in 3D space '
596 '(unit_cell and prim_vec given as 3-tuples).\n')
599def sites(sites):
600 '''
601 Check if *get_lattice* has been called (*coor* not empty).
603 Raises
604 ------
605 RuntimeError
606 Run method lat.get_lattice first.
607 '''
608 if sites == 0:
609 raise RuntimeError('\n\nRun method lat.get_lattice first.\n')
612####################################
613# CLASS SYSTEM EXCEPTION HANDLING
614####################################
617def sys(sys):
618 '''
619 Check if parameter is an instance of the *system*.
621 Raises
622 ------
623 TypeError
624 Parameter must be an instance of the class system.
625 '''
626 names = inspect.getmro(sys.__class__)
627 if str(names).find('system') == -1:
628 raise TypeError('\n\nParameter must be an instance of the class system.\n')
631def print_hopping(n, nmax):
632 '''
633 Check method *print_vec_hopping*.
635 Raises
636 ------
637 TypeError
638 Parameter *nmax* must be an integer.
639 ValueError
640 Parameter *nmax* must be a positive integer.
641 between 1 and n_max-1.
642 '''
643 if not isinstance(n, int):
644 raise TypeError('\n\nParameter n_max must be an integer.\n')
645 if n < 1 or n > nmax-1:
646 raise ValueError('\n\nParameter n_max must be a positive integer'
647 'between 1 and n_max-1.\n')
650def set_onsite(onsite, tags):
651 '''
652 Check method *set_onsite*.
654 Raises
655 ------
656 TypeError
657 Parameter onsite must be a dictionary.
658 ValueError
659 Parameter onsite keys must be a tag.
660 ValueError
661 Parameter onsite values must be
662 real and/or complex numbers, or value functions.
663 '''
664 if not isinstance(onsite, dict):
665 raise TypeError('\n\nParameter onsite must be a dictionary.\n')
666 for tag, val in onsite.items():
667 if tag not in tags:
668 raise ValueError('\n\nParameter onsite keys must be a tag.\n')
669 if not isinstance(val, (int, float, complex)) and not callable(val):
670 raise ValueError('\n\nParameter onsite values must be\n'\
671 'real and/or complex numbers, or value functions.\n')
674def set_hopping(list_hop, n_max):
675 '''
676 Check method *set_hopping*.
678 Raises
679 ------
680 TypeError
681 Parameter *list_hop* must be a list.
682 TypeError
683 Parameter *list_hop* must be a list of dictionary.
684 KeyError
685 "n" and "t" must be dictionary keys.
686 KeyError
687 "tag" or "ang" must be a key.
688 KeyError
689 "tag" and "ang" must be keys.
690 ValueError
691 Dictionaries must be of length 2, 3, or 4.
692 ValueError
693 "n" must be between 1 and nmax"
694 '''
695 if not isinstance(list_hop, list):
696 raise TypeError('\n\nParameter *list_hop* must be a list.\n')
697 for dic in list_hop:
698 if not isinstance(dic, dict):
699 raise TypeError('\n\nParameter *list_hop* must be a list of dictionary.\n')
700 if 'n' not in dic or 't' not in dic:
701 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n')
702 if not isinstance(dic['n'], int):
703 raise TypeError('\n\n"n" value must be an integer.\n')
704 if not 0 < dic['n'] <= n_max:
705 raise ValueError('\n\n"n" value must be between 1 and {}.\n'.format(n_max))
706 if not isinstance(dic['t'], (int, float, complex)) and not callable(dic['t']):
707 raise TypeError('\n\n"t" value must be a real or complex number, or a value function.\n')
708 if len(dic) == 3:
709 if 'tag' not in dic and 'ang' not in dic:
710 raise KeyError('\n\n"tag" or "ang" must be a key.\n')
711 elif len(dic) == 4:
712 if 'tag' not in dic or 'ang' not in dic:
713 raise KeyError('\n\n"tag" and "ang" must be keys.\n')
714 elif len(dic) > 4:
715 raise ValueError('\n\nDictionaries must be of length 2, 3, or 4.\n')
716 if 'tag' in dic:
717 if not isinstance(dic['tag'], str):
718 raise TypeError('\n\n"tag" value must be a string.\n')
719 if len(dic['tag']) != 2:
720 raise ValueError('\n\n"tag" value must be a string of length 2.\n')
721 if 'ang' in dic:
722 if not isinstance(dic['ang'], (int, float)):
723 raise TypeError('\n\n"ang" value must be a real number.\n')
726def index(ind, dic):
727 '''
728 check if *ind* not empy.
729 '''
730 if np.sum(ind) == 0:
731 raise ValueError('\n\nNo hoppings with parameters {}.\n'.format(dic))
734def set_hopping_def(hop, hopping_def, sites, var_name='hopping_def', same_site=False):
735 '''
736 Check methods *set_hopping_def* and *set_hopping_manual*.
738 *hop* is not used (kept for backward compatibility). Indices may be
739 Python or NumPy integers, values Python or NumPy numbers. If *same_site*
740 is True, keys :math:`(i, i)` are accepted.
742 Raises
743 ------
744 TypeError
745 Parameter *hopping_def* must be a dictionary
746 TypeError
747 *hopping_def* keys must be tuples.
748 TypeError
749 *hopping_def* keys must be tuples of length 2.
750 ValueError
751 *hopping_def* keys must be tuples of integers.
752 ValueError
753 *hopping_def* keys must be integers between 0 and sites-1.
754 ValueError
755 *hopping_def* keys must be different integers between 0 and sites-1.
756 TypeError
757 *hopping_def* values must be numbers.
758 '''
759 if not isinstance(hopping_def, dict):
760 raise TypeError('\n\nParameter {} must be a dictionary.\n'.format(var_name))
761 for key, val in hopping_def.items():
762 if not isinstance(key, tuple):
763 raise TypeError('\n\n{} keys must be tuples.\n'.format(var_name))
764 if len(key) != 2:
765 raise TypeError('\n\n{} keys must be tuples of length 2.\n'.format(var_name))
766 if not isinstance(key[0], (int, np.integer)) or not isinstance(key[1], (int, np.integer)):
767 raise ValueError('\n\n{} keys must be tuples of integers.\n'.format(var_name))
768 if key[0] < 0 or key[1] < 0 or key[0] > sites-1 or key[1] > sites-1:
769 raise ValueError('\n\n{} keys must be integers between 0 and sites-1.\n'.format(var_name))
770 if key[0] == key[1] and not same_site:
771 raise ValueError('\n\n{} keys must be different integers between 0 and sites-1.\n'.format(var_name))
772 if not isinstance(val, (int, float, complex, np.number)):
773 raise TypeError('\n\n{} values must be numbers.\n'.format(var_name))
776def set_onsite_def(onsite_def, sites):
777 '''
778 Check method *set_ons_def*.
780 Raises
781 ------
782 TypeError
783 Parameter *onsite_def* must be a dictionary.
784 TypeError
785 *onsite_def* keys must be integers.
786 TypeError
787 *onsite_def* values must be numbers.
788 ValueError
789 *onsite_def* keys must be integers between :math:`[0, sites)`.
790 '''
791 if not isinstance(onsite_def, dict):
792 raise TypeError('\n\nParameter onsite_def must be a dictionary.\n')
793 for key, val in onsite_def.items():
794 if not isinstance(key, int):
795 raise TypeError('\n\nonsite_def keys must be integers.\n')
796 if not isinstance(val, (int, float, complex)):
797 raise TypeError('\n\nonsite_def values must be numbers.\n')
798 if key < 0 or key > sites-1:
799 raise ValueError('\n\nonsite_def keys must be integers between 0 and sites-1.\n')
802def hop_n1(hop):
803 '''
804 Check method if self.hop contains nearest neighbours hoppings.
806 Raises
807 ------
808 ValueError
809 self.hop must contain nearest neighbours hoppings.
810 '''
811 if not np.any(hop['n'] == 1):
812 raise ValueError('\n\nParameter hop must contain nearest neighbours hoppings.\n')
815def empty_onsite(onsite):
816 '''
817 Check if *onsite* not empty.
819 Raises
820 ------
821 RuntimeError
822 Run method set_onsite first.
823 '''
824 if onsite.size == 0:
825 raise RuntimeError('\n\nRun method set_onsite first\n')
828def empty_hop(hop):
829 '''
830 Check if *hop* not empty.
832 Raises
833 ------
834 RuntimeError
835 Run method set_hopping first.
836 '''
837 if hop.size == 0:
838 raise RuntimeError('\n\nRun method set_hopping first\n')
841def hop_sites(hop, sites):
842 '''
843 Check if *hop* indices are smaller than *sites*.
845 Raises
846 ------
847 ValueError
848 Run method sys.clear_hopping and redefine the hoppings.
849 '''
850 ind_max = max(np.max(hop[f]) for f in ('i', 'j') if f in hop.dtype.names)
851 if ind_max >= sites:
852 raise ValueError('\n\nThe hoppings refer to sites that no longer exist.\n'
853 'Run method system.clear_hopping '
854 'and redefine the hoppings.\n')
857def empty_coor(coor):
858 '''
859 Check if *coor* not empty.
861 Raises
862 ------
863 RuntimeError
864 Run method lattice.get_lattice first.
865 '''
866 if coor.size == 0:
867 raise RuntimeError('\n\nRun method lattice.get_lattice first.\n')
870def empty_coor_hop(coor_hop):
871 '''
872 Check if *coor_hop* not empty.
874 Raises
875 ------
876 RuntimeError
877 Run method system.get_coor_hop first.
878 '''
879 if coor_hop.size == 0:
880 raise RuntimeError('\n\nRun method system.get_coor_hop first.\n')
883def empty_ham(ham):
884 '''
885 Check if Hamiltonian not empty.
887 Raises
888 ------
889 RuntimeError
890 Run method system.get_ham first.
891 '''
892 if not ham.nnz:
893 raise RuntimeError('\n\nRun method system.get_ham first.\n')
896def empty_en(en):
897 '''
898 Check if *en* not empty.
900 Raises
901 ------
902 RuntimeError
903 Run method get_ham first.
904 '''
905 if en.size == 0:
906 raise RuntimeError('\n\nRun method get_eig first\n')
909def empty_pola(pola):
910 '''
911 Check if *pola* not empty.
913 Raises
914 ------
915 RuntimeError
916 Run method get_eig(eigenvec=True) first.
917 '''
918 if pola.size == 0:
919 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n')
922def empty_vn(vn):
923 '''
924 Check if *vn* not empty.
926 Raises
927 ------
928 RuntimeError
929 Run method get_eig(eigenvec=True) first.
930 '''
931 if vn.size == 0:
932 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n')
935def empty_ipr(ipr):
936 '''
937 Check if *ipr* not empty.
938 '''
939 if ipr.size == 0:
940 raise RuntimeError('\n\nRun method get_ipr first\n')
943def empty_ndarray(arr, method):
944 '''
945 Check if *arr* is a not empty np.ndarray.
946 '''
947 if arr.size == 0:
948 raise RuntimeError('\n\nRun method {} first\n'. format(method))
951def tag(tag, tags):
952 '''
953 Check tag.
955 Raises
956 ------
957 TypeError
958 Parameter *tag* must be a string.
959 ValueError
960 Parameter *tag* is not in tags.
961 '''
962 if not isinstance(tag, str):
963 raise TypeError('\n\nParameter tag must be a one-character string.\n')
964 if tag not in tags:
965 raise ValueError('\n\nParameter tag is not in tags.\n')
968def angle(angle, angles, upper_part):
969 '''
970 Check angle.
972 Raises
973 ------
974 TypeError
975 Parameter *angle* must be
977 * a positive number if *upper_part* is True
978 * a negative real if *upper_part* is False.
979 ValueError
980 Parameter *angle* is not in hop['ang'].
981 '''
982 if upper_part:
983 positive_real_zero(angle, 'angle, if upper_part=True,')
984 else:
985 negative_real(angle, 'angle, if upper_part=False,')
986 if not np.sum(np.isclose(angle, angles, atol=ATOL)) and \
987 not np.sum(np.isclose(angle, angles - 180, atol=ATOL)):
988 raise ValueError('\n\nParameter angle is not in hop["ang"].\n')
991def lims(lims):
992 '''
993 Check parameter *lims*.
995 Raises
996 ------
997 TypeError
998 Parameter lims must be a list.
999 TypeError
1000 Parameter *lims[0]* must be a real number.
1001 TypeError
1002 Parameter *lims[1]* must be a real number.
1003 ValueError
1004 *lims* must be a list of length 2.
1005 ValueError
1006 *lims[0]* must be smaller than *lims[1]*.
1007 '''
1008 if lims is not None:
1009 list_tuple_2elem(lims, 'lims')
1010 real_number(lims[0], 'lims[0]')
1011 real_number(lims[1], 'lims[1]')
1012 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]')
1015def lims_positive(lims):
1016 '''
1017 Check parameter *lims*.
1019 Raises
1020 ------
1021 TypeError
1022 Parameter lims must be a list.
1023 TypeError
1024 Parameter *lims[0]* must be a positive real number.
1025 TypeError
1026 Parameter *lims[1]* must be a positive real number.
1027 ValueError
1028 *lims* must be a list of length 2.
1029 ValueError
1030 *lims[0]* must be smaller than *lims[1]*.
1031 '''
1032 if lims is not None:
1033 list_tuple_2elem(lims, 'lims')
1034 positive_real(lims[0], 'lims[0]')
1035 positive_real(lims[1], 'lims[1]')
1036 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]')
1039#################################
1040# CLASS PLOT EXCEPTION HANDLING
1041#################################
1044def fig(fig):
1045 '''
1046 Check if fig is an instance of *Figure*.
1048 Raises
1049 ------
1050 TypeError
1051 fig must be an instance of *Figure*.
1052 '''
1053 if not fig.__class__.__name__ == 'Figure':
1054 raise TypeError('\n\nfig must be an instance of *Figure*.\n')
1057def ani(ani):
1058 '''
1059 Check if ani is an instance of *FuncAnimation*.
1061 Raises
1062 ------
1063 TypeError
1064 ani must be an instance of *FuncAnimation*.
1065 '''
1066 if not ani.__class__.__name__ == 'FuncAnimation':
1067 raise TypeError('\n\nani must be an instance of *FuncAnimation*.\n')
1070def file_format(file_format):
1071 '''
1072 Check if file_format is a string 'png', 'pdf', 'ps', 'eps', or 'svg'.
1074 Raises
1075 ------
1076 TypeError
1077 file_format must be a string.
1078 ValueError
1079 file_format must be a string given by 'png', 'pdf', 'ps', 'eps', or 'svg'.
1080 '''
1081 if not isinstance(file_format, str):
1082 raise TypeError('\n\nfile_format must be a string.\n')
1083 if file_format not in ['png', 'pdf', 'ps', 'eps', 'svg']:
1084 raise ValueError('\n\nfile_format must be a string given by,\n'\
1085 ' "png", "pdf", "ps", "eps", or "svg".\n')
1088####################################
1089# PROPAGATION
1090####################################
1093def get_pump(hams):
1094 '''
1095 Check method *propagation.get_pumping*.
1097 Raises
1098 ------
1099 TypeError
1100 hams must be a non-empty list.
1101 RuntimeError
1102 Run method system.get_ham first.
1103 '''
1104 if not isinstance(hams, list) or not hams:
1105 raise TypeError('\n\nhams must be a non-empty list.\n')
1106 for ham in hams:
1107 empty_ham(ham)
1109def prop_type(prop_type):
1110 string(prop_type, 'prop_type')
1111 if prop_type not in ['real', 'imag', 'norm']:
1112 raise ValueError('\n\nParameter prop_type must be a string:\n'
1113 '"real", "imag", "norm".\n')
1116####################################
1117# CLASS KSPACE EXCEPTION HANDLING
1118####################################
1121def k_vector(vec, var_name, ndim):
1122 '''
1123 Check that *vec* is a tuple/list of *ndim* real numbers.
1125 Raises
1126 ------
1127 TypeError
1128 Parameter *var_name* must be a tuple/list.
1129 ValueError
1130 Parameter *var_name* must be of length *ndim*.
1131 TypeError
1132 Parameter *var_name* must contain real numbers.
1133 '''
1134 if not isinstance(vec, (tuple, list, np.ndarray)):
1135 raise TypeError('\n\nParameter {} must be a tuple, list, or ndarray.\n'.format(var_name))
1136 if len(vec) != ndim:
1137 raise ValueError('\n\nParameter {} must be of length {}.\n'.format(var_name, ndim))
1138 for val in vec:
1139 if not isinstance(val, (int, float, np.integer, np.floating)):
1140 raise TypeError('\n\nParameter {} must contain real numbers.\n'.format(var_name))
1143def k_points(k, var_name, ndim):
1144 '''
1145 Check that *k* is one k-point (see *k_vector*) or an array of shape
1146 (nk, *ndim*) of real numbers.
1148 Raises
1149 ------
1150 ValueError
1151 Parameter *var_name* must be of shape (nk, ndim).
1152 '''
1153 if np.ndim(k) != 2:
1154 k_vector(k, var_name, ndim)
1155 return
1156 k = np.asarray(k)
1157 if k.shape[1] != ndim or not np.issubdtype(k.dtype, np.number) or np.iscomplexobj(k):
1158 raise ValueError('\n\nParameter {} must be a real array of shape (nk, {}).\n'.format(var_name, ndim))
1161def ks(ks, ndim):
1162 '''
1163 Check the k-points passed to *kspace.get_bands*: a 2D array of shape
1164 (nk, *ndim*).
1166 Raises
1167 ------
1168 ValueError
1169 Parameter ks must be of shape (nk, ndim).
1170 '''
1171 if ks.ndim != 2 or ks.shape[1] != ndim:
1172 raise ValueError('\n\nParameter ks must be an array of shape (nk, {}).\n'.format(ndim))
1175def spin_matrix(t, var_name):
1176 '''
1177 Check a spinful "t" or onsite value: either a plain number, or a 2x2
1178 complex matrix.
1180 Raises
1181 ------
1182 TypeError
1183 Parameter *var_name* must be a number or a 2x2 matrix.
1184 '''
1185 if isinstance(t, (int, float, complex)):
1186 return
1187 t = np.asarray(t)
1188 if t.shape != (2, 2):
1189 raise TypeError('\n\nParameter {} must be a number or a 2x2 matrix.\n'.format(var_name))
1192def set_hopping_kspace(list_hop, n_sites, ndim, spin=False, values=False):
1193 '''
1194 Check method *kspace.set_hopping*. With *values*, "t" may also be a
1195 value function (a callable).
1197 Raises
1198 ------
1199 TypeError
1200 Parameter *list_hop* must be a list of dictionaries.
1201 KeyError
1202 "i", "j", "R", and "t" must be dictionary keys.
1203 ValueError
1204 "i" and "j" must be site indices between 0 and n_sites-1.
1205 ValueError
1206 "R" must be a tuple of *ndim* integers.
1207 TypeError
1208 "t" must be a real or complex number (or, if *spin*,
1209 a 2x2 matrix).
1210 '''
1211 if not isinstance(list_hop, list):
1212 raise TypeError('\n\nParameter list_hop must be a list.\n')
1213 for dic in list_hop:
1214 if not isinstance(dic, dict):
1215 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n')
1216 if not {'i', 'j', 'R', 't'} <= set(dic):
1217 raise KeyError('\n\n"i", "j", "R", and "t" must be dictionary keys.\n')
1218 if not isinstance(dic['i'], int) or not isinstance(dic['j'], int):
1219 raise TypeError('\n\n"i" and "j" must be integers.\n')
1220 if not (0 <= dic['i'] < n_sites) or not (0 <= dic['j'] < n_sites):
1221 raise ValueError('\n\n"i" and "j" must be site indices between 0 and {}.\n'.format(n_sites-1))
1222 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim:
1223 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim))
1224 if not all(isinstance(n, int) for n in dic['R']):
1225 raise TypeError('\n\n"R" must be a tuple of integers.\n')
1226 if dic['i'] == dic['j'] and dic['R'] == (0,) * ndim:
1227 raise ValueError('\n\nUse kspace.set_onsite for i == j and R == 0 '
1228 '(it accepts a 2x2 spin matrix when spin=True).\n')
1229 if values and callable(dic['t']):
1230 continue
1231 if spin:
1232 spin_matrix(dic['t'], '"t"')
1233 elif not isinstance(dic['t'], (int, float, complex)):
1234 raise TypeError('\n\n"t" value must be a real or complex number.\n')
1237def set_onsite_kspace(dict_onsite, tags, spin=False):
1238 '''
1239 Check method *kspace.set_onsite*.
1241 Raises
1242 ------
1243 TypeError
1244 Parameter *dict_onsite* must be a dictionary.
1245 ValueError
1246 keys must be tags.
1247 TypeError
1248 values must be real or complex numbers (or, if
1249 *spin*, a pair of real/complex numbers, or a 2x2 matrix).
1250 '''
1251 if not isinstance(dict_onsite, dict):
1252 raise TypeError('\n\nParameter dict_onsite must be a dictionary.\n')
1253 for tag, val in dict_onsite.items():
1254 if tag not in tags:
1255 raise ValueError('\n\nParameter dict_onsite keys must be a tag.\n')
1256 if spin and not isinstance(val, (int, float, complex)):
1257 is_pair = (isinstance(val, (tuple, list)) and len(val) == 2
1258 and all(isinstance(v, (int, float, complex)) for v in val))
1259 is_mat = np.ndim(val) == 2 and np.shape(val) == (2, 2)
1260 if not (is_pair or is_mat):
1261 raise TypeError('\n\nParameter dict_onsite values must be a number, or, '
1262 'if spin, a pair of numbers (E_up, E_down) or a '
1263 '2x2 matrix.\n')
1264 elif not spin and not isinstance(val, (int, float, complex)):
1265 raise TypeError('\n\nParameter dict_onsite values must be real and/or complex numbers.\n')
1268def k_path_points(points, ndim):
1269 '''
1270 Check parameter *points* used by *kspace.k_path*.
1272 Raises
1273 ------
1274 TypeError
1275 Parameter points must be a list.
1276 ValueError
1277 Parameter points must contain at least two k-points.
1278 '''
1279 if not isinstance(points, list):
1280 raise TypeError('\n\nParameter points must be a list of k-points.\n')
1281 if len(points) < 2:
1282 raise ValueError('\n\nParameter points must contain at least two k-points.\n')
1283 for i, pt in enumerate(points):
1284 k_vector(pt, 'points[{}]'.format(i), ndim)
1287####################################
1288# DENSITY OF STATES
1289####################################
1292def dos_kernel(kernel):
1293 '''
1294 Check parameter *kernel* used by *dos.density_of_states*.
1296 Raises
1297 ------
1298 TypeError
1299 Parameter kernel must be a string.
1300 ValueError
1301 Parameter kernel must be "gaussian" or "lorentzian".
1302 '''
1303 string(kernel, 'kernel')
1304 if kernel not in ['gaussian', 'lorentzian']:
1305 raise ValueError('\n\nParameter kernel must be a string:\n'
1306 '"gaussian", "lorentzian".\n')
1309def kspace_dos_kernel(kernel):
1310 '''
1311 Check parameter *kernel* used by *kspace.plot_dos*.
1313 Raises
1314 ------
1315 TypeError
1316 Parameter kernel must be a string.
1317 ValueError
1318 Parameter kernel must be "gaussian", "lorentzian" or "tetrahedron".
1319 '''
1320 string(kernel, 'kernel')
1321 if kernel not in ['gaussian', 'lorentzian', 'tetrahedron']:
1322 raise ValueError('\n\nParameter kernel must be a string:\n'
1323 '"gaussian", "lorentzian", "tetrahedron".\n')
1326def mesh_energies(energies):
1327 '''
1328 Check parameter *energies* used by *dos.tetrahedron_dos*: a non-empty
1329 array of bands on a mesh of 1, 2 or 3 dimensions.
1331 Raises
1332 ------
1333 ValueError
1334 Parameter energies must be of shape (n1, ..., nbands), on a mesh of 1 to 3 dimensions.
1335 '''
1336 energies = np.asarray(energies)
1337 if energies.ndim not in (2, 3, 4) or energies.size == 0:
1338 raise ValueError('\n\nParameter energies must be a non-empty array of shape '
1339 '(n1, ..., nbands), on a mesh of 1, 2 or 3 dimensions.\n')
1342def increasing_grid(grid, var_name):
1343 '''
1344 Check that *grid* is a 1D real array of at least two increasing values.
1346 Raises
1347 ------
1348 ValueError
1349 Parameter *var_name* must be a 1D array of at least two increasing real values.
1350 '''
1351 grid = np.asarray(grid)
1352 if grid.ndim != 1 or len(grid) < 2 or np.iscomplexobj(grid) or np.any(np.diff(grid) <= 0):
1353 raise ValueError('\n\nParameter {} must be a 1D array of at least two increasing '
1354 'real values.\n'.format(var_name))
1357def nk(nk, ndim):
1358 '''
1359 Check parameter *nk* used by *kspace.mesh_bands* / *kspace.berry_curvature*.
1361 Raises
1362 ------
1363 TypeError
1364 Parameter nk must be an integer or a tuple of integers.
1365 ValueError
1366 Parameter nk (or each of its elements) must be a
1367 positive integer.
1368 ValueError
1369 Parameter nk must be a tuple of length *ndim*.
1370 '''
1371 if isinstance(nk, int):
1372 positive_int(nk, 'nk')
1373 return
1374 if not isinstance(nk, tuple):
1375 raise TypeError('\n\nParameter nk must be an integer or a tuple of integers.\n')
1376 if len(nk) != ndim:
1377 raise ValueError('\n\nParameter nk must be of length {}.\n'.format(ndim))
1378 for n in nk:
1379 positive_int(n, 'nk')
1382####################################
1383# TOPOLOGY
1384####################################
1387def dim_2(dim):
1388 '''
1389 Check that the model is 2D. Berry curvature / Chern number are only
1390 defined for a 2D Brillouin zone.
1392 Raises
1393 ------
1394 ValueError
1395 This calculation requires a 2D lattice.
1396 '''
1397 if dim != 2:
1398 raise ValueError('\n\nThis calculation requires a 2D lattice '
1399 '(two primitive vectors).\n')
1402def dim_min(dim, dmin):
1403 '''
1404 Check that the model has at least *dmin* periodic dimensions.
1406 Raises
1407 ------
1408 ValueError
1409 This calculation requires a lattice with at least dmin primitive vectors.
1410 '''
1411 if dim < dmin:
1412 raise ValueError('\n\nThis calculation requires a lattice with at least {} '
1413 'primitive vectors.\n'.format(dmin))
1416def plane(plane, dim):
1417 '''
1418 Check parameter *plane* of *kspace.berry_curvature*: two distinct
1419 reciprocal-vector indices below *dim*.
1421 Raises
1422 ------
1423 TypeError
1424 Parameter plane must be a tuple of two integers.
1425 ValueError
1426 Parameter plane must contain two distinct indices below dim.
1427 '''
1428 if not isinstance(plane, tuple) or len(plane) != 2 or \
1429 not all(isinstance(p, int) for p in plane):
1430 raise TypeError('\n\nParameter plane must be a tuple of two integers.\n')
1431 if plane[0] == plane[1] or not all(0 <= p < dim for p in plane):
1432 raise ValueError('\n\nParameter plane must contain two distinct indices '
1433 'between 0 and {}.\n'.format(dim - 1))
1436def direction(direction, dim=2):
1437 '''
1438 Check parameter *direction* used by *kspace.ribbon* (and the Wilson-loop
1439 methods): a primitive-vector index below *dim*.
1441 Raises
1442 ------
1443 TypeError
1444 Parameter direction must be an integer.
1445 ValueError
1446 Parameter direction must be between 0 and dim-1.
1447 '''
1448 if not isinstance(direction, int):
1449 raise TypeError('\n\nParameter direction must be an integer.\n')
1450 if not 0 <= direction < dim:
1451 raise ValueError('\n\nParameter direction must be between 0 and {}.\n'.format(dim - 1))
1454def band_indices(bands, norb):
1455 '''
1456 Check parameter *bands* used by *kspace.berry_curvature*.
1458 Raises
1459 ------
1460 TypeError
1461 Parameter bands must be a non-empty list of integers.
1462 ValueError
1463 Parameter bands must be a list of distinct band
1464 indices between 0 and norb-1.
1465 '''
1466 if not isinstance(bands, list) or not bands:
1467 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n')
1468 if not all(isinstance(b, int) for b in bands):
1469 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n')
1470 if len(set(bands)) != len(bands):
1471 raise ValueError('\n\nParameter bands must be a list of distinct band indices.\n')
1472 if not all(0 <= b < norb for b in bands):
1473 raise ValueError('\n\nParameter bands must be integers between 0 and {}.\n'.format(norb-1))
1476####################################
1477# BERRY PHASES, Z2, SYMMETRIES
1478####################################
1481def k_perp(k_perp, n):
1482 '''
1483 Check parameter *k_perp* of the Wilson-loop methods: a tuple of *n* real
1484 numbers (fractional coordinates along the other reciprocal vectors).
1486 Raises
1487 ------
1488 ValueError
1489 Parameter k_perp must contain n real numbers.
1490 '''
1491 if len(k_perp) != n or not all(isinstance(f, (int, float)) for f in k_perp):
1492 raise ValueError('\n\nParameter k_perp must contain {} real number(s).\n'.format(n))
1495def flow(flow, direction, dim):
1496 '''
1497 Check parameter *flow* of *kspace.wannier_flow*.
1499 Raises
1500 ------
1501 TypeError
1502 Parameter flow must be an integer.
1503 ValueError
1504 Parameter flow must be a direction other than *direction*.
1505 '''
1506 if not isinstance(flow, int):
1507 raise TypeError('\n\nParameter flow must be an integer.\n')
1508 if flow == direction or not 0 <= flow < dim:
1509 raise ValueError('\n\nParameter flow must be a direction between 0 and {}, '
1510 'other than direction.\n'.format(dim - 1))
1513def even_bands(bands):
1514 '''
1515 Check that *bands* is a list of an even number of band indices (Kramers pairs).
1517 Raises
1518 ------
1519 TypeError
1520 Parameter bands must be a list.
1521 ValueError
1522 Parameter bands must contain an even number of bands.
1523 '''
1524 if not isinstance(bands, list):
1525 raise TypeError('\n\nParameter bands must be a list of band indices.\n')
1526 if len(bands) % 2:
1527 raise ValueError('\n\nParameter bands must contain an even number of bands '
1528 '(Kramers pairs).\n')
1531def operator(mat, norb):
1532 '''
1533 Check a symmetry operator: a (norb, norb) matrix.
1535 Raises
1536 ------
1537 ValueError
1538 The operator must be a (norb, norb) matrix.
1539 '''
1540 if mat.shape != (norb, norb):
1541 raise ValueError('\n\nThe operator must be a ({0}, {0}) matrix.\n'.format(norb))
1544def parities(xi, tol=1e-6):
1545 '''
1546 Check that inversion eigenvalues are +-1.
1548 Raises
1549 ------
1550 ValueError
1551 The bands are not an inversion-invariant subspace.
1552 '''
1553 if not np.allclose(np.abs(xi.real), 1., atol=tol) or not np.allclose(xi.imag, 0., atol=tol):
1554 raise ValueError('\n\nThe parity eigenvalues are not +-1: the operator is not '
1555 'an inversion symmetry of these bands (or the bands are '
1556 'degenerate with the others at a TRIM).\n')
1557 if np.sum(xi.real < 0) % 2:
1558 raise ValueError('\n\nOdd number of negative parities: the bands are not '
1559 'Kramers pairs.\n')
1562def k_map(k_map):
1563 '''
1564 Check parameter *k_map* of *kspace.symmetry_error*.
1566 Raises
1567 ------
1568 ValueError
1569 Parameter k_map must be 'minus' or 'identity'.
1570 '''
1571 if k_map not in ('minus', 'identity'):
1572 raise ValueError('\n\nParameter k_map must be "minus" or "identity".\n')
1575def is_symmetry(err, tol):
1576 '''
1577 Check that a symmetry error is below *tol*.
1579 Raises
1580 ------
1581 ValueError
1582 The operator is not a symmetry of the model.
1583 '''
1584 if err > tol:
1585 raise ValueError('\n\nThe operator is not a symmetry of the model '
1586 '(deviation {:.3g}).\n'.format(err))
1589def symmetry_algebra(u, v, tol):
1590 '''
1591 Check that two symmetry operators of the same kind agree up to a phase
1592 (in *kspace.KSpace.non_hermitian_class*, *v* is a product of others).
1594 Raises
1595 ------
1596 ValueError
1597 The symmetry operators leave a unitary symmetry: block-diagonalize it first.
1598 '''
1599 phase = np.trace(u.conj().T @ v) / np.trace(u.conj().T @ u)
1600 if not np.allclose(v, phase * u, atol=tol):
1601 raise ValueError('\n\nThe symmetry operators leave a unitary symmetry (two products of '
1602 'the same kind differ by more than a phase): block-diagonalize '
1603 'the Hamiltonian first.\n')
1606def square(sq, tol):
1607 '''
1608 Check that the square of an antiunitary operator is +-1.
1610 Raises
1611 ------
1612 ValueError
1613 The square of the operator must be +1 or -1.
1614 '''
1615 if not np.allclose(sq, sq[0, 0] * np.eye(len(sq)), atol=tol) or \
1616 not np.isclose(abs(sq[0, 0]), 1., atol=tol):
1617 raise ValueError('\n\nThe square of the antiunitary operator must be +1 or -1.\n')
1620def hermitian(ham):
1621 '''
1622 Check that a (sparse) Hamiltonian is Hermitian.
1624 Raises
1625 ------
1626 ValueError
1627 This calculation requires a Hermitian Hamiltonian.
1628 '''
1629 if (abs(ham - ham.conj().T) > 1e-12).nnz:
1630 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n')
1633def planar_cell(prim_vec):
1634 '''
1635 Check that the lattice has two in-plane primitive vectors (a unit-cell area).
1637 Raises
1638 ------
1639 ValueError
1640 Give the area per site explicitly.
1641 '''
1642 if len(prim_vec) != 2 or len(prim_vec[0]) != 2:
1643 raise ValueError('\n\nThe unit-cell area needs two in-plane primitive vectors: '
1644 'give the area per site explicitly.\n')
1647def integer(var, var_name):
1648 '''
1649 Check if *var* is an integer.
1651 Raises
1652 ------
1653 TypeError
1654 Parameter *var* must be an integer.
1655 '''
1656 if not isinstance(var, int):
1657 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
1660def dim_exact(dim, d):
1661 '''
1662 Check that the model has exactly *d* periodic dimensions.
1664 Raises
1665 ------
1666 ValueError
1667 This calculation requires a lattice with d primitive vectors.
1668 '''
1669 if dim != d:
1670 raise ValueError('\n\nThis calculation requires a lattice with {} primitive '
1671 'vector(s).\n'.format(d))
1674def nonzero_number(var, var_name):
1675 '''
1676 Check if *var* is a nonzero number.
1678 Raises
1679 ------
1680 TypeError
1681 Parameter *var* must be a number.
1682 ValueError
1683 Parameter *var* must be nonzero.
1684 '''
1685 number(var, var_name)
1686 if var == 0:
1687 raise ValueError('\n\nParameter {} must be nonzero.\n'.format(var_name))
1690def gbz_roots(n_roots, p):
1691 '''
1692 Check that det[H(beta) - E] has roots on both sides of the GBZ.
1694 Raises
1695 ------
1696 ValueError
1697 The model has no hoppings in both directions.
1698 '''
1699 if p < 1 or n_roots <= p:
1700 raise ValueError('\n\nThe generalized Brillouin zone needs hoppings in both '
1701 'directions along the chain.\n')
1704####################################
1705# EXCEPTIONAL POINTS
1706####################################
1709def ham_model(model, cls):
1710 '''
1711 Check the model of *tbkit.exceptional*: a 2D **KSpace** instance, or a
1712 callable of a point of a 2D parameter plane.
1714 Raises
1715 ------
1716 TypeError
1717 Parameter model must be a KSpace instance or a callable.
1718 ValueError
1719 A KSpace model must have two primitive vectors.
1720 '''
1721 if isinstance(model, cls):
1722 if model.dim != 2:
1723 raise ValueError('\n\nParameter model must be a KSpace with two primitive '
1724 'vectors (a 2D Brillouin zone).\n')
1725 elif not callable(model):
1726 raise TypeError('\n\nParameter model must be a KSpace instance, or a callable '
1727 'returning a square matrix.\n')
1730def ham_matrix(ham):
1731 '''
1732 Check the matrix returned by the callable model of *tbkit.exceptional*:
1733 square and finite.
1735 Raises
1736 ------
1737 ValueError
1738 The model must return a finite square matrix.
1739 '''
1740 if ham.ndim != 2 or ham.shape[0] != ham.shape[1] or not np.all(np.isfinite(ham)):
1741 raise ValueError('\n\nThe model must return a finite square matrix.\n')
1744def loop(loop):
1745 '''
1746 Check a closed loop of *tbkit.exceptional*: a callable of s in [0, 1]
1747 returning a point of the plane, or the vertices of a polygon.
1749 Raises
1750 ------
1751 TypeError
1752 Parameter loop must be a callable or an array of vertices.
1753 ValueError
1754 A polygon needs at least 3 vertices with 2 real coordinates.
1755 '''
1756 if callable(loop):
1757 return
1758 try:
1759 arr = np.asarray(loop, dtype='f8')
1760 except (TypeError, ValueError):
1761 arr = np.zeros(())
1762 if arr.ndim == 0:
1763 raise TypeError('\n\nParameter loop must be a callable of s in [0, 1], or an '
1764 'array of polygon vertices.\n')
1765 if arr.ndim != 2 or arr.shape[0] < 3 or arr.shape[1] != 2 or not np.all(np.isfinite(arr)):
1766 raise ValueError('\n\nParameter loop must hold at least 3 vertices of 2 real '
1767 'coordinates, shape (m, 2).\n')
1770def loop_point(point):
1771 '''
1772 Check a point returned by a loop callable: 2 finite real numbers.
1774 Raises
1775 ------
1776 ValueError
1777 The loop must return points of 2 real coordinates.
1778 '''
1779 if point.shape != (2,) or not np.all(np.isfinite(point)):
1780 raise ValueError('\n\nParameter loop must return points of 2 finite real '
1781 'coordinates.\n')
1784def closed_loop(ham_start, ham_end):
1785 '''
1786 Check that a loop is closed: the Hamiltonian at its end is the one at its start.
1788 Raises
1789 ------
1790 ValueError
1791 The loop is not closed.
1792 '''
1793 scale = max(1., float(np.max(np.abs(ham_start))))
1794 if np.max(np.abs(ham_end - ham_start)) > 1e-9 * scale:
1795 raise ValueError('\n\nParameter loop must be closed: H(loop(1)) differs from '
1796 'H(loop(0)).\n')
1799def band_pair(bands, norb):
1800 '''
1801 Check a pair of band labels: two distinct integers between 0 and norb-1.
1803 Raises
1804 ------
1805 TypeError
1806 Parameter bands must be a pair of integers.
1807 ValueError
1808 Parameter bands must be two distinct band indices.
1809 '''
1810 if not isinstance(bands, (tuple, list)) or len(bands) != 2 or \
1811 not all(isinstance(b, int) for b in bands):
1812 raise TypeError('\n\nParameter bands must be a pair of integers (m, n).\n')
1813 if bands[0] == bands[1] or not all(0 <= b < norb for b in bands):
1814 raise ValueError('\n\nParameter bands must be two distinct band indices between 0 '
1815 'and {}.\n'.format(norb - 1))
1818def band_index(band, norb):
1819 '''
1820 Check a band label: an integer between 0 and norb-1.
1822 Raises
1823 ------
1824 TypeError
1825 Parameter band must be an integer.
1826 ValueError
1827 Parameter band must be between 0 and norb-1.
1828 '''
1829 integer(band, 'band')
1830 if not 0 <= band < norb:
1831 raise ValueError('\n\nParameter band must be between 0 and {}.\n'.format(norb - 1))
1834def tracking(k):
1835 '''
1836 Eigenvalue continuation failed: the path meets a degeneracy.
1838 Raises
1839 ------
1840 ValueError
1841 Cannot follow the eigenvalues continuously near k.
1842 '''
1843 raise ValueError('\n\nCannot follow the eigenvalues continuously near k = {}: the path '
1844 'passes through (or too close to) a degeneracy. Move or '
1845 'enlarge the loop.\n'.format(np.round(k, 8)))
1848def winding_path(k):
1849 '''
1850 The discriminant vanishes on the path of a winding number.
1852 Raises
1853 ------
1854 ValueError
1855 The loop passes through a zero of the discriminant.
1856 '''
1857 raise ValueError('\n\nThe discriminant vanishes (to numerical precision) on the path, '
1858 'near k = {}: the loop passes through a degeneracy. Move the '
1859 'loop, or change nk.\n'.format(np.round(k, 8)))
1862def discriminant_nonzero(scale):
1863 '''
1864 Check that the discriminant does not vanish identically.
1866 Raises
1867 ------
1868 ValueError
1869 The bands are degenerate everywhere.
1870 '''
1871 if scale == 0.:
1872 raise ValueError('\n\nThe discriminant vanishes everywhere: some bands are '
1873 'degenerate over the whole zone.\n')
1876def gap_kind(gap):
1877 '''
1878 Check the kind of line gap: 'real' (Re E) or 'imaginary' (Im E).
1880 Raises
1881 ------
1882 ValueError
1883 Parameter gap must be "real" or "imaginary".
1884 '''
1885 if gap not in ('real', 'imaginary'):
1886 raise ValueError('\n\nParameter gap must be "real" or "imaginary".\n')
1889def biorthogonal_kind(kind):
1890 '''
1891 Check the kind of Berry connection: 'LR', 'RL', 'RR' or 'LL'.
1893 Raises
1894 ------
1895 ValueError
1896 Parameter kind must be "LR", "RL", "RR" or "LL".
1897 '''
1898 if kind not in ('LR', 'RL', 'RR', 'LL'):
1899 raise ValueError('\n\nParameter kind must be "LR", "RL", "RR" or "LL".\n')
1902def line_gap(keys, bands, gap):
1903 '''
1904 Check that *bands* are separated from the other bands by a line gap:
1905 with the bands ordered by *keys* (Re E or Im E, shape (nk, norb)) at
1906 every k-point, a band inside the group and its neighbour outside it
1907 never overlap over the whole mesh.
1909 Raises
1910 ------
1911 ValueError
1912 The bands have no line gap.
1913 '''
1914 inside = np.zeros(keys.shape[1], bool)
1915 inside[bands] = True
1916 for n in range(keys.shape[1] - 1):
1917 if inside[n] != inside[n + 1] and np.max(keys[:, n]) >= np.min(keys[:, n + 1]):
1918 part = 'Re E' if gap == 'real' else 'Im E'
1919 raise ValueError('\n\nThe bands {} are not separated from the others by a {} line '
1920 'gap: sorted by {}, band {} reaches band {} over the '
1921 'Brillouin zone, so the band labels are not continuous. '
1922 'For a non-Hermitian model, use '
1923 'KSpace.biorthogonal_chern_number with the other kind of '
1924 'gap, or locate the gap-closing exceptional points with '
1925 'tbkit.exceptional.find_exceptional_points.\n'
1926 .format(bands, gap, part, n, n + 1))
1929def band_continuity(en, bands):
1930 '''
1931 Check that a group of bands, labelled at every point of a periodic k
1932 mesh (*en*, complex, shape (n1, n2, norb)), is continuous: matching the
1933 eigenvalues of neighbouring points by distance maps the group onto
1934 itself. A line gap measured on the mesh alone can miss a narrow
1935 crossing between mesh points; this catches the jump of the labels.
1937 Raises
1938 ------
1939 ValueError
1940 The band labels jump between neighbouring k-points.
1941 '''
1942 from scipy.optimize import linear_sum_assignment
1943 inside = np.zeros(en.shape[2], bool)
1944 inside[bands] = True
1945 for axis in (0, 1):
1946 nxt = np.roll(en, -1, axis=axis)
1947 for e_a, e_b in zip(en.reshape(-1, en.shape[2]), nxt.reshape(-1, en.shape[2])):
1948 _, perm = linear_sum_assignment(np.abs(e_a[:, None] - e_b[None, :]))
1949 if np.any(inside != inside[perm]):
1950 raise ValueError('\n\nThe bands {} are not separated from the others over the '
1951 'Brillouin zone: between neighbouring k-points they '
1952 'exchange eigenvalues with the other bands (the line gap '
1953 'closes, e.g. at exceptional points, or nk is too small). '
1954 'For a non-Hermitian model, try '
1955 'KSpace.biorthogonal_chern_number with the other kind of '
1956 'gap, or tbkit.exceptional.find_exceptional_points.\n'
1957 .format(bands))
1960def no_overlap(overlap):
1961 '''
1962 Check that no overlap matrix is set (non-orthogonal bases are not
1963 supported by the biorthogonal tools).
1965 Raises
1966 ------
1967 ValueError
1968 Not available with an overlap matrix.
1969 '''
1970 if overlap:
1971 raise ValueError('\n\nThis method does not support an overlap matrix (set_overlap).\n')
1974def weights(weights, n):
1975 '''
1976 Check the weights of *occupation.fermi_level*: *n* positive numbers.
1978 Raises
1979 ------
1980 ValueError
1981 Parameter weights must contain n positive numbers.
1982 '''
1983 if len(weights) != n or np.any(weights <= 0):
1984 raise ValueError('\n\nParameter weights must contain {} positive numbers.\n'.format(n))
1987def electrons(n_electrons, capacity):
1988 '''
1989 Check a number of electrons: between 0 and the total weight of the levels.
1991 Raises
1992 ------
1993 TypeError
1994 Parameter n_electrons must be a real number.
1995 ValueError
1996 Parameter n_electrons must be between 0 and capacity.
1997 '''
1998 positive_real_zero(n_electrons, 'n_electrons')
1999 if n_electrons > capacity * (1 + 1e-12):
2000 raise ValueError('\n\nParameter n_electrons must be between 0 and {:g} '
2001 '(the number of states).\n'.format(capacity))
2004def n_eig(n_eig, sites):
2005 '''
2006 Check the number of eigenpairs of *system.get_eig_sparse*.
2008 Raises
2009 ------
2010 TypeError
2011 Parameter n_eig must be an integer.
2012 ValueError
2013 Parameter n_eig must be between 1 and sites-2.
2014 '''
2015 positive_int(n_eig, 'n_eig')
2016 if n_eig > sites - 2:
2017 raise ValueError('\n\nParameter n_eig must be between 1 and {} (use get_eig for '
2018 'the full spectrum).\n'.format(sites - 2))
2021####################################
2022# TRANSPORT
2023####################################
2026def square_matrix(mat, var_name):
2027 '''
2028 Check that *mat* is a square 2D array.
2030 Raises
2031 ------
2032 ValueError
2033 Parameter must be a square matrix.
2034 '''
2035 if mat.ndim != 2 or mat.shape[0] != mat.shape[1]:
2036 raise ValueError('\n\nParameter {} must be a square matrix.\n'.format(var_name))
2039def lead(h0, v):
2040 '''
2041 Check a lead: square cell Hamiltonian *h0* and coupling *v* of the same shape.
2043 Raises
2044 ------
2045 ValueError
2046 h0 and v must be square matrices of the same shape.
2047 '''
2048 square_matrix(h0, 'h0')
2049 if v.shape != h0.shape:
2050 raise ValueError('\n\nParameters h0 and v must be square matrices of the same shape.\n')
2053def converged(flag, name):
2054 '''
2055 Check that an iteration converged.
2057 Raises
2058 ------
2059 RuntimeError
2060 The iteration did not converge.
2061 '''
2062 if not flag:
2063 raise RuntimeError('\n\n{} did not converge: increase max_iter.\n'.format(name))
2066def lead_direction(direction):
2067 '''
2068 Check the direction of a lead: +1 or -1.
2070 Raises
2071 ------
2072 ValueError
2073 Parameter direction must be +1 or -1.
2074 '''
2075 if direction not in (1, -1):
2076 raise ValueError('\n\nParameter direction must be +1 or -1.\n')
2079def surface_side(side):
2080 '''
2081 Check parameter *side* of *KSpace.surface_spectral_function*: +1 or -1.
2083 Raises
2084 ------
2085 ValueError
2086 Parameter side must be +1 or -1.
2087 '''
2088 if isinstance(side, bool) or side not in (1, -1):
2089 raise ValueError('\n\nParameter side must be +1 or -1.\n')
2092def nearest_cells(n):
2093 '''
2094 Check that a lead hopping reaches at most the neighbouring cells.
2096 Raises
2097 ------
2098 ValueError
2099 Lead hoppings must connect neighbouring cells only.
2100 '''
2101 if abs(n) > 1:
2102 raise ValueError('\n\nA lead from a KSpace model needs hoppings between '
2103 'neighbouring cells only (use a larger unit cell).\n')
2106def lead_coupling(coupling, sites, m, n_device):
2107 '''
2108 Check the coupling of a lead to the device.
2110 Raises
2111 ------
2112 TypeError
2113 Parameter sites must be a list of integers.
2114 ValueError
2115 sites must be distinct device indices, coupling of shape (len(sites), m).
2116 '''
2117 if not isinstance(sites, list) or not all(isinstance(s, (int, np.integer)) for s in sites):
2118 raise TypeError('\n\nParameter sites must be a list of integers.\n')
2119 if len(set(sites)) != len(sites) or not all(0 <= s < n_device for s in sites):
2120 raise ValueError('\n\nParameter sites must contain distinct device indices '
2121 'between 0 and {}.\n'.format(n_device - 1))
2122 if coupling.shape != (len(sites), m):
2123 raise ValueError('\n\nParameter coupling must be of shape (len(sites), len(h0)) '
2124 '= ({}, {}).\n'.format(len(sites), m))
2127def lead_device_size(sites, n_device):
2128 '''
2129 Check that a System has one site per row of the device Hamiltonian.
2131 Raises
2132 ------
2133 ValueError
2134 The System must have as many sites as the device Hamiltonian has rows.
2135 '''
2136 if sites != n_device:
2137 raise ValueError('\n\nThe System has {} sites but the device Hamiltonian has {} '
2138 'rows.\n'.format(sites, n_device))
2141def lead_space_dim(lead_dim, device_dim):
2142 '''
2143 Check that a lead and the device have positions of the same dimension.
2145 Raises
2146 ------
2147 ValueError
2148 The lead and the device must have the same space dimension.
2149 '''
2150 if lead_dim != device_dim:
2151 raise ValueError('\n\nThe lead has {}D positions but the device {}D '
2152 'ones.\n'.format(lead_dim, device_dim))
2155def lead_overlap(found):
2156 '''
2157 Check that some device site lies on the lattice of a lead.
2159 Raises
2160 ------
2161 ValueError
2162 No device site matches a lead orbital.
2163 '''
2164 if not found:
2165 raise ValueError('\n\nNo device site sits at the position (and tag) of a lead '
2166 'orbital: place the lead unit cell in the coordinates of the device.\n')
2169def lead_interface(missing):
2170 '''
2171 Check that the interface cell of a lead is complete (*missing*: the lead
2172 orbitals coupled to the next cell but absent from the device).
2174 Raises
2175 ------
2176 ValueError
2177 The interface between the device and the lead is incomplete.
2178 '''
2179 if missing:
2180 raise ValueError('\n\nThe device lacks lead orbitals {} in its outermost lead cell: '
2181 'extend the device so that it ends with a complete lead '
2182 'cell (or pass extend=True).\n'.format([int(o) for o in missing]))
2185def lead_extension(inside):
2186 '''
2187 Check that the lead sites added to complete an interface stay within the
2188 device's extent along the lead (*inside* False when they would pass the
2189 innermost lead cell holding a device site).
2191 Raises
2192 ------
2193 ValueError
2194 The lead cannot be closed against the device.
2195 '''
2196 if not inside:
2197 raise ValueError('\n\nThe lead sites missing from the device reach past its far end: '
2198 'the lead is not closed off by the device (wider than it, or '
2199 'off its edge).\n')
2202def lead_index(lead, n_leads):
2203 '''
2204 Check a lead index.
2206 Raises
2207 ------
2208 TypeError
2209 The lead index must be an integer.
2210 ValueError
2211 No such lead.
2212 '''
2213 if not isinstance(lead, int):
2214 raise TypeError('\n\nThe lead index must be an integer.\n')
2215 if not 0 <= lead < n_leads:
2216 raise ValueError('\n\nNo lead {} ({} lead(s) attached with add_lead).\n'.format(lead, n_leads))
2219def lead_band_edge(flag, energy):
2220 '''
2221 Check that the modes of a lead are well defined: the energy is not at a
2222 band edge, where a propagating mode has zero velocity.
2224 Raises
2225 ------
2226 ValueError
2227 Energy at a band edge of a lead.
2228 '''
2229 if not flag:
2230 raise ValueError('\n\nEnergy {} is at a band edge of a lead (a mode with zero velocity): '
2231 'shift it slightly.\n'.format(energy))
2234def conservation_law(law, h0, v, tol=1e-10):
2235 '''
2236 Check the conservation law of a lead: a Hermitian matrix of the shape
2237 of *h0* that commutes with *h0* and *v*.
2239 Raises
2240 ------
2241 ValueError
2242 Wrong shape, not Hermitian, or not conserved by the lead.
2243 '''
2244 if law.shape != h0.shape:
2245 raise ValueError('\n\nParameter conservation_law must have the shape of h0, {}.\n'.format(h0.shape))
2246 hermitian_operator(law, tol)
2247 scale = max(1., np.max(np.abs(h0)), np.max(np.abs(v)))
2248 if max(np.max(np.abs(law @ h0 - h0 @ law)), np.max(np.abs(law @ v - v @ law))) > tol * scale:
2249 raise ValueError('\n\nThe conservation law must commute with the lead\'s h0 and v.\n')
2252def lead_block(lead, n_blocks):
2253 '''
2254 Check a lead of a scattering matrix: a lead index, or a tuple
2255 (lead, block) with a block of that lead's conservation law.
2257 Raises
2258 ------
2259 TypeError
2260 Not an integer or a tuple of two integers.
2261 ValueError
2262 No such lead or block.
2263 '''
2264 if isinstance(lead, tuple):
2265 if len(lead) != 2 or not all(isinstance(x, (int, np.integer)) for x in lead):
2266 raise TypeError('\n\nA lead block must be a tuple (lead, block) of two integers.\n')
2267 lead_index(int(lead[0]), len(n_blocks))
2268 if not 0 <= lead[1] < n_blocks[lead[0]]:
2269 raise ValueError('\n\nLead {} has {} block(s) (its conservation law).\n'.format(
2270 lead[0], n_blocks[lead[0]]))
2271 else:
2272 lead_index(lead, len(n_blocks))
2276####################################
2277# KERNEL POLYNOMIAL METHOD
2278####################################
2281def kpm_kernel(kernel):
2282 '''
2283 Check the KPM kernel name.
2285 Raises
2286 ------
2287 ValueError
2288 Parameter kernel must be "jackson" or "lorentz".
2289 '''
2290 if kernel not in ('jackson', 'lorentz'):
2291 raise ValueError('\n\nParameter kernel must be "jackson" or "lorentz".\n')
2294def kpm_grid(eps):
2295 '''
2296 Check that the KPM energies lie inside the (scaled) spectral bounds.
2298 Raises
2299 ------
2300 ValueError
2301 e_grid must lie inside the spectral bounds.
2302 '''
2303 if np.any(np.abs(eps) >= 1.):
2304 raise ValueError('\n\nParameter e_grid must lie strictly inside the spectral '
2305 'bounds.\n')
2308def site_index(site, n):
2309 '''
2310 Check a site (row) index.
2312 Raises
2313 ------
2314 TypeError
2315 Parameter site must be an integer.
2316 ValueError
2317 Parameter site must be between 0 and n-1.
2318 '''
2319 if not isinstance(site, (int, np.integer)):
2320 raise TypeError('\n\nParameter site must be an integer.\n')
2321 if not 0 <= site < n:
2322 raise ValueError('\n\nParameter site must be between 0 and {}.\n'.format(n - 1))
2325def max_order(list_hop):
2326 '''
2327 Largest neighbour order 'n' requested by a (possibly malformed) list of
2328 hoppings; 1 if there is none (the list is validated afterwards).
2329 '''
2330 try:
2331 orders = [dic['n'] for dic in list_hop if isinstance(dic['n'], int)]
2332 except (TypeError, KeyError):
2333 return 1
2334 return max([n for n in orders if n > 0] + [1])
2337####################################
2338# ORBITALS, SLATER-KOSTER, SPIN
2339####################################
2342def orbitals(orbs):
2343 '''
2344 Check a list of orbital names.
2346 Raises
2347 ------
2348 TypeError
2349 The orbitals must be a list of names.
2350 ValueError
2351 Unknown or repeated orbital.
2352 '''
2353 from tbkit.slater_koster import ORBITALS
2354 if not isinstance(orbs, list) or not orbs:
2355 raise TypeError('\n\nThe orbitals must be a non-empty list of names.\n')
2356 for o in orbs:
2357 if o not in ORBITALS:
2358 raise ValueError('\n\nUnknown orbital {!r}: use one of {}.\n'.format(o, list(ORBITALS)))
2359 if len(set(orbs)) != len(orbs):
2360 raise ValueError('\n\nRepeated orbital in {}.\n'.format(orbs))
2363def orbital_dict(orbital_dict, tags):
2364 '''
2365 Check the orbitals of *OrbitalSystem*: a list of orbitals for every tag.
2367 Raises
2368 ------
2369 TypeError
2370 Parameter orbitals must be a dictionary.
2371 ValueError
2372 Every tag needs its orbitals.
2373 '''
2374 if not isinstance(orbital_dict, dict):
2375 raise TypeError('\n\nParameter orbitals must be a dictionary {tag: list of orbitals}.\n')
2376 for tag in tags:
2377 if tag not in orbital_dict:
2378 raise ValueError('\n\nParameter orbitals has no entry for tag {!r}.\n'.format(tag))
2379 for orbs in orbital_dict.values():
2380 orbitals(orbs)
2383def sk_params(params):
2384 '''
2385 Check a dictionary of Slater-Koster bond integrals.
2387 Raises
2388 ------
2389 TypeError
2390 Parameter params must be a dictionary of numbers.
2391 ValueError
2392 Unknown bond integral.
2393 '''
2394 from tbkit.slater_koster import PARAMS
2395 if not isinstance(params, dict):
2396 raise TypeError('\n\nThe bond integrals must be a dictionary.\n')
2397 for key, val in params.items():
2398 if key not in PARAMS:
2399 raise ValueError('\n\nUnknown bond integral {!r}: use {}.\n'.format(key, PARAMS))
2400 if not isinstance(val, (int, float)):
2401 raise TypeError('\n\nBond integral {!r} must be a real number.\n'.format(key))
2404def sk_pair_params(params, tags_i, tags_j):
2405 '''
2406 Check the bond integrals of *set_slater_koster*: one dictionary, or one
2407 per tag pair of the bonds.
2409 Returns
2410 -------
2411 bool
2412 True if keyed by tag pair.
2414 Raises
2415 ------
2416 ValueError
2417 A tag pair of the bonds has no bond integrals.
2418 '''
2419 from tbkit.slater_koster import PARAMS
2420 if not isinstance(params, dict):
2421 raise TypeError('\n\nThe bond integrals must be a dictionary.\n')
2422 by_pair = bool(params) and all(k not in PARAMS for k in params)
2423 if not by_pair:
2424 sk_params(params)
2425 return False
2426 for a, b in set(zip(tags_i, tags_j)):
2427 if a + b not in params:
2428 raise ValueError('\n\nNo bond integrals for the tag pair {!r}.\n'.format(a + b))
2429 sk_params(params[a + b])
2430 return True
2433def bond_vector(d):
2434 '''
2435 Check a bond vector: 2 or 3 reals, nonzero.
2437 Raises
2438 ------
2439 ValueError
2440 The bond vector must have 2 or 3 components and be nonzero.
2441 '''
2442 if d.ndim != 1 or len(d) not in (2, 3) or not np.any(d):
2443 raise ValueError('\n\nThe bond vector must have 2 or 3 components, not all zero.\n')
2446def orbital_of_site(orbital, orbs):
2447 '''
2448 Check that a site carries an orbital.
2450 Raises
2451 ------
2452 ValueError
2453 The site has no such orbital.
2454 '''
2455 if orbital not in orbs:
2456 raise ValueError('\n\nThe site has no orbital {!r} (it has {}).\n'.format(orbital, orbs))
2459def spin_index(spin, ns):
2460 '''
2461 Check a spin index: 0, or 0/1 with spin.
2463 Raises
2464 ------
2465 ValueError
2466 Parameter spin must be 0 (or 1 with spin).
2467 '''
2468 if spin not in range(ns):
2469 raise ValueError('\n\nParameter spin must be {}.\n'.format('0 or 1' if ns == 2 else '0'))
2472def set_onsite_orb(dict_onsite, tags, orbital_dict):
2473 '''
2474 Check *OrbitalSystem.set_onsite*: numbers, or {orbital: number} dictionaries.
2476 Raises
2477 ------
2478 TypeError
2479 Onsite energies must be numbers or dictionaries.
2480 ValueError
2481 Unknown tag or orbital.
2482 '''
2483 if not isinstance(dict_onsite, dict):
2484 raise TypeError('\n\nParameter onsite must be a dictionary.\n')
2485 for tag, val in dict_onsite.items():
2486 if tag not in tags:
2487 raise ValueError('\n\nParameter onsite keys must be a tag.\n')
2488 if isinstance(val, dict):
2489 for o, e in val.items():
2490 orbital_of_site(o, orbital_dict[tag])
2491 if not isinstance(e, (int, float, complex)):
2492 raise TypeError('\n\nOnsite energies must be numbers.\n')
2493 elif not isinstance(val, (int, float, complex)):
2494 raise TypeError('\n\nOnsite energies must be numbers or {orbital: number} dictionaries.\n')
2497def spinful(spin):
2498 '''
2499 Check that the model has spin.
2501 Raises
2502 ------
2503 ValueError
2504 This term needs spin=True.
2505 '''
2506 if not spin:
2507 raise ValueError('\n\nThis term needs a spinful model (spin=True).\n')
2510def vector3(vec, var_name):
2511 '''
2512 Check a tuple of three real numbers.
2514 Raises
2515 ------
2516 ValueError
2517 Parameter must be a tuple of three real numbers.
2518 '''
2519 if not isinstance(vec, (tuple, list)) or len(vec) != 3 or \
2520 not all(isinstance(c, (int, float)) for c in vec):
2521 raise ValueError('\n\nParameter {} must be a tuple of three real numbers.\n'.format(var_name))
2524def p_shell(tags, orbital_dict):
2525 '''
2526 Check that the tags carry the three p orbitals.
2528 Raises
2529 ------
2530 ValueError
2531 The tags must carry px, py and pz.
2532 '''
2533 if not isinstance(tags, list) or not tags:
2534 raise ValueError('\n\nParameter tags must be a non-empty list of tags with p orbitals.\n')
2535 for tag in tags:
2536 if tag not in orbital_dict or not {'px', 'py', 'pz'} <= set(orbital_dict[tag]):
2537 raise ValueError('\n\nTag {!r} must carry the orbitals px, py and pz.\n'.format(tag))
2540def block_shape(mat, shape):
2541 '''
2542 Check the shape of a hopping block.
2544 Raises
2545 ------
2546 ValueError
2547 The block must have the given shape.
2548 '''
2549 if mat.shape != shape:
2550 raise ValueError('\n\nThe hopping block must have shape {}.\n'.format(shape))
2553def orbital_terms(hop, blocks):
2554 '''
2555 Check that an *OrbitalSystem* has hoppings.
2557 Raises
2558 ------
2559 RuntimeError
2560 Run set_hopping or set_slater_koster first.
2561 '''
2562 if not hop.size and not any(blocks.values()):
2563 raise RuntimeError('\n\nRun method set_hopping or set_slater_koster first.\n')
2566def no_left(left):
2567 '''
2568 Check that left eigenvectors are not asked for with an overlap matrix.
2570 Raises
2571 ------
2572 ValueError
2573 left=True needs an orthogonal basis.
2574 '''
2575 if left:
2576 raise ValueError('\n\nleft=True needs an orthogonal basis (no overlap).\n')
2579def orthogonal(overlap):
2580 '''
2581 Check that the basis is orthogonal.
2583 Raises
2584 ------
2585 ValueError
2586 This calculation needs an orthogonal basis.
2587 '''
2588 if overlap is not None:
2589 raise ValueError('\n\nThis calculation needs an orthogonal basis (no overlap matrix).\n')
2592####################################
2593# MEAN FIELD, SUPERCONDUCTIVITY
2594####################################
2597def hermitian_dense(ham):
2598 '''
2599 Check that a dense matrix is Hermitian.
2601 Raises
2602 ------
2603 ValueError
2604 This calculation requires a Hermitian Hamiltonian.
2605 '''
2606 if not np.allclose(ham, ham.conj().T, atol=1e-12):
2607 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n')
2610def integer_electrons(n_electrons):
2611 '''
2612 Check that the number of electrons is an integer (zero temperature).
2614 Raises
2615 ------
2616 ValueError
2617 At zero temperature, n_electrons must be an integer.
2618 '''
2619 if abs(n_electrons - round(n_electrons)) > 1e-12:
2620 raise ValueError('\n\nAt zero temperature, n_electrons must be an integer '
2621 '(use a small temperature for a fractional filling).\n')
2624def mixing(mixing):
2625 '''
2626 Check the density mixing: a real in (0, 1].
2628 Raises
2629 ------
2630 ValueError
2631 Parameter mixing must be in (0, 1].
2632 '''
2633 positive_real(mixing, 'mixing')
2634 if mixing > 1:
2635 raise ValueError('\n\nParameter mixing must be in (0, 1].\n')
2638def pairing(delta, n):
2639 '''
2640 Check a BdG pairing matrix: (n, n), antisymmetric.
2642 Raises
2643 ------
2644 ValueError
2645 The pairing matrix must be (n, n) and antisymmetric.
2646 '''
2647 if delta.shape != (n, n):
2648 raise ValueError('\n\nThe pairing matrix must have the shape ({0}, {0}) of ham.\n'.format(n))
2649 if (abs(delta + delta.T) > 1e-12).nnz:
2650 raise ValueError('\n\nThe pairing matrix must be antisymmetric (Delta^T = -Delta).\n')
2653def hermitian_kspace(hermitian, overlap):
2654 '''
2655 Check that a KSpace model is Hermitian, with an orthogonal basis.
2657 Raises
2658 ------
2659 ValueError
2660 The model must be Hermitian, without overlap.
2661 '''
2662 if not hermitian or overlap:
2663 raise ValueError('\n\nThe model must be Hermitian, with an orthogonal basis.\n')
2666def pairing_kspace(pairing, norb, ndim):
2667 '''
2668 Check the pairings of *bdg.bdg_kspace*.
2670 Raises
2671 ------
2672 TypeError
2673 Parameter pairing must be a list of dictionaries.
2674 KeyError
2675 "i", "j", "R" and "delta" must be keys.
2676 ValueError
2677 Orbital indices below norb; R a tuple of ndim integers.
2678 '''
2679 if not isinstance(pairing, list):
2680 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n')
2681 for dic in pairing:
2682 if not isinstance(dic, dict):
2683 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n')
2684 if not {'i', 'j', 'R', 'delta'} <= set(dic):
2685 raise KeyError('\n\n"i", "j", "R" and "delta" must be dictionary keys.\n')
2686 if not all(isinstance(dic[k], int) and 0 <= dic[k] < norb for k in ('i', 'j')):
2687 raise ValueError('\n\n"i" and "j" must be orbital indices between 0 and {}.\n'.format(norb - 1))
2688 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim or \
2689 not all(isinstance(n, int) for n in dic['R']):
2690 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim))
2691 number(dic['delta'], '"delta"')
2694####################################
2695# FLOQUET
2696####################################
2699def positive_int_zero(var, var_name):
2700 '''
2701 Check if *var* is a positive integer or zero.
2703 Raises
2704 ------
2705 TypeError
2706 Parameter *var* must be an integer.
2707 ValueError
2708 Parameter *var* must be a positive integer or zero.
2709 '''
2710 if not isinstance(var, int):
2711 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
2712 if var < 0:
2713 raise ValueError('\n\nParameter {} must be a positive integer or zero.\n'.format(var_name))
2716def harmonics(harm):
2717 '''
2718 Check the harmonics of *floquet.sambe_hamiltonian*: a dictionary with (at least) m = 0.
2720 Raises
2721 ------
2722 TypeError
2723 The harmonics must be a dictionary {m: matrix}.
2724 ValueError
2725 The harmonics must contain m = 0 and square matrices of one shape.
2726 '''
2727 if not isinstance(harm, dict):
2728 raise TypeError('\n\nThe harmonics must be a dictionary {m: matrix}.\n')
2729 if 0 not in harm:
2730 raise ValueError('\n\nThe harmonics must contain the static part, m = 0.\n')
2731 shapes = {np.shape(h) for h in harm.values()}
2732 if len(shapes) != 1 or len(shapes.pop()) != 2:
2733 raise ValueError('\n\nThe harmonics must be square matrices of one shape.\n')
2736def kspace(ks, cls):
2737 '''
2738 Check that *ks* is a KSpace instance.
2740 Raises
2741 ------
2742 TypeError
2743 Parameter ks must be a KSpace instance.
2744 '''
2745 if not isinstance(ks, cls):
2746 raise TypeError('\n\nParameter ks must be a KSpace instance.\n')
2749def not_floquet():
2750 '''
2752 Raises
2753 ------
2754 ValueError
2755 Not available for a Floquet (driven) model.
2756 '''
2757 raise ValueError('\n\nThis method needs the static hoppings: it is not available '
2758 'for a Floquet (driven) model.\n')
2761def sk_bonds(bonds):
2762 '''
2763 Check the bonds of *slater_koster.sk_kspace*: {n: bond integrals}.
2765 Raises
2766 ------
2767 TypeError
2768 Parameter bonds must be a dictionary {n: bond integrals}.
2769 '''
2770 if not isinstance(bonds, dict) or not all(isinstance(n, int) and n > 0 for n in bonds):
2771 raise TypeError('\n\nParameter bonds must be a dictionary {n: bond integrals}, '
2772 'n a positive integer.\n')
2773 for params in bonds.values():
2774 sk_params(params)
2777####################################
2778# HALL CONDUCTIVITIES
2779####################################
2782def fermi_energies(e_fermi):
2783 '''
2784 Check the Fermi energies of the Hall conductivities: a real number, or a
2785 non-empty array of finite real numbers.
2787 Raises
2788 ------
2789 TypeError
2790 Parameter e_fermi must be a real number or an array of real numbers.
2791 ValueError
2792 Parameter e_fermi must be non-empty and finite.
2793 '''
2794 arr = np.asarray(e_fermi)
2795 if arr.dtype.kind not in 'iuf':
2796 raise TypeError('\n\nParameter e_fermi must be a real number or an array '
2797 'of real numbers.\n')
2798 if arr.size == 0 or not np.all(np.isfinite(arr)):
2799 raise ValueError('\n\nParameter e_fermi must contain at least one finite number.\n')
2802def hermitian_model(hermitian):
2803 '''
2804 Check that a KSpace model is Hermitian (a Kubo conductivity needs real
2805 bands and orthonormal eigenstates).
2807 Raises
2808 ------
2809 ValueError
2810 This calculation requires a Hermitian model.
2811 '''
2812 if not hermitian:
2813 raise ValueError('\n\nThis calculation requires a Hermitian model (no gain/loss, '
2814 'no hermitian=False hopping).\n')
2817def k_fixed_hall(k_fixed, dim, full_allowed=True):
2818 '''
2819 Check parameter *k_fixed* of the Hall conductivities: None (the whole
2820 Brillouin zone) or a real number (a plane of a 3D model).
2822 Raises
2823 ------
2824 TypeError
2825 Parameter k_fixed must be None or a real number.
2826 ValueError
2827 A 3D spin Hall conductivity needs a plane (k_fixed).
2828 '''
2829 if k_fixed is not None:
2830 real_number(k_fixed, 'k_fixed')
2831 elif dim == 3 and not full_allowed:
2832 raise ValueError('\n\nIn 3D, give the plane of the spin Hall conductivity: '
2833 'k_fixed must be a real number.\n')
2836def spin_axis(axis):
2837 '''
2838 Check the spin quantization axis: 'x', 'y' or 'z'.
2840 Raises
2841 ------
2842 ValueError
2843 Parameter spin_axis must be "x", "y" or "z".
2844 '''
2845 if axis not in ('x', 'y', 'z'):
2846 raise ValueError('\n\nParameter spin_axis must be "x", "y" or "z".\n')
2849def refine_fraction(fraction):
2850 '''
2851 Check the fraction of mesh points refined by the adaptive mesh: a real in (0, 1].
2853 Raises
2854 ------
2855 TypeError
2856 Parameter refine_fraction must be a real number.
2857 ValueError
2858 Parameter refine_fraction must be in (0, 1].
2859 '''
2860 positive_real(fraction, 'refine_fraction')
2861 if fraction > 1:
2862 raise ValueError('\n\nParameter refine_fraction must be in (0, 1].\n')
2865def path_parameter(param):
2866 '''
2867 Check the parameter that a path varies (*KSpace.axion_angle*): a string.
2869 Raises
2870 ------
2871 TypeError
2872 Parameter param must be a string (the name of a value-function parameter).
2873 '''
2874 if not isinstance(param, str):
2875 raise TypeError('\n\nParameter param must be a string (the name of a '
2876 'value-function parameter).\n')
2879def path_values(values):
2880 '''
2881 Check the parameter values of a path: a 1D array of at least two finite
2882 real numbers, strictly increasing or strictly decreasing.
2884 Raises
2885 ------
2886 TypeError
2887 Parameter values must be a 1D array of real numbers.
2888 ValueError
2889 Parameter values must hold at least two finite, strictly monotonic values.
2890 '''
2891 arr = np.asarray(values)
2892 if arr.ndim != 1 or arr.dtype.kind not in 'iuf':
2893 raise TypeError('\n\nParameter values must be a 1D array of real numbers.\n')
2894 step = np.diff(arr)
2895 if len(arr) < 2 or not np.all(np.isfinite(arr)) or not (np.all(step > 0) or np.all(step < 0)):
2896 raise ValueError('\n\nParameter values must hold at least two finite values, '
2897 'strictly increasing or strictly decreasing.\n')
2900def path_gap(gap, param, value, tol=1e-6):
2901 '''
2902 Check that the bands stay separated from the others along a path.
2904 Raises
2905 ------
2906 ValueError
2907 The bands touch the others at param = value.
2908 '''
2909 if gap < tol:
2910 raise ValueError('\n\nThe bands touch the other bands at {} = {} (gap {:.1e}): '
2911 'the path must stay gapped.\n'.format(param, value, gap))
2914def velocity(vel, n, var_name):
2915 '''
2916 Check a velocity operator: a square (sparse or dense) matrix of the
2917 Hamiltonian's shape.
2919 Raises
2920 ------
2921 ValueError
2922 The velocity must be a (n, n) matrix.
2923 '''
2924 if getattr(vel, 'shape', None) != (n, n):
2925 raise ValueError('\n\nParameter {} must be a ({}, {}) matrix, like ham.\n'.format(var_name, n, n))
2928def positive_overlap(s_min):
2929 '''
2930 Check that the overlap matrix S(k) is positive definite.
2932 Raises
2933 ------
2934 ValueError
2935 The overlap matrix must be positive definite.
2936 '''
2937 if s_min <= 0:
2938 raise ValueError('\n\nThe overlap matrix S(k) must be positive definite '
2939 '(smallest eigenvalue {:.3g}).\n'.format(s_min))
2942####################################
2943# FLOQUET: STEP DRIVES, WINDING NUMBER
2944####################################
2947def branch_cut(epsilon):
2948 '''
2949 Check the branch cut of a Floquet Hamiltonian: None or a real number.
2951 Raises
2952 ------
2953 TypeError
2954 Parameter epsilon must be None or a real number.
2955 '''
2956 if epsilon is not None and (isinstance(epsilon, bool)
2957 or not isinstance(epsilon, (int, float, np.integer, np.floating))):
2958 raise TypeError('\n\nParameter epsilon must be None or a real number.\n')
2961def durations(durations, n):
2962 '''
2963 Check the step durations of a step drive: n positive reals.
2965 Raises
2966 ------
2967 TypeError
2968 Parameter durations must be a list of real numbers.
2969 ValueError
2970 Parameter durations must hold one positive number per step.
2971 '''
2972 if not isinstance(durations, (list, tuple, np.ndarray)):
2973 raise TypeError('\n\nParameter durations must be a list of real numbers.\n')
2974 if len(durations) != n:
2975 raise ValueError('\n\nParameter durations must hold one duration per step ({}).\n'.format(n))
2976 for d in durations:
2977 if isinstance(d, bool) or not isinstance(d, (int, float, np.integer, np.floating)):
2978 raise TypeError('\n\nParameter durations must be a list of real numbers.\n')
2979 if d <= 0:
2980 raise ValueError('\n\nParameter durations must be positive numbers.\n')
2983def step_models(models, kspace_cls, system_cls):
2984 '''
2985 Check the models of a step drive: a non-empty list, either of KSpace
2986 instances (Hermitian, orthogonal basis, one lattice size) or of System
2987 instances and square matrices. Returns 'kspace' or 'matrix'.
2989 Raises
2990 ------
2991 TypeError
2992 Parameter models must be a non-empty list of KSpace, System or matrices.
2993 ValueError
2994 The KSpace models must be Hermitian, with no overlap, of one size.
2995 '''
2996 if not isinstance(models, (list, tuple)) or len(models) == 0:
2997 raise TypeError('\n\nParameter models must be a non-empty list of KSpace instances, '
2998 'or of System instances and matrices.\n')
2999 if all(isinstance(m, kspace_cls) for m in models):
3000 for m in models:
3001 if (m.norb, m.dim) != (models[0].norb, models[0].dim):
3002 raise ValueError('\n\nThe KSpace models must have the same orbitals and dimension.\n')
3003 if not m.is_hermitian():
3004 raise ValueError('\n\nThe KSpace models must be Hermitian.\n')
3005 if m._overlap_hop:
3006 raise ValueError('\n\nThe KSpace models need an orthogonal basis (no overlap).\n')
3007 return 'kspace'
3008 for m in models:
3009 if not isinstance(m, system_cls) and getattr(m, 'ndim', None) != 2:
3010 raise TypeError('\n\nParameter models must be a list of KSpace instances only, '
3011 'or of System instances and square matrices.\n')
3012 return 'matrix'
3015def step_matrices(hams):
3016 '''
3017 Check the Hamiltonians of a real-space step drive: Hermitian square
3018 matrices of one shape.
3020 Raises
3021 ------
3022 TypeError
3023 Parameter hams must be a non-empty list of matrices.
3024 ValueError
3025 The matrices must be square, Hermitian and of one shape.
3026 '''
3027 if not isinstance(hams, (list, tuple)) or len(hams) == 0:
3028 raise TypeError('\n\nParameter hams must be a non-empty list of matrices.\n')
3029 shape = np.shape(hams[0])
3030 for h in hams:
3031 h = np.asarray(h)
3032 if h.ndim != 2 or h.shape[0] != h.shape[1] or h.shape != shape:
3033 raise ValueError('\n\nThe step Hamiltonians must be square matrices of one shape.\n')
3034 hermitian_dense(h)
3037def drive_matrix(ham, norb):
3038 '''
3039 Check the output of a Bloch drive ham_kt(k, t): a Hermitian (norb, norb) matrix.
3041 Raises
3042 ------
3043 ValueError
3044 ham_kt(k, t) must return a Hermitian (norb, norb) matrix.
3045 '''
3046 if np.shape(ham) != (norb, norb):
3047 raise ValueError('\n\nham_kt(k, t) must return a ({}, {}) matrix.\n'.format(norb, norb))
3048 hermitian_dense(np.asarray(ham))
3051def time_in_period(t, period):
3052 '''
3053 Check a time within the period: None or a real in [0, period].
3055 Raises
3056 ------
3057 TypeError
3058 Parameter t must be None or a real number.
3059 ValueError
3060 Parameter t must be in [0, period].
3061 '''
3062 if t is None:
3063 return
3064 real_number(t, 't')
3065 if not 0 <= t <= period:
3066 raise ValueError('\n\nParameter t must be in [0, {}].\n'.format(period))
3069def nk_min(nk, n_min):
3070 '''
3071 Check that every k-mesh size is at least n_min.
3073 Raises
3074 ------
3075 ValueError
3076 Parameter nk must be at least n_min.
3077 '''
3078 if min(nk) < n_min:
3079 raise ValueError('\n\nParameter nk must be at least {}.\n'.format(n_min))
3082####################################
3083# MODEL BUILDING, BRIDGES AND I/O
3084# (neighbour-order hoppings in k-space, System <-> KSpace, tbkit.io)
3085####################################
3088def hopping_form(list_hop):
3089 '''
3090 Tell the two forms of *KSpace.set_hopping* apart: the explicit one
3091 (keys 'i', 'j', 'R', 't') and the neighbour-order one of
3092 *System.set_hopping* (keys 'n', 't', and optionally 'ang', 'tag').
3094 Returns
3095 -------
3096 neighbour : Boolean
3097 True for the neighbour-order form.
3099 Raises
3100 ------
3101 TypeError
3102 Parameter list_hop must be a list of dictionaries.
3103 ValueError
3104 list_hop mixes the two forms.
3105 '''
3106 if not isinstance(list_hop, list):
3107 raise TypeError('\n\nParameter list_hop must be a list.\n')
3108 if not all(isinstance(dic, dict) for dic in list_hop):
3109 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n')
3110 by_order = ['n' in dic for dic in list_hop]
3111 if any(by_order) and not all(by_order):
3112 raise ValueError('\n\nParameter list_hop mixes explicit hoppings (keys "i", "j", "R", "t") '
3113 'and neighbour-order ones (keys "n", "t", "ang", "tag"): '
3114 'make one call per form.\n')
3115 return bool(list_hop) and all(by_order)
3118def set_hopping_neighbours(list_hop, spin=False):
3119 '''
3120 Check the neighbour-order form of *KSpace.set_hopping*.
3122 Raises
3123 ------
3124 KeyError
3125 "n" and "t" must be keys, and only "ang" and "tag" may be added.
3126 TypeError
3127 "n" must be an integer, "t" a number (or, if *spin*,
3128 a 2x2 matrix) or a value function, "ang" a real number and "tag" a string.
3129 ValueError
3130 "n" must be positive, "ang" in [-180, 180), "tag" of length 2.
3131 '''
3132 hopping_form(list_hop)
3133 for dic in list_hop:
3134 if 'n' not in dic or 't' not in dic:
3135 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n')
3136 if not set(dic) <= {'n', 't', 'ang', 'tag'}:
3137 raise KeyError('\n\nOnly the keys "n", "t", "ang" and "tag" are allowed.\n')
3138 if isinstance(dic['n'], bool) or not isinstance(dic['n'], int):
3139 raise TypeError('\n\n"n" value must be an integer.\n')
3140 if dic['n'] < 1:
3141 raise ValueError('\n\n"n" value must be a positive integer.\n')
3142 if callable(dic['t']):
3143 pass
3144 elif spin:
3145 spin_matrix(dic['t'], '"t"')
3146 elif not isinstance(dic['t'], (int, float, complex)):
3147 raise TypeError('\n\n"t" value must be a real or complex number.\n')
3148 if 'ang' in dic:
3149 if not isinstance(dic['ang'], (int, float)):
3150 raise TypeError('\n\n"ang" value must be a real number.\n')
3151 if not -180. <= dic['ang'] < 180.:
3152 raise ValueError('\n\n"ang" value must be in [-180, 180).\n')
3153 if 'tag' in dic:
3154 if not isinstance(dic['tag'], str):
3155 raise TypeError('\n\n"tag" value must be a string.\n')
3156 if len(dic['tag']) != 2:
3157 raise ValueError('\n\n"tag" value must be a string of length 2.\n')
3160def shell_angle(ang, angles):
3161 '''
3162 Check that a neighbour shell has bonds along the angle *ang* (in
3163 degrees, either orientation: *angles* are those in [0, 180)).
3165 Raises
3166 ------
3167 ValueError
3168 No bond of the shell points along *ang*.
3169 '''
3170 if not np.any(np.isclose(ang, angles, atol=ATOL)) and \
3171 not np.any(np.isclose(ang, angles - 180., atol=ATOL)):
3172 raise ValueError('\n\nNo bond of this neighbour shell has angle {} (angles: {}).\n'
3173 .format(ang, np.unique(angles.round(4))))
3176def not_orbital_system(sys, orbital_cls):
3177 '''
3178 Check that a System is not an *OrbitalSystem* (several rows per site).
3180 Raises
3181 ------
3182 TypeError
3183 An OrbitalSystem is not supported.
3184 '''
3185 if isinstance(sys, orbital_cls):
3186 raise TypeError('\n\nAn OrbitalSystem (several orbitals per site) is not supported: '
3187 'build its KSpace with tbkit.slater_koster.sk_kspace.\n')
3190def lattice_sites(matches):
3191 '''
3192 Check that every site of a finite lattice is a copy of exactly one
3193 orbital of *unit_cell*, translated by a lattice vector (*matches*: the
3194 number of such orbitals, per site).
3196 Raises
3197 ------
3198 ValueError
3199 A site is not on the periodic lattice.
3200 '''
3201 bad = np.flatnonzero(np.asarray(matches) != 1)
3202 if len(bad):
3203 raise ValueError('\n\nSite {} is not a translate of exactly one unit-cell site (same tag, '
3204 'position r0 + n1 a1 + ...): was the lattice rotated, strained '
3205 'or shifted?\n'.format(int(bad[0])))
3208def translation_invariant(dev, tol):
3209 '''
3210 Check that equivalent matrix elements (same orbitals, same lattice
3211 vector) are equal in every cell, within *tol*.
3213 Raises
3214 ------
3215 ValueError
3216 The model is not translation invariant.
3217 '''
3218 if dev > tol:
3219 raise ValueError('\n\nThe Hamiltonian is not translation invariant: equivalent hoppings '
3220 'or onsite energies differ by {:.3g} (> tol = {:.3g}), e.g. after '
3221 'disorder, defects or a magnetic field.\n'.format(dev, tol))
3224def torus_range(R, sizes):
3225 '''
3226 Check that the lattice vectors of a torus's bonds (shortest images, in
3227 [-N/2, N/2)) are unambiguous: at least 3 cells along every direction,
3228 and no bond reaching half-way around.
3230 Raises
3231 ------
3232 ValueError
3233 The torus is too small for the range of the model.
3234 '''
3235 if np.any(sizes < 3) or np.any(2 * R == -sizes[None, :]):
3236 raise ValueError('\n\nThe torus ({} cells) is too small for the range of the model: '
3237 'a bond reaches half-way around it.\n'.format(tuple(int(n) for n in sizes)))
3240def saveable_model(model, lattice_cls, system_cls, kspace_cls, orbital_cls):
3241 '''
3242 Check that *tbkit.io.save_model* can store *model*.
3244 Raises
3245 ------
3246 TypeError
3247 model must be a Lattice, System or KSpace instance
3248 (not an OrbitalSystem).
3249 '''
3250 if isinstance(model, orbital_cls) or not isinstance(model, (lattice_cls, system_cls, kspace_cls)):
3251 raise TypeError('\n\nParameter model must be a Lattice, System or KSpace instance '
3252 '(an OrbitalSystem or a driven model cannot be saved).\n')
3255def file_path(path, var_name):
3256 '''
3257 Check a file name.
3259 Raises
3260 ------
3261 TypeError
3262 Parameter var_name must be a string or a path.
3263 '''
3264 if not (isinstance(path, str) or hasattr(path, "__fspath__")):
3265 raise TypeError('\n\nParameter {} must be a string or a path.\n'.format(var_name))
3268def model_archive(data, fmt, version):
3269 '''
3270 Check an archive read by *tbkit.io.load_model*.
3272 Raises
3273 ------
3274 ValueError
3275 Not a tbkit model archive, or a newer version.
3276 '''
3277 if 'format' not in data.files or str(data['format']) != fmt:
3278 raise ValueError('\n\nThis file is not a tbkit model archive (format {}).\n'.format(fmt))
3279 if int(data['version']) > version:
3280 raise ValueError('\n\nThis archive has version {}; this tbkit reads versions up to {}: '
3281 'upgrade tbkit.\n'.format(int(data['version']), version))
3284def hr_header(tokens):
3285 '''
3286 Check the header of a Wannier90 ``_hr.dat`` file: num_wann, nrpts and
3287 the nrpts degeneracy weights, as positive integers.
3289 Raises
3290 ------
3291 ValueError
3292 Not a Wannier90 _hr.dat file.
3293 '''
3294 try:
3295 num_wann, nrpts = int(tokens[0]), int(tokens[1])
3296 weights = [int(w) for w in tokens[2:2 + nrpts]]
3297 except (IndexError, ValueError):
3298 raise ValueError('\n\nNot a Wannier90 _hr.dat file: expected num_wann, nrpts and '
3299 'the degeneracy weights after the header line.\n') from None
3300 if num_wann < 1 or nrpts < 1 or len(weights) != nrpts or min(weights) < 1:
3301 raise ValueError('\n\nNot a Wannier90 _hr.dat file: num_wann, nrpts and the '
3302 'degeneracy weights must be positive integers.\n')
3305def hr_body(n_tokens, nrpts, num_wann):
3306 '''
3307 Check the number of entries of a Wannier90 ``_hr.dat`` file.
3309 Raises
3310 ------
3311 ValueError
3312 The file must have nrpts * num_wann**2 lines of 7 numbers.
3313 '''
3314 if n_tokens != 7 * nrpts * num_wann ** 2:
3315 raise ValueError('\n\nThe _hr.dat file must have nrpts * num_wann**2 = {} lines '
3316 '"R1 R2 R3 m n Re Im" after the weights.\n'.format(nrpts * num_wann ** 2))
3319def hr_blocks(ok):
3320 '''
3321 Check that the lines of a ``_hr.dat`` file come in blocks of num_wann**2
3322 with the same lattice vector.
3324 Raises
3325 ------
3326 ValueError
3327 Lines of a lattice vector must be consecutive.
3328 '''
3329 if not ok:
3330 raise ValueError('\n\nThe _hr.dat lines of each lattice vector R must be consecutive '
3331 '(num_wann**2 lines per R).\n')
3334def win_block(found):
3335 '''
3336 Check that a ``.win`` file has a ``unit_cell_cart`` block.
3338 Raises
3339 ------
3340 ValueError
3341 No unit_cell_cart block.
3342 '''
3343 if not found:
3344 raise ValueError('\n\nThe .win file has no "begin unit_cell_cart ... end unit_cell_cart" block.\n')
3347def win_cell(cell):
3348 '''
3349 Check the ``unit_cell_cart`` block of a ``.win`` file: three lines of
3350 three real numbers.
3352 Raises
3353 ------
3354 ValueError
3355 unit_cell_cart must hold three vectors of three numbers.
3356 '''
3357 try:
3358 ok = len(cell) == 3 and all(len(v) == 3 for v in cell) and \
3359 all(np.isfinite([float(c) for v in cell for c in v]))
3360 except ValueError:
3361 ok = False
3362 if not ok:
3363 raise ValueError('\n\nThe unit_cell_cart block must hold three vectors of three real numbers.\n')
3366def centres_count(n, num_wann):
3367 '''
3368 Check that a ``_centres.xyz`` file lists num_wann Wannier centres (X).
3370 Raises
3371 ------
3372 ValueError
3373 Too few Wannier centres.
3374 '''
3375 if n < num_wann:
3376 raise ValueError('\n\nThe _centres.xyz file lists {} Wannier centres ("X"), '
3377 'not {}.\n'.format(n, num_wann))
3380def wannier_dim(dim):
3381 '''
3382 Check the dimension of a Wannier90 model.
3384 Raises
3385 ------
3386 ValueError
3387 dim must be 2 or 3.
3388 '''
3389 if isinstance(dim, bool) or dim not in (2, 3):
3390 raise ValueError('\n\nParameter dim must be 2 or 3.\n')
3393def win_given(win):
3394 '''
3395 Check that the lattice vectors can be found.
3397 Raises
3398 ------
3399 ValueError
3400 Give prim_vec or win.
3401 '''
3402 if win is None:
3403 raise ValueError('\n\nGive the primitive vectors: prim_vec, or the .win file (win).\n')
3406def layer_cell(cell, dim):
3407 '''
3408 Check that, for a 2D model, a1 and a2 lie in the (x, y) plane.
3410 Raises
3411 ------
3412 ValueError
3413 a1 and a2 must have no z component.
3414 '''
3415 if dim == 2 and np.any(np.abs(cell[:2, 2]) > 1e-8):
3416 raise ValueError('\n\nFor dim=2, the first two lattice vectors must lie in the (x, y) plane.\n')
3419def wannier_prim_vec(prim_vec, dim):
3420 '''
3421 Check that the primitive vectors are *dim* vectors of *dim* components.
3423 Raises
3424 ------
3425 ValueError
3426 prim_vec must hold dim vectors of dim components.
3427 '''
3428 if len(prim_vec) != dim or len(prim_vec[0]) != dim:
3429 raise ValueError('\n\nParameter prim_vec must hold {0} vectors of {0} components.\n'.format(dim))
3430 independent(prim_vec)
3433def wannier_positions(shape, num_wann, dim):
3434 '''
3435 Check the orbital positions of a Wannier90 model.
3437 Raises
3438 ------
3439 ValueError
3440 positions must have shape (num_wann, dim) or (num_wann, 3).
3441 '''
3442 if len(shape) != 2 or shape[0] != num_wann or shape[1] not in (dim, 3):
3443 raise ValueError('\n\nParameter positions must have shape ({0}, {1}) or ({0}, 3).\n'
3444 .format(num_wann, dim))
3447def wannier_tags(tags, num_wann):
3448 '''
3449 Check the sublattice tags of a Wannier90 model.
3451 Raises
3452 ------
3453 TypeError
3454 tags must be a list of one-character strings.
3455 ValueError
3456 tags must have num_wann elements.
3457 '''
3458 if not isinstance(tags, list) or not all(isinstance(t, str) and len(t) == 1 for t in tags):
3459 raise TypeError('\n\nParameter tags must be a list of one-character strings.\n')
3460 if len(tags) != num_wann:
3461 raise ValueError('\n\nParameter tags must have {} elements.\n'.format(num_wann))
3464def layer_hoppings(h_max, tol):
3465 '''
3466 Check that a 2D model has no hopping between layers (R3 != 0).
3468 Raises
3469 ------
3470 ValueError
3471 Hoppings with R3 != 0 must vanish for dim=2.
3472 '''
3473 if h_max > tol:
3474 raise ValueError('\n\nFor dim=2, the matrix elements with R3 != 0 must vanish '
3475 '(largest: {:.3g}).\n'.format(h_max))
3478def spinless(spin):
3479 '''
3480 Check that a KSpace model has no spin.
3482 Raises
3483 ------
3484 ValueError
3485 This needs a spinless model.
3486 '''
3487 if spin:
3488 raise ValueError('\n\nThis needs a spinless model (spin=False): a System has one '
3489 'orbital per site; use bridges.finite_model for spinful models.\n')
3490# HIGHER-ORDER TOPOLOGY, MOIRE SUPERCELLS, SELF-CONSISTENT INTERACTIONS
3491####################################
3494def wannier_gap(nu, sector, tol=1e-4):
3495 '''
3496 Check that a Wannier sector is separated from the other Wannier bands
3497 over the mesh (*nu*, shape (n1, n2, nb), sorted at each k): at every k,
3498 the smallest distance on the circle between a Wannier band of the
3499 sector and one outside it (the Wannier gap) exceeds *tol*, and the
3500 Wannier bands of the sector change between neighbouring k-points by
3501 less than the smallest gap (otherwise two Wannier bands crossed between
3502 them, or the sector wrapped around the circle).
3504 Raises
3505 ------
3506 ValueError
3507 The Wannier sector is not separated by a Wannier gap.
3508 '''
3509 nu = np.asarray(nu)
3510 rest = [n for n in range(nu.shape[-1]) if n not in sector]
3511 if not rest:
3512 return
3514 def circ(d):
3515 d = np.abs(d) % 1.
3516 return np.minimum(d, 1. - d)
3517 gap = np.min(circ(nu[..., sector][..., :, None] - nu[..., rest][..., None, :]))
3518 jump = max(np.max(circ(nu[..., sector] - np.roll(nu[..., sector], 1, axis=a)))
3519 for a in range(nu.ndim - 1))
3520 if gap < tol or jump >= gap:
3521 raise ValueError('\n\nThe Wannier sector is not separated from the other Wannier '
3522 'bands by a Wannier gap (smallest distance {:.2e}, largest '
3523 'step {:.2e}).\n'.format(gap, jump))
3526def supercell_matrix(mat, dim):
3527 '''
3528 Check a supercell matrix: integers, shape (dim, dim), nonzero determinant.
3530 Raises
3531 ------
3532 TypeError
3533 The supercell matrix must contain integers.
3534 ValueError
3535 The supercell matrix must be (dim, dim), with a nonzero determinant.
3536 '''
3537 if mat.shape != (dim, dim):
3538 raise ValueError('\n\nThe supercell matrix must have the shape ({0}, {0}).\n'.format(dim))
3539 if not np.issubdtype(mat.dtype, np.integer):
3540 raise TypeError('\n\nThe supercell matrix must contain integers.\n')
3541 if round(abs(np.linalg.det(mat))) == 0:
3542 raise ValueError('\n\nThe supercell matrix must have a nonzero determinant.\n')
3545def supercell_model(sc, cls):
3546 '''
3547 Check that *sc* is a supercell (made by *moire.supercell*).
3549 Raises
3550 ------
3551 TypeError
3552 Parameter sc must be a SupercellKSpace instance.
3553 '''
3554 if not isinstance(sc, cls):
3555 raise TypeError('\n\nParameter sc must be a SupercellKSpace instance '
3556 '(see tbkit.moire.supercell).\n')
3559def bond_vectors(d):
3560 '''
3561 Check bond vectors: shape (n, 3), none of zero length.
3563 Raises
3564 ------
3565 ValueError
3566 Bond vectors must have the shape (n, 3) and a nonzero length.
3567 '''
3568 if d.ndim != 2 or d.shape[1] != 3:
3569 raise ValueError('\n\nThe bond vectors must have the shape (n, 3).\n')
3570 if np.any(np.linalg.norm(d, axis=1) == 0):
3571 raise ValueError('\n\nThe bond vectors must have a nonzero length.\n')
3574def twist_lattice(lattice):
3575 '''
3576 Check the layer lattice of a twisted bilayer.
3578 Raises
3579 ------
3580 ValueError
3581 Parameter lattice must be "honeycomb" or "square".
3582 '''
3583 if lattice not in ('honeycomb', 'square'):
3584 raise ValueError('\n\nParameter lattice must be "honeycomb" or "square".\n')
3587def commensurate(moire_1, moire_2):
3588 '''
3589 Check that the moire vectors of the two layers coincide.
3591 Raises
3592 ------
3593 ValueError
3594 The two layers do not share the moire cell.
3595 '''
3596 if not np.allclose(moire_1, moire_2, atol=1e-9 * max(1., np.abs(moire_1).max())):
3597 raise ValueError('\n\nThe two layers do not share the moire cell.\n')
3600def hopping_values(t, n):
3601 '''
3602 Check the output of a hopping function: n values.
3604 Raises
3605 ------
3606 ValueError
3607 The hopping function must return one value per bond vector.
3608 '''
3609 if np.shape(t) != (n,):
3610 raise ValueError('\n\nThe hopping function must return one value per bond vector, '
3611 'shape ({},).\n'.format(n))
3614def even_dimension(n):
3615 '''
3616 Check that a spinful matrix has an even dimension (two spins per site).
3618 Raises
3619 ------
3620 ValueError
3621 A spinful Hamiltonian must have an even dimension.
3622 '''
3623 if n % 2:
3624 raise ValueError('\n\nA spinful Hamiltonian must have an even dimension '
3625 '(rows site-major, spin up then down).\n')
3628def magnetization(mag, n):
3629 '''
3630 Check initial moments: a real array of shape (n, 3).
3632 Raises
3633 ------
3634 ValueError
3635 Parameter magnetization must have the shape (n, 3).
3636 '''
3637 if mag.shape != (n, 3):
3638 raise ValueError('\n\nParameter magnetization must have the shape ({}, 3).\n'.format(n))
3641def interaction(V, n):
3642 '''
3643 Check an attractive interaction: a positive number, or n non-negative numbers.
3645 Raises
3646 ------
3647 ValueError
3648 The interaction must be positive (or n non-negative values).
3649 '''
3650 V = np.asarray(V, dtype='f8')
3651 if V.ndim == 0:
3652 positive_real(float(V), 'V')
3653 return
3654 if V.shape != (n,) or np.any(V < 0):
3655 raise ValueError('\n\nParameter V must be a positive number, or {} non-negative '
3656 'numbers (one per site).\n'.format(n))
3659def pairing_amplitudes(delta, n, var_name='delta0'):
3660 '''
3661 Check pairing amplitudes: a number, or n numbers.
3663 Raises
3664 ------
3665 ValueError
3666 Parameter *var_name* must be a number or n numbers.
3667 '''
3668 if np.ndim(delta) == 0:
3669 number(delta, var_name)
3670 return
3671 if np.shape(delta) != (n,):
3672 raise ValueError('\n\nParameter {} must be a number, or {} numbers '
3673 '(one per site).\n'.format(var_name, n))
3676def spin_up_model(spin):
3677 '''
3678 Check that a KSpace model is spinless (the spin-up electrons).
3680 Raises
3681 ------
3682 ValueError
3683 This needs the spinless spin-up model.
3684 '''
3685 if spin:
3686 raise ValueError('\n\nThis needs a spinless model (spin=False): the spin-up '
3687 'electrons, whose time-reversed partners are the spin-down ones.\n')
3688# OPTICAL RESPONSE AND TRANSPORT (tbkit.optics, tbkit.transport)
3689####################################
3692def frequencies(omega, var_name):
3693 '''
3694 Check photon energies (or energies): a real number, or a non-empty
3695 array of finite real numbers.
3697 Raises
3698 ------
3699 TypeError
3700 Parameter must be a real number or an array of real numbers.
3701 ValueError
3702 Parameter must contain at least one finite number.
3703 '''
3704 arr = np.asarray(omega)
3705 if arr.dtype.kind not in 'iuf':
3706 raise TypeError('\n\nParameter {} must be a real number or an array '
3707 'of real numbers.\n'.format(var_name))
3708 if arr.size == 0 or not np.all(np.isfinite(arr)):
3709 raise ValueError('\n\nParameter {} must contain at least one finite '
3710 'number.\n'.format(var_name))
3713def tensor_component(component, order):
3714 '''
3715 Check a Cartesian tensor component of a 2D response: a string of
3716 *order* letters among 'x' and 'y'. Returns the axis indices.
3718 Raises
3719 ------
3720 TypeError
3721 Parameter component must be a string.
3722 ValueError
3723 Parameter component must have order letters among x, y.
3724 '''
3725 if not isinstance(component, str):
3726 raise TypeError('\n\nParameter component must be a string.\n')
3727 if len(component) != order or any(c not in 'xy' for c in component):
3728 raise ValueError('\n\nParameter component must be {} letters among '
3729 "'x' and 'y'.\n".format(order))
3730 return tuple('xy'.index(c) for c in component)
3733def band_derivatives(en, vel, w):
3734 '''
3735 Check the band energies (nk, n), velocities (2, nk, n, n) and second
3736 derivatives (2, 2, nk, n, n) of *optics.generalized_derivative*.
3738 Raises
3739 ------
3740 ValueError
3741 Inconsistent shapes.
3742 '''
3743 if en.ndim != 2 or vel.shape != (2,) + en.shape + en.shape[-1:] or \
3744 w.shape != (2,) + vel.shape:
3745 raise ValueError('\n\nThe shapes must be en (nk, n), vel (2, nk, n, n) and '
3746 'w (2, 2, nk, n, n).\n')
3749def lead_pair(pair, n_leads, var_name, distinct):
3750 '''
3751 Check a pair of lead indices (and, with *distinct*, that they differ).
3753 Raises
3754 ------
3755 TypeError
3756 Parameter must be a tuple/list of two lead indices.
3757 ValueError
3758 No such lead, or the two leads must differ.
3759 '''
3760 if not isinstance(pair, (tuple, list)) or len(pair) != 2:
3761 raise TypeError('\n\nParameter {} must be a tuple of two lead indices.\n'.format(var_name))
3762 for lead in pair:
3763 lead_index(lead, n_leads)
3764 if distinct and pair[0] == pair[1]:
3765 raise ValueError('\n\nThe two leads of parameter {} must differ.\n'.format(var_name))
3768def site_positions(positions, n_sites):
3769 '''
3770 Check site positions: a real array of shape (n_sites, d).
3772 Raises
3773 ------
3774 ValueError
3775 Parameter positions must have shape (n_sites, d).
3776 '''
3777 if positions.ndim != 2 or len(positions) != n_sites:
3778 raise ValueError('\n\nParameter positions must have shape ({}, d).\n'.format(n_sites))
3781def slices(slices, n_sites):
3782 '''
3783 Check a partition of the device sites into non-empty slices.
3785 Raises
3786 ------
3787 TypeError
3788 Parameter slices must be a list of lists of integers.
3789 ValueError
3790 The slices must contain every site exactly once.
3791 '''
3792 if not isinstance(slices, list) or not all(
3793 isinstance(s, (list, tuple)) and len(s) > 0
3794 and all(isinstance(i, (int, np.integer)) for i in s) for s in slices):
3795 raise TypeError('\n\nParameter slices must be a list of non-empty lists of integers.\n')
3796 flat = sorted(int(i) for s in slices for i in s)
3797 if flat != list(range(n_sites)):
3798 raise ValueError('\n\nThe slices must contain every site (0 to {}) exactly '
3799 'once.\n'.format(n_sites - 1))
3802def block_tridiagonal(max_distance):
3803 '''
3804 Check that hoppings only couple a slice to itself or to its neighbours.
3806 Raises
3807 ------
3808 ValueError
3809 Hoppings must connect neighbouring slices only.
3810 '''
3811 if max_distance > 1:
3812 raise ValueError('\n\nThe Hamiltonian couples slices that are not neighbours: '
3813 'use thicker slices.\n')
3816def lead_tuple(lead):
3817 '''
3818 Check a lead given as a tuple (h0, v, coupling).
3820 Raises
3821 ------
3822 TypeError
3823 A lead must be a tuple (h0, v, coupling).
3824 '''
3825 if not isinstance(lead, (tuple, list)) or len(lead) != 3:
3826 raise TypeError('\n\nA lead must be a tuple (h0, v, coupling).\n')
3829####################################
3830# SECTOR CHERN NUMBERS, BDG, BOTT, WEYL
3831####################################
3834def hermitian_operator(ops, tol=1e-10):
3835 '''
3836 Check that an operator (shape (..., norb, norb)) is Hermitian.
3838 Raises
3839 ------
3840 ValueError
3841 The operator must be Hermitian.
3842 '''
3843 if np.max(np.abs(ops - ops.conj().swapaxes(-1, -2))) > tol:
3844 raise ValueError('\n\nThe operator must be Hermitian.\n')
3847def projected_gap(w, tol=1e-6):
3848 '''
3849 Check that the projected operator POP (eigenvalues *w*, shape (nk, n),
3850 sorted) has no eigenvalue near zero, and the same number of negative
3851 eigenvalues at every k-point; return that number.
3853 Raises
3854 ------
3855 ValueError
3856 The spectrum of POP must have a gap around zero.
3857 '''
3858 n_minus = np.sum(w < 0., axis=1)
3859 if np.min(np.abs(w)) < tol or np.any(n_minus != n_minus[0]):
3860 raise ValueError('\n\nThe spectrum of the projected operator POP closes around zero '
3861 '(smallest |eigenvalue| {:.3g}): the two sectors are not '
3862 'defined.\n'.format(float(np.min(np.abs(w)))))
3863 return int(n_minus[0])
3866def mirror_square(squares, tol):
3867 '''
3868 Check that a mirror operator squares to +1 or -1 (*squares*, shape
3869 (..., norb, norb)); return the sign.
3871 Raises
3872 ------
3873 ValueError
3874 The mirror operator must square to +1 or -1.
3875 '''
3876 sign = np.sign(squares[0, 0, 0].real)
3877 eye = np.eye(squares.shape[-1])
3878 if sign == 0 or np.max(np.abs(squares - sign * eye)) > tol:
3879 raise ValueError('\n\nThe mirror operator must be unitary and square to +1 or -1.\n')
3880 return int(sign)
3883def commutes(err, tol):
3884 '''
3885 Check that an operator commutes with H(k) on the plane (*err*: largest
3886 entry of the commutator).
3888 Raises
3889 ------
3890 ValueError
3891 The operator does not commute with H(k) on this plane.
3892 '''
3893 if err > tol:
3894 raise ValueError('\n\nThe mirror operator does not commute with H(k) on this plane '
3895 '(largest commutator entry {:.3g}): choose a '
3896 'mirror-invariant plane.\n'.format(err))
3899def strong_index(strong):
3900 '''
3901 Check that the strong Z2 index comes out the same from the three pairs
3902 of time-reversal-invariant planes.
3904 Raises
3905 ------
3906 ValueError
3907 The six Z2 invariants are inconsistent.
3908 '''
3909 if len(set(int(s) for s in strong)) != 1:
3910 raise ValueError('\n\nThe six planar Z2 invariants give different strong indices: '
3911 'increase nk or nk_perp (or the bands are not gapped).\n')
3914def region(region, n):
3915 '''
3916 Check a region: a non-empty list of distinct indices between 0 and n-1.
3918 Raises
3919 ------
3920 TypeError
3921 Parameter region must be a non-empty list of integers.
3922 ValueError
3923 Parameter region must hold distinct indices between 0 and n-1.
3924 '''
3925 if not isinstance(region, list) or not region or \
3926 not all(isinstance(i, (int, np.integer)) for i in region):
3927 raise TypeError('\n\nParameter region must be a non-empty list of integers.\n')
3928 if len(set(region)) != len(region) or not all(0 <= i < n for i in region):
3929 raise ValueError('\n\nParameter region must hold distinct indices between 0 and '
3930 '{}.\n'.format(n - 1))
3933def torus_cell(cell, d):
3934 '''
3935 Check the two vectors spanning a torus: shape (2, d), linearly independent.
3937 Raises
3938 ------
3939 ValueError
3940 Parameter cell must hold two independent vectors of d components.
3941 '''
3942 if cell.shape != (2, d) or np.linalg.matrix_rank(cell) < 2:
3943 raise ValueError('\n\nParameter cell must hold two linearly independent vectors '
3944 'of {} components.\n'.format(d))
3947def band_below_top(n, norb):
3948 '''
3949 Check that a band has a band above it.
3951 Raises
3952 ------
3953 ValueError
3954 The highest band of bands must not be the top band.
3955 '''
3956 if n >= norb - 1:
3957 raise ValueError('\n\nThe highest band of bands must have a band above it.\n')
3960def antisymmetric(a, tol=1e-10):
3961 '''
3962 Check that a matrix is antisymmetric.
3964 Raises
3965 ------
3966 ValueError
3967 The matrix must be antisymmetric.
3968 '''
3969 if np.max(np.abs(a + a.T), initial=0.) > tol:
3970 raise ValueError('\n\nThe matrix must be antisymmetric.\n')
3973def bdg_orbitals(norb):
3974 '''
3975 Check that a BdG model has an even number of orbitals (particles, then holes).
3977 Raises
3978 ------
3979 ValueError
3980 A BdG model must have an even number of orbitals.
3981 '''
3982 if norb % 2:
3983 raise ValueError('\n\nA BdG model must have an even number of orbitals '
3984 '(particles, then holes).\n')
3987def majorana_form(a, tol=1e-8):
3988 '''
3989 Check that the Majorana form -i U H U^dagger of a BdG Hamiltonian at a
3990 time-reversal-invariant momentum is real (particle-hole symmetry).
3992 Raises
3993 ------
3994 ValueError
3995 The model is not particle-hole symmetric.
3996 '''
3997 if np.max(np.abs(a.imag)) > tol:
3998 raise ValueError('\n\nThe model is not particle-hole symmetric in the BdG basis '
3999 '(particles, then holes; see bdg.bdg_kspace).\n')
4002def gapped_trim(pf, tol=1e-12):
4003 '''
4004 Check that a Pfaffian is nonzero (the BdG gap is open at the momentum).
4006 Raises
4007 ------
4008 ValueError
4009 The gap closes at a time-reversal-invariant momentum.
4010 '''
4011 if abs(pf) < tol:
4012 raise ValueError('\n\nThe gap closes at a time-reversal-invariant momentum: the '
4013 'Majorana number is not defined.\n')
4016####################################
4017# PERIODIC BOUNDARIES (SYSTEM)
4018####################################
4021def periodic(periodic, dim):
4022 '''
4023 Check parameter *periodic* of *System*: a boolean, or a tuple of *dim*
4024 booleans (one per primitive vector); return the tuple.
4026 Raises
4027 ------
4028 TypeError
4029 Parameter periodic must be a bool or a tuple of bools.
4030 ValueError
4031 Parameter periodic must have one entry per primitive vector.
4032 '''
4033 if isinstance(periodic, bool):
4034 return (periodic,) * dim
4035 if not isinstance(periodic, tuple) or not all(isinstance(p, bool) for p in periodic):
4036 raise TypeError('\n\nParameter periodic must be a bool or a tuple of bools.\n')
4037 if len(periodic) != dim:
4038 raise ValueError('\n\nParameter periodic must have one entry per primitive vector '
4039 '({}).\n'.format(dim))
4040 return periodic
4043def torus_lattice(n1):
4044 '''
4045 Check that the lattice of a periodic System comes from *get_lattice*.
4047 Raises
4048 ------
4049 ValueError
4050 Periodic boundaries need lat.get_lattice first.
4051 '''
4052 if n1 == 0:
4053 raise ValueError('\n\nPeriodic boundaries wrap the n1 x n2 (x n3) cells of '
4054 'lat.get_lattice: call it first.\n')
4057def torus_size(dist, period, n):
4058 '''
4059 Check that bonds of length *dist* (order *n*) reach less than half-way
4060 around the torus (shortest period *period*), so that each one has a
4061 single shortest image.
4063 Raises
4064 ------
4065 ValueError
4066 The torus is too small for this neighbour order.
4067 '''
4068 if 2 * dist >= period - ATOL:
4069 raise ValueError('\n\nThe torus is too small for the neighbours of order {}: their '
4070 'length {:.3f} reaches half-way around it (shortest period '
4071 '{:.3f}). Use more cells.\n'.format(n, dist, period))
4074def not_periodic(periodic, method):
4075 '''
4076 Check that a System has open boundaries.
4078 Raises
4079 ------
4080 ValueError
4081 The method needs open boundaries.
4082 '''
4083 if any(periodic):
4084 raise ValueError('\n\n{} needs open boundaries (periodic=False); on a torus, see '
4085 'System.get_bott_index and set_peierls_phase.\n'.format(method))
4088def torus_2d(periodic):
4089 '''
4090 Check that a System is a 2D torus (periodic along two primitive vectors).
4092 Raises
4093 ------
4094 ValueError
4095 This calculation needs a 2D lattice periodic along both vectors.
4096 '''
4097 if len(periodic) != 2 or not all(periodic):
4098 raise ValueError('\n\nThis calculation needs a 2D lattice with periodic boundaries '
4099 'along both primitive vectors (periodic=True).\n')
4102####################################
4103# ANALYSIS AND PLOTTING (projections, contours, k-paths)
4104####################################
4107def projector(proj, norb, tags):
4108 '''
4109 Check parameter *projector* of *kspace.band_weights*: an orbital index,
4110 a non-empty list of distinct orbital indices, a sublattice tag, or a
4111 (norb, norb) Hermitian matrix.
4113 Raises
4114 ------
4115 TypeError
4116 Parameter projector must be an orbital index, a list of them, a tag or a matrix.
4117 ValueError
4118 Orbital indices must be distinct and between 0 and norb-1.
4119 ValueError
4120 Parameter projector must be a tag of the unit cell.
4121 ValueError
4122 The operator must be a (norb, norb) Hermitian matrix.
4123 '''
4124 if isinstance(proj, str):
4125 if proj not in tags:
4126 raise ValueError('\n\nParameter projector must be a tag of the unit cell: '
4127 '{}.\n'.format(sorted(set(tags))))
4128 return
4129 if isinstance(proj, np.ndarray) and proj.ndim == 2:
4130 operator(proj, norb)
4131 hermitian_operator(proj)
4132 return
4133 if isinstance(proj, int):
4134 proj = [proj]
4135 if not isinstance(proj, list) or not proj or not all(isinstance(p, int) for p in proj):
4136 raise TypeError('\n\nParameter projector must be an orbital index, a list of '
4137 'orbital indices, a sublattice tag or a (norb, norb) matrix.\n')
4138 if len(set(proj)) != len(proj) or not all(0 <= p < norb for p in proj):
4139 raise ValueError('\n\nOrbital indices must be distinct and between 0 and '
4140 '{}.\n'.format(norb - 1))
4143def band_weights(weights, shape):
4144 '''
4145 Check parameter *weights* of *kspace.plot_bands*: real values, one per
4146 band and k-point of the stored band structure.
4148 Raises
4149 ------
4150 ValueError
4151 Parameter weights must be a real array of the shape of the bands.
4152 '''
4153 weights = np.asarray(weights)
4154 if weights.shape != shape or np.iscomplexobj(weights) or not np.all(np.isfinite(weights)):
4155 raise ValueError('\n\nParameter weights must be a finite real array of shape {}, '
4156 'the shape of the bands (see band_weights).\n'.format(shape))
4159def weight_style(style):
4160 '''
4161 Check parameter *style* of *kspace.plot_bands*.
4163 Raises
4164 ------
4165 ValueError
4166 Parameter style must be "color" or "size".
4167 '''
4168 if style not in ('color', 'size'):
4169 raise ValueError('\n\nParameter style must be "color" or "size".\n')
4172def contour_found(n_elements, energy):
4173 '''
4174 Check that a band crosses the energy of a constant-energy contour.
4176 Raises
4177 ------
4178 ValueError
4179 The band does not cross the energy.
4180 '''
4181 if n_elements == 0:
4182 raise ValueError('\n\nThe band does not cross the energy {}: there is no '
4183 'constant-energy contour.\n'.format(energy))
4186def cubic_path(points, names, axes):
4187 '''
4188 Check the parameters of *kspace.cubic_path*.
4190 Raises
4191 ------
4192 TypeError
4193 Parameter points must be a dictionary.
4194 ValueError
4195 Each point must have 3 real coordinates.
4196 ValueError
4197 Parameter names must list at least two keys of points.
4198 ValueError
4199 Parameter axes must be 3 orthonormal rows.
4200 '''
4201 if not isinstance(points, dict):
4202 raise TypeError('\n\nParameter points must be a dictionary (label: point).\n')
4203 for key, point in points.items():
4204 point = np.asarray(point)
4205 if point.shape != (3,) or not np.isrealobj(point) or not np.all(np.isfinite(point)):
4206 raise ValueError('\n\nPoint {} must have 3 real coordinates.\n'.format(key))
4207 if (not isinstance(names, (list, tuple)) or len(names) < 2
4208 or any(n not in points for n in names)):
4209 raise ValueError('\n\nParameter names must list at least two keys of points.\n')
4210 if axes is not None:
4211 axes = np.asarray(axes)
4212 if axes.shape != (3, 3) or not np.isrealobj(axes) or not np.allclose(axes @ axes.T, np.eye(3)):
4213 raise ValueError('\n\nParameter axes must be 3 orthonormal rows (shape (3, 3)).\n')
4216def bravais_lattice(kind, dim):
4217 '''
4218 Check that *kspace.high_symmetry_path* recognized the Bravais lattice.
4220 Raises
4221 ------
4222 ValueError
4223 No standard path for this lattice.
4224 '''
4225 if kind is None:
4226 raise ValueError('\n\nNo standard high-symmetry path for this {}D lattice: '
4227 'high_symmetry_path knows the 1D and 2D Bravais lattices, '
4228 'and the simple cubic, fcc, bcc, simple tetragonal and '
4229 'hexagonal ones in 3D. Pass the k-points to k_path '
4230 'directly.\n'.format(dim))
4233###############################
4234# CONTINUUM DISCRETIZATION
4235###############################
4238def sympy_module(module):
4239 '''
4240 Check that sympy, the optional dependency of *tbkit.continuum*, is installed.
4242 Raises
4243 ------
4244 ImportError
4245 sympy is not installed.
4246 '''
4247 if module is None:
4248 raise ImportError('\n\ntbkit.continuum needs sympy: pip install tbkit[continuum].\n')
4251def continuum_type(ham, types):
4252 '''
4253 Check the type of the Hamiltonian of *continuum.discretize*.
4255 Raises
4256 ------
4257 TypeError
4258 The Hamiltonian must be a string, a sympy expression
4259 or a sympy Matrix.
4260 '''
4261 if not isinstance(ham, types):
4262 raise TypeError('\n\nParameter hamiltonian must be a string, a sympy expression '
4263 'or a sympy Matrix.\n')
4266def continuum_parsed(error, string):
4267 '''
4268 Check that the string Hamiltonian of *continuum.discretize* was parsed.
4270 Raises
4271 ------
4272 ValueError
4273 The string is not a valid expression.
4274 '''
4275 if error is not None:
4276 raise ValueError('\n\nParameter hamiltonian {!r} is not a valid sympy expression '
4277 '({}: {}).\n'.format(string, type(error).__name__, error))
4280def continuum_square(shape):
4281 '''
4282 Check that the Hamiltonian of *continuum.discretize* is a square matrix.
4284 Raises
4285 ------
4286 ValueError
4287 The Hamiltonian must be a square matrix.
4288 '''
4289 if len(shape) != 2 or shape[0] != shape[1] or shape[0] == 0:
4290 raise ValueError('\n\nParameter hamiltonian must be a scalar or a square matrix '
4291 '(shape {}).\n'.format(shape))
4294def continuum_symbols(names):
4295 '''
4296 Check the free symbols of the Hamiltonian of *continuum.discretize*:
4297 no position (the model is translation invariant), no symbol named
4298 like the grid spacing *a*, and parameter names that can be passed as
4299 keyword arguments.
4301 Raises
4302 ------
4303 ValueError
4304 x, y, z are not supported.
4305 ValueError
4306 a is reserved for the grid spacing.
4307 ValueError
4308 Parameter names must be Python identifiers.
4309 '''
4310 position = sorted(names & {'x', 'y', 'z'})
4311 if position:
4312 raise ValueError('\n\nThe Hamiltonian depends on the position ({}): only translation-'
4313 'invariant continuum models (constant coefficients) can be '
4314 'discretized into a KSpace.\n'.format(', '.join(position)))
4315 if 'a' in names:
4316 raise ValueError('\n\nThe symbol a is reserved for the grid spacing: rename the '
4317 'parameter.\n')
4318 bad = sorted(n for n in names if not n.isidentifier() or n in ('site_i', 'site_j'))
4319 if bad:
4320 raise ValueError('\n\nParameter names must be Python identifiers (other than site_i, '
4321 'site_j), to be passed to get_ham: {}.\n'.format(', '.join(bad)))
4324def continuum_dim(dim, used):
4325 '''
4326 Check the dimension of *continuum.discretize*, given the momenta
4327 k_x, k_y, k_z (axes 0, 1, 2) used by the Hamiltonian.
4329 Raises
4330 ------
4331 TypeError
4332 Parameter dim must be an integer.
4333 ValueError
4334 Parameter dim must be 1, 2 or 3.
4335 ValueError
4336 The Hamiltonian uses a momentum beyond dim.
4337 '''
4338 if not isinstance(dim, int):
4339 raise TypeError('\n\nParameter dim must be an integer.\n')
4340 if dim not in (1, 2, 3):
4341 raise ValueError('\n\nParameter dim must be 1, 2 or 3.\n')
4342 if used and max(used) >= dim:
4343 raise ValueError('\n\nThe Hamiltonian uses {} in dimension {}.\n'.format(
4344 ('k_x', 'k_y', 'k_z')[max(used)], dim))
4347def continuum_polynomial(entry, is_polynomial):
4348 '''
4349 Check that a matrix element of *continuum.discretize* is a polynomial
4350 in the momenta.
4352 Raises
4353 ------
4354 ValueError
4355 The Hamiltonian must be a polynomial in k_x, k_y, k_z.
4356 '''
4357 if not is_polynomial:
4358 raise ValueError('\n\nThe Hamiltonian must be a polynomial in k_x, k_y, k_z '
4359 '(matrix element {}).\n'.format(entry))
4362def continuum_tags(tags, norb):
4363 '''
4364 Check the orbital tags of *continuum.discretize*.
4366 Raises
4367 ------
4368 ValueError
4369 More than 52 orbitals need explicit tags.
4370 TypeError
4371 Parameter tags must be a string or a list of strings.
4372 ValueError
4373 Parameter tags must hold one one-character tag per orbital.
4374 '''
4375 if tags is None:
4376 if norb > 52:
4377 raise ValueError('\n\nThe Hamiltonian has {} orbitals: pass their tags (default '
4378 'tags are the 52 letters).\n'.format(norb))
4379 return
4380 if not isinstance(tags, (str, list, tuple)) or not all(isinstance(t, str) for t in tags):
4381 raise TypeError('\n\nParameter tags must be a string or a list of strings.\n')
4382 if len(tags) != norb or not all(len(t) == 1 for t in tags):
4383 raise ValueError('\n\nParameter tags must hold {} one-character tags, one per '
4384 'orbital.\n'.format(norb))
4387def continuum_spacing(a):
4388 '''
4389 Check the grid spacing of *continuum.discretize*: a **Lattice**
4390 refuses primitive vectors whose squared norm is below 0.1.
4392 Raises
4393 ------
4394 ValueError
4395 Parameter a must be at least sqrt(0.1).
4396 '''
4397 if a * a < 0.1:
4398 raise ValueError('\n\nParameter a must be at least sqrt(0.1) = 0.316 (the shortest '
4399 'primitive vector of a Lattice): measure lengths in a smaller '
4400 'unit, and scale the coefficients of the Hamiltonian '
4401 'accordingly.\n')
4404####################################
4405# WANNIER FUNCTIONS
4406####################################
4409def trial_orbitals(trial, norb, n_bands):
4410 '''
4411 Check the trial orbitals of *wannier.wannierize*: a list of orbital
4412 indices, or an array of shape (norb, n_wann), with at most *n_bands*
4413 trial orbitals (one per Wannier function; fewer than the bands
4414 disentangles them).
4416 Raises
4417 ------
4418 TypeError
4419 Parameter trial must be a list of orbital indices or an array.
4420 ValueError
4421 Parameter trial must hold between 1 and n_bands trial orbitals.
4422 '''
4423 if isinstance(trial, list) and trial and all(isinstance(o, int) for o in trial):
4424 if len(trial) > n_bands or not all(0 <= o < norb for o in trial):
4425 raise ValueError('\n\nParameter trial must hold at most {} orbital indices (one per '
4426 'Wannier function, at most one per band) between 0 and {}.\n'
4427 .format(n_bands, norb - 1))
4428 return
4429 try:
4430 g = np.asarray(trial, dtype='c16')
4431 except (TypeError, ValueError):
4432 raise TypeError('\n\nParameter trial must be a list of orbital indices or a complex '
4433 'array of shape (norb, n_wann).\n') from None
4434 if g.ndim != 2 or g.shape[0] != norb or not 1 <= g.shape[1] <= n_bands:
4435 raise ValueError('\n\nParameter trial must be an array of shape ({}, n_wann), with '
4436 '1 <= n_wann <= {}: one column (trial orbital) per Wannier '
4437 'function, at most one per band.\n'.format(norb, n_bands))
4440def energy_window(window, var_name):
4441 '''
4442 Check an energy window of *wannier.wannierize*: a tuple of two
4443 increasing real numbers.
4445 Raises
4446 ------
4447 TypeError
4448 Parameter var_name must be a tuple of two real numbers.
4449 ValueError
4450 Parameter var_name must be increasing.
4451 '''
4452 if not isinstance(window, tuple) or len(window) != 2 or \
4453 not all(isinstance(e, (int, float)) and not isinstance(e, bool) for e in window):
4454 raise TypeError('\n\nParameter {} must be a tuple (E_min, E_max) of two real '
4455 'numbers.\n'.format(var_name))
4456 if window[0] >= window[1]:
4457 raise ValueError('\n\nParameter {} must satisfy E_min < E_max.\n'.format(var_name))
4460def frozen_window(frozen, window):
4461 '''
4462 Check that the frozen (inner) window of *wannier.wannierize* lies
4463 inside the outer window.
4465 Raises
4466 ------
4467 ValueError
4468 The frozen window must lie inside the outer window.
4469 '''
4470 if frozen[0] < window[0] or frozen[1] > window[1]:
4471 raise ValueError('\n\nParameter frozen {} must lie inside the outer window {}.\n'
4472 .format(frozen, window))
4475def window_states(n_inside, n_frozen, n_wann):
4476 '''
4477 Check the number of states of the disentanglement windows at every
4478 k-point: at least *n_wann* in the outer window, at most *n_wann* in the
4479 frozen window.
4481 Raises
4482 ------
4483 ValueError
4484 The outer window holds fewer than n_wann states at some k-point.
4485 ValueError
4486 The frozen window holds more than n_wann states at some k-point.
4487 '''
4488 if n_inside < n_wann:
4489 raise ValueError('\n\nThe outer window holds only {} of the bands at some k-point, '
4490 'fewer than the {} Wannier functions: widen it, or add bands.\n'
4491 .format(n_inside, n_wann))
4492 if n_frozen > n_wann:
4493 raise ValueError('\n\nThe frozen window holds {} bands at some k-point, more than the '
4494 '{} Wannier functions: narrow it.\n'.format(n_frozen, n_wann))
4497def isolated_bands(en, bands, tol=1e-8):
4498 '''
4499 Check that a group of bands is separated in energy from the other bands
4500 at every point of the mesh (*en*, shape (nk, norb), sorted).
4502 Raises
4503 ------
4504 ValueError
4505 The bands are not isolated.
4506 '''
4507 inside = np.zeros(en.shape[1], bool)
4508 inside[bands] = True
4509 for n in range(en.shape[1] - 1):
4510 if inside[n] != inside[n + 1] and np.min(en[:, n + 1] - en[:, n]) < tol:
4511 raise ValueError('\n\nThe bands {} touch band {} on the k-mesh: Wannier functions '
4512 'need an isolated group of bands: add the bands they touch to the group, '
4513 'or disentangle them (more bands than trial orbitals).\n'
4514 .format(bands, n + 1 if inside[n] else n))
4517def projection(s_min, tol=1e-10):
4518 '''
4519 Check that the projection of the bands onto the trial orbitals has full
4520 rank at every k-point.
4522 Raises
4523 ------
4524 ValueError
4525 The trial orbitals miss the bands at some k-point.
4526 '''
4527 if s_min < tol:
4528 raise ValueError('\n\nThe trial orbitals have no overlap with the bands at some '
4529 'k-point (singular projection): choose other trial orbitals, or '
4530 'another mesh. A band group with a nonzero Chern number has no '
4531 'localized Wannier functions, and any projection vanishes '
4532 'somewhere.\n')