Coverage for tbkit/error_handling.py: 100%
1275 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-15 06:13 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-15 06:13 +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 TypeError: Parameter *var* must be a bool.
18 '''
19 if not isinstance(var, bool):
20 raise TypeError('\n\nParameter {} must be a bool.\n'.format(var_name))
23def positive_int(var, var_name):
24 '''
25 Check if *var* is a positive integer.
27 :raises TypeError: Parameter *var* must be an integer.
28 :raises ValueError: Parameter *var* must be a positive integer.
29 '''
30 if not isinstance(var, int):
31 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
32 if var < 1:
33 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name))
36def positive_int_lim(var, var_name, nmax):
37 '''
38 Check if *var* is a positive integer smaller than nmax.
40 :raises TypeError: Parameter *var* must be an integer.
41 :raises ValueError: Parameter *var* must be a positive integer.
42 :raises ValueError: Parameter *var* must be a positive integer
43 smaller than nmax.
44 '''
45 if not isinstance(var, int):
46 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
47 if var < 1:
48 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name))
49 if var > nmax:
50 raise ValueError('\n\nParameter {} must be a positive integer '
51 'smaller than {}.\n'.format(var_name, nmax))
54def real_number(var, var_name):
55 '''
56 Check if parameter *var* is a real number.
58 :raises TypeError: Parameter *var* must be a real number.
59 '''
60 if not isinstance(var, (int, float)):
61 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
64def positive_real(var, var_name):
65 '''
66 Check if parameter *var* is a positive number.
68 :raises TypeError: Parameter *var* must be a real number.
69 :raises ValueError: Parameter *var* must be a positive number.
70 '''
71 if not isinstance(var, (int, float)):
72 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
73 if var <= 0:
74 raise ValueError('\n\nParameter {} must be a positive number.\n'.format(var_name))
77def positive_real_zero(var, var_name):
78 '''
79 Check if parameter *var* is a positive number or zero.
81 :raises TypeError: Parameter *var* must be a real number.
82 :raises ValueError: Parameter *var* must be a positive number or zero.
83 '''
84 if not isinstance(var, (int, float)):
85 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
86 if var < 0:
87 raise ValueError('\n\nParameter {} must be a positive number or zero.\n'.format(var_name))
90def negative_real(var, var_name):
91 '''
92 Check if parameter *var* is a negative number.
94 :raises TypeError: Parameter *var* must be a real number.
95 :raises ValueError: Parameter *var* must be a negative number.
97 '''
98 if not isinstance(var, (int, float)):
99 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name))
100 if var >= 0:
101 raise ValueError('\n\nParameter {} must be a negative number.\n'.format(var_name))
104def number(var, var_name):
105 '''
106 Check if parameter *var* is a number.
108 :raises TypeError: Parameter *var* must be a real or complex number.
109 '''
110 if not isinstance(var, (int, float, complex)):
111 raise TypeError('\n\nParameter {} must be a real or complex number.\n'.format(var_name))
114def value_params(required, params, func):
115 '''
116 Check that *params* holds every parameter that the value function *func*
117 needs (see *tbkit.values*).
119 :raises TypeError: The value function needs parameters missing from get_ham.
120 '''
121 missing = [name for name in required if name not in params]
122 if missing:
123 raise TypeError('\n\nThe value function {} needs the parameter(s) {}: '
124 'pass them to get_ham (or KSpace.set_params).\n'.format(
125 getattr(func, '__name__', func), ', '.join(missing)))
128def value_shape(val, size, spin, func):
129 '''
130 Check the values returned by the value function *func* for *size*
131 bonds or sites: a number or one per entry (or, if *spin*, also a 2x2
132 matrix or one per entry).
134 :raises ValueError: The value function must return a number or an array of one value per bond/site.
135 '''
136 shapes = [(), (size,)] + ([(2, 2), (size, 2, 2)] if spin else [])
137 if val.shape not in shapes:
138 raise ValueError('\n\nThe value function {} returned shape {}: it must return a number, '
139 'or one per bond/site (shape ({},)){}.\n'.format(
140 getattr(func, '__name__', func), val.shape, size,
141 ', or a 2x2 matrix (one per bond, shape ({}, 2, 2))'.format(size)
142 if spin else ''))
145def no_value_functions(model, method):
146 '''
147 Check that *model* has no value functions (callable hoppings or onsite
148 energies), which *method* cannot represent.
150 :raises ValueError: method does not support value functions.
151 '''
152 if getattr(model, '_hop_values', None) or getattr(model, '_onsite_values', None):
153 raise ValueError('\n\n{} does not support value functions (callable hoppings or '
154 'onsite energies): use plain numbers.\n'.format(method))
157def is_callable(var, var_name):
158 '''
159 Check if parameter *var* is callable.
161 :raises TypeError: Parameter *var* must be callable.
162 '''
163 if not callable(var):
164 raise TypeError('\n\nParameter {} must be callable.\n'.format(var_name))
167def larger(var1, var_name1, var2, var_name2):
168 '''
169 Check if *var1* larger than *val*.
171 :raises ValueError: Parameter *var1* larger than *var2*.
172 '''
173 if var1 >= var2:
174 raise ValueError('\n\n{} must be larger than {}.\n'
175 .format(var_name1, var_name2))
178def smaller(var1, var_name1, var2, var_name2):
179 '''
180 Check if *var1* smaller than *var2*.
182 :raises ValueError: Parameter *var1* must be smaller than *var2*.
183 '''
184 if var1 >= var2:
185 raise ValueError('\n\n{} must be smaller than {}.\n'
186 .format(var_name1, var_name2))
189def string(var, var_name):
190 '''
191 Check if parameter *var* is a string.
193 :raises TypeError: Parameter *var* must be a string.
194 '''
195 if var is None:
196 return
197 if not isinstance(var, str):
198 raise TypeError('\n\nParameter {} must be a string.\n'.format(var_name))
201def ndarray(var, var_name, length):
202 '''
203 Check if parameter *var* is a numpy array.
205 :raises TypeError: Parameter *var* must be a numpy ndarray.
206 :raises ValueError: length array must be equal to length.
207 '''
208 if not isinstance(var, np.ndarray):
209 raise TypeError('\n\nParameter {} must be a numpy ndarray.\n'.format(var_name))
210 if len(var) != length:
211 raise ValueError('\n\nParameter {} must be a numpy ndarray of {}\n'
212 ''.format(var_name, length))
215def ndarray_null(var, var_name):
216 '''
217 Check if parameter *var* is not a null numpy array.
219 :raises ValueError: Parameter *var* must not be a null numpy ndarray.
220 '''
221 array_null = np.zeros(len(var))
222 if np.allclose(var, array_null):
223 raise ValueError('\n\nParameter {} must not be a null numpy ndarray.\n'.format(var_name))
226def ndarray_empty(var, var_name):
227 '''
228 Check if parameter *var* is not an empty numpy array.
230 :raises ValueError: Parameter *var* must not be an emptynumpy ndarray.
231 '''
232 if var.size == 0:
233 raise ValueError('\n\nParameter {} must not be an empty numpy ndarray.\n'.format(var_name))
236def list_tuple_2elem(var, var_name):
237 '''
238 Check if parameter *var* is a list/tuple with 2 elements.
240 :raises TypeError: Parameter *var* must be a list/tuple.
241 :raises ValueError: Parameter *var* must contain 2 elements.
242 '''
243 if var is None:
244 return
245 if not isinstance(var, (list, tuple)):
246 raise TypeError('\n\nParameter {} must be a list/tuple.\n'.format(var_name))
247 if len(var) != 2:
248 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name))
251def tuple_2elem(var, var_name):
252 '''
253 Check if parameter *var* is a tuple with 2 elements.
255 :raises TypeError: Parameter *var* must be a tuple.
256 :raises ValueError: Parameter *var* must contain 2 elements.
257 '''
258 if var is None:
259 return
260 if not isinstance(var, tuple):
261 raise TypeError('\n\nParameter {} must be a tuple.\n'.format(var_name))
262 if len(var) != 2:
263 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name))
266###############################
267# LATTICE EXCEPTION HANDLING
268###############################
271def lat(lat):
272 '''
273 Check if parameter is an instance of the *lattice*.
274 :raises TypeError: Parameter must be an instance of the class lattice.
275 '''
276 names = inspect.getmro(lat.__class__)
277 if str(names).find('lattice') == -1:
278 raise TypeError('\n\nParameter must be an instance of the class lattice.\n')
281def unit_cell(unit_cell):
282 '''
283 Check parameter *unit_cell*.
285 :raises TypeError: Parameter unit_cell must be a list.
286 :raises KeyError: Dictionaries must contain the key "tag".
287 :raises KeyError: Dictionaries must contain the key "r0".
288 :raises TypeError: Key "tags" must contain a one-character string.
289 :raises ValueError: Key "tags" must contain a one-character string.
290 :raises ValueError: Key "r0" must contain be a list.
291 :raises TypeError: Key "r0" must contain be a tuple.
292 :raises ValueError: Key "r0" must contain a tuple of length two.
293 :raises ValueError: Key "r0" must contain a tuple of two real numbers.
294 '''
295 if not isinstance(unit_cell, list):
296 raise TypeError('\n\nParameter unit_cell must be a list.\n')
297 for dic in unit_cell:
298 if 'tag' not in dic:
299 raise KeyError('\n\nDictionaries must contain the key "tag".\n')
300 if 'r0' not in dic:
301 raise KeyError('\n\nDictionaries must contain the key "r0".\n')
302 if not isinstance(dic['tag'], str):
303 raise TypeError('\n\nKey "tag" must contain a one-character string.\n')
304 if not len(dic['tag']) == 1:
305 raise ValueError('\n\nKey "tag" must be a one-character string.\n')
306 if not isinstance(dic['r0'], tuple):
307 raise TypeError('\n\nKey "r0" must be a tuple.\n')
308 if len(dic['r0']) not in (2, 3):
309 raise ValueError('\n\nKey "r0" must contain a tuple of length two (or three, '
310 'for a lattice in 3D space).\n')
311 if not all(isinstance(c, (int, float)) for c in dic['r0']):
312 raise ValueError('\n\nKey "r0" must contain a tuple of real numbers.\n')
313 if len({len(dic['r0']) for dic in unit_cell}) > 1:
314 raise ValueError('\n\nAll "r0" must have the same length.\n')
317def prim_vec(prim_vec):
318 '''
319 Check parameter *prim_vec*.
321 :raises TypeError: Parameter prim_vec must be a list.
322 :raises ValueError: Parameter prim_vec must be a list
323 of length 1 for 1D lattices or length 2 fro 2D lattices.
324 :raises TypeError: List elements must be tuples.
325 :raises ValueError: List elements must be 1 or 2 tuples.
326 :raises ValueError: Tuples must be of length 2.
327 :raises ValueError: Tuples must containt real numbers.
328 :raises ValueError: Norm of coor should be larger than 0.1.
329 '''
330 if not isinstance(prim_vec, list):
331 raise TypeError('\n\nParameter prim_vec must be a list.\n')
332 if len(prim_vec) not in (1, 2, 3):
333 raise ValueError('\n\nParameter prim_vec must be a list of length 1 '
334 'for 1D lattices, 2 for 2D lattices, or 3 for 3D lattices.\n')
335 for coor in prim_vec:
336 if not isinstance(coor, tuple):
337 raise TypeError('\n\nParameter prim_vec must contain tuples.\n')
338 if len(coor) not in (2, 3):
339 raise ValueError('\n\nParameter prim_vec must contain tuples of length 2 '
340 '(or 3, for a lattice in 3D space).\n')
341 if not all(isinstance(c, (int, float)) for c in coor):
342 raise ValueError('\n\nParameter prim_vec must contain tuples of '
343 'real numbers.\n')
344 if sum(c ** 2 for c in coor) < 0.1:
345 raise ValueError('\n\nEach primitive vector must have a norm larger '
346 'than 0.1.\n')
347 if len({len(coor) for coor in prim_vec}) > 1:
348 raise ValueError('\n\nAll primitive vectors must have the same length.\n')
349 if len(prim_vec) > len(prim_vec[0]):
350 raise ValueError('\n\nA lattice cannot have more primitive vectors than '
351 'space dimensions (use 3-tuples for a 3D lattice).\n')
354def independent(prim_vec):
355 '''
356 Check that the primitive vectors are linearly independent (needed for
357 the reciprocal lattice).
359 :raises ValueError: The primitive vectors must be linearly independent.
360 '''
361 if np.linalg.matrix_rank(np.array(prim_vec, dtype='f8'), tol=1e-8) != len(prim_vec):
362 raise ValueError('\n\nThe primitive vectors must be linearly independent.\n')
365def space_dim(unit_cell, prim_vec):
366 '''
367 Check that the unit cell positions and the primitive vectors live in the
368 same space (2D or 3D).
370 :raises ValueError: "r0" and prim_vec tuples must have the same length.
371 '''
372 if unit_cell and len(unit_cell[0]['r0']) != len(prim_vec[0]):
373 raise ValueError('\n\nThe "r0" positions and the primitive vectors must have '
374 'the same number of components.\n')
377def get_lattice(prim_vec, n1, n2, n3=1):
378 '''
379 Check method *get_lattice*.
381 :raises TypeError: Parameters n1, n2, n3 must be integers.
382 :raises ValueError: Parameters n1, n2, n3 must be positive integers.
383 :raises ValueError: n2 (n3) must be 1 with fewer than 2 (3) primitive vectors.
384 '''
385 positive_int(n1, 'n1')
386 positive_int(n2, 'n2')
387 positive_int(n3, 'n3')
388 if len(prim_vec) == 1 and n2 > 1:
389 raise ValueError('\n\nParameter n2 should be equal to 1\n'
390 'if one primitive vector is given\n')
391 if len(prim_vec) < 3 and n3 > 1:
392 raise ValueError('\n\nParameter n3 should be equal to 1\n'
393 'if fewer than three primitive vectors are given\n')
396def coor(coor, dtype=None):
397 '''
398 Check if *coor* is a structured array with
399 dtype=[('x', 'f8'), ('y', 'f8'), ('tag', 'U1')], or, for a lattice in 3D
400 space, dtype=[('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')]. If
401 *dtype* is given, *coor* must have exactly that dtype.
402 '''
403 dtypes = [np.dtype([('x', 'f8'), ('y', 'f8'), ('tag', 'U1')]),
404 np.dtype([('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')])]
405 if dtype is not None:
406 dtypes = [np.dtype(dtype)]
407 if coor.dtype not in dtypes:
408 raise TypeError('\n\nParameter coor dtype must be\n'
409 'dtype=[("x", "f8"), ("y", "f8"), ("tag", "U1")]\n'
410 '(with a ("z", "f8") field after "y" for a lattice in 3D space).\n')
413def coor_1d(coor):
414 '''
415 Check if *coor* is 1d (coor['y'] = cst).
416 :raises ValueError: *coor* must be 1d( coor['y'] = cst)..
417 '''
418 if not np.allclose(coor['y'][0], coor['y']):
419 raise ValueError('\n\ncoor["y"] must be constant.\n')
422def remove_sites(index, sites):
423 '''
424 Check method *remove_sites*.
426 :raises TypeError: Parameter index must be a list.
427 :raises ValueError: Parameter index must be a list of integers.
428 :raises ValueError: Indices must be between 0 and sites -1.
429 of integers between 0 and sites
430 '''
431 if not isinstance(index, list):
432 raise TypeError('\n\nParameter index must be a list.\n')
433 if not all(isinstance(i, int) for i in index):
434 raise ValueError('\n\nParameter index must be a list of integers.\n')
435 if not all(-1 < i < sites for i in index):
436 raise ValueError('\n\nElements of index must be between 0 and sites - 1.\n')
439def shift(shift):
440 '''
441 Check *shift_x* and *shift_y*.
442 :raises TypeError: Parameter delta must be a real number.
443 '''
444 if not isinstance(shift, (int, float)):
445 raise TypeError('\n\nParameter shift must be a real number.\n')
448def boundary_line(cx, cy, co):
449 '''
450 Check *boundary_line*.
451 :raises TypeError: Parameter cx must be a real number.
452 :raises TypeError: Parameter cy must be a real number.
453 :raises TypeError: Parameter co must be a real number.
454 '''
455 if not isinstance(cx, (int, float)):
456 raise TypeError('\n\nParameter cx must be a real number.\n')
457 if not isinstance(cy, (int, float)):
458 raise TypeError('\n\nParameter cy must be a real number.\n')
459 if not isinstance(co, (int, float)):
460 raise TypeError('\n\nParameter co must be a real number.\n')
463def space_3d(space_dim):
464 '''
465 Check that the lattice lives in 3D space.
467 :raises ValueError: This method requires a lattice in 3D space.
468 '''
469 if space_dim != 3:
470 raise ValueError('\n\nThis method requires a lattice in 3D space '
471 '(unit_cell and prim_vec given as 3-tuples).\n')
474def sites(sites):
475 '''
476 Check if *get_lattice* has been called (*coor* not empty).
477 :raises RuntimeError: Run method lat.get_lattice first.
478 '''
479 if sites == 0:
480 raise RuntimeError('\n\nRun method lat.get_lattice first.\n')
483####################################
484# CLASS SYSTEM EXCEPTION HANDLING
485####################################
488def sys(sys):
489 '''
490 Check if parameter is an instance of the *system*.
491 :raises TypeError: Parameter must be an instance of the class system.
492 '''
493 names = inspect.getmro(sys.__class__)
494 if str(names).find('system') == -1:
495 raise TypeError('\n\nParameter must be an instance of the class system.\n')
498def print_hopping(n, nmax):
499 '''
500 Check method *print_vec_hopping*.
502 :raises TypeError: Parameter *nmax* must be an integer.
503 :raises ValueError: Parameter *nmax* must be a positive integer.
504 between 1 and n_max-1.
505 '''
506 if not isinstance(n, int):
507 raise TypeError('\n\nParameter n_max must be an integer.\n')
508 if n < 1 or n > nmax-1:
509 raise ValueError('\n\nParameter n_max must be a positive integer'
510 'between 1 and n_max-1.\n')
513def set_onsite(onsite, tags):
514 '''
515 Check method *set_onsite*.
517 :raises TypeError: Parameter onsite must be a dictionary.
518 :raises ValueError: Parameter onsite keys must be a tag.
519 :raises ValueError: Parameter onsite values must be
520 real and/or complex numbers, or value functions.
521 '''
522 if not isinstance(onsite, dict):
523 raise TypeError('\n\nParameter onsite must be a dictionary.\n')
524 for tag, val in onsite.items():
525 if tag not in tags:
526 raise ValueError('\n\nParameter onsite keys must be a tag.\n')
527 if not isinstance(val, (int, float, complex)) and not callable(val):
528 raise ValueError('\n\nParameter onsite values must be\n'\
529 'real and/or complex numbers, or value functions.\n')
532def set_hopping(list_hop, n_max):
533 '''
534 Check method *set_hopping*.
536 :raises TypeError: Parameter *list_hop* must be a list.
537 :raises TypeError: Parameter *list_hop* must be a list of dictionary.
538 :raises KeyError: "n" and "t" must be dictionary keys.
539 :raises KeyError: "tag" or "ang" must be a key.
540 :raises KeyError: "tag" and "ang" must be keys.
541 :raises ValueError: Dictionaries must be of length 2, 3, or 4.
542 :raises ValueError: "n" must be between 1 and nmax"
544 '''
545 if not isinstance(list_hop, list):
546 raise TypeError('\n\nParameter *list_hop* must be a list.\n')
547 for dic in list_hop:
548 if not isinstance(dic, dict):
549 raise TypeError('\n\nParameter *list_hop* must be a list of dictionary.\n')
550 if 'n' not in dic or 't' not in dic:
551 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n')
552 if not isinstance(dic['n'], int):
553 raise TypeError('\n\n"n" value must be an integer.\n')
554 if not 0 < dic['n'] <= n_max:
555 raise ValueError('\n\n"n" value must be between 1 and {}.\n'.format(n_max))
556 if not isinstance(dic['t'], (int, float, complex)) and not callable(dic['t']):
557 raise TypeError('\n\n"t" value must be a real or complex number, or a value function.\n')
558 if len(dic) == 3:
559 if 'tag' not in dic and 'ang' not in dic:
560 raise KeyError('\n\n"tag" or "ang" must be a key.\n')
561 elif len(dic) == 4:
562 if 'tag' not in dic or 'ang' not in dic:
563 raise KeyError('\n\n"tag" and "ang" must be keys.\n')
564 elif len(dic) > 4:
565 raise ValueError('\n\nDictionaries must be of length 2, 3, or 4.\n')
566 if 'tag' in dic:
567 if not isinstance(dic['tag'], str):
568 raise TypeError('\n\n"tag" value must be a string.\n')
569 if len(dic['tag']) != 2:
570 raise ValueError('\n\n"tag" value must be a string of length 2.\n')
571 if 'ang' in dic:
572 if not isinstance(dic['ang'], (int, float)):
573 raise TypeError('\n\n"ang" value must be a real number.\n')
576def index(ind, dic):
577 '''
578 check if *ind* not empy.
579 '''
580 if np.sum(ind) == 0:
581 raise ValueError('\n\nNo hoppings with parameters {}.\n'.format(dic))
584def set_hopping_def(hop, hopping_def, sites, var_name='hopping_def', same_site=False):
585 '''
586 Check methods *set_hopping_def* and *set_hopping_manual*.
588 *hop* is not used (kept for backward compatibility). Indices may be
589 Python or NumPy integers, values Python or NumPy numbers. If *same_site*
590 is True, keys :math:`(i, i)` are accepted.
592 :raises TypeError: Parameter *hopping_def* must be a dictionary
593 :raises TypeError: *hopping_def* keys must be tuples.
594 :raises TypeError: *hopping_def* keys must be tuples of length 2.
595 :raises ValueError: *hopping_def* keys must be tuples of integers.
596 :raises ValueError: *hopping_def* keys must be integers between 0 and sites-1.
597 :raises ValueError: *hopping_def* keys must be different integers between 0 and sites-1.
598 :raises TypeError: *hopping_def* values must be numbers.
599 '''
600 if not isinstance(hopping_def, dict):
601 raise TypeError('\n\nParameter {} must be a dictionary.\n'.format(var_name))
602 for key, val in hopping_def.items():
603 if not isinstance(key, tuple):
604 raise TypeError('\n\n{} keys must be tuples.\n'.format(var_name))
605 if len(key) != 2:
606 raise TypeError('\n\n{} keys must be tuples of length 2.\n'.format(var_name))
607 if not isinstance(key[0], (int, np.integer)) or not isinstance(key[1], (int, np.integer)):
608 raise ValueError('\n\n{} keys must be tuples of integers.\n'.format(var_name))
609 if key[0] < 0 or key[1] < 0 or key[0] > sites-1 or key[1] > sites-1:
610 raise ValueError('\n\n{} keys must be integers between 0 and sites-1.\n'.format(var_name))
611 if key[0] == key[1] and not same_site:
612 raise ValueError('\n\n{} keys must be different integers between 0 and sites-1.\n'.format(var_name))
613 if not isinstance(val, (int, float, complex, np.number)):
614 raise TypeError('\n\n{} values must be numbers.\n'.format(var_name))
617def set_onsite_def(onsite_def, sites):
618 '''
619 Check method *set_ons_def*.
621 :raises TypeError: Parameter *onsite_def* must be a dictionary.
622 :raises TypeError: *onsite_def* keys must be integers.
623 :raises TypeError: *onsite_def* values must be numbers.
624 :raises ValueError: *onsite_def* keys must be integers between :math:`[0, sites)`.
625 '''
626 if not isinstance(onsite_def, dict):
627 raise TypeError('\n\nParameter onsite_def must be a dictionary.\n')
628 for key, val in onsite_def.items():
629 if not isinstance(key, int):
630 raise TypeError('\n\nonsite_def keys must be integers.\n')
631 if not isinstance(val, (int, float, complex)):
632 raise TypeError('\n\nonsite_def values must be numbers.\n')
633 if key < 0 or key > sites-1:
634 raise ValueError('\n\nonsite_def keys must be integers between 0 and sites-1.\n')
637def hop_n1(hop):
638 '''
639 Check method if self.hop contains nearest neighbours hoppings.
641 :raises ValueError: self.hop must contain nearest neighbours hoppings.
642 '''
643 if not np.any(hop['n'] == 1):
644 raise ValueError('\n\nParameter hop must contain nearest neighbours hoppings.\n')
647def empty_onsite(onsite):
648 '''
649 Check if *onsite* not empty.
651 :raises RuntimeError: Run method set_onsite first.
652 '''
653 if onsite.size == 0:
654 raise RuntimeError('\n\nRun method set_onsite first\n')
657def empty_hop(hop):
658 '''
659 Check if *hop* not empty.
661 :raises RuntimeError: Run method set_hopping first.
662 '''
663 if hop.size == 0:
664 raise RuntimeError('\n\nRun method set_hopping first\n')
667def hop_sites(hop, sites):
668 '''
669 Check if *hop* indices are smaller than *sites*.
671 :raises ValueError: Run method sys.clear_hopping and redefine the hoppings.
672 '''
673 ind_max = max(np.max(hop[f]) for f in ('i', 'j') if f in hop.dtype.names)
674 if ind_max >= sites:
675 raise ValueError('\n\nThe hoppings refer to sites that no longer exist.\n'
676 'Run method system.clear_hopping '
677 'and redefine the hoppings.\n')
680def empty_coor(coor):
681 '''
682 Check if *coor* not empty.
684 :raises RuntimeError: Run method lattice.get_lattice first.
685 '''
686 if coor.size == 0:
687 raise RuntimeError('\n\nRun method lattice.get_lattice first.\n')
690def empty_coor_hop(coor_hop):
691 '''
692 Check if *coor_hop* not empty.
694 :raises RuntimeError: Run method system.get_coor_hop first.
695 '''
696 if coor_hop.size == 0:
697 raise RuntimeError('\n\nRun method system.get_coor_hop first.\n')
700def empty_ham(ham):
701 '''
702 Check if Hamiltonian not empty.
704 :raises RuntimeError: Run method system.get_ham first.
705 '''
706 if not ham.nnz:
707 raise RuntimeError('\n\nRun method system.get_ham first.\n')
710def empty_en(en):
711 '''
712 Check if *en* not empty.
714 :raises RuntimeError: Run method get_ham first.
715 '''
716 if en.size == 0:
717 raise RuntimeError('\n\nRun method get_eig first\n')
720def empty_pola(pola):
721 '''
722 Check if *pola* not empty.
724 :raises RuntimeError: Run method get_eig(eigenvec=True) first.
725 '''
726 if pola.size == 0:
727 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n')
730def empty_vn(vn):
731 '''
732 Check if *vn* not empty.
734 :raises RuntimeError: Run method get_eig(eigenvec=True) first.
735 '''
736 if vn.size == 0:
737 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n')
740def empty_ipr(ipr):
741 '''
742 Check if *ipr* not empty.
743 '''
744 if ipr.size == 0:
745 raise RuntimeError('\n\nRun method get_ipr first\n')
748def empty_ndarray(arr, method):
749 '''
750 Check if *arr* is a not empty np.ndarray.
751 '''
752 if arr.size == 0:
753 raise RuntimeError('\n\nRun method {} first\n'. format(method))
756def tag(tag, tags):
757 '''
758 Check tag.
760 :raises TypeError: Parameter *tag* must be a string.
761 :raises ValueError: Parameter *tag* is not in tags.
762 '''
763 if not isinstance(tag, str):
764 raise TypeError('\n\nParameter tag must be a one-character string.\n')
765 if tag not in tags:
766 raise ValueError('\n\nParameter tag is not in tags.\n')
769def angle(angle, angles, upper_part):
770 '''
771 Check angle.
773 :raises TypeError: Parameter *angle* must be
775 * a positive number if *upper_part* is True
776 * a negative real if *upper_part* is False.
777 :raises ValueError: Parameter *angle* is not in hop['ang'].
778 '''
779 if upper_part:
780 positive_real_zero(angle, 'angle, if upper_part=True,')
781 else:
782 negative_real(angle, 'angle, if upper_part=False,')
783 if not np.sum(np.isclose(angle, angles, atol=ATOL)) and \
784 not np.sum(np.isclose(angle, angles - 180, atol=ATOL)):
785 raise ValueError('\n\nParameter angle is not in hop["ang"].\n')
788def lims(lims):
789 '''
790 Check parameter *lims*.
792 :raises TypeError: Parameter lims must be a list.
793 :raises TypeError: Parameter *lims[0]* must be a real number.
794 :raises TypeError: Parameter *lims[1]* must be a real number.
795 :raises ValueError: *lims* must be a list of length 2.
796 :raises ValueError: *lims[0]* must be smaller than *lims[1]*.
797 '''
798 if lims is not None:
799 list_tuple_2elem(lims, 'lims')
800 real_number(lims[0], 'lims[0]')
801 real_number(lims[1], 'lims[1]')
802 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]')
805def lims_positive(lims):
806 '''
807 Check parameter *lims*.
809 :raises TypeError: Parameter lims must be a list.
810 :raises TypeError: Parameter *lims[0]* must be a positive real number.
811 :raises TypeError: Parameter *lims[1]* must be a positive real number.
812 :raises ValueError: *lims* must be a list of length 2.
813 :raises ValueError: *lims[0]* must be smaller than *lims[1]*.
814 '''
815 if lims is not None:
816 list_tuple_2elem(lims, 'lims')
817 positive_real(lims[0], 'lims[0]')
818 positive_real(lims[1], 'lims[1]')
819 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]')
822#################################
823# CLASS PLOT EXCEPTION HANDLING
824#################################
827def fig(fig):
828 '''
829 Check if fig is an instance of *Figure*.
831 :raises TypeError: fig must be an instance of *Figure*.
832 '''
833 if not fig.__class__.__name__ == 'Figure':
834 raise TypeError('\n\nfig must be an instance of *Figure*.\n')
837def ani(ani):
838 '''
839 Check if ani is an instance of *FuncAnimation*.
841 :raises TypeError: ani must be an instance of *FuncAnimation*.
842 '''
843 if not ani.__class__.__name__ == 'FuncAnimation':
844 raise TypeError('\n\nani must be an instance of *FuncAnimation*.\n')
847def file_format(file_format):
848 '''
849 Check if file_format is a string 'png', 'pdf', 'ps', 'eps', or 'svg'.
851 :raises TypeError: file_format must be a string.
852 :raises ValueError: file_format must be a string given by 'png', 'pdf', 'ps', 'eps', or 'svg'.
854 '''
855 if not isinstance(file_format, str):
856 raise TypeError('\n\nfile_format must be a string.\n')
857 if file_format not in ['png', 'pdf', 'ps', 'eps', 'svg']:
858 raise ValueError('\n\nfile_format must be a string given by,\n'\
859 ' "png", "pdf", "ps", "eps", or "svg".\n')
862####################################
863# PROPAGATION
864####################################
867def get_pump(hams):
868 '''
869 Check method *propagation.get_pumping*.
871 :raises TypeError: hams must be a non-empty list.
872 :raises RuntimeError: Run method system.get_ham first.
873 '''
874 if not isinstance(hams, list) or not hams:
875 raise TypeError('\n\nhams must be a non-empty list.\n')
876 for ham in hams:
877 empty_ham(ham)
879def prop_type(prop_type):
880 string(prop_type, 'prop_type')
881 if prop_type not in ['real', 'imag', 'norm']:
882 raise ValueError('\n\nParameter prop_type must be a string:\n'
883 '"real", "imag", "norm".\n')
886####################################
887# CLASS KSPACE EXCEPTION HANDLING
888####################################
891def k_vector(vec, var_name, ndim):
892 '''
893 Check that *vec* is a tuple/list of *ndim* real numbers.
895 :raises TypeError: Parameter *var_name* must be a tuple/list.
896 :raises ValueError: Parameter *var_name* must be of length *ndim*.
897 :raises TypeError: Parameter *var_name* must contain real numbers.
898 '''
899 if not isinstance(vec, (tuple, list, np.ndarray)):
900 raise TypeError('\n\nParameter {} must be a tuple, list, or ndarray.\n'.format(var_name))
901 if len(vec) != ndim:
902 raise ValueError('\n\nParameter {} must be of length {}.\n'.format(var_name, ndim))
903 for val in vec:
904 if not isinstance(val, (int, float, np.integer, np.floating)):
905 raise TypeError('\n\nParameter {} must contain real numbers.\n'.format(var_name))
908def ks(ks, ndim):
909 '''
910 Check the k-points passed to *kspace.get_bands*: a 2D array of shape
911 (nk, *ndim*).
913 :raises ValueError: Parameter ks must be of shape (nk, ndim).
914 '''
915 if ks.ndim != 2 or ks.shape[1] != ndim:
916 raise ValueError('\n\nParameter ks must be an array of shape (nk, {}).\n'.format(ndim))
919def spin_matrix(t, var_name):
920 '''
921 Check a spinful "t" or onsite value: either a plain number, or a 2x2
922 complex matrix.
924 :raises TypeError: Parameter *var_name* must be a number or a 2x2 matrix.
925 '''
926 if isinstance(t, (int, float, complex)):
927 return
928 t = np.asarray(t)
929 if t.shape != (2, 2):
930 raise TypeError('\n\nParameter {} must be a number or a 2x2 matrix.\n'.format(var_name))
933def set_hopping_kspace(list_hop, n_sites, ndim, spin=False, values=False):
934 '''
935 Check method *kspace.set_hopping*. With *values*, "t" may also be a
936 value function (a callable).
938 :raises TypeError: Parameter *list_hop* must be a list of dictionaries.
939 :raises KeyError: "i", "j", "R", and "t" must be dictionary keys.
940 :raises ValueError: "i" and "j" must be site indices between 0 and n_sites-1.
941 :raises ValueError: "R" must be a tuple of *ndim* integers.
942 :raises TypeError: "t" must be a real or complex number (or, if *spin*,
943 a 2x2 matrix).
944 '''
945 if not isinstance(list_hop, list):
946 raise TypeError('\n\nParameter list_hop must be a list.\n')
947 for dic in list_hop:
948 if not isinstance(dic, dict):
949 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n')
950 if not {'i', 'j', 'R', 't'} <= set(dic):
951 raise KeyError('\n\n"i", "j", "R", and "t" must be dictionary keys.\n')
952 if not isinstance(dic['i'], int) or not isinstance(dic['j'], int):
953 raise TypeError('\n\n"i" and "j" must be integers.\n')
954 if not (0 <= dic['i'] < n_sites) or not (0 <= dic['j'] < n_sites):
955 raise ValueError('\n\n"i" and "j" must be site indices between 0 and {}.\n'.format(n_sites-1))
956 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim:
957 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim))
958 if not all(isinstance(n, int) for n in dic['R']):
959 raise TypeError('\n\n"R" must be a tuple of integers.\n')
960 if dic['i'] == dic['j'] and dic['R'] == (0,) * ndim:
961 raise ValueError('\n\nUse kspace.set_onsite for i == j and R == 0 '
962 '(it accepts a 2x2 spin matrix when spin=True).\n')
963 if values and callable(dic['t']):
964 continue
965 if spin:
966 spin_matrix(dic['t'], '"t"')
967 elif not isinstance(dic['t'], (int, float, complex)):
968 raise TypeError('\n\n"t" value must be a real or complex number.\n')
971def set_onsite_kspace(dict_onsite, tags, spin=False):
972 '''
973 Check method *kspace.set_onsite*.
975 :raises TypeError: Parameter *dict_onsite* must be a dictionary.
976 :raises ValueError: keys must be tags.
977 :raises TypeError: values must be real or complex numbers (or, if
978 *spin*, a pair of real/complex numbers, or a 2x2 matrix).
979 '''
980 if not isinstance(dict_onsite, dict):
981 raise TypeError('\n\nParameter dict_onsite must be a dictionary.\n')
982 for tag, val in dict_onsite.items():
983 if tag not in tags:
984 raise ValueError('\n\nParameter dict_onsite keys must be a tag.\n')
985 if spin and not isinstance(val, (int, float, complex)):
986 is_pair = (isinstance(val, (tuple, list)) and len(val) == 2
987 and all(isinstance(v, (int, float, complex)) for v in val))
988 is_mat = np.ndim(val) == 2 and np.shape(val) == (2, 2)
989 if not (is_pair or is_mat):
990 raise TypeError('\n\nParameter dict_onsite values must be a number, or, '
991 'if spin, a pair of numbers (E_up, E_down) or a '
992 '2x2 matrix.\n')
993 elif not spin and not isinstance(val, (int, float, complex)):
994 raise TypeError('\n\nParameter dict_onsite values must be real and/or complex numbers.\n')
997def k_path_points(points, ndim):
998 '''
999 Check parameter *points* used by *kspace.k_path*.
1001 :raises TypeError: Parameter points must be a list.
1002 :raises ValueError: Parameter points must contain at least two k-points.
1003 '''
1004 if not isinstance(points, list):
1005 raise TypeError('\n\nParameter points must be a list of k-points.\n')
1006 if len(points) < 2:
1007 raise ValueError('\n\nParameter points must contain at least two k-points.\n')
1008 for i, pt in enumerate(points):
1009 k_vector(pt, 'points[{}]'.format(i), ndim)
1012####################################
1013# DENSITY OF STATES
1014####################################
1017def dos_kernel(kernel):
1018 '''
1019 Check parameter *kernel* used by *dos.density_of_states*.
1021 :raises TypeError: Parameter kernel must be a string.
1022 :raises ValueError: Parameter kernel must be "gaussian" or "lorentzian".
1023 '''
1024 string(kernel, 'kernel')
1025 if kernel not in ['gaussian', 'lorentzian']:
1026 raise ValueError('\n\nParameter kernel must be a string:\n'
1027 '"gaussian", "lorentzian".\n')
1030def kspace_dos_kernel(kernel):
1031 '''
1032 Check parameter *kernel* used by *kspace.plot_dos*.
1034 :raises TypeError: Parameter kernel must be a string.
1035 :raises ValueError: Parameter kernel must be "gaussian", "lorentzian" or "tetrahedron".
1036 '''
1037 string(kernel, 'kernel')
1038 if kernel not in ['gaussian', 'lorentzian', 'tetrahedron']:
1039 raise ValueError('\n\nParameter kernel must be a string:\n'
1040 '"gaussian", "lorentzian", "tetrahedron".\n')
1043def mesh_energies(energies):
1044 '''
1045 Check parameter *energies* used by *dos.tetrahedron_dos*: a non-empty
1046 array of bands on a mesh of 1, 2 or 3 dimensions.
1048 :raises ValueError: Parameter energies must be of shape (n1, ..., nbands), on a mesh of 1 to 3 dimensions.
1049 '''
1050 energies = np.asarray(energies)
1051 if energies.ndim not in (2, 3, 4) or energies.size == 0:
1052 raise ValueError('\n\nParameter energies must be a non-empty array of shape '
1053 '(n1, ..., nbands), on a mesh of 1, 2 or 3 dimensions.\n')
1056def increasing_grid(grid, var_name):
1057 '''
1058 Check that *grid* is a 1D real array of at least two increasing values.
1060 :raises ValueError: Parameter *var_name* must be a 1D array of at least two increasing real values.
1061 '''
1062 grid = np.asarray(grid)
1063 if grid.ndim != 1 or len(grid) < 2 or np.iscomplexobj(grid) or np.any(np.diff(grid) <= 0):
1064 raise ValueError('\n\nParameter {} must be a 1D array of at least two increasing '
1065 'real values.\n'.format(var_name))
1068def nk(nk, ndim):
1069 '''
1070 Check parameter *nk* used by *kspace.mesh_bands* / *kspace.berry_curvature*.
1072 :raises TypeError: Parameter nk must be an integer or a tuple of integers.
1073 :raises ValueError: Parameter nk (or each of its elements) must be a
1074 positive integer.
1075 :raises ValueError: Parameter nk must be a tuple of length *ndim*.
1076 '''
1077 if isinstance(nk, int):
1078 positive_int(nk, 'nk')
1079 return
1080 if not isinstance(nk, tuple):
1081 raise TypeError('\n\nParameter nk must be an integer or a tuple of integers.\n')
1082 if len(nk) != ndim:
1083 raise ValueError('\n\nParameter nk must be of length {}.\n'.format(ndim))
1084 for n in nk:
1085 positive_int(n, 'nk')
1088####################################
1089# TOPOLOGY
1090####################################
1093def dim_2(dim):
1094 '''
1095 Check that the model is 2D. Berry curvature / Chern number are only
1096 defined for a 2D Brillouin zone.
1098 :raises ValueError: This calculation requires a 2D lattice.
1099 '''
1100 if dim != 2:
1101 raise ValueError('\n\nThis calculation requires a 2D lattice '
1102 '(two primitive vectors).\n')
1105def dim_min(dim, dmin):
1106 '''
1107 Check that the model has at least *dmin* periodic dimensions.
1109 :raises ValueError: This calculation requires a lattice with at least dmin primitive vectors.
1110 '''
1111 if dim < dmin:
1112 raise ValueError('\n\nThis calculation requires a lattice with at least {} '
1113 'primitive vectors.\n'.format(dmin))
1116def plane(plane, dim):
1117 '''
1118 Check parameter *plane* of *kspace.berry_curvature*: two distinct
1119 reciprocal-vector indices below *dim*.
1121 :raises TypeError: Parameter plane must be a tuple of two integers.
1122 :raises ValueError: Parameter plane must contain two distinct indices below dim.
1123 '''
1124 if not isinstance(plane, tuple) or len(plane) != 2 or \
1125 not all(isinstance(p, int) for p in plane):
1126 raise TypeError('\n\nParameter plane must be a tuple of two integers.\n')
1127 if plane[0] == plane[1] or not all(0 <= p < dim for p in plane):
1128 raise ValueError('\n\nParameter plane must contain two distinct indices '
1129 'between 0 and {}.\n'.format(dim - 1))
1132def direction(direction, dim=2):
1133 '''
1134 Check parameter *direction* used by *kspace.ribbon* (and the Wilson-loop
1135 methods): a primitive-vector index below *dim*.
1137 :raises TypeError: Parameter direction must be an integer.
1138 :raises ValueError: Parameter direction must be between 0 and dim-1.
1139 '''
1140 if not isinstance(direction, int):
1141 raise TypeError('\n\nParameter direction must be an integer.\n')
1142 if not 0 <= direction < dim:
1143 raise ValueError('\n\nParameter direction must be between 0 and {}.\n'.format(dim - 1))
1146def band_indices(bands, norb):
1147 '''
1148 Check parameter *bands* used by *kspace.berry_curvature*.
1150 :raises TypeError: Parameter bands must be a non-empty list of integers.
1151 :raises ValueError: Parameter bands must be a list of distinct band
1152 indices between 0 and norb-1.
1153 '''
1154 if not isinstance(bands, list) or not bands:
1155 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n')
1156 if not all(isinstance(b, int) for b in bands):
1157 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n')
1158 if len(set(bands)) != len(bands):
1159 raise ValueError('\n\nParameter bands must be a list of distinct band indices.\n')
1160 if not all(0 <= b < norb for b in bands):
1161 raise ValueError('\n\nParameter bands must be integers between 0 and {}.\n'.format(norb-1))
1164####################################
1165# BERRY PHASES, Z2, SYMMETRIES
1166####################################
1169def k_perp(k_perp, n):
1170 '''
1171 Check parameter *k_perp* of the Wilson-loop methods: a tuple of *n* real
1172 numbers (fractional coordinates along the other reciprocal vectors).
1174 :raises ValueError: Parameter k_perp must contain n real numbers.
1175 '''
1176 if len(k_perp) != n or not all(isinstance(f, (int, float)) for f in k_perp):
1177 raise ValueError('\n\nParameter k_perp must contain {} real number(s).\n'.format(n))
1180def flow(flow, direction, dim):
1181 '''
1182 Check parameter *flow* of *kspace.wannier_flow*.
1184 :raises TypeError: Parameter flow must be an integer.
1185 :raises ValueError: Parameter flow must be a direction other than *direction*.
1186 '''
1187 if not isinstance(flow, int):
1188 raise TypeError('\n\nParameter flow must be an integer.\n')
1189 if flow == direction or not 0 <= flow < dim:
1190 raise ValueError('\n\nParameter flow must be a direction between 0 and {}, '
1191 'other than direction.\n'.format(dim - 1))
1194def even_bands(bands):
1195 '''
1196 Check that *bands* is a list of an even number of band indices (Kramers pairs).
1198 :raises TypeError: Parameter bands must be a list.
1199 :raises ValueError: Parameter bands must contain an even number of bands.
1200 '''
1201 if not isinstance(bands, list):
1202 raise TypeError('\n\nParameter bands must be a list of band indices.\n')
1203 if len(bands) % 2:
1204 raise ValueError('\n\nParameter bands must contain an even number of bands '
1205 '(Kramers pairs).\n')
1208def operator(mat, norb):
1209 '''
1210 Check a symmetry operator: a (norb, norb) matrix.
1212 :raises ValueError: The operator must be a (norb, norb) matrix.
1213 '''
1214 if mat.shape != (norb, norb):
1215 raise ValueError('\n\nThe operator must be a ({0}, {0}) matrix.\n'.format(norb))
1218def parities(xi, tol=1e-6):
1219 '''
1220 Check that inversion eigenvalues are +-1.
1222 :raises ValueError: The bands are not an inversion-invariant subspace.
1223 '''
1224 if not np.allclose(np.abs(xi.real), 1., atol=tol) or not np.allclose(xi.imag, 0., atol=tol):
1225 raise ValueError('\n\nThe parity eigenvalues are not +-1: the operator is not '
1226 'an inversion symmetry of these bands (or the bands are '
1227 'degenerate with the others at a TRIM).\n')
1228 if np.sum(xi.real < 0) % 2:
1229 raise ValueError('\n\nOdd number of negative parities: the bands are not '
1230 'Kramers pairs.\n')
1233def k_map(k_map):
1234 '''
1235 Check parameter *k_map* of *kspace.symmetry_error*.
1237 :raises ValueError: Parameter k_map must be 'minus' or 'identity'.
1238 '''
1239 if k_map not in ('minus', 'identity'):
1240 raise ValueError('\n\nParameter k_map must be "minus" or "identity".\n')
1243def is_symmetry(err, tol):
1244 '''
1245 Check that a symmetry error is below *tol*.
1247 :raises ValueError: The operator is not a symmetry of the model.
1248 '''
1249 if err > tol:
1250 raise ValueError('\n\nThe operator is not a symmetry of the model '
1251 '(deviation {:.3g}).\n'.format(err))
1254def square(sq, tol):
1255 '''
1256 Check that the square of an antiunitary operator is +-1.
1258 :raises ValueError: The square of the operator must be +1 or -1.
1259 '''
1260 if not np.allclose(sq, sq[0, 0] * np.eye(len(sq)), atol=tol) or \
1261 not np.isclose(abs(sq[0, 0]), 1., atol=tol):
1262 raise ValueError('\n\nThe square of the antiunitary operator must be +1 or -1.\n')
1265def hermitian(ham):
1266 '''
1267 Check that a (sparse) Hamiltonian is Hermitian.
1269 :raises ValueError: This calculation requires a Hermitian Hamiltonian.
1270 '''
1271 if (abs(ham - ham.conj().T) > 1e-12).nnz:
1272 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n')
1275def planar_cell(prim_vec):
1276 '''
1277 Check that the lattice has two in-plane primitive vectors (a unit-cell area).
1279 :raises ValueError: Give the area per site explicitly.
1280 '''
1281 if len(prim_vec) != 2 or len(prim_vec[0]) != 2:
1282 raise ValueError('\n\nThe unit-cell area needs two in-plane primitive vectors: '
1283 'give the area per site explicitly.\n')
1286def integer(var, var_name):
1287 '''
1288 Check if *var* is an integer.
1290 :raises TypeError: Parameter *var* must be an integer.
1291 '''
1292 if not isinstance(var, int):
1293 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
1296def dim_exact(dim, d):
1297 '''
1298 Check that the model has exactly *d* periodic dimensions.
1300 :raises ValueError: This calculation requires a lattice with d primitive vectors.
1301 '''
1302 if dim != d:
1303 raise ValueError('\n\nThis calculation requires a lattice with {} primitive '
1304 'vector(s).\n'.format(d))
1307def nonzero_number(var, var_name):
1308 '''
1309 Check if *var* is a nonzero number.
1311 :raises TypeError: Parameter *var* must be a number.
1312 :raises ValueError: Parameter *var* must be nonzero.
1313 '''
1314 number(var, var_name)
1315 if var == 0:
1316 raise ValueError('\n\nParameter {} must be nonzero.\n'.format(var_name))
1319def gbz_roots(n_roots, p):
1320 '''
1321 Check that det[H(beta) - E] has roots on both sides of the GBZ.
1323 :raises ValueError: The model has no hoppings in both directions.
1324 '''
1325 if p < 1 or n_roots <= p:
1326 raise ValueError('\n\nThe generalized Brillouin zone needs hoppings in both '
1327 'directions along the chain.\n')
1330####################################
1331# EXCEPTIONAL POINTS
1332####################################
1335def ham_model(model, cls):
1336 '''
1337 Check the model of *tbkit.exceptional*: a 2D **KSpace** instance, or a
1338 callable of a point of a 2D parameter plane.
1340 :raises TypeError: Parameter model must be a KSpace instance or a callable.
1341 :raises ValueError: A KSpace model must have two primitive vectors.
1342 '''
1343 if isinstance(model, cls):
1344 if model.dim != 2:
1345 raise ValueError('\n\nParameter model must be a KSpace with two primitive '
1346 'vectors (a 2D Brillouin zone).\n')
1347 elif not callable(model):
1348 raise TypeError('\n\nParameter model must be a KSpace instance, or a callable '
1349 'returning a square matrix.\n')
1352def ham_matrix(ham):
1353 '''
1354 Check the matrix returned by the callable model of *tbkit.exceptional*:
1355 square and finite.
1357 :raises ValueError: The model must return a finite square matrix.
1358 '''
1359 if ham.ndim != 2 or ham.shape[0] != ham.shape[1] or not np.all(np.isfinite(ham)):
1360 raise ValueError('\n\nThe model must return a finite square matrix.\n')
1363def loop(loop):
1364 '''
1365 Check a closed loop of *tbkit.exceptional*: a callable of s in [0, 1]
1366 returning a point of the plane, or the vertices of a polygon.
1368 :raises TypeError: Parameter loop must be a callable or an array of vertices.
1369 :raises ValueError: A polygon needs at least 3 vertices with 2 real coordinates.
1370 '''
1371 if callable(loop):
1372 return
1373 try:
1374 arr = np.asarray(loop, dtype='f8')
1375 except (TypeError, ValueError):
1376 arr = np.zeros(())
1377 if arr.ndim == 0:
1378 raise TypeError('\n\nParameter loop must be a callable of s in [0, 1], or an '
1379 'array of polygon vertices.\n')
1380 if arr.ndim != 2 or arr.shape[0] < 3 or arr.shape[1] != 2 or not np.all(np.isfinite(arr)):
1381 raise ValueError('\n\nParameter loop must hold at least 3 vertices of 2 real '
1382 'coordinates, shape (m, 2).\n')
1385def loop_point(point):
1386 '''
1387 Check a point returned by a loop callable: 2 finite real numbers.
1389 :raises ValueError: The loop must return points of 2 real coordinates.
1390 '''
1391 if point.shape != (2,) or not np.all(np.isfinite(point)):
1392 raise ValueError('\n\nParameter loop must return points of 2 finite real '
1393 'coordinates.\n')
1396def closed_loop(ham_start, ham_end):
1397 '''
1398 Check that a loop is closed: the Hamiltonian at its end is the one at its start.
1400 :raises ValueError: The loop is not closed.
1401 '''
1402 scale = max(1., float(np.max(np.abs(ham_start))))
1403 if np.max(np.abs(ham_end - ham_start)) > 1e-9 * scale:
1404 raise ValueError('\n\nParameter loop must be closed: H(loop(1)) differs from '
1405 'H(loop(0)).\n')
1408def band_pair(bands, norb):
1409 '''
1410 Check a pair of band labels: two distinct integers between 0 and norb-1.
1412 :raises TypeError: Parameter bands must be a pair of integers.
1413 :raises ValueError: Parameter bands must be two distinct band indices.
1414 '''
1415 if not isinstance(bands, (tuple, list)) or len(bands) != 2 or \
1416 not all(isinstance(b, int) for b in bands):
1417 raise TypeError('\n\nParameter bands must be a pair of integers (m, n).\n')
1418 if bands[0] == bands[1] or not all(0 <= b < norb for b in bands):
1419 raise ValueError('\n\nParameter bands must be two distinct band indices between 0 '
1420 'and {}.\n'.format(norb - 1))
1423def band_index(band, norb):
1424 '''
1425 Check a band label: an integer between 0 and norb-1.
1427 :raises TypeError: Parameter band must be an integer.
1428 :raises ValueError: Parameter band must be between 0 and norb-1.
1429 '''
1430 integer(band, 'band')
1431 if not 0 <= band < norb:
1432 raise ValueError('\n\nParameter band must be between 0 and {}.\n'.format(norb - 1))
1435def tracking(k):
1436 '''
1437 Eigenvalue continuation failed: the path meets a degeneracy.
1439 :raises ValueError: Cannot follow the eigenvalues continuously near k.
1440 '''
1441 raise ValueError('\n\nCannot follow the eigenvalues continuously near k = {}: the path '
1442 'passes through (or too close to) a degeneracy. Move or '
1443 'enlarge the loop.\n'.format(np.round(k, 8)))
1446def winding_path(k):
1447 '''
1448 The discriminant vanishes on the path of a winding number.
1450 :raises ValueError: The loop passes through a zero of the discriminant.
1451 '''
1452 raise ValueError('\n\nThe discriminant vanishes (to numerical precision) on the path, '
1453 'near k = {}: the loop passes through a degeneracy. Move the '
1454 'loop, or change nk.\n'.format(np.round(k, 8)))
1457def discriminant_nonzero(scale):
1458 '''
1459 Check that the discriminant does not vanish identically.
1461 :raises ValueError: The bands are degenerate everywhere.
1462 '''
1463 if scale == 0.:
1464 raise ValueError('\n\nThe discriminant vanishes everywhere: some bands are '
1465 'degenerate over the whole zone.\n')
1468def gap_kind(gap):
1469 '''
1470 Check the kind of line gap: 'real' (Re E) or 'imaginary' (Im E).
1472 :raises ValueError: Parameter gap must be "real" or "imaginary".
1473 '''
1474 if gap not in ('real', 'imaginary'):
1475 raise ValueError('\n\nParameter gap must be "real" or "imaginary".\n')
1478def biorthogonal_kind(kind):
1479 '''
1480 Check the kind of Berry connection: 'LR', 'RL', 'RR' or 'LL'.
1482 :raises ValueError: Parameter kind must be "LR", "RL", "RR" or "LL".
1483 '''
1484 if kind not in ('LR', 'RL', 'RR', 'LL'):
1485 raise ValueError('\n\nParameter kind must be "LR", "RL", "RR" or "LL".\n')
1488def line_gap(keys, bands, gap):
1489 '''
1490 Check that *bands* are separated from the other bands by a line gap:
1491 with the bands ordered by *keys* (Re E or Im E, shape (nk, norb)) at
1492 every k-point, a band inside the group and its neighbour outside it
1493 never overlap over the whole mesh.
1495 :raises ValueError: The bands have no line gap.
1496 '''
1497 inside = np.zeros(keys.shape[1], bool)
1498 inside[bands] = True
1499 for n in range(keys.shape[1] - 1):
1500 if inside[n] != inside[n + 1] and np.max(keys[:, n]) >= np.min(keys[:, n + 1]):
1501 part = 'Re E' if gap == 'real' else 'Im E'
1502 raise ValueError('\n\nThe bands {} are not separated from the others by a {} line '
1503 'gap: sorted by {}, band {} reaches band {} over the '
1504 'Brillouin zone, so the band labels are not continuous. '
1505 'For a non-Hermitian model, use '
1506 'KSpace.biorthogonal_chern_number with the other kind of '
1507 'gap, or locate the gap-closing exceptional points with '
1508 'tbkit.exceptional.find_exceptional_points.\n'
1509 .format(bands, gap, part, n, n + 1))
1512def band_continuity(en, bands):
1513 '''
1514 Check that a group of bands, labelled at every point of a periodic k
1515 mesh (*en*, complex, shape (n1, n2, norb)), is continuous: matching the
1516 eigenvalues of neighbouring points by distance maps the group onto
1517 itself. A line gap measured on the mesh alone can miss a narrow
1518 crossing between mesh points; this catches the jump of the labels.
1520 :raises ValueError: The band labels jump between neighbouring k-points.
1521 '''
1522 from scipy.optimize import linear_sum_assignment
1523 inside = np.zeros(en.shape[2], bool)
1524 inside[bands] = True
1525 for axis in (0, 1):
1526 nxt = np.roll(en, -1, axis=axis)
1527 for e_a, e_b in zip(en.reshape(-1, en.shape[2]), nxt.reshape(-1, en.shape[2])):
1528 _, perm = linear_sum_assignment(np.abs(e_a[:, None] - e_b[None, :]))
1529 if np.any(inside != inside[perm]):
1530 raise ValueError('\n\nThe bands {} are not separated from the others over the '
1531 'Brillouin zone: between neighbouring k-points they '
1532 'exchange eigenvalues with the other bands (the line gap '
1533 'closes, e.g. at exceptional points, or nk is too small). '
1534 'For a non-Hermitian model, try '
1535 'KSpace.biorthogonal_chern_number with the other kind of '
1536 'gap, or tbkit.exceptional.find_exceptional_points.\n'
1537 .format(bands))
1540def no_overlap(overlap):
1541 '''
1542 Check that no overlap matrix is set (non-orthogonal bases are not
1543 supported by the biorthogonal tools).
1545 :raises ValueError: Not available with an overlap matrix.
1546 '''
1547 if overlap:
1548 raise ValueError('\n\nThis method does not support an overlap matrix (set_overlap).\n')
1551def weights(weights, n):
1552 '''
1553 Check the weights of *occupation.fermi_level*: *n* positive numbers.
1555 :raises ValueError: Parameter weights must contain n positive numbers.
1556 '''
1557 if len(weights) != n or np.any(weights <= 0):
1558 raise ValueError('\n\nParameter weights must contain {} positive numbers.\n'.format(n))
1561def electrons(n_electrons, capacity):
1562 '''
1563 Check a number of electrons: between 0 and the total weight of the levels.
1565 :raises TypeError: Parameter n_electrons must be a real number.
1566 :raises ValueError: Parameter n_electrons must be between 0 and capacity.
1567 '''
1568 positive_real_zero(n_electrons, 'n_electrons')
1569 if n_electrons > capacity * (1 + 1e-12):
1570 raise ValueError('\n\nParameter n_electrons must be between 0 and {:g} '
1571 '(the number of states).\n'.format(capacity))
1574def n_eig(n_eig, sites):
1575 '''
1576 Check the number of eigenpairs of *system.get_eig_sparse*.
1578 :raises TypeError: Parameter n_eig must be an integer.
1579 :raises ValueError: Parameter n_eig must be between 1 and sites-2.
1580 '''
1581 positive_int(n_eig, 'n_eig')
1582 if n_eig > sites - 2:
1583 raise ValueError('\n\nParameter n_eig must be between 1 and {} (use get_eig for '
1584 'the full spectrum).\n'.format(sites - 2))
1587####################################
1588# TRANSPORT
1589####################################
1592def square_matrix(mat, var_name):
1593 '''
1594 Check that *mat* is a square 2D array.
1596 :raises ValueError: Parameter must be a square matrix.
1597 '''
1598 if mat.ndim != 2 or mat.shape[0] != mat.shape[1]:
1599 raise ValueError('\n\nParameter {} must be a square matrix.\n'.format(var_name))
1602def lead(h0, v):
1603 '''
1604 Check a lead: square cell Hamiltonian *h0* and coupling *v* of the same shape.
1606 :raises ValueError: h0 and v must be square matrices of the same shape.
1607 '''
1608 square_matrix(h0, 'h0')
1609 if v.shape != h0.shape:
1610 raise ValueError('\n\nParameters h0 and v must be square matrices of the same shape.\n')
1613def converged(flag, name):
1614 '''
1615 Check that an iteration converged.
1617 :raises RuntimeError: The iteration did not converge.
1618 '''
1619 if not flag:
1620 raise RuntimeError('\n\n{} did not converge: increase max_iter.\n'.format(name))
1623def lead_direction(direction):
1624 '''
1625 Check the direction of a lead: +1 or -1.
1627 :raises ValueError: Parameter direction must be +1 or -1.
1628 '''
1629 if direction not in (1, -1):
1630 raise ValueError('\n\nParameter direction must be +1 or -1.\n')
1633def surface_side(side):
1634 '''
1635 Check parameter *side* of *KSpace.surface_spectral_function*: +1 or -1.
1637 :raises ValueError: Parameter side must be +1 or -1.
1638 '''
1639 if isinstance(side, bool) or side not in (1, -1):
1640 raise ValueError('\n\nParameter side must be +1 or -1.\n')
1643def nearest_cells(n):
1644 '''
1645 Check that a lead hopping reaches at most the neighbouring cells.
1647 :raises ValueError: Lead hoppings must connect neighbouring cells only.
1648 '''
1649 if abs(n) > 1:
1650 raise ValueError('\n\nA lead from a KSpace model needs hoppings between '
1651 'neighbouring cells only (use a larger unit cell).\n')
1654def lead_coupling(coupling, sites, m, n_device):
1655 '''
1656 Check the coupling of a lead to the device.
1658 :raises TypeError: Parameter sites must be a list of integers.
1659 :raises ValueError: sites must be distinct device indices, coupling of shape (len(sites), m).
1660 '''
1661 if not isinstance(sites, list) or not all(isinstance(s, (int, np.integer)) for s in sites):
1662 raise TypeError('\n\nParameter sites must be a list of integers.\n')
1663 if len(set(sites)) != len(sites) or not all(0 <= s < n_device for s in sites):
1664 raise ValueError('\n\nParameter sites must contain distinct device indices '
1665 'between 0 and {}.\n'.format(n_device - 1))
1666 if coupling.shape != (len(sites), m):
1667 raise ValueError('\n\nParameter coupling must be of shape (len(sites), len(h0)) '
1668 '= ({}, {}).\n'.format(len(sites), m))
1671def lead_device_size(sites, n_device):
1672 '''
1673 Check that a System has one site per row of the device Hamiltonian.
1675 :raises ValueError: The System must have as many sites as the device Hamiltonian has rows.
1676 '''
1677 if sites != n_device:
1678 raise ValueError('\n\nThe System has {} sites but the device Hamiltonian has {} '
1679 'rows.\n'.format(sites, n_device))
1682def lead_space_dim(lead_dim, device_dim):
1683 '''
1684 Check that a lead and the device have positions of the same dimension.
1686 :raises ValueError: The lead and the device must have the same space dimension.
1687 '''
1688 if lead_dim != device_dim:
1689 raise ValueError('\n\nThe lead has {}D positions but the device {}D '
1690 'ones.\n'.format(lead_dim, device_dim))
1693def lead_overlap(found):
1694 '''
1695 Check that some device site lies on the lattice of a lead.
1697 :raises ValueError: No device site matches a lead orbital.
1698 '''
1699 if not found:
1700 raise ValueError('\n\nNo device site sits at the position (and tag) of a lead '
1701 'orbital: place the lead unit cell in the coordinates of the device.\n')
1704def lead_interface(missing):
1705 '''
1706 Check that the interface cell of a lead is complete (*missing*: the lead
1707 orbitals coupled to the next cell but absent from the device).
1709 :raises ValueError: The interface between the device and the lead is incomplete.
1710 '''
1711 if missing:
1712 raise ValueError('\n\nThe device lacks lead orbitals {} in its outermost lead cell: '
1713 'extend the device so that it ends with a complete lead '
1714 'cell (or pass extend=True).\n'.format([int(o) for o in missing]))
1717def lead_extension(inside):
1718 '''
1719 Check that the lead sites added to complete an interface stay within the
1720 device's extent along the lead (*inside* False when they would pass the
1721 innermost lead cell holding a device site).
1723 :raises ValueError: The lead cannot be closed against the device.
1724 '''
1725 if not inside:
1726 raise ValueError('\n\nThe lead sites missing from the device reach past its far end: '
1727 'the lead is not closed off by the device (wider than it, or '
1728 'off its edge).\n')
1731def lead_index(lead, n_leads):
1732 '''
1733 Check a lead index.
1735 :raises TypeError: The lead index must be an integer.
1736 :raises ValueError: No such lead.
1737 '''
1738 if not isinstance(lead, int):
1739 raise TypeError('\n\nThe lead index must be an integer.\n')
1740 if not 0 <= lead < n_leads:
1741 raise ValueError('\n\nNo lead {} ({} lead(s) attached with add_lead).\n'.format(lead, n_leads))
1744def lead_band_edge(flag, energy):
1745 '''
1746 Check that the modes of a lead are well defined: the energy is not at a
1747 band edge, where a propagating mode has zero velocity.
1749 :raises ValueError: Energy at a band edge of a lead.
1750 '''
1751 if not flag:
1752 raise ValueError('\n\nEnergy {} is at a band edge of a lead (a mode with zero velocity): '
1753 'shift it slightly.\n'.format(energy))
1756def conservation_law(law, h0, v, tol=1e-10):
1757 '''
1758 Check the conservation law of a lead: a Hermitian matrix of the shape
1759 of *h0* that commutes with *h0* and *v*.
1761 :raises ValueError: Wrong shape, not Hermitian, or not conserved by the lead.
1762 '''
1763 if law.shape != h0.shape:
1764 raise ValueError('\n\nParameter conservation_law must have the shape of h0, {}.\n'.format(h0.shape))
1765 hermitian_operator(law, tol)
1766 scale = max(1., np.max(np.abs(h0)), np.max(np.abs(v)))
1767 if max(np.max(np.abs(law @ h0 - h0 @ law)), np.max(np.abs(law @ v - v @ law))) > tol * scale:
1768 raise ValueError('\n\nThe conservation law must commute with the lead\'s h0 and v.\n')
1771def lead_block(lead, n_blocks):
1772 '''
1773 Check a lead of a scattering matrix: a lead index, or a tuple
1774 (lead, block) with a block of that lead's conservation law.
1776 :raises TypeError: Not an integer or a tuple of two integers.
1777 :raises ValueError: No such lead or block.
1778 '''
1779 if isinstance(lead, tuple):
1780 if len(lead) != 2 or not all(isinstance(x, (int, np.integer)) for x in lead):
1781 raise TypeError('\n\nA lead block must be a tuple (lead, block) of two integers.\n')
1782 lead_index(int(lead[0]), len(n_blocks))
1783 if not 0 <= lead[1] < n_blocks[lead[0]]:
1784 raise ValueError('\n\nLead {} has {} block(s) (its conservation law).\n'.format(
1785 lead[0], n_blocks[lead[0]]))
1786 else:
1787 lead_index(lead, len(n_blocks))
1791####################################
1792# KERNEL POLYNOMIAL METHOD
1793####################################
1796def kpm_kernel(kernel):
1797 '''
1798 Check the KPM kernel name.
1800 :raises ValueError: Parameter kernel must be "jackson" or "lorentz".
1801 '''
1802 if kernel not in ('jackson', 'lorentz'):
1803 raise ValueError('\n\nParameter kernel must be "jackson" or "lorentz".\n')
1806def kpm_grid(eps):
1807 '''
1808 Check that the KPM energies lie inside the (scaled) spectral bounds.
1810 :raises ValueError: e_grid must lie inside the spectral bounds.
1811 '''
1812 if np.any(np.abs(eps) >= 1.):
1813 raise ValueError('\n\nParameter e_grid must lie strictly inside the spectral '
1814 'bounds.\n')
1817def site_index(site, n):
1818 '''
1819 Check a site (row) index.
1821 :raises TypeError: Parameter site must be an integer.
1822 :raises ValueError: Parameter site must be between 0 and n-1.
1823 '''
1824 if not isinstance(site, (int, np.integer)):
1825 raise TypeError('\n\nParameter site must be an integer.\n')
1826 if not 0 <= site < n:
1827 raise ValueError('\n\nParameter site must be between 0 and {}.\n'.format(n - 1))
1830def max_order(list_hop):
1831 '''
1832 Largest neighbour order 'n' requested by a (possibly malformed) list of
1833 hoppings; 1 if there is none (the list is validated afterwards).
1834 '''
1835 try:
1836 orders = [dic['n'] for dic in list_hop if isinstance(dic['n'], int)]
1837 except (TypeError, KeyError):
1838 return 1
1839 return max([n for n in orders if n > 0] + [1])
1842####################################
1843# ORBITALS, SLATER-KOSTER, SPIN
1844####################################
1847def orbitals(orbs):
1848 '''
1849 Check a list of orbital names.
1851 :raises TypeError: The orbitals must be a list of names.
1852 :raises ValueError: Unknown or repeated orbital.
1853 '''
1854 from tbkit.slater_koster import ORBITALS
1855 if not isinstance(orbs, list) or not orbs:
1856 raise TypeError('\n\nThe orbitals must be a non-empty list of names.\n')
1857 for o in orbs:
1858 if o not in ORBITALS:
1859 raise ValueError('\n\nUnknown orbital {!r}: use one of {}.\n'.format(o, list(ORBITALS)))
1860 if len(set(orbs)) != len(orbs):
1861 raise ValueError('\n\nRepeated orbital in {}.\n'.format(orbs))
1864def orbital_dict(orbital_dict, tags):
1865 '''
1866 Check the orbitals of *OrbitalSystem*: a list of orbitals for every tag.
1868 :raises TypeError: Parameter orbitals must be a dictionary.
1869 :raises ValueError: Every tag needs its orbitals.
1870 '''
1871 if not isinstance(orbital_dict, dict):
1872 raise TypeError('\n\nParameter orbitals must be a dictionary {tag: list of orbitals}.\n')
1873 for tag in tags:
1874 if tag not in orbital_dict:
1875 raise ValueError('\n\nParameter orbitals has no entry for tag {!r}.\n'.format(tag))
1876 for orbs in orbital_dict.values():
1877 orbitals(orbs)
1880def sk_params(params):
1881 '''
1882 Check a dictionary of Slater-Koster bond integrals.
1884 :raises TypeError: Parameter params must be a dictionary of numbers.
1885 :raises ValueError: Unknown bond integral.
1886 '''
1887 from tbkit.slater_koster import PARAMS
1888 if not isinstance(params, dict):
1889 raise TypeError('\n\nThe bond integrals must be a dictionary.\n')
1890 for key, val in params.items():
1891 if key not in PARAMS:
1892 raise ValueError('\n\nUnknown bond integral {!r}: use {}.\n'.format(key, PARAMS))
1893 if not isinstance(val, (int, float)):
1894 raise TypeError('\n\nBond integral {!r} must be a real number.\n'.format(key))
1897def sk_pair_params(params, tags_i, tags_j):
1898 '''
1899 Check the bond integrals of *set_slater_koster*: one dictionary, or one
1900 per tag pair of the bonds.
1902 :returns: True if keyed by tag pair.
1903 :raises ValueError: A tag pair of the bonds has no bond integrals.
1904 '''
1905 from tbkit.slater_koster import PARAMS
1906 if not isinstance(params, dict):
1907 raise TypeError('\n\nThe bond integrals must be a dictionary.\n')
1908 by_pair = bool(params) and all(k not in PARAMS for k in params)
1909 if not by_pair:
1910 sk_params(params)
1911 return False
1912 for a, b in set(zip(tags_i, tags_j)):
1913 if a + b not in params:
1914 raise ValueError('\n\nNo bond integrals for the tag pair {!r}.\n'.format(a + b))
1915 sk_params(params[a + b])
1916 return True
1919def bond_vector(d):
1920 '''
1921 Check a bond vector: 2 or 3 reals, nonzero.
1923 :raises ValueError: The bond vector must have 2 or 3 components and be nonzero.
1924 '''
1925 if d.ndim != 1 or len(d) not in (2, 3) or not np.any(d):
1926 raise ValueError('\n\nThe bond vector must have 2 or 3 components, not all zero.\n')
1929def orbital_of_site(orbital, orbs):
1930 '''
1931 Check that a site carries an orbital.
1933 :raises ValueError: The site has no such orbital.
1934 '''
1935 if orbital not in orbs:
1936 raise ValueError('\n\nThe site has no orbital {!r} (it has {}).\n'.format(orbital, orbs))
1939def spin_index(spin, ns):
1940 '''
1941 Check a spin index: 0, or 0/1 with spin.
1943 :raises ValueError: Parameter spin must be 0 (or 1 with spin).
1944 '''
1945 if spin not in range(ns):
1946 raise ValueError('\n\nParameter spin must be {}.\n'.format('0 or 1' if ns == 2 else '0'))
1949def set_onsite_orb(dict_onsite, tags, orbital_dict):
1950 '''
1951 Check *OrbitalSystem.set_onsite*: numbers, or {orbital: number} dictionaries.
1953 :raises TypeError: Onsite energies must be numbers or dictionaries.
1954 :raises ValueError: Unknown tag or orbital.
1955 '''
1956 if not isinstance(dict_onsite, dict):
1957 raise TypeError('\n\nParameter onsite must be a dictionary.\n')
1958 for tag, val in dict_onsite.items():
1959 if tag not in tags:
1960 raise ValueError('\n\nParameter onsite keys must be a tag.\n')
1961 if isinstance(val, dict):
1962 for o, e in val.items():
1963 orbital_of_site(o, orbital_dict[tag])
1964 if not isinstance(e, (int, float, complex)):
1965 raise TypeError('\n\nOnsite energies must be numbers.\n')
1966 elif not isinstance(val, (int, float, complex)):
1967 raise TypeError('\n\nOnsite energies must be numbers or {orbital: number} dictionaries.\n')
1970def spinful(spin):
1971 '''
1972 Check that the model has spin.
1974 :raises ValueError: This term needs spin=True.
1975 '''
1976 if not spin:
1977 raise ValueError('\n\nThis term needs a spinful model (spin=True).\n')
1980def vector3(vec, var_name):
1981 '''
1982 Check a tuple of three real numbers.
1984 :raises ValueError: Parameter must be a tuple of three real numbers.
1985 '''
1986 if not isinstance(vec, (tuple, list)) or len(vec) != 3 or \
1987 not all(isinstance(c, (int, float)) for c in vec):
1988 raise ValueError('\n\nParameter {} must be a tuple of three real numbers.\n'.format(var_name))
1991def p_shell(tags, orbital_dict):
1992 '''
1993 Check that the tags carry the three p orbitals.
1995 :raises ValueError: The tags must carry px, py and pz.
1996 '''
1997 if not isinstance(tags, list) or not tags:
1998 raise ValueError('\n\nParameter tags must be a non-empty list of tags with p orbitals.\n')
1999 for tag in tags:
2000 if tag not in orbital_dict or not {'px', 'py', 'pz'} <= set(orbital_dict[tag]):
2001 raise ValueError('\n\nTag {!r} must carry the orbitals px, py and pz.\n'.format(tag))
2004def block_shape(mat, shape):
2005 '''
2006 Check the shape of a hopping block.
2008 :raises ValueError: The block must have the given shape.
2009 '''
2010 if mat.shape != shape:
2011 raise ValueError('\n\nThe hopping block must have shape {}.\n'.format(shape))
2014def orbital_terms(hop, blocks):
2015 '''
2016 Check that an *OrbitalSystem* has hoppings.
2018 :raises RuntimeError: Run set_hopping or set_slater_koster first.
2019 '''
2020 if not hop.size and not any(blocks.values()):
2021 raise RuntimeError('\n\nRun method set_hopping or set_slater_koster first.\n')
2024def no_left(left):
2025 '''
2026 Check that left eigenvectors are not asked for with an overlap matrix.
2028 :raises ValueError: left=True needs an orthogonal basis.
2029 '''
2030 if left:
2031 raise ValueError('\n\nleft=True needs an orthogonal basis (no overlap).\n')
2034def orthogonal(overlap):
2035 '''
2036 Check that the basis is orthogonal.
2038 :raises ValueError: This calculation needs an orthogonal basis.
2039 '''
2040 if overlap is not None:
2041 raise ValueError('\n\nThis calculation needs an orthogonal basis (no overlap matrix).\n')
2044####################################
2045# MEAN FIELD, SUPERCONDUCTIVITY
2046####################################
2049def hermitian_dense(ham):
2050 '''
2051 Check that a dense matrix is Hermitian.
2053 :raises ValueError: This calculation requires a Hermitian Hamiltonian.
2054 '''
2055 if not np.allclose(ham, ham.conj().T, atol=1e-12):
2056 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n')
2059def integer_electrons(n_electrons):
2060 '''
2061 Check that the number of electrons is an integer (zero temperature).
2063 :raises ValueError: At zero temperature, n_electrons must be an integer.
2064 '''
2065 if abs(n_electrons - round(n_electrons)) > 1e-12:
2066 raise ValueError('\n\nAt zero temperature, n_electrons must be an integer '
2067 '(use a small temperature for a fractional filling).\n')
2070def mixing(mixing):
2071 '''
2072 Check the density mixing: a real in (0, 1].
2074 :raises ValueError: Parameter mixing must be in (0, 1].
2075 '''
2076 positive_real(mixing, 'mixing')
2077 if mixing > 1:
2078 raise ValueError('\n\nParameter mixing must be in (0, 1].\n')
2081def pairing(delta, n):
2082 '''
2083 Check a BdG pairing matrix: (n, n), antisymmetric.
2085 :raises ValueError: The pairing matrix must be (n, n) and antisymmetric.
2086 '''
2087 if delta.shape != (n, n):
2088 raise ValueError('\n\nThe pairing matrix must have the shape ({0}, {0}) of ham.\n'.format(n))
2089 if (abs(delta + delta.T) > 1e-12).nnz:
2090 raise ValueError('\n\nThe pairing matrix must be antisymmetric (Delta^T = -Delta).\n')
2093def hermitian_kspace(hermitian, overlap):
2094 '''
2095 Check that a KSpace model is Hermitian, with an orthogonal basis.
2097 :raises ValueError: The model must be Hermitian, without overlap.
2098 '''
2099 if not hermitian or overlap:
2100 raise ValueError('\n\nThe model must be Hermitian, with an orthogonal basis.\n')
2103def pairing_kspace(pairing, norb, ndim):
2104 '''
2105 Check the pairings of *bdg.bdg_kspace*.
2107 :raises TypeError: Parameter pairing must be a list of dictionaries.
2108 :raises KeyError: "i", "j", "R" and "delta" must be keys.
2109 :raises ValueError: Orbital indices below norb; R a tuple of ndim integers.
2110 '''
2111 if not isinstance(pairing, list):
2112 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n')
2113 for dic in pairing:
2114 if not isinstance(dic, dict):
2115 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n')
2116 if not {'i', 'j', 'R', 'delta'} <= set(dic):
2117 raise KeyError('\n\n"i", "j", "R" and "delta" must be dictionary keys.\n')
2118 if not all(isinstance(dic[k], int) and 0 <= dic[k] < norb for k in ('i', 'j')):
2119 raise ValueError('\n\n"i" and "j" must be orbital indices between 0 and {}.\n'.format(norb - 1))
2120 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim or \
2121 not all(isinstance(n, int) for n in dic['R']):
2122 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim))
2123 number(dic['delta'], '"delta"')
2126####################################
2127# FLOQUET
2128####################################
2131def positive_int_zero(var, var_name):
2132 '''
2133 Check if *var* is a positive integer or zero.
2135 :raises TypeError: Parameter *var* must be an integer.
2136 :raises ValueError: Parameter *var* must be a positive integer or zero.
2137 '''
2138 if not isinstance(var, int):
2139 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name))
2140 if var < 0:
2141 raise ValueError('\n\nParameter {} must be a positive integer or zero.\n'.format(var_name))
2144def harmonics(harm):
2145 '''
2146 Check the harmonics of *floquet.sambe_hamiltonian*: a dictionary with (at least) m = 0.
2148 :raises TypeError: The harmonics must be a dictionary {m: matrix}.
2149 :raises ValueError: The harmonics must contain m = 0 and square matrices of one shape.
2150 '''
2151 if not isinstance(harm, dict):
2152 raise TypeError('\n\nThe harmonics must be a dictionary {m: matrix}.\n')
2153 if 0 not in harm:
2154 raise ValueError('\n\nThe harmonics must contain the static part, m = 0.\n')
2155 shapes = {np.shape(h) for h in harm.values()}
2156 if len(shapes) != 1 or len(shapes.pop()) != 2:
2157 raise ValueError('\n\nThe harmonics must be square matrices of one shape.\n')
2160def kspace(ks, cls):
2161 '''
2162 Check that *ks* is a KSpace instance.
2164 :raises TypeError: Parameter ks must be a KSpace instance.
2165 '''
2166 if not isinstance(ks, cls):
2167 raise TypeError('\n\nParameter ks must be a KSpace instance.\n')
2170def not_floquet():
2171 '''
2172 :raises ValueError: Not available for a Floquet (driven) model.
2173 '''
2174 raise ValueError('\n\nThis method needs the static hoppings: it is not available '
2175 'for a Floquet (driven) model.\n')
2178def sk_bonds(bonds):
2179 '''
2180 Check the bonds of *slater_koster.sk_kspace*: {n: bond integrals}.
2182 :raises TypeError: Parameter bonds must be a dictionary {n: bond integrals}.
2183 '''
2184 if not isinstance(bonds, dict) or not all(isinstance(n, int) and n > 0 for n in bonds):
2185 raise TypeError('\n\nParameter bonds must be a dictionary {n: bond integrals}, '
2186 'n a positive integer.\n')
2187 for params in bonds.values():
2188 sk_params(params)
2191####################################
2192# HALL CONDUCTIVITIES
2193####################################
2196def fermi_energies(e_fermi):
2197 '''
2198 Check the Fermi energies of the Hall conductivities: a real number, or a
2199 non-empty array of finite real numbers.
2201 :raises TypeError: Parameter e_fermi must be a real number or an array of real numbers.
2202 :raises ValueError: Parameter e_fermi must be non-empty and finite.
2203 '''
2204 arr = np.asarray(e_fermi)
2205 if arr.dtype.kind not in 'iuf':
2206 raise TypeError('\n\nParameter e_fermi must be a real number or an array '
2207 'of real numbers.\n')
2208 if arr.size == 0 or not np.all(np.isfinite(arr)):
2209 raise ValueError('\n\nParameter e_fermi must contain at least one finite number.\n')
2212def hermitian_model(hermitian):
2213 '''
2214 Check that a KSpace model is Hermitian (a Kubo conductivity needs real
2215 bands and orthonormal eigenstates).
2217 :raises ValueError: This calculation requires a Hermitian model.
2218 '''
2219 if not hermitian:
2220 raise ValueError('\n\nThis calculation requires a Hermitian model (no gain/loss, '
2221 'no hermitian=False hopping).\n')
2224def k_fixed_hall(k_fixed, dim, full_allowed=True):
2225 '''
2226 Check parameter *k_fixed* of the Hall conductivities: None (the whole
2227 Brillouin zone) or a real number (a plane of a 3D model).
2229 :raises TypeError: Parameter k_fixed must be None or a real number.
2230 :raises ValueError: A 3D spin Hall conductivity needs a plane (k_fixed).
2231 '''
2232 if k_fixed is not None:
2233 real_number(k_fixed, 'k_fixed')
2234 elif dim == 3 and not full_allowed:
2235 raise ValueError('\n\nIn 3D, give the plane of the spin Hall conductivity: '
2236 'k_fixed must be a real number.\n')
2239def spin_axis(axis):
2240 '''
2241 Check the spin quantization axis: 'x', 'y' or 'z'.
2243 :raises ValueError: Parameter spin_axis must be "x", "y" or "z".
2244 '''
2245 if axis not in ('x', 'y', 'z'):
2246 raise ValueError('\n\nParameter spin_axis must be "x", "y" or "z".\n')
2249def refine_fraction(fraction):
2250 '''
2251 Check the fraction of mesh points refined by the adaptive mesh: a real in (0, 1].
2253 :raises TypeError: Parameter refine_fraction must be a real number.
2254 :raises ValueError: Parameter refine_fraction must be in (0, 1].
2255 '''
2256 positive_real(fraction, 'refine_fraction')
2257 if fraction > 1:
2258 raise ValueError('\n\nParameter refine_fraction must be in (0, 1].\n')
2261def path_parameter(param):
2262 '''
2263 Check the parameter that a path varies (*KSpace.axion_angle*): a string.
2265 :raises TypeError: Parameter param must be a string (the name of a value-function parameter).
2266 '''
2267 if not isinstance(param, str):
2268 raise TypeError('\n\nParameter param must be a string (the name of a '
2269 'value-function parameter).\n')
2272def path_values(values):
2273 '''
2274 Check the parameter values of a path: a 1D array of at least two finite
2275 real numbers, strictly increasing or strictly decreasing.
2277 :raises TypeError: Parameter values must be a 1D array of real numbers.
2278 :raises ValueError: Parameter values must hold at least two finite, strictly monotonic values.
2279 '''
2280 arr = np.asarray(values)
2281 if arr.ndim != 1 or arr.dtype.kind not in 'iuf':
2282 raise TypeError('\n\nParameter values must be a 1D array of real numbers.\n')
2283 step = np.diff(arr)
2284 if len(arr) < 2 or not np.all(np.isfinite(arr)) or not (np.all(step > 0) or np.all(step < 0)):
2285 raise ValueError('\n\nParameter values must hold at least two finite values, '
2286 'strictly increasing or strictly decreasing.\n')
2289def path_gap(gap, param, value, tol=1e-6):
2290 '''
2291 Check that the bands stay separated from the others along a path.
2293 :raises ValueError: The bands touch the others at param = value.
2294 '''
2295 if gap < tol:
2296 raise ValueError('\n\nThe bands touch the other bands at {} = {} (gap {:.1e}): '
2297 'the path must stay gapped.\n'.format(param, value, gap))
2300def velocity(vel, n, var_name):
2301 '''
2302 Check a velocity operator: a square (sparse or dense) matrix of the
2303 Hamiltonian's shape.
2305 :raises ValueError: The velocity must be a (n, n) matrix.
2306 '''
2307 if getattr(vel, 'shape', None) != (n, n):
2308 raise ValueError('\n\nParameter {} must be a ({}, {}) matrix, like ham.\n'.format(var_name, n, n))
2311def positive_overlap(s_min):
2312 '''
2313 Check that the overlap matrix S(k) is positive definite.
2315 :raises ValueError: The overlap matrix must be positive definite.
2316 '''
2317 if s_min <= 0:
2318 raise ValueError('\n\nThe overlap matrix S(k) must be positive definite '
2319 '(smallest eigenvalue {:.3g}).\n'.format(s_min))
2322####################################
2323# FLOQUET: STEP DRIVES, WINDING NUMBER
2324####################################
2327def branch_cut(epsilon):
2328 '''
2329 Check the branch cut of a Floquet Hamiltonian: None or a real number.
2331 :raises TypeError: Parameter epsilon must be None or a real number.
2332 '''
2333 if epsilon is not None and (isinstance(epsilon, bool)
2334 or not isinstance(epsilon, (int, float, np.integer, np.floating))):
2335 raise TypeError('\n\nParameter epsilon must be None or a real number.\n')
2338def durations(durations, n):
2339 '''
2340 Check the step durations of a step drive: n positive reals.
2342 :raises TypeError: Parameter durations must be a list of real numbers.
2343 :raises ValueError: Parameter durations must hold one positive number per step.
2344 '''
2345 if not isinstance(durations, (list, tuple, np.ndarray)):
2346 raise TypeError('\n\nParameter durations must be a list of real numbers.\n')
2347 if len(durations) != n:
2348 raise ValueError('\n\nParameter durations must hold one duration per step ({}).\n'.format(n))
2349 for d in durations:
2350 if isinstance(d, bool) or not isinstance(d, (int, float, np.integer, np.floating)):
2351 raise TypeError('\n\nParameter durations must be a list of real numbers.\n')
2352 if d <= 0:
2353 raise ValueError('\n\nParameter durations must be positive numbers.\n')
2356def step_models(models, kspace_cls, system_cls):
2357 '''
2358 Check the models of a step drive: a non-empty list, either of KSpace
2359 instances (Hermitian, orthogonal basis, one lattice size) or of System
2360 instances and square matrices. Returns 'kspace' or 'matrix'.
2362 :raises TypeError: Parameter models must be a non-empty list of KSpace, System or matrices.
2363 :raises ValueError: The KSpace models must be Hermitian, with no overlap, of one size.
2364 '''
2365 if not isinstance(models, (list, tuple)) or len(models) == 0:
2366 raise TypeError('\n\nParameter models must be a non-empty list of KSpace instances, '
2367 'or of System instances and matrices.\n')
2368 if all(isinstance(m, kspace_cls) for m in models):
2369 for m in models:
2370 if (m.norb, m.dim) != (models[0].norb, models[0].dim):
2371 raise ValueError('\n\nThe KSpace models must have the same orbitals and dimension.\n')
2372 if not m.is_hermitian():
2373 raise ValueError('\n\nThe KSpace models must be Hermitian.\n')
2374 if m._overlap_hop:
2375 raise ValueError('\n\nThe KSpace models need an orthogonal basis (no overlap).\n')
2376 return 'kspace'
2377 for m in models:
2378 if not isinstance(m, system_cls) and getattr(m, 'ndim', None) != 2:
2379 raise TypeError('\n\nParameter models must be a list of KSpace instances only, '
2380 'or of System instances and square matrices.\n')
2381 return 'matrix'
2384def step_matrices(hams):
2385 '''
2386 Check the Hamiltonians of a real-space step drive: Hermitian square
2387 matrices of one shape.
2389 :raises TypeError: Parameter hams must be a non-empty list of matrices.
2390 :raises ValueError: The matrices must be square, Hermitian and of one shape.
2391 '''
2392 if not isinstance(hams, (list, tuple)) or len(hams) == 0:
2393 raise TypeError('\n\nParameter hams must be a non-empty list of matrices.\n')
2394 shape = np.shape(hams[0])
2395 for h in hams:
2396 h = np.asarray(h)
2397 if h.ndim != 2 or h.shape[0] != h.shape[1] or h.shape != shape:
2398 raise ValueError('\n\nThe step Hamiltonians must be square matrices of one shape.\n')
2399 hermitian_dense(h)
2402def drive_matrix(ham, norb):
2403 '''
2404 Check the output of a Bloch drive ham_kt(k, t): a Hermitian (norb, norb) matrix.
2406 :raises ValueError: ham_kt(k, t) must return a Hermitian (norb, norb) matrix.
2407 '''
2408 if np.shape(ham) != (norb, norb):
2409 raise ValueError('\n\nham_kt(k, t) must return a ({}, {}) matrix.\n'.format(norb, norb))
2410 hermitian_dense(np.asarray(ham))
2413def time_in_period(t, period):
2414 '''
2415 Check a time within the period: None or a real in [0, period].
2417 :raises TypeError: Parameter t must be None or a real number.
2418 :raises ValueError: Parameter t must be in [0, period].
2419 '''
2420 if t is None:
2421 return
2422 real_number(t, 't')
2423 if not 0 <= t <= period:
2424 raise ValueError('\n\nParameter t must be in [0, {}].\n'.format(period))
2427def nk_min(nk, n_min):
2428 '''
2429 Check that every k-mesh size is at least n_min.
2431 :raises ValueError: Parameter nk must be at least n_min.
2432 '''
2433 if min(nk) < n_min:
2434 raise ValueError('\n\nParameter nk must be at least {}.\n'.format(n_min))
2437####################################
2438# MODEL BUILDING, BRIDGES AND I/O
2439# (neighbour-order hoppings in k-space, System <-> KSpace, tbkit.io)
2440####################################
2443def hopping_form(list_hop):
2444 '''
2445 Tell the two forms of *KSpace.set_hopping* apart: the explicit one
2446 (keys 'i', 'j', 'R', 't') and the neighbour-order one of
2447 *System.set_hopping* (keys 'n', 't', and optionally 'ang', 'tag').
2449 :raises TypeError: Parameter list_hop must be a list of dictionaries.
2450 :raises ValueError: list_hop mixes the two forms.
2452 :returns:
2453 * **neighbour** -- Boolean. True for the neighbour-order form.
2454 '''
2455 if not isinstance(list_hop, list):
2456 raise TypeError('\n\nParameter list_hop must be a list.\n')
2457 if not all(isinstance(dic, dict) for dic in list_hop):
2458 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n')
2459 by_order = ['n' in dic for dic in list_hop]
2460 if any(by_order) and not all(by_order):
2461 raise ValueError('\n\nParameter list_hop mixes explicit hoppings (keys "i", "j", "R", "t") '
2462 'and neighbour-order ones (keys "n", "t", "ang", "tag"): '
2463 'make one call per form.\n')
2464 return bool(list_hop) and all(by_order)
2467def set_hopping_neighbours(list_hop, spin=False):
2468 '''
2469 Check the neighbour-order form of *KSpace.set_hopping*.
2471 :raises KeyError: "n" and "t" must be keys, and only "ang" and "tag" may be added.
2472 :raises TypeError: "n" must be an integer, "t" a number (or, if *spin*,
2473 a 2x2 matrix) or a value function, "ang" a real number and "tag" a string.
2474 :raises ValueError: "n" must be positive, "ang" in [-180, 180), "tag" of length 2.
2475 '''
2476 hopping_form(list_hop)
2477 for dic in list_hop:
2478 if 'n' not in dic or 't' not in dic:
2479 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n')
2480 if not set(dic) <= {'n', 't', 'ang', 'tag'}:
2481 raise KeyError('\n\nOnly the keys "n", "t", "ang" and "tag" are allowed.\n')
2482 if isinstance(dic['n'], bool) or not isinstance(dic['n'], int):
2483 raise TypeError('\n\n"n" value must be an integer.\n')
2484 if dic['n'] < 1:
2485 raise ValueError('\n\n"n" value must be a positive integer.\n')
2486 if callable(dic['t']):
2487 pass
2488 elif spin:
2489 spin_matrix(dic['t'], '"t"')
2490 elif not isinstance(dic['t'], (int, float, complex)):
2491 raise TypeError('\n\n"t" value must be a real or complex number.\n')
2492 if 'ang' in dic:
2493 if not isinstance(dic['ang'], (int, float)):
2494 raise TypeError('\n\n"ang" value must be a real number.\n')
2495 if not -180. <= dic['ang'] < 180.:
2496 raise ValueError('\n\n"ang" value must be in [-180, 180).\n')
2497 if 'tag' in dic:
2498 if not isinstance(dic['tag'], str):
2499 raise TypeError('\n\n"tag" value must be a string.\n')
2500 if len(dic['tag']) != 2:
2501 raise ValueError('\n\n"tag" value must be a string of length 2.\n')
2504def shell_angle(ang, angles):
2505 '''
2506 Check that a neighbour shell has bonds along the angle *ang* (in
2507 degrees, either orientation: *angles* are those in [0, 180)).
2509 :raises ValueError: No bond of the shell points along *ang*.
2510 '''
2511 if not np.any(np.isclose(ang, angles, atol=ATOL)) and \
2512 not np.any(np.isclose(ang, angles - 180., atol=ATOL)):
2513 raise ValueError('\n\nNo bond of this neighbour shell has angle {} (angles: {}).\n'
2514 .format(ang, np.unique(angles.round(4))))
2517def not_orbital_system(sys, orbital_cls):
2518 '''
2519 Check that a System is not an *OrbitalSystem* (several rows per site).
2521 :raises TypeError: An OrbitalSystem is not supported.
2522 '''
2523 if isinstance(sys, orbital_cls):
2524 raise TypeError('\n\nAn OrbitalSystem (several orbitals per site) is not supported: '
2525 'build its KSpace with tbkit.slater_koster.sk_kspace.\n')
2528def lattice_sites(matches):
2529 '''
2530 Check that every site of a finite lattice is a copy of exactly one
2531 orbital of *unit_cell*, translated by a lattice vector (*matches*: the
2532 number of such orbitals, per site).
2534 :raises ValueError: A site is not on the periodic lattice.
2535 '''
2536 bad = np.flatnonzero(np.asarray(matches) != 1)
2537 if len(bad):
2538 raise ValueError('\n\nSite {} is not a translate of exactly one unit-cell site (same tag, '
2539 'position r0 + n1 a1 + ...): was the lattice rotated, strained '
2540 'or shifted?\n'.format(int(bad[0])))
2543def translation_invariant(dev, tol):
2544 '''
2545 Check that equivalent matrix elements (same orbitals, same lattice
2546 vector) are equal in every cell, within *tol*.
2548 :raises ValueError: The model is not translation invariant.
2549 '''
2550 if dev > tol:
2551 raise ValueError('\n\nThe Hamiltonian is not translation invariant: equivalent hoppings '
2552 'or onsite energies differ by {:.3g} (> tol = {:.3g}), e.g. after '
2553 'disorder, defects or a magnetic field.\n'.format(dev, tol))
2556def torus_range(R, sizes):
2557 '''
2558 Check that the lattice vectors of a torus's bonds (shortest images, in
2559 [-N/2, N/2)) are unambiguous: at least 3 cells along every direction,
2560 and no bond reaching half-way around.
2562 :raises ValueError: The torus is too small for the range of the model.
2563 '''
2564 if np.any(sizes < 3) or np.any(2 * R == -sizes[None, :]):
2565 raise ValueError('\n\nThe torus ({} cells) is too small for the range of the model: '
2566 'a bond reaches half-way around it.\n'.format(tuple(int(n) for n in sizes)))
2569def saveable_model(model, lattice_cls, system_cls, kspace_cls, orbital_cls):
2570 '''
2571 Check that *tbkit.io.save_model* can store *model*.
2573 :raises TypeError: model must be a Lattice, System or KSpace instance
2574 (not an OrbitalSystem).
2575 '''
2576 if isinstance(model, orbital_cls) or not isinstance(model, (lattice_cls, system_cls, kspace_cls)):
2577 raise TypeError('\n\nParameter model must be a Lattice, System or KSpace instance '
2578 '(an OrbitalSystem or a driven model cannot be saved).\n')
2581def file_path(path, var_name):
2582 '''
2583 Check a file name.
2585 :raises TypeError: Parameter var_name must be a string or a path.
2586 '''
2587 if not (isinstance(path, str) or hasattr(path, "__fspath__")):
2588 raise TypeError('\n\nParameter {} must be a string or a path.\n'.format(var_name))
2591def model_archive(data, fmt, version):
2592 '''
2593 Check an archive read by *tbkit.io.load_model*.
2595 :raises ValueError: Not a tbkit model archive, or a newer version.
2596 '''
2597 if 'format' not in data.files or str(data['format']) != fmt:
2598 raise ValueError('\n\nThis file is not a tbkit model archive (format {}).\n'.format(fmt))
2599 if int(data['version']) > version:
2600 raise ValueError('\n\nThis archive has version {}; this tbkit reads versions up to {}: '
2601 'upgrade tbkit.\n'.format(int(data['version']), version))
2604def hr_header(tokens):
2605 '''
2606 Check the header of a Wannier90 ``_hr.dat`` file: num_wann, nrpts and
2607 the nrpts degeneracy weights, as positive integers.
2609 :raises ValueError: Not a Wannier90 _hr.dat file.
2610 '''
2611 try:
2612 num_wann, nrpts = int(tokens[0]), int(tokens[1])
2613 weights = [int(w) for w in tokens[2:2 + nrpts]]
2614 except (IndexError, ValueError):
2615 raise ValueError('\n\nNot a Wannier90 _hr.dat file: expected num_wann, nrpts and '
2616 'the degeneracy weights after the header line.\n') from None
2617 if num_wann < 1 or nrpts < 1 or len(weights) != nrpts or min(weights) < 1:
2618 raise ValueError('\n\nNot a Wannier90 _hr.dat file: num_wann, nrpts and the '
2619 'degeneracy weights must be positive integers.\n')
2622def hr_body(n_tokens, nrpts, num_wann):
2623 '''
2624 Check the number of entries of a Wannier90 ``_hr.dat`` file.
2626 :raises ValueError: The file must have nrpts * num_wann**2 lines of 7 numbers.
2627 '''
2628 if n_tokens != 7 * nrpts * num_wann ** 2:
2629 raise ValueError('\n\nThe _hr.dat file must have nrpts * num_wann**2 = {} lines '
2630 '"R1 R2 R3 m n Re Im" after the weights.\n'.format(nrpts * num_wann ** 2))
2633def hr_blocks(ok):
2634 '''
2635 Check that the lines of a ``_hr.dat`` file come in blocks of num_wann**2
2636 with the same lattice vector.
2638 :raises ValueError: Lines of a lattice vector must be consecutive.
2639 '''
2640 if not ok:
2641 raise ValueError('\n\nThe _hr.dat lines of each lattice vector R must be consecutive '
2642 '(num_wann**2 lines per R).\n')
2645def win_block(found):
2646 '''
2647 Check that a ``.win`` file has a ``unit_cell_cart`` block.
2649 :raises ValueError: No unit_cell_cart block.
2650 '''
2651 if not found:
2652 raise ValueError('\n\nThe .win file has no "begin unit_cell_cart ... end unit_cell_cart" block.\n')
2655def win_cell(cell):
2656 '''
2657 Check the ``unit_cell_cart`` block of a ``.win`` file: three lines of
2658 three real numbers.
2660 :raises ValueError: unit_cell_cart must hold three vectors of three numbers.
2661 '''
2662 try:
2663 ok = len(cell) == 3 and all(len(v) == 3 for v in cell) and \
2664 all(np.isfinite([float(c) for v in cell for c in v]))
2665 except ValueError:
2666 ok = False
2667 if not ok:
2668 raise ValueError('\n\nThe unit_cell_cart block must hold three vectors of three real numbers.\n')
2671def centres_count(n, num_wann):
2672 '''
2673 Check that a ``_centres.xyz`` file lists num_wann Wannier centres (X).
2675 :raises ValueError: Too few Wannier centres.
2676 '''
2677 if n < num_wann:
2678 raise ValueError('\n\nThe _centres.xyz file lists {} Wannier centres ("X"), '
2679 'not {}.\n'.format(n, num_wann))
2682def wannier_dim(dim):
2683 '''
2684 Check the dimension of a Wannier90 model.
2686 :raises ValueError: dim must be 2 or 3.
2687 '''
2688 if isinstance(dim, bool) or dim not in (2, 3):
2689 raise ValueError('\n\nParameter dim must be 2 or 3.\n')
2692def win_given(win):
2693 '''
2694 Check that the lattice vectors can be found.
2696 :raises ValueError: Give prim_vec or win.
2697 '''
2698 if win is None:
2699 raise ValueError('\n\nGive the primitive vectors: prim_vec, or the .win file (win).\n')
2702def layer_cell(cell, dim):
2703 '''
2704 Check that, for a 2D model, a1 and a2 lie in the (x, y) plane.
2706 :raises ValueError: a1 and a2 must have no z component.
2707 '''
2708 if dim == 2 and np.any(np.abs(cell[:2, 2]) > 1e-8):
2709 raise ValueError('\n\nFor dim=2, the first two lattice vectors must lie in the (x, y) plane.\n')
2712def wannier_prim_vec(prim_vec, dim):
2713 '''
2714 Check that the primitive vectors are *dim* vectors of *dim* components.
2716 :raises ValueError: prim_vec must hold dim vectors of dim components.
2717 '''
2718 if len(prim_vec) != dim or len(prim_vec[0]) != dim:
2719 raise ValueError('\n\nParameter prim_vec must hold {0} vectors of {0} components.\n'.format(dim))
2720 independent(prim_vec)
2723def wannier_positions(shape, num_wann, dim):
2724 '''
2725 Check the orbital positions of a Wannier90 model.
2727 :raises ValueError: positions must have shape (num_wann, dim) or (num_wann, 3).
2728 '''
2729 if len(shape) != 2 or shape[0] != num_wann or shape[1] not in (dim, 3):
2730 raise ValueError('\n\nParameter positions must have shape ({0}, {1}) or ({0}, 3).\n'
2731 .format(num_wann, dim))
2734def wannier_tags(tags, num_wann):
2735 '''
2736 Check the sublattice tags of a Wannier90 model.
2738 :raises TypeError: tags must be a list of one-character strings.
2739 :raises ValueError: tags must have num_wann elements.
2740 '''
2741 if not isinstance(tags, list) or not all(isinstance(t, str) and len(t) == 1 for t in tags):
2742 raise TypeError('\n\nParameter tags must be a list of one-character strings.\n')
2743 if len(tags) != num_wann:
2744 raise ValueError('\n\nParameter tags must have {} elements.\n'.format(num_wann))
2747def layer_hoppings(h_max, tol):
2748 '''
2749 Check that a 2D model has no hopping between layers (R3 != 0).
2751 :raises ValueError: Hoppings with R3 != 0 must vanish for dim=2.
2752 '''
2753 if h_max > tol:
2754 raise ValueError('\n\nFor dim=2, the matrix elements with R3 != 0 must vanish '
2755 '(largest: {:.3g}).\n'.format(h_max))
2758def spinless(spin):
2759 '''
2760 Check that a KSpace model has no spin.
2762 :raises ValueError: This needs a spinless model.
2763 '''
2764 if spin:
2765 raise ValueError('\n\nThis needs a spinless model (spin=False): a System has one '
2766 'orbital per site; use bridges.finite_model for spinful models.\n')
2767# HIGHER-ORDER TOPOLOGY, MOIRE SUPERCELLS, SELF-CONSISTENT INTERACTIONS
2768####################################
2771def wannier_gap(nu, sector, tol=1e-4):
2772 '''
2773 Check that a Wannier sector is separated from the other Wannier bands
2774 over the mesh (*nu*, shape (n1, n2, nb), sorted at each k): at every k,
2775 the smallest distance on the circle between a Wannier band of the
2776 sector and one outside it (the Wannier gap) exceeds *tol*, and the
2777 Wannier bands of the sector change between neighbouring k-points by
2778 less than the smallest gap (otherwise two Wannier bands crossed between
2779 them, or the sector wrapped around the circle).
2781 :raises ValueError: The Wannier sector is not separated by a Wannier gap.
2782 '''
2783 nu = np.asarray(nu)
2784 rest = [n for n in range(nu.shape[-1]) if n not in sector]
2785 if not rest:
2786 return
2788 def circ(d):
2789 d = np.abs(d) % 1.
2790 return np.minimum(d, 1. - d)
2791 gap = np.min(circ(nu[..., sector][..., :, None] - nu[..., rest][..., None, :]))
2792 jump = max(np.max(circ(nu[..., sector] - np.roll(nu[..., sector], 1, axis=a)))
2793 for a in range(nu.ndim - 1))
2794 if gap < tol or jump >= gap:
2795 raise ValueError('\n\nThe Wannier sector is not separated from the other Wannier '
2796 'bands by a Wannier gap (smallest distance {:.2e}, largest '
2797 'step {:.2e}).\n'.format(gap, jump))
2800def supercell_matrix(mat, dim):
2801 '''
2802 Check a supercell matrix: integers, shape (dim, dim), nonzero determinant.
2804 :raises TypeError: The supercell matrix must contain integers.
2805 :raises ValueError: The supercell matrix must be (dim, dim), with a nonzero determinant.
2806 '''
2807 if mat.shape != (dim, dim):
2808 raise ValueError('\n\nThe supercell matrix must have the shape ({0}, {0}).\n'.format(dim))
2809 if not np.issubdtype(mat.dtype, np.integer):
2810 raise TypeError('\n\nThe supercell matrix must contain integers.\n')
2811 if round(abs(np.linalg.det(mat))) == 0:
2812 raise ValueError('\n\nThe supercell matrix must have a nonzero determinant.\n')
2815def supercell_model(sc, cls):
2816 '''
2817 Check that *sc* is a supercell (made by *moire.supercell*).
2819 :raises TypeError: Parameter sc must be a SupercellKSpace instance.
2820 '''
2821 if not isinstance(sc, cls):
2822 raise TypeError('\n\nParameter sc must be a SupercellKSpace instance '
2823 '(see tbkit.moire.supercell).\n')
2826def bond_vectors(d):
2827 '''
2828 Check bond vectors: shape (n, 3), none of zero length.
2830 :raises ValueError: Bond vectors must have the shape (n, 3) and a nonzero length.
2831 '''
2832 if d.ndim != 2 or d.shape[1] != 3:
2833 raise ValueError('\n\nThe bond vectors must have the shape (n, 3).\n')
2834 if np.any(np.linalg.norm(d, axis=1) == 0):
2835 raise ValueError('\n\nThe bond vectors must have a nonzero length.\n')
2838def twist_lattice(lattice):
2839 '''
2840 Check the layer lattice of a twisted bilayer.
2842 :raises ValueError: Parameter lattice must be "honeycomb" or "square".
2843 '''
2844 if lattice not in ('honeycomb', 'square'):
2845 raise ValueError('\n\nParameter lattice must be "honeycomb" or "square".\n')
2848def commensurate(moire_1, moire_2):
2849 '''
2850 Check that the moire vectors of the two layers coincide.
2852 :raises ValueError: The two layers do not share the moire cell.
2853 '''
2854 if not np.allclose(moire_1, moire_2, atol=1e-9 * max(1., np.abs(moire_1).max())):
2855 raise ValueError('\n\nThe two layers do not share the moire cell.\n')
2858def hopping_values(t, n):
2859 '''
2860 Check the output of a hopping function: n values.
2862 :raises ValueError: The hopping function must return one value per bond vector.
2863 '''
2864 if np.shape(t) != (n,):
2865 raise ValueError('\n\nThe hopping function must return one value per bond vector, '
2866 'shape ({},).\n'.format(n))
2869def even_dimension(n):
2870 '''
2871 Check that a spinful matrix has an even dimension (two spins per site).
2873 :raises ValueError: A spinful Hamiltonian must have an even dimension.
2874 '''
2875 if n % 2:
2876 raise ValueError('\n\nA spinful Hamiltonian must have an even dimension '
2877 '(rows site-major, spin up then down).\n')
2880def magnetization(mag, n):
2881 '''
2882 Check initial moments: a real array of shape (n, 3).
2884 :raises ValueError: Parameter magnetization must have the shape (n, 3).
2885 '''
2886 if mag.shape != (n, 3):
2887 raise ValueError('\n\nParameter magnetization must have the shape ({}, 3).\n'.format(n))
2890def interaction(V, n):
2891 '''
2892 Check an attractive interaction: a positive number, or n non-negative numbers.
2894 :raises ValueError: The interaction must be positive (or n non-negative values).
2895 '''
2896 V = np.asarray(V, dtype='f8')
2897 if V.ndim == 0:
2898 positive_real(float(V), 'V')
2899 return
2900 if V.shape != (n,) or np.any(V < 0):
2901 raise ValueError('\n\nParameter V must be a positive number, or {} non-negative '
2902 'numbers (one per site).\n'.format(n))
2905def pairing_amplitudes(delta, n):
2906 '''
2907 Check initial pairing amplitudes: a number, or n numbers.
2909 :raises ValueError: Parameter delta0 must be a number or n numbers.
2910 '''
2911 if np.ndim(delta) == 0:
2912 number(delta, 'delta0')
2913 return
2914 if np.shape(delta) != (n,):
2915 raise ValueError('\n\nParameter delta0 must be a number, or {} numbers '
2916 '(one per site).\n'.format(n))
2917# OPTICAL RESPONSE AND TRANSPORT (tbkit.optics, tbkit.transport)
2918####################################
2921def frequencies(omega, var_name):
2922 '''
2923 Check photon energies (or energies): a real number, or a non-empty
2924 array of finite real numbers.
2926 :raises TypeError: Parameter must be a real number or an array of real numbers.
2927 :raises ValueError: Parameter must contain at least one finite number.
2928 '''
2929 arr = np.asarray(omega)
2930 if arr.dtype.kind not in 'iuf':
2931 raise TypeError('\n\nParameter {} must be a real number or an array '
2932 'of real numbers.\n'.format(var_name))
2933 if arr.size == 0 or not np.all(np.isfinite(arr)):
2934 raise ValueError('\n\nParameter {} must contain at least one finite '
2935 'number.\n'.format(var_name))
2938def tensor_component(component, order):
2939 '''
2940 Check a Cartesian tensor component of a 2D response: a string of
2941 *order* letters among 'x' and 'y'. Returns the axis indices.
2943 :raises TypeError: Parameter component must be a string.
2944 :raises ValueError: Parameter component must have order letters among x, y.
2945 '''
2946 if not isinstance(component, str):
2947 raise TypeError('\n\nParameter component must be a string.\n')
2948 if len(component) != order or any(c not in 'xy' for c in component):
2949 raise ValueError('\n\nParameter component must be {} letters among '
2950 "'x' and 'y'.\n".format(order))
2951 return tuple('xy'.index(c) for c in component)
2954def band_derivatives(en, vel, w):
2955 '''
2956 Check the band energies (nk, n), velocities (2, nk, n, n) and second
2957 derivatives (2, 2, nk, n, n) of *optics.generalized_derivative*.
2959 :raises ValueError: Inconsistent shapes.
2960 '''
2961 if en.ndim != 2 or vel.shape != (2,) + en.shape + en.shape[-1:] or \
2962 w.shape != (2,) + vel.shape:
2963 raise ValueError('\n\nThe shapes must be en (nk, n), vel (2, nk, n, n) and '
2964 'w (2, 2, nk, n, n).\n')
2967def lead_pair(pair, n_leads, var_name, distinct):
2968 '''
2969 Check a pair of lead indices (and, with *distinct*, that they differ).
2971 :raises TypeError: Parameter must be a tuple/list of two lead indices.
2972 :raises ValueError: No such lead, or the two leads must differ.
2973 '''
2974 if not isinstance(pair, (tuple, list)) or len(pair) != 2:
2975 raise TypeError('\n\nParameter {} must be a tuple of two lead indices.\n'.format(var_name))
2976 for lead in pair:
2977 lead_index(lead, n_leads)
2978 if distinct and pair[0] == pair[1]:
2979 raise ValueError('\n\nThe two leads of parameter {} must differ.\n'.format(var_name))
2982def site_positions(positions, n_sites):
2983 '''
2984 Check site positions: a real array of shape (n_sites, d).
2986 :raises ValueError: Parameter positions must have shape (n_sites, d).
2987 '''
2988 if positions.ndim != 2 or len(positions) != n_sites:
2989 raise ValueError('\n\nParameter positions must have shape ({}, d).\n'.format(n_sites))
2992def slices(slices, n_sites):
2993 '''
2994 Check a partition of the device sites into non-empty slices.
2996 :raises TypeError: Parameter slices must be a list of lists of integers.
2997 :raises ValueError: The slices must contain every site exactly once.
2998 '''
2999 if not isinstance(slices, list) or not all(
3000 isinstance(s, (list, tuple)) and len(s) > 0
3001 and all(isinstance(i, (int, np.integer)) for i in s) for s in slices):
3002 raise TypeError('\n\nParameter slices must be a list of non-empty lists of integers.\n')
3003 flat = sorted(int(i) for s in slices for i in s)
3004 if flat != list(range(n_sites)):
3005 raise ValueError('\n\nThe slices must contain every site (0 to {}) exactly '
3006 'once.\n'.format(n_sites - 1))
3009def block_tridiagonal(max_distance):
3010 '''
3011 Check that hoppings only couple a slice to itself or to its neighbours.
3013 :raises ValueError: Hoppings must connect neighbouring slices only.
3014 '''
3015 if max_distance > 1:
3016 raise ValueError('\n\nThe Hamiltonian couples slices that are not neighbours: '
3017 'use thicker slices.\n')
3020def lead_tuple(lead):
3021 '''
3022 Check a lead given as a tuple (h0, v, coupling).
3024 :raises TypeError: A lead must be a tuple (h0, v, coupling).
3025 '''
3026 if not isinstance(lead, (tuple, list)) or len(lead) != 3:
3027 raise TypeError('\n\nA lead must be a tuple (h0, v, coupling).\n')
3030####################################
3031# SECTOR CHERN NUMBERS, BDG, BOTT, WEYL
3032####################################
3035def hermitian_operator(ops, tol=1e-10):
3036 '''
3037 Check that an operator (shape (..., norb, norb)) is Hermitian.
3039 :raises ValueError: The operator must be Hermitian.
3040 '''
3041 if np.max(np.abs(ops - ops.conj().swapaxes(-1, -2))) > tol:
3042 raise ValueError('\n\nThe operator must be Hermitian.\n')
3045def projected_gap(w, tol=1e-6):
3046 '''
3047 Check that the projected operator POP (eigenvalues *w*, shape (nk, n),
3048 sorted) has no eigenvalue near zero, and the same number of negative
3049 eigenvalues at every k-point; return that number.
3051 :raises ValueError: The spectrum of POP must have a gap around zero.
3052 '''
3053 n_minus = np.sum(w < 0., axis=1)
3054 if np.min(np.abs(w)) < tol or np.any(n_minus != n_minus[0]):
3055 raise ValueError('\n\nThe spectrum of the projected operator POP closes around zero '
3056 '(smallest |eigenvalue| {:.3g}): the two sectors are not '
3057 'defined.\n'.format(float(np.min(np.abs(w)))))
3058 return int(n_minus[0])
3061def mirror_square(squares, tol):
3062 '''
3063 Check that a mirror operator squares to +1 or -1 (*squares*, shape
3064 (..., norb, norb)); return the sign.
3066 :raises ValueError: The mirror operator must square to +1 or -1.
3067 '''
3068 sign = np.sign(squares[0, 0, 0].real)
3069 eye = np.eye(squares.shape[-1])
3070 if sign == 0 or np.max(np.abs(squares - sign * eye)) > tol:
3071 raise ValueError('\n\nThe mirror operator must be unitary and square to +1 or -1.\n')
3072 return int(sign)
3075def commutes(err, tol):
3076 '''
3077 Check that an operator commutes with H(k) on the plane (*err*: largest
3078 entry of the commutator).
3080 :raises ValueError: The operator does not commute with H(k) on this plane.
3081 '''
3082 if err > tol:
3083 raise ValueError('\n\nThe mirror operator does not commute with H(k) on this plane '
3084 '(largest commutator entry {:.3g}): choose a '
3085 'mirror-invariant plane.\n'.format(err))
3088def strong_index(strong):
3089 '''
3090 Check that the strong Z2 index comes out the same from the three pairs
3091 of time-reversal-invariant planes.
3093 :raises ValueError: The six Z2 invariants are inconsistent.
3094 '''
3095 if len(set(int(s) for s in strong)) != 1:
3096 raise ValueError('\n\nThe six planar Z2 invariants give different strong indices: '
3097 'increase nk or nk_perp (or the bands are not gapped).\n')
3100def region(region, n):
3101 '''
3102 Check a region: a non-empty list of distinct indices between 0 and n-1.
3104 :raises TypeError: Parameter region must be a non-empty list of integers.
3105 :raises ValueError: Parameter region must hold distinct indices between 0 and n-1.
3106 '''
3107 if not isinstance(region, list) or not region or \
3108 not all(isinstance(i, (int, np.integer)) for i in region):
3109 raise TypeError('\n\nParameter region must be a non-empty list of integers.\n')
3110 if len(set(region)) != len(region) or not all(0 <= i < n for i in region):
3111 raise ValueError('\n\nParameter region must hold distinct indices between 0 and '
3112 '{}.\n'.format(n - 1))
3115def torus_cell(cell, d):
3116 '''
3117 Check the two vectors spanning a torus: shape (2, d), linearly independent.
3119 :raises ValueError: Parameter cell must hold two independent vectors of d components.
3120 '''
3121 if cell.shape != (2, d) or np.linalg.matrix_rank(cell) < 2:
3122 raise ValueError('\n\nParameter cell must hold two linearly independent vectors '
3123 'of {} components.\n'.format(d))
3126def band_below_top(n, norb):
3127 '''
3128 Check that a band has a band above it.
3130 :raises ValueError: The highest band of bands must not be the top band.
3131 '''
3132 if n >= norb - 1:
3133 raise ValueError('\n\nThe highest band of bands must have a band above it.\n')
3136def antisymmetric(a, tol=1e-10):
3137 '''
3138 Check that a matrix is antisymmetric.
3140 :raises ValueError: The matrix must be antisymmetric.
3141 '''
3142 if np.max(np.abs(a + a.T), initial=0.) > tol:
3143 raise ValueError('\n\nThe matrix must be antisymmetric.\n')
3146def bdg_orbitals(norb):
3147 '''
3148 Check that a BdG model has an even number of orbitals (particles, then holes).
3150 :raises ValueError: A BdG model must have an even number of orbitals.
3151 '''
3152 if norb % 2:
3153 raise ValueError('\n\nA BdG model must have an even number of orbitals '
3154 '(particles, then holes).\n')
3157def majorana_form(a, tol=1e-8):
3158 '''
3159 Check that the Majorana form -i U H U^dagger of a BdG Hamiltonian at a
3160 time-reversal-invariant momentum is real (particle-hole symmetry).
3162 :raises ValueError: The model is not particle-hole symmetric.
3163 '''
3164 if np.max(np.abs(a.imag)) > tol:
3165 raise ValueError('\n\nThe model is not particle-hole symmetric in the BdG basis '
3166 '(particles, then holes; see bdg.bdg_kspace).\n')
3169def gapped_trim(pf, tol=1e-12):
3170 '''
3171 Check that a Pfaffian is nonzero (the BdG gap is open at the momentum).
3173 :raises ValueError: The gap closes at a time-reversal-invariant momentum.
3174 '''
3175 if abs(pf) < tol:
3176 raise ValueError('\n\nThe gap closes at a time-reversal-invariant momentum: the '
3177 'Majorana number is not defined.\n')
3180####################################
3181# PERIODIC BOUNDARIES (SYSTEM)
3182####################################
3185def periodic(periodic, dim):
3186 '''
3187 Check parameter *periodic* of *System*: a boolean, or a tuple of *dim*
3188 booleans (one per primitive vector); return the tuple.
3190 :raises TypeError: Parameter periodic must be a bool or a tuple of bools.
3191 :raises ValueError: Parameter periodic must have one entry per primitive vector.
3192 '''
3193 if isinstance(periodic, bool):
3194 return (periodic,) * dim
3195 if not isinstance(periodic, tuple) or not all(isinstance(p, bool) for p in periodic):
3196 raise TypeError('\n\nParameter periodic must be a bool or a tuple of bools.\n')
3197 if len(periodic) != dim:
3198 raise ValueError('\n\nParameter periodic must have one entry per primitive vector '
3199 '({}).\n'.format(dim))
3200 return periodic
3203def torus_lattice(n1):
3204 '''
3205 Check that the lattice of a periodic System comes from *get_lattice*.
3207 :raises ValueError: Periodic boundaries need lat.get_lattice first.
3208 '''
3209 if n1 == 0:
3210 raise ValueError('\n\nPeriodic boundaries wrap the n1 x n2 (x n3) cells of '
3211 'lat.get_lattice: call it first.\n')
3214def torus_size(dist, period, n):
3215 '''
3216 Check that bonds of length *dist* (order *n*) reach less than half-way
3217 around the torus (shortest period *period*), so that each one has a
3218 single shortest image.
3220 :raises ValueError: The torus is too small for this neighbour order.
3221 '''
3222 if 2 * dist >= period - ATOL:
3223 raise ValueError('\n\nThe torus is too small for the neighbours of order {}: their '
3224 'length {:.3f} reaches half-way around it (shortest period '
3225 '{:.3f}). Use more cells.\n'.format(n, dist, period))
3228def not_periodic(periodic, method):
3229 '''
3230 Check that a System has open boundaries.
3232 :raises ValueError: The method needs open boundaries.
3233 '''
3234 if any(periodic):
3235 raise ValueError('\n\n{} needs open boundaries (periodic=False); on a torus, see '
3236 'System.get_bott_index and set_peierls_phase.\n'.format(method))
3239def torus_2d(periodic):
3240 '''
3241 Check that a System is a 2D torus (periodic along two primitive vectors).
3243 :raises ValueError: This calculation needs a 2D lattice periodic along both vectors.
3244 '''
3245 if len(periodic) != 2 or not all(periodic):
3246 raise ValueError('\n\nThis calculation needs a 2D lattice with periodic boundaries '
3247 'along both primitive vectors (periodic=True).\n')
3250####################################
3251# ANALYSIS AND PLOTTING (projections, contours, k-paths)
3252####################################
3255def projector(proj, norb, tags):
3256 '''
3257 Check parameter *projector* of *kspace.band_weights*: an orbital index,
3258 a non-empty list of distinct orbital indices, a sublattice tag, or a
3259 (norb, norb) Hermitian matrix.
3261 :raises TypeError: Parameter projector must be an orbital index, a list of them, a tag or a matrix.
3262 :raises ValueError: Orbital indices must be distinct and between 0 and norb-1.
3263 :raises ValueError: Parameter projector must be a tag of the unit cell.
3264 :raises ValueError: The operator must be a (norb, norb) Hermitian matrix.
3265 '''
3266 if isinstance(proj, str):
3267 if proj not in tags:
3268 raise ValueError('\n\nParameter projector must be a tag of the unit cell: '
3269 '{}.\n'.format(sorted(set(tags))))
3270 return
3271 if isinstance(proj, np.ndarray) and proj.ndim == 2:
3272 operator(proj, norb)
3273 hermitian_operator(proj)
3274 return
3275 if isinstance(proj, int):
3276 proj = [proj]
3277 if not isinstance(proj, list) or not proj or not all(isinstance(p, int) for p in proj):
3278 raise TypeError('\n\nParameter projector must be an orbital index, a list of '
3279 'orbital indices, a sublattice tag or a (norb, norb) matrix.\n')
3280 if len(set(proj)) != len(proj) or not all(0 <= p < norb for p in proj):
3281 raise ValueError('\n\nOrbital indices must be distinct and between 0 and '
3282 '{}.\n'.format(norb - 1))
3285def band_weights(weights, shape):
3286 '''
3287 Check parameter *weights* of *kspace.plot_bands*: real values, one per
3288 band and k-point of the stored band structure.
3290 :raises ValueError: Parameter weights must be a real array of the shape of the bands.
3291 '''
3292 weights = np.asarray(weights)
3293 if weights.shape != shape or np.iscomplexobj(weights) or not np.all(np.isfinite(weights)):
3294 raise ValueError('\n\nParameter weights must be a finite real array of shape {}, '
3295 'the shape of the bands (see band_weights).\n'.format(shape))
3298def weight_style(style):
3299 '''
3300 Check parameter *style* of *kspace.plot_bands*.
3302 :raises ValueError: Parameter style must be "color" or "size".
3303 '''
3304 if style not in ('color', 'size'):
3305 raise ValueError('\n\nParameter style must be "color" or "size".\n')
3308def contour_found(n_elements, energy):
3309 '''
3310 Check that a band crosses the energy of a constant-energy contour.
3312 :raises ValueError: The band does not cross the energy.
3313 '''
3314 if n_elements == 0:
3315 raise ValueError('\n\nThe band does not cross the energy {}: there is no '
3316 'constant-energy contour.\n'.format(energy))
3319def cubic_path(points, names, axes):
3320 '''
3321 Check the parameters of *kspace.cubic_path*.
3323 :raises TypeError: Parameter points must be a dictionary.
3324 :raises ValueError: Each point must have 3 real coordinates.
3325 :raises ValueError: Parameter names must list at least two keys of points.
3326 :raises ValueError: Parameter axes must be 3 orthonormal rows.
3327 '''
3328 if not isinstance(points, dict):
3329 raise TypeError('\n\nParameter points must be a dictionary (label: point).\n')
3330 for key, point in points.items():
3331 point = np.asarray(point)
3332 if point.shape != (3,) or not np.isrealobj(point) or not np.all(np.isfinite(point)):
3333 raise ValueError('\n\nPoint {} must have 3 real coordinates.\n'.format(key))
3334 if (not isinstance(names, (list, tuple)) or len(names) < 2
3335 or any(n not in points for n in names)):
3336 raise ValueError('\n\nParameter names must list at least two keys of points.\n')
3337 if axes is not None:
3338 axes = np.asarray(axes)
3339 if axes.shape != (3, 3) or not np.isrealobj(axes) or not np.allclose(axes @ axes.T, np.eye(3)):
3340 raise ValueError('\n\nParameter axes must be 3 orthonormal rows (shape (3, 3)).\n')
3343def bravais_lattice(kind, dim):
3344 '''
3345 Check that *kspace.high_symmetry_path* recognized the Bravais lattice.
3347 :raises ValueError: No standard path for this lattice.
3348 '''
3349 if kind is None:
3350 raise ValueError('\n\nNo standard high-symmetry path for this {}D lattice: '
3351 'high_symmetry_path knows the 1D and 2D Bravais lattices, '
3352 'and the simple cubic, fcc, bcc, simple tetragonal and '
3353 'hexagonal ones in 3D. Pass the k-points to k_path '
3354 'directly.\n'.format(dim))
3357###############################
3358# CONTINUUM DISCRETIZATION
3359###############################
3362def sympy_module(module):
3363 '''
3364 Check that sympy, the optional dependency of *tbkit.continuum*, is installed.
3366 :raises ImportError: sympy is not installed.
3367 '''
3368 if module is None:
3369 raise ImportError('\n\ntbkit.continuum needs sympy: pip install tbkit[continuum].\n')
3372def continuum_type(ham, types):
3373 '''
3374 Check the type of the Hamiltonian of *continuum.discretize*.
3376 :raises TypeError: The Hamiltonian must be a string, a sympy expression
3377 or a sympy Matrix.
3378 '''
3379 if not isinstance(ham, types):
3380 raise TypeError('\n\nParameter hamiltonian must be a string, a sympy expression '
3381 'or a sympy Matrix.\n')
3384def continuum_parsed(error, string):
3385 '''
3386 Check that the string Hamiltonian of *continuum.discretize* was parsed.
3388 :raises ValueError: The string is not a valid expression.
3389 '''
3390 if error is not None:
3391 raise ValueError('\n\nParameter hamiltonian {!r} is not a valid sympy expression '
3392 '({}: {}).\n'.format(string, type(error).__name__, error))
3395def continuum_square(shape):
3396 '''
3397 Check that the Hamiltonian of *continuum.discretize* is a square matrix.
3399 :raises ValueError: The Hamiltonian must be a square matrix.
3400 '''
3401 if len(shape) != 2 or shape[0] != shape[1] or shape[0] == 0:
3402 raise ValueError('\n\nParameter hamiltonian must be a scalar or a square matrix '
3403 '(shape {}).\n'.format(shape))
3406def continuum_symbols(names):
3407 '''
3408 Check the free symbols of the Hamiltonian of *continuum.discretize*:
3409 no position (the model is translation invariant), no symbol named
3410 like the grid spacing *a*, and parameter names that can be passed as
3411 keyword arguments.
3413 :raises ValueError: x, y, z are not supported.
3414 :raises ValueError: a is reserved for the grid spacing.
3415 :raises ValueError: Parameter names must be Python identifiers.
3416 '''
3417 position = sorted(names & {'x', 'y', 'z'})
3418 if position:
3419 raise ValueError('\n\nThe Hamiltonian depends on the position ({}): only translation-'
3420 'invariant continuum models (constant coefficients) can be '
3421 'discretized into a KSpace.\n'.format(', '.join(position)))
3422 if 'a' in names:
3423 raise ValueError('\n\nThe symbol a is reserved for the grid spacing: rename the '
3424 'parameter.\n')
3425 bad = sorted(n for n in names if not n.isidentifier() or n in ('site_i', 'site_j'))
3426 if bad:
3427 raise ValueError('\n\nParameter names must be Python identifiers (other than site_i, '
3428 'site_j), to be passed to get_ham: {}.\n'.format(', '.join(bad)))
3431def continuum_dim(dim, used):
3432 '''
3433 Check the dimension of *continuum.discretize*, given the momenta
3434 k_x, k_y, k_z (axes 0, 1, 2) used by the Hamiltonian.
3436 :raises TypeError: Parameter dim must be an integer.
3437 :raises ValueError: Parameter dim must be 1, 2 or 3.
3438 :raises ValueError: The Hamiltonian uses a momentum beyond dim.
3439 '''
3440 if not isinstance(dim, int):
3441 raise TypeError('\n\nParameter dim must be an integer.\n')
3442 if dim not in (1, 2, 3):
3443 raise ValueError('\n\nParameter dim must be 1, 2 or 3.\n')
3444 if used and max(used) >= dim:
3445 raise ValueError('\n\nThe Hamiltonian uses {} in dimension {}.\n'.format(
3446 ('k_x', 'k_y', 'k_z')[max(used)], dim))
3449def continuum_polynomial(entry, is_polynomial):
3450 '''
3451 Check that a matrix element of *continuum.discretize* is a polynomial
3452 in the momenta.
3454 :raises ValueError: The Hamiltonian must be a polynomial in k_x, k_y, k_z.
3455 '''
3456 if not is_polynomial:
3457 raise ValueError('\n\nThe Hamiltonian must be a polynomial in k_x, k_y, k_z '
3458 '(matrix element {}).\n'.format(entry))
3461def continuum_tags(tags, norb):
3462 '''
3463 Check the orbital tags of *continuum.discretize*.
3465 :raises ValueError: More than 52 orbitals need explicit tags.
3466 :raises TypeError: Parameter tags must be a string or a list of strings.
3467 :raises ValueError: Parameter tags must hold one one-character tag per orbital.
3468 '''
3469 if tags is None:
3470 if norb > 52:
3471 raise ValueError('\n\nThe Hamiltonian has {} orbitals: pass their tags (default '
3472 'tags are the 52 letters).\n'.format(norb))
3473 return
3474 if not isinstance(tags, (str, list, tuple)) or not all(isinstance(t, str) for t in tags):
3475 raise TypeError('\n\nParameter tags must be a string or a list of strings.\n')
3476 if len(tags) != norb or not all(len(t) == 1 for t in tags):
3477 raise ValueError('\n\nParameter tags must hold {} one-character tags, one per '
3478 'orbital.\n'.format(norb))
3481def continuum_spacing(a):
3482 '''
3483 Check the grid spacing of *continuum.discretize*: a **Lattice**
3484 refuses primitive vectors whose squared norm is below 0.1.
3486 :raises ValueError: Parameter a must be at least sqrt(0.1).
3487 '''
3488 if a * a < 0.1:
3489 raise ValueError('\n\nParameter a must be at least sqrt(0.1) = 0.316 (the shortest '
3490 'primitive vector of a Lattice): measure lengths in a smaller '
3491 'unit, and scale the coefficients of the Hamiltonian '
3492 'accordingly.\n')
3495####################################
3496# WANNIER FUNCTIONS
3497####################################
3500def trial_orbitals(trial, norb, n_bands):
3501 '''
3502 Check the trial orbitals of *wannier.wannierize*: a list of orbital
3503 indices, or an array of shape (norb, n_wann), with at most *n_bands*
3504 trial orbitals (one per Wannier function; fewer than the bands
3505 disentangles them).
3507 :raises TypeError: Parameter trial must be a list of orbital indices or an array.
3508 :raises ValueError: Parameter trial must hold between 1 and n_bands trial orbitals.
3509 '''
3510 if isinstance(trial, list) and trial and all(isinstance(o, int) for o in trial):
3511 if len(trial) > n_bands or not all(0 <= o < norb for o in trial):
3512 raise ValueError('\n\nParameter trial must hold at most {} orbital indices (one per '
3513 'Wannier function, at most one per band) between 0 and {}.\n'
3514 .format(n_bands, norb - 1))
3515 return
3516 try:
3517 g = np.asarray(trial, dtype='c16')
3518 except (TypeError, ValueError):
3519 raise TypeError('\n\nParameter trial must be a list of orbital indices or a complex '
3520 'array of shape (norb, n_wann).\n') from None
3521 if g.ndim != 2 or g.shape[0] != norb or not 1 <= g.shape[1] <= n_bands:
3522 raise ValueError('\n\nParameter trial must be an array of shape ({}, n_wann), with '
3523 '1 <= n_wann <= {}: one column (trial orbital) per Wannier '
3524 'function, at most one per band.\n'.format(norb, n_bands))
3527def energy_window(window, var_name):
3528 '''
3529 Check an energy window of *wannier.wannierize*: a tuple of two
3530 increasing real numbers.
3532 :raises TypeError: Parameter var_name must be a tuple of two real numbers.
3533 :raises ValueError: Parameter var_name must be increasing.
3534 '''
3535 if not isinstance(window, tuple) or len(window) != 2 or \
3536 not all(isinstance(e, (int, float)) and not isinstance(e, bool) for e in window):
3537 raise TypeError('\n\nParameter {} must be a tuple (E_min, E_max) of two real '
3538 'numbers.\n'.format(var_name))
3539 if window[0] >= window[1]:
3540 raise ValueError('\n\nParameter {} must satisfy E_min < E_max.\n'.format(var_name))
3543def frozen_window(frozen, window):
3544 '''
3545 Check that the frozen (inner) window of *wannier.wannierize* lies
3546 inside the outer window.
3548 :raises ValueError: The frozen window must lie inside the outer window.
3549 '''
3550 if frozen[0] < window[0] or frozen[1] > window[1]:
3551 raise ValueError('\n\nParameter frozen {} must lie inside the outer window {}.\n'
3552 .format(frozen, window))
3555def window_states(n_inside, n_frozen, n_wann):
3556 '''
3557 Check the number of states of the disentanglement windows at every
3558 k-point: at least *n_wann* in the outer window, at most *n_wann* in the
3559 frozen window.
3561 :raises ValueError: The outer window holds fewer than n_wann states at some k-point.
3562 :raises ValueError: The frozen window holds more than n_wann states at some k-point.
3563 '''
3564 if n_inside < n_wann:
3565 raise ValueError('\n\nThe outer window holds only {} of the bands at some k-point, '
3566 'fewer than the {} Wannier functions: widen it, or add bands.\n'
3567 .format(n_inside, n_wann))
3568 if n_frozen > n_wann:
3569 raise ValueError('\n\nThe frozen window holds {} bands at some k-point, more than the '
3570 '{} Wannier functions: narrow it.\n'.format(n_frozen, n_wann))
3573def isolated_bands(en, bands, tol=1e-8):
3574 '''
3575 Check that a group of bands is separated in energy from the other bands
3576 at every point of the mesh (*en*, shape (nk, norb), sorted).
3578 :raises ValueError: The bands are not isolated.
3579 '''
3580 inside = np.zeros(en.shape[1], bool)
3581 inside[bands] = True
3582 for n in range(en.shape[1] - 1):
3583 if inside[n] != inside[n + 1] and np.min(en[:, n + 1] - en[:, n]) < tol:
3584 raise ValueError('\n\nThe bands {} touch band {} on the k-mesh: Wannier functions '
3585 'need an isolated group of bands: add the bands they touch to the group, '
3586 'or disentangle them (more bands than trial orbitals).\n'
3587 .format(bands, n + 1 if inside[n] else n))
3590def projection(s_min, tol=1e-10):
3591 '''
3592 Check that the projection of the bands onto the trial orbitals has full
3593 rank at every k-point.
3595 :raises ValueError: The trial orbitals miss the bands at some k-point.
3596 '''
3597 if s_min < tol:
3598 raise ValueError('\n\nThe trial orbitals have no overlap with the bands at some '
3599 'k-point (singular projection): choose other trial orbitals, or '
3600 'another mesh. A band group with a nonzero Chern number has no '
3601 'localized Wannier functions, and any projection vanishes '
3602 'somewhere.\n')