Coverage for tbkit/error_handling.py: 100%

1289 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-30 11:04 +0000

1import numpy as np 

2import inspect 

3 

4 

5ATOL = 1e-3 # matches the distance/angle tolerance used throughout tbkit.system 

6 

7 

8############################### 

9# GENERIC EXCEPTION HANDLING 

10############################### 

11 

12 

13def boolean(var, var_name): 

14 ''' 

15 Check if *var* is a boolean. 

16 

17 Raises 

18 ------ 

19 TypeError 

20 Parameter *var* must be a bool. 

21 ''' 

22 if not isinstance(var, bool): 

23 raise TypeError('\n\nParameter {} must be a bool.\n'.format(var_name)) 

24 

25 

26def positive_int(var, var_name): 

27 ''' 

28 Check if *var* is a positive integer. 

29 

30 Raises 

31 ------ 

32 TypeError 

33 Parameter *var* must be an integer. 

34 ValueError 

35 Parameter *var* must be a positive integer. 

36 ''' 

37 if not isinstance(var, int): 

38 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name)) 

39 if var < 1: 

40 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name)) 

41 

42 

43def positive_int_lim(var, var_name, nmax): 

44 ''' 

45 Check if *var* is a positive integer smaller than nmax. 

46 

47 Raises 

48 ------ 

49 TypeError 

50 Parameter *var* must be an integer. 

51 ValueError 

52 Parameter *var* must be a positive integer. 

53 ValueError 

54 Parameter *var* must be a positive integer 

55 smaller than nmax. 

56 ''' 

57 if not isinstance(var, int): 

58 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name)) 

59 if var < 1: 

60 raise ValueError('\n\nParameter {} must be a positive integer.\n'.format(var_name)) 

61 if var > nmax: 

62 raise ValueError('\n\nParameter {} must be a positive integer ' 

63 'smaller than {}.\n'.format(var_name, nmax)) 

64 

65 

66def real_number(var, var_name): 

67 ''' 

68 Check if parameter *var* is a real number. 

69 

70 Raises 

71 ------ 

72 TypeError 

73 Parameter *var* must be a real number. 

74 ''' 

75 if not isinstance(var, (int, float)): 

76 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name)) 

77 

78 

79def positive_real(var, var_name): 

80 ''' 

81 Check if parameter *var* is a positive number. 

82 

83 Raises 

84 ------ 

85 TypeError 

86 Parameter *var* must be a real number. 

87 ValueError 

88 Parameter *var* must be a positive number. 

89 ''' 

90 if not isinstance(var, (int, float)): 

91 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name)) 

92 if var <= 0: 

93 raise ValueError('\n\nParameter {} must be a positive number.\n'.format(var_name)) 

94 

95 

96def positive_real_zero(var, var_name): 

97 ''' 

98 Check if parameter *var* is a positive number or zero. 

99 

100 Raises 

101 ------ 

102 TypeError 

103 Parameter *var* must be a real number. 

104 ValueError 

105 Parameter *var* must be a positive number or zero. 

106 ''' 

107 if not isinstance(var, (int, float)): 

108 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name)) 

109 if var < 0: 

110 raise ValueError('\n\nParameter {} must be a positive number or zero.\n'.format(var_name)) 

111 

112 

113def negative_real(var, var_name): 

114 ''' 

115 Check if parameter *var* is a negative number. 

116 

117 Raises 

118 ------ 

119 TypeError 

120 Parameter *var* must be a real number. 

121 ValueError 

122 Parameter *var* must be a negative number. 

123 ''' 

124 if not isinstance(var, (int, float)): 

125 raise TypeError('\n\nParameter {} must be a real number.\n'.format(var_name)) 

126 if var >= 0: 

127 raise ValueError('\n\nParameter {} must be a negative number.\n'.format(var_name)) 

128 

129 

130def number(var, var_name): 

131 ''' 

132 Check if parameter *var* is a number. 

133 

134 Raises 

135 ------ 

136 TypeError 

137 Parameter *var* must be a real or complex number. 

138 ''' 

139 if not isinstance(var, (int, float, complex)): 

140 raise TypeError('\n\nParameter {} must be a real or complex number.\n'.format(var_name)) 

141 

142 

143def value_params(required, params, func): 

144 ''' 

145 Check that *params* holds every parameter that the value function *func* 

146 needs (see *tbkit.values*). 

147 

148 Raises 

149 ------ 

150 TypeError 

151 The value function needs parameters missing from get_ham. 

152 ''' 

153 missing = [name for name in required if name not in params] 

154 if missing: 

155 raise TypeError('\n\nThe value function {} needs the parameter(s) {}: ' 

156 'pass them to get_ham (or KSpace.set_params).\n'.format( 

157 getattr(func, '__name__', func), ', '.join(missing))) 

158 

159 

160def value_shape(val, size, spin, func): 

161 ''' 

162 Check the values returned by the value function *func* for *size* 

163 bonds or sites: a number or one per entry (or, if *spin*, also a 2x2 

164 matrix or one per entry). 

165 

166 Raises 

167 ------ 

168 ValueError 

169 The value function must return a number or an array of one value per bond/site. 

170 ''' 

171 shapes = [(), (size,)] + ([(2, 2), (size, 2, 2)] if spin else []) 

172 if val.shape not in shapes: 

173 raise ValueError('\n\nThe value function {} returned shape {}: it must return a number, ' 

174 'or one per bond/site (shape ({},)){}.\n'.format( 

175 getattr(func, '__name__', func), val.shape, size, 

176 ', or a 2x2 matrix (one per bond, shape ({}, 2, 2))'.format(size) 

177 if spin else '')) 

178 

179 

180def no_value_functions(model, method): 

181 ''' 

182 Check that *model* has no value functions (callable hoppings or onsite 

183 energies), which *method* cannot represent. 

184 

185 Raises 

186 ------ 

187 ValueError 

188 method does not support value functions. 

189 ''' 

190 if getattr(model, '_hop_values', None) or getattr(model, '_onsite_values', None): 

191 raise ValueError('\n\n{} does not support value functions (callable hoppings or ' 

192 'onsite energies): use plain numbers.\n'.format(method)) 

193 

194 

195def is_callable(var, var_name): 

196 ''' 

197 Check if parameter *var* is callable. 

198 

199 Raises 

200 ------ 

201 TypeError 

202 Parameter *var* must be callable. 

203 ''' 

204 if not callable(var): 

205 raise TypeError('\n\nParameter {} must be callable.\n'.format(var_name)) 

206 

207 

208def larger(var1, var_name1, var2, var_name2): 

209 ''' 

210 Check if *var1* larger than *val*. 

211 

212 Raises 

213 ------ 

214 ValueError 

215 Parameter *var1* larger than *var2*. 

216 ''' 

217 if var1 >= var2: 

218 raise ValueError('\n\n{} must be larger than {}.\n' 

219 .format(var_name1, var_name2)) 

220 

221 

222def smaller(var1, var_name1, var2, var_name2): 

223 ''' 

224 Check if *var1* smaller than *var2*. 

225 

226 Raises 

227 ------ 

228 ValueError 

229 Parameter *var1* must be smaller than *var2*. 

230 ''' 

231 if var1 >= var2: 

232 raise ValueError('\n\n{} must be smaller than {}.\n' 

233 .format(var_name1, var_name2)) 

234 

235 

236def string(var, var_name): 

237 ''' 

238 Check if parameter *var* is a string. 

239 

240 Raises 

241 ------ 

242 TypeError 

243 Parameter *var* must be a string. 

244 ''' 

245 if var is None: 

246 return 

247 if not isinstance(var, str): 

248 raise TypeError('\n\nParameter {} must be a string.\n'.format(var_name)) 

249 

250 

251def ndarray(var, var_name, length): 

252 ''' 

253 Check if parameter *var* is a numpy array. 

254 

255 Raises 

256 ------ 

257 TypeError 

258 Parameter *var* must be a numpy ndarray. 

259 ValueError 

260 length array must be equal to length. 

261 ''' 

262 if not isinstance(var, np.ndarray): 

263 raise TypeError('\n\nParameter {} must be a numpy ndarray.\n'.format(var_name)) 

264 if len(var) != length: 

265 raise ValueError('\n\nParameter {} must be a numpy ndarray of {}\n' 

266 ''.format(var_name, length)) 

267 

268 

269def ndarray_null(var, var_name): 

270 ''' 

271 Check if parameter *var* is not a null numpy array. 

272 

273 Raises 

274 ------ 

275 ValueError 

276 Parameter *var* must not be a null numpy ndarray. 

277 ''' 

278 array_null = np.zeros(len(var)) 

279 if np.allclose(var, array_null): 

280 raise ValueError('\n\nParameter {} must not be a null numpy ndarray.\n'.format(var_name)) 

281 

282 

283def ndarray_empty(var, var_name): 

284 ''' 

285 Check if parameter *var* is not an empty numpy array. 

286 

287 Raises 

288 ------ 

289 ValueError 

290 Parameter *var* must not be an emptynumpy ndarray. 

291 ''' 

292 if var.size == 0: 

293 raise ValueError('\n\nParameter {} must not be an empty numpy ndarray.\n'.format(var_name)) 

294 

295 

296def list_tuple_2elem(var, var_name): 

297 ''' 

298 Check if parameter *var* is a list/tuple with 2 elements. 

299 

300 Raises 

301 ------ 

302 TypeError 

303 Parameter *var* must be a list/tuple. 

304 ValueError 

305 Parameter *var* must contain 2 elements. 

306 ''' 

307 if var is None: 

308 return 

309 if not isinstance(var, (list, tuple)): 

310 raise TypeError('\n\nParameter {} must be a list/tuple.\n'.format(var_name)) 

311 if len(var) != 2: 

312 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name)) 

313 

314 

315def tuple_2elem(var, var_name): 

316 ''' 

317 Check if parameter *var* is a tuple with 2 elements. 

318 

319 Raises 

320 ------ 

321 TypeError 

322 Parameter *var* must be a tuple. 

323 ValueError 

324 Parameter *var* must contain 2 elements. 

325 ''' 

326 if var is None: 

327 return 

328 if not isinstance(var, tuple): 

329 raise TypeError('\n\nParameter {} must be a tuple.\n'.format(var_name)) 

330 if len(var) != 2: 

331 raise ValueError('\n\nParameter {} must be a list/tuple of length two.\n'.format(var_name)) 

332 

333 

334############################### 

335# LATTICE EXCEPTION HANDLING 

336############################### 

337 

338 

339def lat(lat): 

340 ''' 

341 Check if parameter is an instance of the *lattice*. 

342 

343 Raises 

344 ------ 

345 TypeError 

346 Parameter must be an instance of the class lattice. 

347 ''' 

348 names = inspect.getmro(lat.__class__) 

349 if str(names).find('lattice') == -1: 

350 raise TypeError('\n\nParameter must be an instance of the class lattice.\n') 

351 

352 

353def unit_cell(unit_cell): 

354 ''' 

355 Check parameter *unit_cell*. 

356 

357 Raises 

358 ------ 

359 TypeError 

360 Parameter unit_cell must be a list. 

361 KeyError 

362 Dictionaries must contain the key "tag". 

363 KeyError 

364 Dictionaries must contain the key "r0". 

365 TypeError 

366 Key "tags" must contain a one-character string. 

367 ValueError 

368 Key "tags" must contain a one-character string. 

369 ValueError 

370 Key "r0" must contain be a list. 

371 TypeError 

372 Key "r0" must contain be a tuple. 

373 ValueError 

374 Key "r0" must contain a tuple of length two. 

375 ValueError 

376 Key "r0" must contain a tuple of two real numbers. 

377 ''' 

378 if not isinstance(unit_cell, list): 

379 raise TypeError('\n\nParameter unit_cell must be a list.\n') 

380 for dic in unit_cell: 

381 if 'tag' not in dic: 

382 raise KeyError('\n\nDictionaries must contain the key "tag".\n') 

383 if 'r0' not in dic: 

384 raise KeyError('\n\nDictionaries must contain the key "r0".\n') 

385 if not isinstance(dic['tag'], str): 

386 raise TypeError('\n\nKey "tag" must contain a one-character string.\n') 

387 if not len(dic['tag']) == 1: 

388 raise ValueError('\n\nKey "tag" must be a one-character string.\n') 

389 if not isinstance(dic['r0'], tuple): 

390 raise TypeError('\n\nKey "r0" must be a tuple.\n') 

391 if len(dic['r0']) not in (2, 3): 

392 raise ValueError('\n\nKey "r0" must contain a tuple of length two (or three, ' 

393 'for a lattice in 3D space).\n') 

394 if not all(isinstance(c, (int, float)) for c in dic['r0']): 

395 raise ValueError('\n\nKey "r0" must contain a tuple of real numbers.\n') 

396 if len({len(dic['r0']) for dic in unit_cell}) > 1: 

397 raise ValueError('\n\nAll "r0" must have the same length.\n') 

398 

399 

400def prim_vec(prim_vec): 

401 ''' 

402 Check parameter *prim_vec*. 

403 

404 Raises 

405 ------ 

406 TypeError 

407 Parameter prim_vec must be a list. 

408 ValueError 

409 Parameter prim_vec must be a list 

410 of length 1 for 1D lattices or length 2 fro 2D lattices. 

411 TypeError 

412 List elements must be tuples. 

413 ValueError 

414 List elements must be 1 or 2 tuples. 

415 ValueError 

416 Tuples must be of length 2. 

417 ValueError 

418 Tuples must containt real numbers. 

419 ValueError 

420 Norm of coor should be larger than 0.1. 

421 ''' 

422 if not isinstance(prim_vec, list): 

423 raise TypeError('\n\nParameter prim_vec must be a list.\n') 

424 if len(prim_vec) not in (1, 2, 3): 

425 raise ValueError('\n\nParameter prim_vec must be a list of length 1 ' 

426 'for 1D lattices, 2 for 2D lattices, or 3 for 3D lattices.\n') 

427 for coor in prim_vec: 

428 if not isinstance(coor, tuple): 

429 raise TypeError('\n\nParameter prim_vec must contain tuples.\n') 

430 if len(coor) not in (2, 3): 

431 raise ValueError('\n\nParameter prim_vec must contain tuples of length 2 ' 

432 '(or 3, for a lattice in 3D space).\n') 

433 if not all(isinstance(c, (int, float)) for c in coor): 

434 raise ValueError('\n\nParameter prim_vec must contain tuples of ' 

435 'real numbers.\n') 

436 if sum(c ** 2 for c in coor) < 0.1: 

437 raise ValueError('\n\nEach primitive vector must have a norm larger ' 

438 'than 0.1.\n') 

439 if len({len(coor) for coor in prim_vec}) > 1: 

440 raise ValueError('\n\nAll primitive vectors must have the same length.\n') 

441 if len(prim_vec) > len(prim_vec[0]): 

442 raise ValueError('\n\nA lattice cannot have more primitive vectors than ' 

443 'space dimensions (use 3-tuples for a 3D lattice).\n') 

444 

445 

446def independent(prim_vec): 

447 ''' 

448 Check that the primitive vectors are linearly independent (needed for 

449 the reciprocal lattice). 

450 

451 Raises 

452 ------ 

453 ValueError 

454 The primitive vectors must be linearly independent. 

455 ''' 

456 if np.linalg.matrix_rank(np.array(prim_vec, dtype='f8'), tol=1e-8) != len(prim_vec): 

457 raise ValueError('\n\nThe primitive vectors must be linearly independent.\n') 

458 

459 

460def space_dim(unit_cell, prim_vec): 

461 ''' 

462 Check that the unit cell positions and the primitive vectors live in the 

463 same space (2D or 3D). 

464 

465 Raises 

466 ------ 

467 ValueError 

468 "r0" and prim_vec tuples must have the same length. 

469 ''' 

470 if unit_cell and len(unit_cell[0]['r0']) != len(prim_vec[0]): 

471 raise ValueError('\n\nThe "r0" positions and the primitive vectors must have ' 

472 'the same number of components.\n') 

473 

474 

475def get_lattice(prim_vec, n1, n2, n3=1): 

476 ''' 

477 Check method *get_lattice*. 

478 

479 Raises 

480 ------ 

481 TypeError 

482 Parameters n1, n2, n3 must be integers. 

483 ValueError 

484 Parameters n1, n2, n3 must be positive integers. 

485 ValueError 

486 n2 (n3) must be 1 with fewer than 2 (3) primitive vectors. 

487 ''' 

488 positive_int(n1, 'n1') 

489 positive_int(n2, 'n2') 

490 positive_int(n3, 'n3') 

491 if len(prim_vec) == 1 and n2 > 1: 

492 raise ValueError('\n\nParameter n2 should be equal to 1\n' 

493 'if one primitive vector is given\n') 

494 if len(prim_vec) < 3 and n3 > 1: 

495 raise ValueError('\n\nParameter n3 should be equal to 1\n' 

496 'if fewer than three primitive vectors are given\n') 

497 

498 

499def coor(coor, dtype=None): 

500 ''' 

501 Check if *coor* is a structured array with 

502 dtype=[('x', 'f8'), ('y', 'f8'), ('tag', 'U1')], or, for a lattice in 3D 

503 space, dtype=[('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')]. If 

504 *dtype* is given, *coor* must have exactly that dtype. 

505 ''' 

506 dtypes = [np.dtype([('x', 'f8'), ('y', 'f8'), ('tag', 'U1')]), 

507 np.dtype([('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('tag', 'U1')])] 

508 if dtype is not None: 

509 dtypes = [np.dtype(dtype)] 

510 if coor.dtype not in dtypes: 

511 raise TypeError('\n\nParameter coor dtype must be\n' 

512 'dtype=[("x", "f8"), ("y", "f8"), ("tag", "U1")]\n' 

513 '(with a ("z", "f8") field after "y" for a lattice in 3D space).\n') 

514 

515 

516def coor_1d(coor): 

517 ''' 

518 Check if *coor* is 1d (coor['y'] = cst). 

519 

520 Raises 

521 ------ 

522 ValueError 

523 *coor* must be 1d( coor['y'] = cst).. 

524 ''' 

525 if not np.allclose(coor['y'][0], coor['y']): 

526 raise ValueError('\n\ncoor["y"] must be constant.\n') 

527 

528 

529def remove_sites(index, sites): 

530 ''' 

531 Check method *remove_sites*. 

532 

533 Raises 

534 ------ 

535 TypeError 

536 Parameter index must be a list. 

537 ValueError 

538 Parameter index must be a list of integers. 

539 ValueError 

540 Indices must be between 0 and sites -1. 

541 of integers between 0 and sites 

542 ''' 

543 if not isinstance(index, list): 

544 raise TypeError('\n\nParameter index must be a list.\n') 

545 if not all(isinstance(i, int) for i in index): 

546 raise ValueError('\n\nParameter index must be a list of integers.\n') 

547 if not all(-1 < i < sites for i in index): 

548 raise ValueError('\n\nElements of index must be between 0 and sites - 1.\n') 

549 

550 

551def shift(shift): 

552 ''' 

553 Check *shift_x* and *shift_y*. 

554 

555 Raises 

556 ------ 

557 TypeError 

558 Parameter delta must be a real number. 

559 ''' 

560 if not isinstance(shift, (int, float)): 

561 raise TypeError('\n\nParameter shift must be a real number.\n') 

562 

563 

564def boundary_line(cx, cy, co): 

565 ''' 

566 Check *boundary_line*. 

567 

568 Raises 

569 ------ 

570 TypeError 

571 Parameter cx must be a real number. 

572 TypeError 

573 Parameter cy must be a real number. 

574 TypeError 

575 Parameter co must be a real number. 

576 ''' 

577 if not isinstance(cx, (int, float)): 

578 raise TypeError('\n\nParameter cx must be a real number.\n') 

579 if not isinstance(cy, (int, float)): 

580 raise TypeError('\n\nParameter cy must be a real number.\n') 

581 if not isinstance(co, (int, float)): 

582 raise TypeError('\n\nParameter co must be a real number.\n') 

583 

584 

585def space_3d(space_dim): 

586 ''' 

587 Check that the lattice lives in 3D space. 

588 

589 Raises 

590 ------ 

591 ValueError 

592 This method requires a lattice in 3D space. 

593 ''' 

594 if space_dim != 3: 

595 raise ValueError('\n\nThis method requires a lattice in 3D space ' 

596 '(unit_cell and prim_vec given as 3-tuples).\n') 

597 

598 

599def sites(sites): 

600 ''' 

601 Check if *get_lattice* has been called (*coor* not empty). 

602 

603 Raises 

604 ------ 

605 RuntimeError 

606 Run method lat.get_lattice first. 

607 ''' 

608 if sites == 0: 

609 raise RuntimeError('\n\nRun method lat.get_lattice first.\n') 

610 

611 

612#################################### 

613# CLASS SYSTEM EXCEPTION HANDLING 

614#################################### 

615 

616 

617def sys(sys): 

618 ''' 

619 Check if parameter is an instance of the *system*. 

620 

621 Raises 

622 ------ 

623 TypeError 

624 Parameter must be an instance of the class system. 

625 ''' 

626 names = inspect.getmro(sys.__class__) 

627 if str(names).find('system') == -1: 

628 raise TypeError('\n\nParameter must be an instance of the class system.\n') 

629 

630 

631def print_hopping(n, nmax): 

632 ''' 

633 Check method *print_vec_hopping*. 

634 

635 Raises 

636 ------ 

637 TypeError 

638 Parameter *nmax* must be an integer. 

639 ValueError 

640 Parameter *nmax* must be a positive integer. 

641 between 1 and n_max-1. 

642 ''' 

643 if not isinstance(n, int): 

644 raise TypeError('\n\nParameter n_max must be an integer.\n') 

645 if n < 1 or n > nmax-1: 

646 raise ValueError('\n\nParameter n_max must be a positive integer' 

647 'between 1 and n_max-1.\n') 

648 

649 

650def set_onsite(onsite, tags): 

651 ''' 

652 Check method *set_onsite*. 

653 

654 Raises 

655 ------ 

656 TypeError 

657 Parameter onsite must be a dictionary. 

658 ValueError 

659 Parameter onsite keys must be a tag. 

660 ValueError 

661 Parameter onsite values must be 

662 real and/or complex numbers, or value functions. 

663 ''' 

664 if not isinstance(onsite, dict): 

665 raise TypeError('\n\nParameter onsite must be a dictionary.\n') 

666 for tag, val in onsite.items(): 

667 if tag not in tags: 

668 raise ValueError('\n\nParameter onsite keys must be a tag.\n') 

669 if not isinstance(val, (int, float, complex)) and not callable(val): 

670 raise ValueError('\n\nParameter onsite values must be\n'\ 

671 'real and/or complex numbers, or value functions.\n') 

672 

673 

674def set_hopping(list_hop, n_max): 

675 ''' 

676 Check method *set_hopping*. 

677 

678 Raises 

679 ------ 

680 TypeError 

681 Parameter *list_hop* must be a list. 

682 TypeError 

683 Parameter *list_hop* must be a list of dictionary. 

684 KeyError 

685 "n" and "t" must be dictionary keys. 

686 KeyError 

687 "tag" or "ang" must be a key. 

688 KeyError 

689 "tag" and "ang" must be keys. 

690 ValueError 

691 Dictionaries must be of length 2, 3, or 4. 

692 ValueError 

693 "n" must be between 1 and nmax" 

694 ''' 

695 if not isinstance(list_hop, list): 

696 raise TypeError('\n\nParameter *list_hop* must be a list.\n') 

697 for dic in list_hop: 

698 if not isinstance(dic, dict): 

699 raise TypeError('\n\nParameter *list_hop* must be a list of dictionary.\n') 

700 if 'n' not in dic or 't' not in dic: 

701 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n') 

702 if not isinstance(dic['n'], int): 

703 raise TypeError('\n\n"n" value must be an integer.\n') 

704 if not 0 < dic['n'] <= n_max: 

705 raise ValueError('\n\n"n" value must be between 1 and {}.\n'.format(n_max)) 

706 if not isinstance(dic['t'], (int, float, complex)) and not callable(dic['t']): 

707 raise TypeError('\n\n"t" value must be a real or complex number, or a value function.\n') 

708 if len(dic) == 3: 

709 if 'tag' not in dic and 'ang' not in dic: 

710 raise KeyError('\n\n"tag" or "ang" must be a key.\n') 

711 elif len(dic) == 4: 

712 if 'tag' not in dic or 'ang' not in dic: 

713 raise KeyError('\n\n"tag" and "ang" must be keys.\n') 

714 elif len(dic) > 4: 

715 raise ValueError('\n\nDictionaries must be of length 2, 3, or 4.\n') 

716 if 'tag' in dic: 

717 if not isinstance(dic['tag'], str): 

718 raise TypeError('\n\n"tag" value must be a string.\n') 

719 if len(dic['tag']) != 2: 

720 raise ValueError('\n\n"tag" value must be a string of length 2.\n') 

721 if 'ang' in dic: 

722 if not isinstance(dic['ang'], (int, float)): 

723 raise TypeError('\n\n"ang" value must be a real number.\n') 

724 

725 

726def index(ind, dic): 

727 ''' 

728 check if *ind* not empy. 

729 ''' 

730 if np.sum(ind) == 0: 

731 raise ValueError('\n\nNo hoppings with parameters {}.\n'.format(dic)) 

732 

733 

734def set_hopping_def(hop, hopping_def, sites, var_name='hopping_def', same_site=False): 

735 ''' 

736 Check methods *set_hopping_def* and *set_hopping_manual*. 

737 

738 *hop* is not used (kept for backward compatibility). Indices may be 

739 Python or NumPy integers, values Python or NumPy numbers. If *same_site* 

740 is True, keys :math:`(i, i)` are accepted. 

741 

742 Raises 

743 ------ 

744 TypeError 

745 Parameter *hopping_def* must be a dictionary 

746 TypeError 

747 *hopping_def* keys must be tuples. 

748 TypeError 

749 *hopping_def* keys must be tuples of length 2. 

750 ValueError 

751 *hopping_def* keys must be tuples of integers. 

752 ValueError 

753 *hopping_def* keys must be integers between 0 and sites-1. 

754 ValueError 

755 *hopping_def* keys must be different integers between 0 and sites-1. 

756 TypeError 

757 *hopping_def* values must be numbers. 

758 ''' 

759 if not isinstance(hopping_def, dict): 

760 raise TypeError('\n\nParameter {} must be a dictionary.\n'.format(var_name)) 

761 for key, val in hopping_def.items(): 

762 if not isinstance(key, tuple): 

763 raise TypeError('\n\n{} keys must be tuples.\n'.format(var_name)) 

764 if len(key) != 2: 

765 raise TypeError('\n\n{} keys must be tuples of length 2.\n'.format(var_name)) 

766 if not isinstance(key[0], (int, np.integer)) or not isinstance(key[1], (int, np.integer)): 

767 raise ValueError('\n\n{} keys must be tuples of integers.\n'.format(var_name)) 

768 if key[0] < 0 or key[1] < 0 or key[0] > sites-1 or key[1] > sites-1: 

769 raise ValueError('\n\n{} keys must be integers between 0 and sites-1.\n'.format(var_name)) 

770 if key[0] == key[1] and not same_site: 

771 raise ValueError('\n\n{} keys must be different integers between 0 and sites-1.\n'.format(var_name)) 

772 if not isinstance(val, (int, float, complex, np.number)): 

773 raise TypeError('\n\n{} values must be numbers.\n'.format(var_name)) 

774 

775 

776def set_onsite_def(onsite_def, sites): 

777 ''' 

778 Check method *set_ons_def*. 

779 

780 Raises 

781 ------ 

782 TypeError 

783 Parameter *onsite_def* must be a dictionary. 

784 TypeError 

785 *onsite_def* keys must be integers. 

786 TypeError 

787 *onsite_def* values must be numbers. 

788 ValueError 

789 *onsite_def* keys must be integers between :math:`[0, sites)`. 

790 ''' 

791 if not isinstance(onsite_def, dict): 

792 raise TypeError('\n\nParameter onsite_def must be a dictionary.\n') 

793 for key, val in onsite_def.items(): 

794 if not isinstance(key, int): 

795 raise TypeError('\n\nonsite_def keys must be integers.\n') 

796 if not isinstance(val, (int, float, complex)): 

797 raise TypeError('\n\nonsite_def values must be numbers.\n') 

798 if key < 0 or key > sites-1: 

799 raise ValueError('\n\nonsite_def keys must be integers between 0 and sites-1.\n') 

800 

801 

802def hop_n1(hop): 

803 ''' 

804 Check method if self.hop contains nearest neighbours hoppings. 

805 

806 Raises 

807 ------ 

808 ValueError 

809 self.hop must contain nearest neighbours hoppings. 

810 ''' 

811 if not np.any(hop['n'] == 1): 

812 raise ValueError('\n\nParameter hop must contain nearest neighbours hoppings.\n') 

813 

814 

815def empty_onsite(onsite): 

816 ''' 

817 Check if *onsite* not empty. 

818 

819 Raises 

820 ------ 

821 RuntimeError 

822 Run method set_onsite first. 

823 ''' 

824 if onsite.size == 0: 

825 raise RuntimeError('\n\nRun method set_onsite first\n') 

826 

827 

828def empty_hop(hop): 

829 ''' 

830 Check if *hop* not empty. 

831 

832 Raises 

833 ------ 

834 RuntimeError 

835 Run method set_hopping first. 

836 ''' 

837 if hop.size == 0: 

838 raise RuntimeError('\n\nRun method set_hopping first\n') 

839 

840 

841def hop_sites(hop, sites): 

842 ''' 

843 Check if *hop* indices are smaller than *sites*. 

844 

845 Raises 

846 ------ 

847 ValueError 

848 Run method sys.clear_hopping and redefine the hoppings. 

849 ''' 

850 ind_max = max(np.max(hop[f]) for f in ('i', 'j') if f in hop.dtype.names) 

851 if ind_max >= sites: 

852 raise ValueError('\n\nThe hoppings refer to sites that no longer exist.\n' 

853 'Run method system.clear_hopping ' 

854 'and redefine the hoppings.\n') 

855 

856 

857def empty_coor(coor): 

858 ''' 

859 Check if *coor* not empty. 

860 

861 Raises 

862 ------ 

863 RuntimeError 

864 Run method lattice.get_lattice first. 

865 ''' 

866 if coor.size == 0: 

867 raise RuntimeError('\n\nRun method lattice.get_lattice first.\n') 

868 

869 

870def empty_coor_hop(coor_hop): 

871 ''' 

872 Check if *coor_hop* not empty. 

873 

874 Raises 

875 ------ 

876 RuntimeError 

877 Run method system.get_coor_hop first. 

878 ''' 

879 if coor_hop.size == 0: 

880 raise RuntimeError('\n\nRun method system.get_coor_hop first.\n') 

881 

882 

883def empty_ham(ham): 

884 ''' 

885 Check if Hamiltonian not empty. 

886 

887 Raises 

888 ------ 

889 RuntimeError 

890 Run method system.get_ham first. 

891 ''' 

892 if not ham.nnz: 

893 raise RuntimeError('\n\nRun method system.get_ham first.\n') 

894 

895 

896def empty_en(en): 

897 ''' 

898 Check if *en* not empty. 

899 

900 Raises 

901 ------ 

902 RuntimeError 

903 Run method get_ham first. 

904 ''' 

905 if en.size == 0: 

906 raise RuntimeError('\n\nRun method get_eig first\n') 

907 

908 

909def empty_pola(pola): 

910 ''' 

911 Check if *pola* not empty. 

912 

913 Raises 

914 ------ 

915 RuntimeError 

916 Run method get_eig(eigenvec=True) first. 

917 ''' 

918 if pola.size == 0: 

919 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n') 

920 

921 

922def empty_vn(vn): 

923 ''' 

924 Check if *vn* not empty. 

925 

926 Raises 

927 ------ 

928 RuntimeError 

929 Run method get_eig(eigenvec=True) first. 

930 ''' 

931 if vn.size == 0: 

932 raise RuntimeError('\n\nRun method get_eig(eigenvec=True) first\n') 

933 

934 

935def empty_ipr(ipr): 

936 ''' 

937 Check if *ipr* not empty. 

938 ''' 

939 if ipr.size == 0: 

940 raise RuntimeError('\n\nRun method get_ipr first\n') 

941 

942 

943def empty_ndarray(arr, method): 

944 ''' 

945 Check if *arr* is a not empty np.ndarray. 

946 ''' 

947 if arr.size == 0: 

948 raise RuntimeError('\n\nRun method {} first\n'. format(method)) 

949 

950 

951def tag(tag, tags): 

952 ''' 

953 Check tag. 

954 

955 Raises 

956 ------ 

957 TypeError 

958 Parameter *tag* must be a string. 

959 ValueError 

960 Parameter *tag* is not in tags. 

961 ''' 

962 if not isinstance(tag, str): 

963 raise TypeError('\n\nParameter tag must be a one-character string.\n') 

964 if tag not in tags: 

965 raise ValueError('\n\nParameter tag is not in tags.\n') 

966 

967 

968def angle(angle, angles, upper_part): 

969 ''' 

970 Check angle. 

971 

972 Raises 

973 ------ 

974 TypeError 

975 Parameter *angle* must be 

976 

977 * a positive number if *upper_part* is True 

978 * a negative real if *upper_part* is False. 

979 ValueError 

980 Parameter *angle* is not in hop['ang']. 

981 ''' 

982 if upper_part: 

983 positive_real_zero(angle, 'angle, if upper_part=True,') 

984 else: 

985 negative_real(angle, 'angle, if upper_part=False,') 

986 if not np.sum(np.isclose(angle, angles, atol=ATOL)) and \ 

987 not np.sum(np.isclose(angle, angles - 180, atol=ATOL)): 

988 raise ValueError('\n\nParameter angle is not in hop["ang"].\n') 

989 

990 

991def lims(lims): 

992 ''' 

993 Check parameter *lims*. 

994 

995 Raises 

996 ------ 

997 TypeError 

998 Parameter lims must be a list. 

999 TypeError 

1000 Parameter *lims[0]* must be a real number. 

1001 TypeError 

1002 Parameter *lims[1]* must be a real number. 

1003 ValueError 

1004 *lims* must be a list of length 2. 

1005 ValueError 

1006 *lims[0]* must be smaller than *lims[1]*. 

1007 ''' 

1008 if lims is not None: 

1009 list_tuple_2elem(lims, 'lims') 

1010 real_number(lims[0], 'lims[0]') 

1011 real_number(lims[1], 'lims[1]') 

1012 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]') 

1013 

1014 

1015def lims_positive(lims): 

1016 ''' 

1017 Check parameter *lims*. 

1018 

1019 Raises 

1020 ------ 

1021 TypeError 

1022 Parameter lims must be a list. 

1023 TypeError 

1024 Parameter *lims[0]* must be a positive real number. 

1025 TypeError 

1026 Parameter *lims[1]* must be a positive real number. 

1027 ValueError 

1028 *lims* must be a list of length 2. 

1029 ValueError 

1030 *lims[0]* must be smaller than *lims[1]*. 

1031 ''' 

1032 if lims is not None: 

1033 list_tuple_2elem(lims, 'lims') 

1034 positive_real(lims[0], 'lims[0]') 

1035 positive_real(lims[1], 'lims[1]') 

1036 smaller(lims[0], 'lims[0]', lims[1], 'lims[1]') 

1037 

1038 

1039################################# 

1040# CLASS PLOT EXCEPTION HANDLING 

1041################################# 

1042 

1043 

1044def fig(fig): 

1045 ''' 

1046 Check if fig is an instance of *Figure*. 

1047 

1048 Raises 

1049 ------ 

1050 TypeError 

1051 fig must be an instance of *Figure*. 

1052 ''' 

1053 if not fig.__class__.__name__ == 'Figure': 

1054 raise TypeError('\n\nfig must be an instance of *Figure*.\n') 

1055 

1056 

1057def ani(ani): 

1058 ''' 

1059 Check if ani is an instance of *FuncAnimation*. 

1060 

1061 Raises 

1062 ------ 

1063 TypeError 

1064 ani must be an instance of *FuncAnimation*. 

1065 ''' 

1066 if not ani.__class__.__name__ == 'FuncAnimation': 

1067 raise TypeError('\n\nani must be an instance of *FuncAnimation*.\n') 

1068 

1069 

1070def file_format(file_format): 

1071 ''' 

1072 Check if file_format is a string 'png', 'pdf', 'ps', 'eps', or 'svg'. 

1073 

1074 Raises 

1075 ------ 

1076 TypeError 

1077 file_format must be a string. 

1078 ValueError 

1079 file_format must be a string given by 'png', 'pdf', 'ps', 'eps', or 'svg'. 

1080 ''' 

1081 if not isinstance(file_format, str): 

1082 raise TypeError('\n\nfile_format must be a string.\n') 

1083 if file_format not in ['png', 'pdf', 'ps', 'eps', 'svg']: 

1084 raise ValueError('\n\nfile_format must be a string given by,\n'\ 

1085 ' "png", "pdf", "ps", "eps", or "svg".\n') 

1086 

1087 

1088#################################### 

1089# PROPAGATION 

1090#################################### 

1091 

1092 

1093def get_pump(hams): 

1094 ''' 

1095 Check method *propagation.get_pumping*. 

1096 

1097 Raises 

1098 ------ 

1099 TypeError 

1100 hams must be a non-empty list. 

1101 RuntimeError 

1102 Run method system.get_ham first. 

1103 ''' 

1104 if not isinstance(hams, list) or not hams: 

1105 raise TypeError('\n\nhams must be a non-empty list.\n') 

1106 for ham in hams: 

1107 empty_ham(ham) 

1108 

1109def prop_type(prop_type): 

1110 string(prop_type, 'prop_type') 

1111 if prop_type not in ['real', 'imag', 'norm']: 

1112 raise ValueError('\n\nParameter prop_type must be a string:\n' 

1113 '"real", "imag", "norm".\n') 

1114 

1115 

1116#################################### 

1117# CLASS KSPACE EXCEPTION HANDLING 

1118#################################### 

1119 

1120 

1121def k_vector(vec, var_name, ndim): 

1122 ''' 

1123 Check that *vec* is a tuple/list of *ndim* real numbers. 

1124 

1125 Raises 

1126 ------ 

1127 TypeError 

1128 Parameter *var_name* must be a tuple/list. 

1129 ValueError 

1130 Parameter *var_name* must be of length *ndim*. 

1131 TypeError 

1132 Parameter *var_name* must contain real numbers. 

1133 ''' 

1134 if not isinstance(vec, (tuple, list, np.ndarray)): 

1135 raise TypeError('\n\nParameter {} must be a tuple, list, or ndarray.\n'.format(var_name)) 

1136 if len(vec) != ndim: 

1137 raise ValueError('\n\nParameter {} must be of length {}.\n'.format(var_name, ndim)) 

1138 for val in vec: 

1139 if not isinstance(val, (int, float, np.integer, np.floating)): 

1140 raise TypeError('\n\nParameter {} must contain real numbers.\n'.format(var_name)) 

1141 

1142 

1143def k_points(k, var_name, ndim): 

1144 ''' 

1145 Check that *k* is one k-point (see *k_vector*) or an array of shape 

1146 (nk, *ndim*) of real numbers. 

1147 

1148 Raises 

1149 ------ 

1150 ValueError 

1151 Parameter *var_name* must be of shape (nk, ndim). 

1152 ''' 

1153 if np.ndim(k) != 2: 

1154 k_vector(k, var_name, ndim) 

1155 return 

1156 k = np.asarray(k) 

1157 if k.shape[1] != ndim or not np.issubdtype(k.dtype, np.number) or np.iscomplexobj(k): 

1158 raise ValueError('\n\nParameter {} must be a real array of shape (nk, {}).\n'.format(var_name, ndim)) 

1159 

1160 

1161def ks(ks, ndim): 

1162 ''' 

1163 Check the k-points passed to *kspace.get_bands*: a 2D array of shape 

1164 (nk, *ndim*). 

1165 

1166 Raises 

1167 ------ 

1168 ValueError 

1169 Parameter ks must be of shape (nk, ndim). 

1170 ''' 

1171 if ks.ndim != 2 or ks.shape[1] != ndim: 

1172 raise ValueError('\n\nParameter ks must be an array of shape (nk, {}).\n'.format(ndim)) 

1173 

1174 

1175def spin_matrix(t, var_name): 

1176 ''' 

1177 Check a spinful "t" or onsite value: either a plain number, or a 2x2 

1178 complex matrix. 

1179 

1180 Raises 

1181 ------ 

1182 TypeError 

1183 Parameter *var_name* must be a number or a 2x2 matrix. 

1184 ''' 

1185 if isinstance(t, (int, float, complex)): 

1186 return 

1187 t = np.asarray(t) 

1188 if t.shape != (2, 2): 

1189 raise TypeError('\n\nParameter {} must be a number or a 2x2 matrix.\n'.format(var_name)) 

1190 

1191 

1192def set_hopping_kspace(list_hop, n_sites, ndim, spin=False, values=False): 

1193 ''' 

1194 Check method *kspace.set_hopping*. With *values*, "t" may also be a 

1195 value function (a callable). 

1196 

1197 Raises 

1198 ------ 

1199 TypeError 

1200 Parameter *list_hop* must be a list of dictionaries. 

1201 KeyError 

1202 "i", "j", "R", and "t" must be dictionary keys. 

1203 ValueError 

1204 "i" and "j" must be site indices between 0 and n_sites-1. 

1205 ValueError 

1206 "R" must be a tuple of *ndim* integers. 

1207 TypeError 

1208 "t" must be a real or complex number (or, if *spin*, 

1209 a 2x2 matrix). 

1210 ''' 

1211 if not isinstance(list_hop, list): 

1212 raise TypeError('\n\nParameter list_hop must be a list.\n') 

1213 for dic in list_hop: 

1214 if not isinstance(dic, dict): 

1215 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n') 

1216 if not {'i', 'j', 'R', 't'} <= set(dic): 

1217 raise KeyError('\n\n"i", "j", "R", and "t" must be dictionary keys.\n') 

1218 if not isinstance(dic['i'], int) or not isinstance(dic['j'], int): 

1219 raise TypeError('\n\n"i" and "j" must be integers.\n') 

1220 if not (0 <= dic['i'] < n_sites) or not (0 <= dic['j'] < n_sites): 

1221 raise ValueError('\n\n"i" and "j" must be site indices between 0 and {}.\n'.format(n_sites-1)) 

1222 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim: 

1223 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim)) 

1224 if not all(isinstance(n, int) for n in dic['R']): 

1225 raise TypeError('\n\n"R" must be a tuple of integers.\n') 

1226 if dic['i'] == dic['j'] and dic['R'] == (0,) * ndim: 

1227 raise ValueError('\n\nUse kspace.set_onsite for i == j and R == 0 ' 

1228 '(it accepts a 2x2 spin matrix when spin=True).\n') 

1229 if values and callable(dic['t']): 

1230 continue 

1231 if spin: 

1232 spin_matrix(dic['t'], '"t"') 

1233 elif not isinstance(dic['t'], (int, float, complex)): 

1234 raise TypeError('\n\n"t" value must be a real or complex number.\n') 

1235 

1236 

1237def set_onsite_kspace(dict_onsite, tags, spin=False): 

1238 ''' 

1239 Check method *kspace.set_onsite*. 

1240 

1241 Raises 

1242 ------ 

1243 TypeError 

1244 Parameter *dict_onsite* must be a dictionary. 

1245 ValueError 

1246 keys must be tags. 

1247 TypeError 

1248 values must be real or complex numbers (or, if 

1249 *spin*, a pair of real/complex numbers, or a 2x2 matrix). 

1250 ''' 

1251 if not isinstance(dict_onsite, dict): 

1252 raise TypeError('\n\nParameter dict_onsite must be a dictionary.\n') 

1253 for tag, val in dict_onsite.items(): 

1254 if tag not in tags: 

1255 raise ValueError('\n\nParameter dict_onsite keys must be a tag.\n') 

1256 if spin and not isinstance(val, (int, float, complex)): 

1257 is_pair = (isinstance(val, (tuple, list)) and len(val) == 2 

1258 and all(isinstance(v, (int, float, complex)) for v in val)) 

1259 is_mat = np.ndim(val) == 2 and np.shape(val) == (2, 2) 

1260 if not (is_pair or is_mat): 

1261 raise TypeError('\n\nParameter dict_onsite values must be a number, or, ' 

1262 'if spin, a pair of numbers (E_up, E_down) or a ' 

1263 '2x2 matrix.\n') 

1264 elif not spin and not isinstance(val, (int, float, complex)): 

1265 raise TypeError('\n\nParameter dict_onsite values must be real and/or complex numbers.\n') 

1266 

1267 

1268def k_path_points(points, ndim): 

1269 ''' 

1270 Check parameter *points* used by *kspace.k_path*. 

1271 

1272 Raises 

1273 ------ 

1274 TypeError 

1275 Parameter points must be a list. 

1276 ValueError 

1277 Parameter points must contain at least two k-points. 

1278 ''' 

1279 if not isinstance(points, list): 

1280 raise TypeError('\n\nParameter points must be a list of k-points.\n') 

1281 if len(points) < 2: 

1282 raise ValueError('\n\nParameter points must contain at least two k-points.\n') 

1283 for i, pt in enumerate(points): 

1284 k_vector(pt, 'points[{}]'.format(i), ndim) 

1285 

1286 

1287#################################### 

1288# DENSITY OF STATES 

1289#################################### 

1290 

1291 

1292def dos_kernel(kernel): 

1293 ''' 

1294 Check parameter *kernel* used by *dos.density_of_states*. 

1295 

1296 Raises 

1297 ------ 

1298 TypeError 

1299 Parameter kernel must be a string. 

1300 ValueError 

1301 Parameter kernel must be "gaussian" or "lorentzian". 

1302 ''' 

1303 string(kernel, 'kernel') 

1304 if kernel not in ['gaussian', 'lorentzian']: 

1305 raise ValueError('\n\nParameter kernel must be a string:\n' 

1306 '"gaussian", "lorentzian".\n') 

1307 

1308 

1309def kspace_dos_kernel(kernel): 

1310 ''' 

1311 Check parameter *kernel* used by *kspace.plot_dos*. 

1312 

1313 Raises 

1314 ------ 

1315 TypeError 

1316 Parameter kernel must be a string. 

1317 ValueError 

1318 Parameter kernel must be "gaussian", "lorentzian" or "tetrahedron". 

1319 ''' 

1320 string(kernel, 'kernel') 

1321 if kernel not in ['gaussian', 'lorentzian', 'tetrahedron']: 

1322 raise ValueError('\n\nParameter kernel must be a string:\n' 

1323 '"gaussian", "lorentzian", "tetrahedron".\n') 

1324 

1325 

1326def mesh_energies(energies): 

1327 ''' 

1328 Check parameter *energies* used by *dos.tetrahedron_dos*: a non-empty 

1329 array of bands on a mesh of 1, 2 or 3 dimensions. 

1330 

1331 Raises 

1332 ------ 

1333 ValueError 

1334 Parameter energies must be of shape (n1, ..., nbands), on a mesh of 1 to 3 dimensions. 

1335 ''' 

1336 energies = np.asarray(energies) 

1337 if energies.ndim not in (2, 3, 4) or energies.size == 0: 

1338 raise ValueError('\n\nParameter energies must be a non-empty array of shape ' 

1339 '(n1, ..., nbands), on a mesh of 1, 2 or 3 dimensions.\n') 

1340 

1341 

1342def increasing_grid(grid, var_name): 

1343 ''' 

1344 Check that *grid* is a 1D real array of at least two increasing values. 

1345 

1346 Raises 

1347 ------ 

1348 ValueError 

1349 Parameter *var_name* must be a 1D array of at least two increasing real values. 

1350 ''' 

1351 grid = np.asarray(grid) 

1352 if grid.ndim != 1 or len(grid) < 2 or np.iscomplexobj(grid) or np.any(np.diff(grid) <= 0): 

1353 raise ValueError('\n\nParameter {} must be a 1D array of at least two increasing ' 

1354 'real values.\n'.format(var_name)) 

1355 

1356 

1357def nk(nk, ndim): 

1358 ''' 

1359 Check parameter *nk* used by *kspace.mesh_bands* / *kspace.berry_curvature*. 

1360 

1361 Raises 

1362 ------ 

1363 TypeError 

1364 Parameter nk must be an integer or a tuple of integers. 

1365 ValueError 

1366 Parameter nk (or each of its elements) must be a 

1367 positive integer. 

1368 ValueError 

1369 Parameter nk must be a tuple of length *ndim*. 

1370 ''' 

1371 if isinstance(nk, int): 

1372 positive_int(nk, 'nk') 

1373 return 

1374 if not isinstance(nk, tuple): 

1375 raise TypeError('\n\nParameter nk must be an integer or a tuple of integers.\n') 

1376 if len(nk) != ndim: 

1377 raise ValueError('\n\nParameter nk must be of length {}.\n'.format(ndim)) 

1378 for n in nk: 

1379 positive_int(n, 'nk') 

1380 

1381 

1382#################################### 

1383# TOPOLOGY 

1384#################################### 

1385 

1386 

1387def dim_2(dim): 

1388 ''' 

1389 Check that the model is 2D. Berry curvature / Chern number are only 

1390 defined for a 2D Brillouin zone. 

1391 

1392 Raises 

1393 ------ 

1394 ValueError 

1395 This calculation requires a 2D lattice. 

1396 ''' 

1397 if dim != 2: 

1398 raise ValueError('\n\nThis calculation requires a 2D lattice ' 

1399 '(two primitive vectors).\n') 

1400 

1401 

1402def dim_min(dim, dmin): 

1403 ''' 

1404 Check that the model has at least *dmin* periodic dimensions. 

1405 

1406 Raises 

1407 ------ 

1408 ValueError 

1409 This calculation requires a lattice with at least dmin primitive vectors. 

1410 ''' 

1411 if dim < dmin: 

1412 raise ValueError('\n\nThis calculation requires a lattice with at least {} ' 

1413 'primitive vectors.\n'.format(dmin)) 

1414 

1415 

1416def plane(plane, dim): 

1417 ''' 

1418 Check parameter *plane* of *kspace.berry_curvature*: two distinct 

1419 reciprocal-vector indices below *dim*. 

1420 

1421 Raises 

1422 ------ 

1423 TypeError 

1424 Parameter plane must be a tuple of two integers. 

1425 ValueError 

1426 Parameter plane must contain two distinct indices below dim. 

1427 ''' 

1428 if not isinstance(plane, tuple) or len(plane) != 2 or \ 

1429 not all(isinstance(p, int) for p in plane): 

1430 raise TypeError('\n\nParameter plane must be a tuple of two integers.\n') 

1431 if plane[0] == plane[1] or not all(0 <= p < dim for p in plane): 

1432 raise ValueError('\n\nParameter plane must contain two distinct indices ' 

1433 'between 0 and {}.\n'.format(dim - 1)) 

1434 

1435 

1436def direction(direction, dim=2): 

1437 ''' 

1438 Check parameter *direction* used by *kspace.ribbon* (and the Wilson-loop 

1439 methods): a primitive-vector index below *dim*. 

1440 

1441 Raises 

1442 ------ 

1443 TypeError 

1444 Parameter direction must be an integer. 

1445 ValueError 

1446 Parameter direction must be between 0 and dim-1. 

1447 ''' 

1448 if not isinstance(direction, int): 

1449 raise TypeError('\n\nParameter direction must be an integer.\n') 

1450 if not 0 <= direction < dim: 

1451 raise ValueError('\n\nParameter direction must be between 0 and {}.\n'.format(dim - 1)) 

1452 

1453 

1454def band_indices(bands, norb): 

1455 ''' 

1456 Check parameter *bands* used by *kspace.berry_curvature*. 

1457 

1458 Raises 

1459 ------ 

1460 TypeError 

1461 Parameter bands must be a non-empty list of integers. 

1462 ValueError 

1463 Parameter bands must be a list of distinct band 

1464 indices between 0 and norb-1. 

1465 ''' 

1466 if not isinstance(bands, list) or not bands: 

1467 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n') 

1468 if not all(isinstance(b, int) for b in bands): 

1469 raise TypeError('\n\nParameter bands must be a non-empty list of integers.\n') 

1470 if len(set(bands)) != len(bands): 

1471 raise ValueError('\n\nParameter bands must be a list of distinct band indices.\n') 

1472 if not all(0 <= b < norb for b in bands): 

1473 raise ValueError('\n\nParameter bands must be integers between 0 and {}.\n'.format(norb-1)) 

1474 

1475 

1476#################################### 

1477# BERRY PHASES, Z2, SYMMETRIES 

1478#################################### 

1479 

1480 

1481def k_perp(k_perp, n): 

1482 ''' 

1483 Check parameter *k_perp* of the Wilson-loop methods: a tuple of *n* real 

1484 numbers (fractional coordinates along the other reciprocal vectors). 

1485 

1486 Raises 

1487 ------ 

1488 ValueError 

1489 Parameter k_perp must contain n real numbers. 

1490 ''' 

1491 if len(k_perp) != n or not all(isinstance(f, (int, float)) for f in k_perp): 

1492 raise ValueError('\n\nParameter k_perp must contain {} real number(s).\n'.format(n)) 

1493 

1494 

1495def flow(flow, direction, dim): 

1496 ''' 

1497 Check parameter *flow* of *kspace.wannier_flow*. 

1498 

1499 Raises 

1500 ------ 

1501 TypeError 

1502 Parameter flow must be an integer. 

1503 ValueError 

1504 Parameter flow must be a direction other than *direction*. 

1505 ''' 

1506 if not isinstance(flow, int): 

1507 raise TypeError('\n\nParameter flow must be an integer.\n') 

1508 if flow == direction or not 0 <= flow < dim: 

1509 raise ValueError('\n\nParameter flow must be a direction between 0 and {}, ' 

1510 'other than direction.\n'.format(dim - 1)) 

1511 

1512 

1513def even_bands(bands): 

1514 ''' 

1515 Check that *bands* is a list of an even number of band indices (Kramers pairs). 

1516 

1517 Raises 

1518 ------ 

1519 TypeError 

1520 Parameter bands must be a list. 

1521 ValueError 

1522 Parameter bands must contain an even number of bands. 

1523 ''' 

1524 if not isinstance(bands, list): 

1525 raise TypeError('\n\nParameter bands must be a list of band indices.\n') 

1526 if len(bands) % 2: 

1527 raise ValueError('\n\nParameter bands must contain an even number of bands ' 

1528 '(Kramers pairs).\n') 

1529 

1530 

1531def operator(mat, norb): 

1532 ''' 

1533 Check a symmetry operator: a (norb, norb) matrix. 

1534 

1535 Raises 

1536 ------ 

1537 ValueError 

1538 The operator must be a (norb, norb) matrix. 

1539 ''' 

1540 if mat.shape != (norb, norb): 

1541 raise ValueError('\n\nThe operator must be a ({0}, {0}) matrix.\n'.format(norb)) 

1542 

1543 

1544def parities(xi, tol=1e-6): 

1545 ''' 

1546 Check that inversion eigenvalues are +-1. 

1547 

1548 Raises 

1549 ------ 

1550 ValueError 

1551 The bands are not an inversion-invariant subspace. 

1552 ''' 

1553 if not np.allclose(np.abs(xi.real), 1., atol=tol) or not np.allclose(xi.imag, 0., atol=tol): 

1554 raise ValueError('\n\nThe parity eigenvalues are not +-1: the operator is not ' 

1555 'an inversion symmetry of these bands (or the bands are ' 

1556 'degenerate with the others at a TRIM).\n') 

1557 if np.sum(xi.real < 0) % 2: 

1558 raise ValueError('\n\nOdd number of negative parities: the bands are not ' 

1559 'Kramers pairs.\n') 

1560 

1561 

1562def k_map(k_map): 

1563 ''' 

1564 Check parameter *k_map* of *kspace.symmetry_error*. 

1565 

1566 Raises 

1567 ------ 

1568 ValueError 

1569 Parameter k_map must be 'minus' or 'identity'. 

1570 ''' 

1571 if k_map not in ('minus', 'identity'): 

1572 raise ValueError('\n\nParameter k_map must be "minus" or "identity".\n') 

1573 

1574 

1575def is_symmetry(err, tol): 

1576 ''' 

1577 Check that a symmetry error is below *tol*. 

1578 

1579 Raises 

1580 ------ 

1581 ValueError 

1582 The operator is not a symmetry of the model. 

1583 ''' 

1584 if err > tol: 

1585 raise ValueError('\n\nThe operator is not a symmetry of the model ' 

1586 '(deviation {:.3g}).\n'.format(err)) 

1587 

1588 

1589def symmetry_algebra(u, v, tol): 

1590 ''' 

1591 Check that two symmetry operators of the same kind agree up to a phase 

1592 (in *kspace.KSpace.non_hermitian_class*, *v* is a product of others). 

1593 

1594 Raises 

1595 ------ 

1596 ValueError 

1597 The symmetry operators leave a unitary symmetry: block-diagonalize it first. 

1598 ''' 

1599 phase = np.trace(u.conj().T @ v) / np.trace(u.conj().T @ u) 

1600 if not np.allclose(v, phase * u, atol=tol): 

1601 raise ValueError('\n\nThe symmetry operators leave a unitary symmetry (two products of ' 

1602 'the same kind differ by more than a phase): block-diagonalize ' 

1603 'the Hamiltonian first.\n') 

1604 

1605 

1606def square(sq, tol): 

1607 ''' 

1608 Check that the square of an antiunitary operator is +-1. 

1609 

1610 Raises 

1611 ------ 

1612 ValueError 

1613 The square of the operator must be +1 or -1. 

1614 ''' 

1615 if not np.allclose(sq, sq[0, 0] * np.eye(len(sq)), atol=tol) or \ 

1616 not np.isclose(abs(sq[0, 0]), 1., atol=tol): 

1617 raise ValueError('\n\nThe square of the antiunitary operator must be +1 or -1.\n') 

1618 

1619 

1620def hermitian(ham): 

1621 ''' 

1622 Check that a (sparse) Hamiltonian is Hermitian. 

1623 

1624 Raises 

1625 ------ 

1626 ValueError 

1627 This calculation requires a Hermitian Hamiltonian. 

1628 ''' 

1629 if (abs(ham - ham.conj().T) > 1e-12).nnz: 

1630 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n') 

1631 

1632 

1633def planar_cell(prim_vec): 

1634 ''' 

1635 Check that the lattice has two in-plane primitive vectors (a unit-cell area). 

1636 

1637 Raises 

1638 ------ 

1639 ValueError 

1640 Give the area per site explicitly. 

1641 ''' 

1642 if len(prim_vec) != 2 or len(prim_vec[0]) != 2: 

1643 raise ValueError('\n\nThe unit-cell area needs two in-plane primitive vectors: ' 

1644 'give the area per site explicitly.\n') 

1645 

1646 

1647def integer(var, var_name): 

1648 ''' 

1649 Check if *var* is an integer. 

1650 

1651 Raises 

1652 ------ 

1653 TypeError 

1654 Parameter *var* must be an integer. 

1655 ''' 

1656 if not isinstance(var, int): 

1657 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name)) 

1658 

1659 

1660def dim_exact(dim, d): 

1661 ''' 

1662 Check that the model has exactly *d* periodic dimensions. 

1663 

1664 Raises 

1665 ------ 

1666 ValueError 

1667 This calculation requires a lattice with d primitive vectors. 

1668 ''' 

1669 if dim != d: 

1670 raise ValueError('\n\nThis calculation requires a lattice with {} primitive ' 

1671 'vector(s).\n'.format(d)) 

1672 

1673 

1674def nonzero_number(var, var_name): 

1675 ''' 

1676 Check if *var* is a nonzero number. 

1677 

1678 Raises 

1679 ------ 

1680 TypeError 

1681 Parameter *var* must be a number. 

1682 ValueError 

1683 Parameter *var* must be nonzero. 

1684 ''' 

1685 number(var, var_name) 

1686 if var == 0: 

1687 raise ValueError('\n\nParameter {} must be nonzero.\n'.format(var_name)) 

1688 

1689 

1690def gbz_roots(n_roots, p): 

1691 ''' 

1692 Check that det[H(beta) - E] has roots on both sides of the GBZ. 

1693 

1694 Raises 

1695 ------ 

1696 ValueError 

1697 The model has no hoppings in both directions. 

1698 ''' 

1699 if p < 1 or n_roots <= p: 

1700 raise ValueError('\n\nThe generalized Brillouin zone needs hoppings in both ' 

1701 'directions along the chain.\n') 

1702 

1703 

1704#################################### 

1705# EXCEPTIONAL POINTS 

1706#################################### 

1707 

1708 

1709def ham_model(model, cls): 

1710 ''' 

1711 Check the model of *tbkit.exceptional*: a 2D **KSpace** instance, or a 

1712 callable of a point of a 2D parameter plane. 

1713 

1714 Raises 

1715 ------ 

1716 TypeError 

1717 Parameter model must be a KSpace instance or a callable. 

1718 ValueError 

1719 A KSpace model must have two primitive vectors. 

1720 ''' 

1721 if isinstance(model, cls): 

1722 if model.dim != 2: 

1723 raise ValueError('\n\nParameter model must be a KSpace with two primitive ' 

1724 'vectors (a 2D Brillouin zone).\n') 

1725 elif not callable(model): 

1726 raise TypeError('\n\nParameter model must be a KSpace instance, or a callable ' 

1727 'returning a square matrix.\n') 

1728 

1729 

1730def ham_matrix(ham): 

1731 ''' 

1732 Check the matrix returned by the callable model of *tbkit.exceptional*: 

1733 square and finite. 

1734 

1735 Raises 

1736 ------ 

1737 ValueError 

1738 The model must return a finite square matrix. 

1739 ''' 

1740 if ham.ndim != 2 or ham.shape[0] != ham.shape[1] or not np.all(np.isfinite(ham)): 

1741 raise ValueError('\n\nThe model must return a finite square matrix.\n') 

1742 

1743 

1744def loop(loop): 

1745 ''' 

1746 Check a closed loop of *tbkit.exceptional*: a callable of s in [0, 1] 

1747 returning a point of the plane, or the vertices of a polygon. 

1748 

1749 Raises 

1750 ------ 

1751 TypeError 

1752 Parameter loop must be a callable or an array of vertices. 

1753 ValueError 

1754 A polygon needs at least 3 vertices with 2 real coordinates. 

1755 ''' 

1756 if callable(loop): 

1757 return 

1758 try: 

1759 arr = np.asarray(loop, dtype='f8') 

1760 except (TypeError, ValueError): 

1761 arr = np.zeros(()) 

1762 if arr.ndim == 0: 

1763 raise TypeError('\n\nParameter loop must be a callable of s in [0, 1], or an ' 

1764 'array of polygon vertices.\n') 

1765 if arr.ndim != 2 or arr.shape[0] < 3 or arr.shape[1] != 2 or not np.all(np.isfinite(arr)): 

1766 raise ValueError('\n\nParameter loop must hold at least 3 vertices of 2 real ' 

1767 'coordinates, shape (m, 2).\n') 

1768 

1769 

1770def loop_point(point): 

1771 ''' 

1772 Check a point returned by a loop callable: 2 finite real numbers. 

1773 

1774 Raises 

1775 ------ 

1776 ValueError 

1777 The loop must return points of 2 real coordinates. 

1778 ''' 

1779 if point.shape != (2,) or not np.all(np.isfinite(point)): 

1780 raise ValueError('\n\nParameter loop must return points of 2 finite real ' 

1781 'coordinates.\n') 

1782 

1783 

1784def closed_loop(ham_start, ham_end): 

1785 ''' 

1786 Check that a loop is closed: the Hamiltonian at its end is the one at its start. 

1787 

1788 Raises 

1789 ------ 

1790 ValueError 

1791 The loop is not closed. 

1792 ''' 

1793 scale = max(1., float(np.max(np.abs(ham_start)))) 

1794 if np.max(np.abs(ham_end - ham_start)) > 1e-9 * scale: 

1795 raise ValueError('\n\nParameter loop must be closed: H(loop(1)) differs from ' 

1796 'H(loop(0)).\n') 

1797 

1798 

1799def band_pair(bands, norb): 

1800 ''' 

1801 Check a pair of band labels: two distinct integers between 0 and norb-1. 

1802 

1803 Raises 

1804 ------ 

1805 TypeError 

1806 Parameter bands must be a pair of integers. 

1807 ValueError 

1808 Parameter bands must be two distinct band indices. 

1809 ''' 

1810 if not isinstance(bands, (tuple, list)) or len(bands) != 2 or \ 

1811 not all(isinstance(b, int) for b in bands): 

1812 raise TypeError('\n\nParameter bands must be a pair of integers (m, n).\n') 

1813 if bands[0] == bands[1] or not all(0 <= b < norb for b in bands): 

1814 raise ValueError('\n\nParameter bands must be two distinct band indices between 0 ' 

1815 'and {}.\n'.format(norb - 1)) 

1816 

1817 

1818def band_index(band, norb): 

1819 ''' 

1820 Check a band label: an integer between 0 and norb-1. 

1821 

1822 Raises 

1823 ------ 

1824 TypeError 

1825 Parameter band must be an integer. 

1826 ValueError 

1827 Parameter band must be between 0 and norb-1. 

1828 ''' 

1829 integer(band, 'band') 

1830 if not 0 <= band < norb: 

1831 raise ValueError('\n\nParameter band must be between 0 and {}.\n'.format(norb - 1)) 

1832 

1833 

1834def tracking(k): 

1835 ''' 

1836 Eigenvalue continuation failed: the path meets a degeneracy. 

1837 

1838 Raises 

1839 ------ 

1840 ValueError 

1841 Cannot follow the eigenvalues continuously near k. 

1842 ''' 

1843 raise ValueError('\n\nCannot follow the eigenvalues continuously near k = {}: the path ' 

1844 'passes through (or too close to) a degeneracy. Move or ' 

1845 'enlarge the loop.\n'.format(np.round(k, 8))) 

1846 

1847 

1848def winding_path(k): 

1849 ''' 

1850 The discriminant vanishes on the path of a winding number. 

1851 

1852 Raises 

1853 ------ 

1854 ValueError 

1855 The loop passes through a zero of the discriminant. 

1856 ''' 

1857 raise ValueError('\n\nThe discriminant vanishes (to numerical precision) on the path, ' 

1858 'near k = {}: the loop passes through a degeneracy. Move the ' 

1859 'loop, or change nk.\n'.format(np.round(k, 8))) 

1860 

1861 

1862def discriminant_nonzero(scale): 

1863 ''' 

1864 Check that the discriminant does not vanish identically. 

1865 

1866 Raises 

1867 ------ 

1868 ValueError 

1869 The bands are degenerate everywhere. 

1870 ''' 

1871 if scale == 0.: 

1872 raise ValueError('\n\nThe discriminant vanishes everywhere: some bands are ' 

1873 'degenerate over the whole zone.\n') 

1874 

1875 

1876def gap_kind(gap): 

1877 ''' 

1878 Check the kind of line gap: 'real' (Re E) or 'imaginary' (Im E). 

1879 

1880 Raises 

1881 ------ 

1882 ValueError 

1883 Parameter gap must be "real" or "imaginary". 

1884 ''' 

1885 if gap not in ('real', 'imaginary'): 

1886 raise ValueError('\n\nParameter gap must be "real" or "imaginary".\n') 

1887 

1888 

1889def biorthogonal_kind(kind): 

1890 ''' 

1891 Check the kind of Berry connection: 'LR', 'RL', 'RR' or 'LL'. 

1892 

1893 Raises 

1894 ------ 

1895 ValueError 

1896 Parameter kind must be "LR", "RL", "RR" or "LL". 

1897 ''' 

1898 if kind not in ('LR', 'RL', 'RR', 'LL'): 

1899 raise ValueError('\n\nParameter kind must be "LR", "RL", "RR" or "LL".\n') 

1900 

1901 

1902def line_gap(keys, bands, gap): 

1903 ''' 

1904 Check that *bands* are separated from the other bands by a line gap: 

1905 with the bands ordered by *keys* (Re E or Im E, shape (nk, norb)) at 

1906 every k-point, a band inside the group and its neighbour outside it 

1907 never overlap over the whole mesh. 

1908 

1909 Raises 

1910 ------ 

1911 ValueError 

1912 The bands have no line gap. 

1913 ''' 

1914 inside = np.zeros(keys.shape[1], bool) 

1915 inside[bands] = True 

1916 for n in range(keys.shape[1] - 1): 

1917 if inside[n] != inside[n + 1] and np.max(keys[:, n]) >= np.min(keys[:, n + 1]): 

1918 part = 'Re E' if gap == 'real' else 'Im E' 

1919 raise ValueError('\n\nThe bands {} are not separated from the others by a {} line ' 

1920 'gap: sorted by {}, band {} reaches band {} over the ' 

1921 'Brillouin zone, so the band labels are not continuous. ' 

1922 'For a non-Hermitian model, use ' 

1923 'KSpace.biorthogonal_chern_number with the other kind of ' 

1924 'gap, or locate the gap-closing exceptional points with ' 

1925 'tbkit.exceptional.find_exceptional_points.\n' 

1926 .format(bands, gap, part, n, n + 1)) 

1927 

1928 

1929def band_continuity(en, bands): 

1930 ''' 

1931 Check that a group of bands, labelled at every point of a periodic k 

1932 mesh (*en*, complex, shape (n1, n2, norb)), is continuous: matching the 

1933 eigenvalues of neighbouring points by distance maps the group onto 

1934 itself. A line gap measured on the mesh alone can miss a narrow 

1935 crossing between mesh points; this catches the jump of the labels. 

1936 

1937 Raises 

1938 ------ 

1939 ValueError 

1940 The band labels jump between neighbouring k-points. 

1941 ''' 

1942 from scipy.optimize import linear_sum_assignment 

1943 inside = np.zeros(en.shape[2], bool) 

1944 inside[bands] = True 

1945 for axis in (0, 1): 

1946 nxt = np.roll(en, -1, axis=axis) 

1947 for e_a, e_b in zip(en.reshape(-1, en.shape[2]), nxt.reshape(-1, en.shape[2])): 

1948 _, perm = linear_sum_assignment(np.abs(e_a[:, None] - e_b[None, :])) 

1949 if np.any(inside != inside[perm]): 

1950 raise ValueError('\n\nThe bands {} are not separated from the others over the ' 

1951 'Brillouin zone: between neighbouring k-points they ' 

1952 'exchange eigenvalues with the other bands (the line gap ' 

1953 'closes, e.g. at exceptional points, or nk is too small). ' 

1954 'For a non-Hermitian model, try ' 

1955 'KSpace.biorthogonal_chern_number with the other kind of ' 

1956 'gap, or tbkit.exceptional.find_exceptional_points.\n' 

1957 .format(bands)) 

1958 

1959 

1960def no_overlap(overlap): 

1961 ''' 

1962 Check that no overlap matrix is set (non-orthogonal bases are not 

1963 supported by the biorthogonal tools). 

1964 

1965 Raises 

1966 ------ 

1967 ValueError 

1968 Not available with an overlap matrix. 

1969 ''' 

1970 if overlap: 

1971 raise ValueError('\n\nThis method does not support an overlap matrix (set_overlap).\n') 

1972 

1973 

1974def weights(weights, n): 

1975 ''' 

1976 Check the weights of *occupation.fermi_level*: *n* positive numbers. 

1977 

1978 Raises 

1979 ------ 

1980 ValueError 

1981 Parameter weights must contain n positive numbers. 

1982 ''' 

1983 if len(weights) != n or np.any(weights <= 0): 

1984 raise ValueError('\n\nParameter weights must contain {} positive numbers.\n'.format(n)) 

1985 

1986 

1987def electrons(n_electrons, capacity): 

1988 ''' 

1989 Check a number of electrons: between 0 and the total weight of the levels. 

1990 

1991 Raises 

1992 ------ 

1993 TypeError 

1994 Parameter n_electrons must be a real number. 

1995 ValueError 

1996 Parameter n_electrons must be between 0 and capacity. 

1997 ''' 

1998 positive_real_zero(n_electrons, 'n_electrons') 

1999 if n_electrons > capacity * (1 + 1e-12): 

2000 raise ValueError('\n\nParameter n_electrons must be between 0 and {:g} ' 

2001 '(the number of states).\n'.format(capacity)) 

2002 

2003 

2004def n_eig(n_eig, sites): 

2005 ''' 

2006 Check the number of eigenpairs of *system.get_eig_sparse*. 

2007 

2008 Raises 

2009 ------ 

2010 TypeError 

2011 Parameter n_eig must be an integer. 

2012 ValueError 

2013 Parameter n_eig must be between 1 and sites-2. 

2014 ''' 

2015 positive_int(n_eig, 'n_eig') 

2016 if n_eig > sites - 2: 

2017 raise ValueError('\n\nParameter n_eig must be between 1 and {} (use get_eig for ' 

2018 'the full spectrum).\n'.format(sites - 2)) 

2019 

2020 

2021#################################### 

2022# TRANSPORT 

2023#################################### 

2024 

2025 

2026def square_matrix(mat, var_name): 

2027 ''' 

2028 Check that *mat* is a square 2D array. 

2029 

2030 Raises 

2031 ------ 

2032 ValueError 

2033 Parameter must be a square matrix. 

2034 ''' 

2035 if mat.ndim != 2 or mat.shape[0] != mat.shape[1]: 

2036 raise ValueError('\n\nParameter {} must be a square matrix.\n'.format(var_name)) 

2037 

2038 

2039def lead(h0, v): 

2040 ''' 

2041 Check a lead: square cell Hamiltonian *h0* and coupling *v* of the same shape. 

2042 

2043 Raises 

2044 ------ 

2045 ValueError 

2046 h0 and v must be square matrices of the same shape. 

2047 ''' 

2048 square_matrix(h0, 'h0') 

2049 if v.shape != h0.shape: 

2050 raise ValueError('\n\nParameters h0 and v must be square matrices of the same shape.\n') 

2051 

2052 

2053def converged(flag, name): 

2054 ''' 

2055 Check that an iteration converged. 

2056 

2057 Raises 

2058 ------ 

2059 RuntimeError 

2060 The iteration did not converge. 

2061 ''' 

2062 if not flag: 

2063 raise RuntimeError('\n\n{} did not converge: increase max_iter.\n'.format(name)) 

2064 

2065 

2066def lead_direction(direction): 

2067 ''' 

2068 Check the direction of a lead: +1 or -1. 

2069 

2070 Raises 

2071 ------ 

2072 ValueError 

2073 Parameter direction must be +1 or -1. 

2074 ''' 

2075 if direction not in (1, -1): 

2076 raise ValueError('\n\nParameter direction must be +1 or -1.\n') 

2077 

2078 

2079def surface_side(side): 

2080 ''' 

2081 Check parameter *side* of *KSpace.surface_spectral_function*: +1 or -1. 

2082 

2083 Raises 

2084 ------ 

2085 ValueError 

2086 Parameter side must be +1 or -1. 

2087 ''' 

2088 if isinstance(side, bool) or side not in (1, -1): 

2089 raise ValueError('\n\nParameter side must be +1 or -1.\n') 

2090 

2091 

2092def nearest_cells(n): 

2093 ''' 

2094 Check that a lead hopping reaches at most the neighbouring cells. 

2095 

2096 Raises 

2097 ------ 

2098 ValueError 

2099 Lead hoppings must connect neighbouring cells only. 

2100 ''' 

2101 if abs(n) > 1: 

2102 raise ValueError('\n\nA lead from a KSpace model needs hoppings between ' 

2103 'neighbouring cells only (use a larger unit cell).\n') 

2104 

2105 

2106def lead_coupling(coupling, sites, m, n_device): 

2107 ''' 

2108 Check the coupling of a lead to the device. 

2109 

2110 Raises 

2111 ------ 

2112 TypeError 

2113 Parameter sites must be a list of integers. 

2114 ValueError 

2115 sites must be distinct device indices, coupling of shape (len(sites), m). 

2116 ''' 

2117 if not isinstance(sites, list) or not all(isinstance(s, (int, np.integer)) for s in sites): 

2118 raise TypeError('\n\nParameter sites must be a list of integers.\n') 

2119 if len(set(sites)) != len(sites) or not all(0 <= s < n_device for s in sites): 

2120 raise ValueError('\n\nParameter sites must contain distinct device indices ' 

2121 'between 0 and {}.\n'.format(n_device - 1)) 

2122 if coupling.shape != (len(sites), m): 

2123 raise ValueError('\n\nParameter coupling must be of shape (len(sites), len(h0)) ' 

2124 '= ({}, {}).\n'.format(len(sites), m)) 

2125 

2126 

2127def lead_device_size(sites, n_device): 

2128 ''' 

2129 Check that a System has one site per row of the device Hamiltonian. 

2130 

2131 Raises 

2132 ------ 

2133 ValueError 

2134 The System must have as many sites as the device Hamiltonian has rows. 

2135 ''' 

2136 if sites != n_device: 

2137 raise ValueError('\n\nThe System has {} sites but the device Hamiltonian has {} ' 

2138 'rows.\n'.format(sites, n_device)) 

2139 

2140 

2141def lead_space_dim(lead_dim, device_dim): 

2142 ''' 

2143 Check that a lead and the device have positions of the same dimension. 

2144 

2145 Raises 

2146 ------ 

2147 ValueError 

2148 The lead and the device must have the same space dimension. 

2149 ''' 

2150 if lead_dim != device_dim: 

2151 raise ValueError('\n\nThe lead has {}D positions but the device {}D ' 

2152 'ones.\n'.format(lead_dim, device_dim)) 

2153 

2154 

2155def lead_overlap(found): 

2156 ''' 

2157 Check that some device site lies on the lattice of a lead. 

2158 

2159 Raises 

2160 ------ 

2161 ValueError 

2162 No device site matches a lead orbital. 

2163 ''' 

2164 if not found: 

2165 raise ValueError('\n\nNo device site sits at the position (and tag) of a lead ' 

2166 'orbital: place the lead unit cell in the coordinates of the device.\n') 

2167 

2168 

2169def lead_interface(missing): 

2170 ''' 

2171 Check that the interface cell of a lead is complete (*missing*: the lead 

2172 orbitals coupled to the next cell but absent from the device). 

2173 

2174 Raises 

2175 ------ 

2176 ValueError 

2177 The interface between the device and the lead is incomplete. 

2178 ''' 

2179 if missing: 

2180 raise ValueError('\n\nThe device lacks lead orbitals {} in its outermost lead cell: ' 

2181 'extend the device so that it ends with a complete lead ' 

2182 'cell (or pass extend=True).\n'.format([int(o) for o in missing])) 

2183 

2184 

2185def lead_extension(inside): 

2186 ''' 

2187 Check that the lead sites added to complete an interface stay within the 

2188 device's extent along the lead (*inside* False when they would pass the 

2189 innermost lead cell holding a device site). 

2190 

2191 Raises 

2192 ------ 

2193 ValueError 

2194 The lead cannot be closed against the device. 

2195 ''' 

2196 if not inside: 

2197 raise ValueError('\n\nThe lead sites missing from the device reach past its far end: ' 

2198 'the lead is not closed off by the device (wider than it, or ' 

2199 'off its edge).\n') 

2200 

2201 

2202def lead_index(lead, n_leads): 

2203 ''' 

2204 Check a lead index. 

2205 

2206 Raises 

2207 ------ 

2208 TypeError 

2209 The lead index must be an integer. 

2210 ValueError 

2211 No such lead. 

2212 ''' 

2213 if not isinstance(lead, int): 

2214 raise TypeError('\n\nThe lead index must be an integer.\n') 

2215 if not 0 <= lead < n_leads: 

2216 raise ValueError('\n\nNo lead {} ({} lead(s) attached with add_lead).\n'.format(lead, n_leads)) 

2217 

2218 

2219def lead_band_edge(flag, energy): 

2220 ''' 

2221 Check that the modes of a lead are well defined: the energy is not at a 

2222 band edge, where a propagating mode has zero velocity. 

2223 

2224 Raises 

2225 ------ 

2226 ValueError 

2227 Energy at a band edge of a lead. 

2228 ''' 

2229 if not flag: 

2230 raise ValueError('\n\nEnergy {} is at a band edge of a lead (a mode with zero velocity): ' 

2231 'shift it slightly.\n'.format(energy)) 

2232 

2233 

2234def conservation_law(law, h0, v, tol=1e-10): 

2235 ''' 

2236 Check the conservation law of a lead: a Hermitian matrix of the shape 

2237 of *h0* that commutes with *h0* and *v*. 

2238 

2239 Raises 

2240 ------ 

2241 ValueError 

2242 Wrong shape, not Hermitian, or not conserved by the lead. 

2243 ''' 

2244 if law.shape != h0.shape: 

2245 raise ValueError('\n\nParameter conservation_law must have the shape of h0, {}.\n'.format(h0.shape)) 

2246 hermitian_operator(law, tol) 

2247 scale = max(1., np.max(np.abs(h0)), np.max(np.abs(v))) 

2248 if max(np.max(np.abs(law @ h0 - h0 @ law)), np.max(np.abs(law @ v - v @ law))) > tol * scale: 

2249 raise ValueError('\n\nThe conservation law must commute with the lead\'s h0 and v.\n') 

2250 

2251 

2252def lead_block(lead, n_blocks): 

2253 ''' 

2254 Check a lead of a scattering matrix: a lead index, or a tuple 

2255 (lead, block) with a block of that lead's conservation law. 

2256 

2257 Raises 

2258 ------ 

2259 TypeError 

2260 Not an integer or a tuple of two integers. 

2261 ValueError 

2262 No such lead or block. 

2263 ''' 

2264 if isinstance(lead, tuple): 

2265 if len(lead) != 2 or not all(isinstance(x, (int, np.integer)) for x in lead): 

2266 raise TypeError('\n\nA lead block must be a tuple (lead, block) of two integers.\n') 

2267 lead_index(int(lead[0]), len(n_blocks)) 

2268 if not 0 <= lead[1] < n_blocks[lead[0]]: 

2269 raise ValueError('\n\nLead {} has {} block(s) (its conservation law).\n'.format( 

2270 lead[0], n_blocks[lead[0]])) 

2271 else: 

2272 lead_index(lead, len(n_blocks)) 

2273 

2274 

2275 

2276#################################### 

2277# KERNEL POLYNOMIAL METHOD 

2278#################################### 

2279 

2280 

2281def kpm_kernel(kernel): 

2282 ''' 

2283 Check the KPM kernel name. 

2284 

2285 Raises 

2286 ------ 

2287 ValueError 

2288 Parameter kernel must be "jackson" or "lorentz". 

2289 ''' 

2290 if kernel not in ('jackson', 'lorentz'): 

2291 raise ValueError('\n\nParameter kernel must be "jackson" or "lorentz".\n') 

2292 

2293 

2294def kpm_grid(eps): 

2295 ''' 

2296 Check that the KPM energies lie inside the (scaled) spectral bounds. 

2297 

2298 Raises 

2299 ------ 

2300 ValueError 

2301 e_grid must lie inside the spectral bounds. 

2302 ''' 

2303 if np.any(np.abs(eps) >= 1.): 

2304 raise ValueError('\n\nParameter e_grid must lie strictly inside the spectral ' 

2305 'bounds.\n') 

2306 

2307 

2308def site_index(site, n): 

2309 ''' 

2310 Check a site (row) index. 

2311 

2312 Raises 

2313 ------ 

2314 TypeError 

2315 Parameter site must be an integer. 

2316 ValueError 

2317 Parameter site must be between 0 and n-1. 

2318 ''' 

2319 if not isinstance(site, (int, np.integer)): 

2320 raise TypeError('\n\nParameter site must be an integer.\n') 

2321 if not 0 <= site < n: 

2322 raise ValueError('\n\nParameter site must be between 0 and {}.\n'.format(n - 1)) 

2323 

2324 

2325def max_order(list_hop): 

2326 ''' 

2327 Largest neighbour order 'n' requested by a (possibly malformed) list of 

2328 hoppings; 1 if there is none (the list is validated afterwards). 

2329 ''' 

2330 try: 

2331 orders = [dic['n'] for dic in list_hop if isinstance(dic['n'], int)] 

2332 except (TypeError, KeyError): 

2333 return 1 

2334 return max([n for n in orders if n > 0] + [1]) 

2335 

2336 

2337#################################### 

2338# ORBITALS, SLATER-KOSTER, SPIN 

2339#################################### 

2340 

2341 

2342def orbitals(orbs): 

2343 ''' 

2344 Check a list of orbital names. 

2345 

2346 Raises 

2347 ------ 

2348 TypeError 

2349 The orbitals must be a list of names. 

2350 ValueError 

2351 Unknown or repeated orbital. 

2352 ''' 

2353 from tbkit.slater_koster import ORBITALS 

2354 if not isinstance(orbs, list) or not orbs: 

2355 raise TypeError('\n\nThe orbitals must be a non-empty list of names.\n') 

2356 for o in orbs: 

2357 if o not in ORBITALS: 

2358 raise ValueError('\n\nUnknown orbital {!r}: use one of {}.\n'.format(o, list(ORBITALS))) 

2359 if len(set(orbs)) != len(orbs): 

2360 raise ValueError('\n\nRepeated orbital in {}.\n'.format(orbs)) 

2361 

2362 

2363def orbital_dict(orbital_dict, tags): 

2364 ''' 

2365 Check the orbitals of *OrbitalSystem*: a list of orbitals for every tag. 

2366 

2367 Raises 

2368 ------ 

2369 TypeError 

2370 Parameter orbitals must be a dictionary. 

2371 ValueError 

2372 Every tag needs its orbitals. 

2373 ''' 

2374 if not isinstance(orbital_dict, dict): 

2375 raise TypeError('\n\nParameter orbitals must be a dictionary {tag: list of orbitals}.\n') 

2376 for tag in tags: 

2377 if tag not in orbital_dict: 

2378 raise ValueError('\n\nParameter orbitals has no entry for tag {!r}.\n'.format(tag)) 

2379 for orbs in orbital_dict.values(): 

2380 orbitals(orbs) 

2381 

2382 

2383def sk_params(params): 

2384 ''' 

2385 Check a dictionary of Slater-Koster bond integrals. 

2386 

2387 Raises 

2388 ------ 

2389 TypeError 

2390 Parameter params must be a dictionary of numbers. 

2391 ValueError 

2392 Unknown bond integral. 

2393 ''' 

2394 from tbkit.slater_koster import PARAMS 

2395 if not isinstance(params, dict): 

2396 raise TypeError('\n\nThe bond integrals must be a dictionary.\n') 

2397 for key, val in params.items(): 

2398 if key not in PARAMS: 

2399 raise ValueError('\n\nUnknown bond integral {!r}: use {}.\n'.format(key, PARAMS)) 

2400 if not isinstance(val, (int, float)): 

2401 raise TypeError('\n\nBond integral {!r} must be a real number.\n'.format(key)) 

2402 

2403 

2404def sk_pair_params(params, tags_i, tags_j): 

2405 ''' 

2406 Check the bond integrals of *set_slater_koster*: one dictionary, or one 

2407 per tag pair of the bonds. 

2408 

2409 Returns 

2410 ------- 

2411 bool 

2412 True if keyed by tag pair. 

2413 

2414 Raises 

2415 ------ 

2416 ValueError 

2417 A tag pair of the bonds has no bond integrals. 

2418 ''' 

2419 from tbkit.slater_koster import PARAMS 

2420 if not isinstance(params, dict): 

2421 raise TypeError('\n\nThe bond integrals must be a dictionary.\n') 

2422 by_pair = bool(params) and all(k not in PARAMS for k in params) 

2423 if not by_pair: 

2424 sk_params(params) 

2425 return False 

2426 for a, b in set(zip(tags_i, tags_j)): 

2427 if a + b not in params: 

2428 raise ValueError('\n\nNo bond integrals for the tag pair {!r}.\n'.format(a + b)) 

2429 sk_params(params[a + b]) 

2430 return True 

2431 

2432 

2433def bond_vector(d): 

2434 ''' 

2435 Check a bond vector: 2 or 3 reals, nonzero. 

2436 

2437 Raises 

2438 ------ 

2439 ValueError 

2440 The bond vector must have 2 or 3 components and be nonzero. 

2441 ''' 

2442 if d.ndim != 1 or len(d) not in (2, 3) or not np.any(d): 

2443 raise ValueError('\n\nThe bond vector must have 2 or 3 components, not all zero.\n') 

2444 

2445 

2446def orbital_of_site(orbital, orbs): 

2447 ''' 

2448 Check that a site carries an orbital. 

2449 

2450 Raises 

2451 ------ 

2452 ValueError 

2453 The site has no such orbital. 

2454 ''' 

2455 if orbital not in orbs: 

2456 raise ValueError('\n\nThe site has no orbital {!r} (it has {}).\n'.format(orbital, orbs)) 

2457 

2458 

2459def spin_index(spin, ns): 

2460 ''' 

2461 Check a spin index: 0, or 0/1 with spin. 

2462 

2463 Raises 

2464 ------ 

2465 ValueError 

2466 Parameter spin must be 0 (or 1 with spin). 

2467 ''' 

2468 if spin not in range(ns): 

2469 raise ValueError('\n\nParameter spin must be {}.\n'.format('0 or 1' if ns == 2 else '0')) 

2470 

2471 

2472def set_onsite_orb(dict_onsite, tags, orbital_dict): 

2473 ''' 

2474 Check *OrbitalSystem.set_onsite*: numbers, or {orbital: number} dictionaries. 

2475 

2476 Raises 

2477 ------ 

2478 TypeError 

2479 Onsite energies must be numbers or dictionaries. 

2480 ValueError 

2481 Unknown tag or orbital. 

2482 ''' 

2483 if not isinstance(dict_onsite, dict): 

2484 raise TypeError('\n\nParameter onsite must be a dictionary.\n') 

2485 for tag, val in dict_onsite.items(): 

2486 if tag not in tags: 

2487 raise ValueError('\n\nParameter onsite keys must be a tag.\n') 

2488 if isinstance(val, dict): 

2489 for o, e in val.items(): 

2490 orbital_of_site(o, orbital_dict[tag]) 

2491 if not isinstance(e, (int, float, complex)): 

2492 raise TypeError('\n\nOnsite energies must be numbers.\n') 

2493 elif not isinstance(val, (int, float, complex)): 

2494 raise TypeError('\n\nOnsite energies must be numbers or {orbital: number} dictionaries.\n') 

2495 

2496 

2497def spinful(spin): 

2498 ''' 

2499 Check that the model has spin. 

2500 

2501 Raises 

2502 ------ 

2503 ValueError 

2504 This term needs spin=True. 

2505 ''' 

2506 if not spin: 

2507 raise ValueError('\n\nThis term needs a spinful model (spin=True).\n') 

2508 

2509 

2510def vector3(vec, var_name): 

2511 ''' 

2512 Check a tuple of three real numbers. 

2513 

2514 Raises 

2515 ------ 

2516 ValueError 

2517 Parameter must be a tuple of three real numbers. 

2518 ''' 

2519 if not isinstance(vec, (tuple, list)) or len(vec) != 3 or \ 

2520 not all(isinstance(c, (int, float)) for c in vec): 

2521 raise ValueError('\n\nParameter {} must be a tuple of three real numbers.\n'.format(var_name)) 

2522 

2523 

2524def p_shell(tags, orbital_dict): 

2525 ''' 

2526 Check that the tags carry the three p orbitals. 

2527 

2528 Raises 

2529 ------ 

2530 ValueError 

2531 The tags must carry px, py and pz. 

2532 ''' 

2533 if not isinstance(tags, list) or not tags: 

2534 raise ValueError('\n\nParameter tags must be a non-empty list of tags with p orbitals.\n') 

2535 for tag in tags: 

2536 if tag not in orbital_dict or not {'px', 'py', 'pz'} <= set(orbital_dict[tag]): 

2537 raise ValueError('\n\nTag {!r} must carry the orbitals px, py and pz.\n'.format(tag)) 

2538 

2539 

2540def block_shape(mat, shape): 

2541 ''' 

2542 Check the shape of a hopping block. 

2543 

2544 Raises 

2545 ------ 

2546 ValueError 

2547 The block must have the given shape. 

2548 ''' 

2549 if mat.shape != shape: 

2550 raise ValueError('\n\nThe hopping block must have shape {}.\n'.format(shape)) 

2551 

2552 

2553def orbital_terms(hop, blocks): 

2554 ''' 

2555 Check that an *OrbitalSystem* has hoppings. 

2556 

2557 Raises 

2558 ------ 

2559 RuntimeError 

2560 Run set_hopping or set_slater_koster first. 

2561 ''' 

2562 if not hop.size and not any(blocks.values()): 

2563 raise RuntimeError('\n\nRun method set_hopping or set_slater_koster first.\n') 

2564 

2565 

2566def no_left(left): 

2567 ''' 

2568 Check that left eigenvectors are not asked for with an overlap matrix. 

2569 

2570 Raises 

2571 ------ 

2572 ValueError 

2573 left=True needs an orthogonal basis. 

2574 ''' 

2575 if left: 

2576 raise ValueError('\n\nleft=True needs an orthogonal basis (no overlap).\n') 

2577 

2578 

2579def orthogonal(overlap): 

2580 ''' 

2581 Check that the basis is orthogonal. 

2582 

2583 Raises 

2584 ------ 

2585 ValueError 

2586 This calculation needs an orthogonal basis. 

2587 ''' 

2588 if overlap is not None: 

2589 raise ValueError('\n\nThis calculation needs an orthogonal basis (no overlap matrix).\n') 

2590 

2591 

2592#################################### 

2593# MEAN FIELD, SUPERCONDUCTIVITY 

2594#################################### 

2595 

2596 

2597def hermitian_dense(ham): 

2598 ''' 

2599 Check that a dense matrix is Hermitian. 

2600 

2601 Raises 

2602 ------ 

2603 ValueError 

2604 This calculation requires a Hermitian Hamiltonian. 

2605 ''' 

2606 if not np.allclose(ham, ham.conj().T, atol=1e-12): 

2607 raise ValueError('\n\nThis calculation requires a Hermitian Hamiltonian.\n') 

2608 

2609 

2610def integer_electrons(n_electrons): 

2611 ''' 

2612 Check that the number of electrons is an integer (zero temperature). 

2613 

2614 Raises 

2615 ------ 

2616 ValueError 

2617 At zero temperature, n_electrons must be an integer. 

2618 ''' 

2619 if abs(n_electrons - round(n_electrons)) > 1e-12: 

2620 raise ValueError('\n\nAt zero temperature, n_electrons must be an integer ' 

2621 '(use a small temperature for a fractional filling).\n') 

2622 

2623 

2624def mixing(mixing): 

2625 ''' 

2626 Check the density mixing: a real in (0, 1]. 

2627 

2628 Raises 

2629 ------ 

2630 ValueError 

2631 Parameter mixing must be in (0, 1]. 

2632 ''' 

2633 positive_real(mixing, 'mixing') 

2634 if mixing > 1: 

2635 raise ValueError('\n\nParameter mixing must be in (0, 1].\n') 

2636 

2637 

2638def pairing(delta, n): 

2639 ''' 

2640 Check a BdG pairing matrix: (n, n), antisymmetric. 

2641 

2642 Raises 

2643 ------ 

2644 ValueError 

2645 The pairing matrix must be (n, n) and antisymmetric. 

2646 ''' 

2647 if delta.shape != (n, n): 

2648 raise ValueError('\n\nThe pairing matrix must have the shape ({0}, {0}) of ham.\n'.format(n)) 

2649 if (abs(delta + delta.T) > 1e-12).nnz: 

2650 raise ValueError('\n\nThe pairing matrix must be antisymmetric (Delta^T = -Delta).\n') 

2651 

2652 

2653def hermitian_kspace(hermitian, overlap): 

2654 ''' 

2655 Check that a KSpace model is Hermitian, with an orthogonal basis. 

2656 

2657 Raises 

2658 ------ 

2659 ValueError 

2660 The model must be Hermitian, without overlap. 

2661 ''' 

2662 if not hermitian or overlap: 

2663 raise ValueError('\n\nThe model must be Hermitian, with an orthogonal basis.\n') 

2664 

2665 

2666def pairing_kspace(pairing, norb, ndim): 

2667 ''' 

2668 Check the pairings of *bdg.bdg_kspace*. 

2669 

2670 Raises 

2671 ------ 

2672 TypeError 

2673 Parameter pairing must be a list of dictionaries. 

2674 KeyError 

2675 "i", "j", "R" and "delta" must be keys. 

2676 ValueError 

2677 Orbital indices below norb; R a tuple of ndim integers. 

2678 ''' 

2679 if not isinstance(pairing, list): 

2680 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n') 

2681 for dic in pairing: 

2682 if not isinstance(dic, dict): 

2683 raise TypeError('\n\nParameter pairing must be a list of dictionaries.\n') 

2684 if not {'i', 'j', 'R', 'delta'} <= set(dic): 

2685 raise KeyError('\n\n"i", "j", "R" and "delta" must be dictionary keys.\n') 

2686 if not all(isinstance(dic[k], int) and 0 <= dic[k] < norb for k in ('i', 'j')): 

2687 raise ValueError('\n\n"i" and "j" must be orbital indices between 0 and {}.\n'.format(norb - 1)) 

2688 if not isinstance(dic['R'], tuple) or len(dic['R']) != ndim or \ 

2689 not all(isinstance(n, int) for n in dic['R']): 

2690 raise ValueError('\n\n"R" must be a tuple of {} integers.\n'.format(ndim)) 

2691 number(dic['delta'], '"delta"') 

2692 

2693 

2694#################################### 

2695# FLOQUET 

2696#################################### 

2697 

2698 

2699def positive_int_zero(var, var_name): 

2700 ''' 

2701 Check if *var* is a positive integer or zero. 

2702 

2703 Raises 

2704 ------ 

2705 TypeError 

2706 Parameter *var* must be an integer. 

2707 ValueError 

2708 Parameter *var* must be a positive integer or zero. 

2709 ''' 

2710 if not isinstance(var, int): 

2711 raise TypeError('\n\nParameter {} must be an integer.\n'.format(var_name)) 

2712 if var < 0: 

2713 raise ValueError('\n\nParameter {} must be a positive integer or zero.\n'.format(var_name)) 

2714 

2715 

2716def harmonics(harm): 

2717 ''' 

2718 Check the harmonics of *floquet.sambe_hamiltonian*: a dictionary with (at least) m = 0. 

2719 

2720 Raises 

2721 ------ 

2722 TypeError 

2723 The harmonics must be a dictionary {m: matrix}. 

2724 ValueError 

2725 The harmonics must contain m = 0 and square matrices of one shape. 

2726 ''' 

2727 if not isinstance(harm, dict): 

2728 raise TypeError('\n\nThe harmonics must be a dictionary {m: matrix}.\n') 

2729 if 0 not in harm: 

2730 raise ValueError('\n\nThe harmonics must contain the static part, m = 0.\n') 

2731 shapes = {np.shape(h) for h in harm.values()} 

2732 if len(shapes) != 1 or len(shapes.pop()) != 2: 

2733 raise ValueError('\n\nThe harmonics must be square matrices of one shape.\n') 

2734 

2735 

2736def kspace(ks, cls): 

2737 ''' 

2738 Check that *ks* is a KSpace instance. 

2739 

2740 Raises 

2741 ------ 

2742 TypeError 

2743 Parameter ks must be a KSpace instance. 

2744 ''' 

2745 if not isinstance(ks, cls): 

2746 raise TypeError('\n\nParameter ks must be a KSpace instance.\n') 

2747 

2748 

2749def not_floquet(): 

2750 ''' 

2751 

2752 Raises 

2753 ------ 

2754 ValueError 

2755 Not available for a Floquet (driven) model. 

2756 ''' 

2757 raise ValueError('\n\nThis method needs the static hoppings: it is not available ' 

2758 'for a Floquet (driven) model.\n') 

2759 

2760 

2761def sk_bonds(bonds): 

2762 ''' 

2763 Check the bonds of *slater_koster.sk_kspace*: {n: bond integrals}. 

2764 

2765 Raises 

2766 ------ 

2767 TypeError 

2768 Parameter bonds must be a dictionary {n: bond integrals}. 

2769 ''' 

2770 if not isinstance(bonds, dict) or not all(isinstance(n, int) and n > 0 for n in bonds): 

2771 raise TypeError('\n\nParameter bonds must be a dictionary {n: bond integrals}, ' 

2772 'n a positive integer.\n') 

2773 for params in bonds.values(): 

2774 sk_params(params) 

2775 

2776 

2777#################################### 

2778# HALL CONDUCTIVITIES 

2779#################################### 

2780 

2781 

2782def fermi_energies(e_fermi): 

2783 ''' 

2784 Check the Fermi energies of the Hall conductivities: a real number, or a 

2785 non-empty array of finite real numbers. 

2786 

2787 Raises 

2788 ------ 

2789 TypeError 

2790 Parameter e_fermi must be a real number or an array of real numbers. 

2791 ValueError 

2792 Parameter e_fermi must be non-empty and finite. 

2793 ''' 

2794 arr = np.asarray(e_fermi) 

2795 if arr.dtype.kind not in 'iuf': 

2796 raise TypeError('\n\nParameter e_fermi must be a real number or an array ' 

2797 'of real numbers.\n') 

2798 if arr.size == 0 or not np.all(np.isfinite(arr)): 

2799 raise ValueError('\n\nParameter e_fermi must contain at least one finite number.\n') 

2800 

2801 

2802def hermitian_model(hermitian): 

2803 ''' 

2804 Check that a KSpace model is Hermitian (a Kubo conductivity needs real 

2805 bands and orthonormal eigenstates). 

2806 

2807 Raises 

2808 ------ 

2809 ValueError 

2810 This calculation requires a Hermitian model. 

2811 ''' 

2812 if not hermitian: 

2813 raise ValueError('\n\nThis calculation requires a Hermitian model (no gain/loss, ' 

2814 'no hermitian=False hopping).\n') 

2815 

2816 

2817def k_fixed_hall(k_fixed, dim, full_allowed=True): 

2818 ''' 

2819 Check parameter *k_fixed* of the Hall conductivities: None (the whole 

2820 Brillouin zone) or a real number (a plane of a 3D model). 

2821 

2822 Raises 

2823 ------ 

2824 TypeError 

2825 Parameter k_fixed must be None or a real number. 

2826 ValueError 

2827 A 3D spin Hall conductivity needs a plane (k_fixed). 

2828 ''' 

2829 if k_fixed is not None: 

2830 real_number(k_fixed, 'k_fixed') 

2831 elif dim == 3 and not full_allowed: 

2832 raise ValueError('\n\nIn 3D, give the plane of the spin Hall conductivity: ' 

2833 'k_fixed must be a real number.\n') 

2834 

2835 

2836def spin_axis(axis): 

2837 ''' 

2838 Check the spin quantization axis: 'x', 'y' or 'z'. 

2839 

2840 Raises 

2841 ------ 

2842 ValueError 

2843 Parameter spin_axis must be "x", "y" or "z". 

2844 ''' 

2845 if axis not in ('x', 'y', 'z'): 

2846 raise ValueError('\n\nParameter spin_axis must be "x", "y" or "z".\n') 

2847 

2848 

2849def refine_fraction(fraction): 

2850 ''' 

2851 Check the fraction of mesh points refined by the adaptive mesh: a real in (0, 1]. 

2852 

2853 Raises 

2854 ------ 

2855 TypeError 

2856 Parameter refine_fraction must be a real number. 

2857 ValueError 

2858 Parameter refine_fraction must be in (0, 1]. 

2859 ''' 

2860 positive_real(fraction, 'refine_fraction') 

2861 if fraction > 1: 

2862 raise ValueError('\n\nParameter refine_fraction must be in (0, 1].\n') 

2863 

2864 

2865def path_parameter(param): 

2866 ''' 

2867 Check the parameter that a path varies (*KSpace.axion_angle*): a string. 

2868 

2869 Raises 

2870 ------ 

2871 TypeError 

2872 Parameter param must be a string (the name of a value-function parameter). 

2873 ''' 

2874 if not isinstance(param, str): 

2875 raise TypeError('\n\nParameter param must be a string (the name of a ' 

2876 'value-function parameter).\n') 

2877 

2878 

2879def path_values(values): 

2880 ''' 

2881 Check the parameter values of a path: a 1D array of at least two finite 

2882 real numbers, strictly increasing or strictly decreasing. 

2883 

2884 Raises 

2885 ------ 

2886 TypeError 

2887 Parameter values must be a 1D array of real numbers. 

2888 ValueError 

2889 Parameter values must hold at least two finite, strictly monotonic values. 

2890 ''' 

2891 arr = np.asarray(values) 

2892 if arr.ndim != 1 or arr.dtype.kind not in 'iuf': 

2893 raise TypeError('\n\nParameter values must be a 1D array of real numbers.\n') 

2894 step = np.diff(arr) 

2895 if len(arr) < 2 or not np.all(np.isfinite(arr)) or not (np.all(step > 0) or np.all(step < 0)): 

2896 raise ValueError('\n\nParameter values must hold at least two finite values, ' 

2897 'strictly increasing or strictly decreasing.\n') 

2898 

2899 

2900def path_gap(gap, param, value, tol=1e-6): 

2901 ''' 

2902 Check that the bands stay separated from the others along a path. 

2903 

2904 Raises 

2905 ------ 

2906 ValueError 

2907 The bands touch the others at param = value. 

2908 ''' 

2909 if gap < tol: 

2910 raise ValueError('\n\nThe bands touch the other bands at {} = {} (gap {:.1e}): ' 

2911 'the path must stay gapped.\n'.format(param, value, gap)) 

2912 

2913 

2914def velocity(vel, n, var_name): 

2915 ''' 

2916 Check a velocity operator: a square (sparse or dense) matrix of the 

2917 Hamiltonian's shape. 

2918 

2919 Raises 

2920 ------ 

2921 ValueError 

2922 The velocity must be a (n, n) matrix. 

2923 ''' 

2924 if getattr(vel, 'shape', None) != (n, n): 

2925 raise ValueError('\n\nParameter {} must be a ({}, {}) matrix, like ham.\n'.format(var_name, n, n)) 

2926 

2927 

2928def positive_overlap(s_min): 

2929 ''' 

2930 Check that the overlap matrix S(k) is positive definite. 

2931 

2932 Raises 

2933 ------ 

2934 ValueError 

2935 The overlap matrix must be positive definite. 

2936 ''' 

2937 if s_min <= 0: 

2938 raise ValueError('\n\nThe overlap matrix S(k) must be positive definite ' 

2939 '(smallest eigenvalue {:.3g}).\n'.format(s_min)) 

2940 

2941 

2942#################################### 

2943# FLOQUET: STEP DRIVES, WINDING NUMBER 

2944#################################### 

2945 

2946 

2947def branch_cut(epsilon): 

2948 ''' 

2949 Check the branch cut of a Floquet Hamiltonian: None or a real number. 

2950 

2951 Raises 

2952 ------ 

2953 TypeError 

2954 Parameter epsilon must be None or a real number. 

2955 ''' 

2956 if epsilon is not None and (isinstance(epsilon, bool) 

2957 or not isinstance(epsilon, (int, float, np.integer, np.floating))): 

2958 raise TypeError('\n\nParameter epsilon must be None or a real number.\n') 

2959 

2960 

2961def durations(durations, n): 

2962 ''' 

2963 Check the step durations of a step drive: n positive reals. 

2964 

2965 Raises 

2966 ------ 

2967 TypeError 

2968 Parameter durations must be a list of real numbers. 

2969 ValueError 

2970 Parameter durations must hold one positive number per step. 

2971 ''' 

2972 if not isinstance(durations, (list, tuple, np.ndarray)): 

2973 raise TypeError('\n\nParameter durations must be a list of real numbers.\n') 

2974 if len(durations) != n: 

2975 raise ValueError('\n\nParameter durations must hold one duration per step ({}).\n'.format(n)) 

2976 for d in durations: 

2977 if isinstance(d, bool) or not isinstance(d, (int, float, np.integer, np.floating)): 

2978 raise TypeError('\n\nParameter durations must be a list of real numbers.\n') 

2979 if d <= 0: 

2980 raise ValueError('\n\nParameter durations must be positive numbers.\n') 

2981 

2982 

2983def step_models(models, kspace_cls, system_cls): 

2984 ''' 

2985 Check the models of a step drive: a non-empty list, either of KSpace 

2986 instances (Hermitian, orthogonal basis, one lattice size) or of System 

2987 instances and square matrices. Returns 'kspace' or 'matrix'. 

2988 

2989 Raises 

2990 ------ 

2991 TypeError 

2992 Parameter models must be a non-empty list of KSpace, System or matrices. 

2993 ValueError 

2994 The KSpace models must be Hermitian, with no overlap, of one size. 

2995 ''' 

2996 if not isinstance(models, (list, tuple)) or len(models) == 0: 

2997 raise TypeError('\n\nParameter models must be a non-empty list of KSpace instances, ' 

2998 'or of System instances and matrices.\n') 

2999 if all(isinstance(m, kspace_cls) for m in models): 

3000 for m in models: 

3001 if (m.norb, m.dim) != (models[0].norb, models[0].dim): 

3002 raise ValueError('\n\nThe KSpace models must have the same orbitals and dimension.\n') 

3003 if not m.is_hermitian(): 

3004 raise ValueError('\n\nThe KSpace models must be Hermitian.\n') 

3005 if m._overlap_hop: 

3006 raise ValueError('\n\nThe KSpace models need an orthogonal basis (no overlap).\n') 

3007 return 'kspace' 

3008 for m in models: 

3009 if not isinstance(m, system_cls) and getattr(m, 'ndim', None) != 2: 

3010 raise TypeError('\n\nParameter models must be a list of KSpace instances only, ' 

3011 'or of System instances and square matrices.\n') 

3012 return 'matrix' 

3013 

3014 

3015def step_matrices(hams): 

3016 ''' 

3017 Check the Hamiltonians of a real-space step drive: Hermitian square 

3018 matrices of one shape. 

3019 

3020 Raises 

3021 ------ 

3022 TypeError 

3023 Parameter hams must be a non-empty list of matrices. 

3024 ValueError 

3025 The matrices must be square, Hermitian and of one shape. 

3026 ''' 

3027 if not isinstance(hams, (list, tuple)) or len(hams) == 0: 

3028 raise TypeError('\n\nParameter hams must be a non-empty list of matrices.\n') 

3029 shape = np.shape(hams[0]) 

3030 for h in hams: 

3031 h = np.asarray(h) 

3032 if h.ndim != 2 or h.shape[0] != h.shape[1] or h.shape != shape: 

3033 raise ValueError('\n\nThe step Hamiltonians must be square matrices of one shape.\n') 

3034 hermitian_dense(h) 

3035 

3036 

3037def drive_matrix(ham, norb): 

3038 ''' 

3039 Check the output of a Bloch drive ham_kt(k, t): a Hermitian (norb, norb) matrix. 

3040 

3041 Raises 

3042 ------ 

3043 ValueError 

3044 ham_kt(k, t) must return a Hermitian (norb, norb) matrix. 

3045 ''' 

3046 if np.shape(ham) != (norb, norb): 

3047 raise ValueError('\n\nham_kt(k, t) must return a ({}, {}) matrix.\n'.format(norb, norb)) 

3048 hermitian_dense(np.asarray(ham)) 

3049 

3050 

3051def time_in_period(t, period): 

3052 ''' 

3053 Check a time within the period: None or a real in [0, period]. 

3054 

3055 Raises 

3056 ------ 

3057 TypeError 

3058 Parameter t must be None or a real number. 

3059 ValueError 

3060 Parameter t must be in [0, period]. 

3061 ''' 

3062 if t is None: 

3063 return 

3064 real_number(t, 't') 

3065 if not 0 <= t <= period: 

3066 raise ValueError('\n\nParameter t must be in [0, {}].\n'.format(period)) 

3067 

3068 

3069def nk_min(nk, n_min): 

3070 ''' 

3071 Check that every k-mesh size is at least n_min. 

3072 

3073 Raises 

3074 ------ 

3075 ValueError 

3076 Parameter nk must be at least n_min. 

3077 ''' 

3078 if min(nk) < n_min: 

3079 raise ValueError('\n\nParameter nk must be at least {}.\n'.format(n_min)) 

3080 

3081 

3082#################################### 

3083# MODEL BUILDING, BRIDGES AND I/O 

3084# (neighbour-order hoppings in k-space, System <-> KSpace, tbkit.io) 

3085#################################### 

3086 

3087 

3088def hopping_form(list_hop): 

3089 ''' 

3090 Tell the two forms of *KSpace.set_hopping* apart: the explicit one 

3091 (keys 'i', 'j', 'R', 't') and the neighbour-order one of 

3092 *System.set_hopping* (keys 'n', 't', and optionally 'ang', 'tag'). 

3093 

3094 Returns 

3095 ------- 

3096 neighbour : Boolean 

3097 True for the neighbour-order form. 

3098 

3099 Raises 

3100 ------ 

3101 TypeError 

3102 Parameter list_hop must be a list of dictionaries. 

3103 ValueError 

3104 list_hop mixes the two forms. 

3105 ''' 

3106 if not isinstance(list_hop, list): 

3107 raise TypeError('\n\nParameter list_hop must be a list.\n') 

3108 if not all(isinstance(dic, dict) for dic in list_hop): 

3109 raise TypeError('\n\nParameter list_hop must be a list of dictionaries.\n') 

3110 by_order = ['n' in dic for dic in list_hop] 

3111 if any(by_order) and not all(by_order): 

3112 raise ValueError('\n\nParameter list_hop mixes explicit hoppings (keys "i", "j", "R", "t") ' 

3113 'and neighbour-order ones (keys "n", "t", "ang", "tag"): ' 

3114 'make one call per form.\n') 

3115 return bool(list_hop) and all(by_order) 

3116 

3117 

3118def set_hopping_neighbours(list_hop, spin=False): 

3119 ''' 

3120 Check the neighbour-order form of *KSpace.set_hopping*. 

3121 

3122 Raises 

3123 ------ 

3124 KeyError 

3125 "n" and "t" must be keys, and only "ang" and "tag" may be added. 

3126 TypeError 

3127 "n" must be an integer, "t" a number (or, if *spin*, 

3128 a 2x2 matrix) or a value function, "ang" a real number and "tag" a string. 

3129 ValueError 

3130 "n" must be positive, "ang" in [-180, 180), "tag" of length 2. 

3131 ''' 

3132 hopping_form(list_hop) 

3133 for dic in list_hop: 

3134 if 'n' not in dic or 't' not in dic: 

3135 raise KeyError('\n\n"n" and "t" must be dictionary keys.\n') 

3136 if not set(dic) <= {'n', 't', 'ang', 'tag'}: 

3137 raise KeyError('\n\nOnly the keys "n", "t", "ang" and "tag" are allowed.\n') 

3138 if isinstance(dic['n'], bool) or not isinstance(dic['n'], int): 

3139 raise TypeError('\n\n"n" value must be an integer.\n') 

3140 if dic['n'] < 1: 

3141 raise ValueError('\n\n"n" value must be a positive integer.\n') 

3142 if callable(dic['t']): 

3143 pass 

3144 elif spin: 

3145 spin_matrix(dic['t'], '"t"') 

3146 elif not isinstance(dic['t'], (int, float, complex)): 

3147 raise TypeError('\n\n"t" value must be a real or complex number.\n') 

3148 if 'ang' in dic: 

3149 if not isinstance(dic['ang'], (int, float)): 

3150 raise TypeError('\n\n"ang" value must be a real number.\n') 

3151 if not -180. <= dic['ang'] < 180.: 

3152 raise ValueError('\n\n"ang" value must be in [-180, 180).\n') 

3153 if 'tag' in dic: 

3154 if not isinstance(dic['tag'], str): 

3155 raise TypeError('\n\n"tag" value must be a string.\n') 

3156 if len(dic['tag']) != 2: 

3157 raise ValueError('\n\n"tag" value must be a string of length 2.\n') 

3158 

3159 

3160def shell_angle(ang, angles): 

3161 ''' 

3162 Check that a neighbour shell has bonds along the angle *ang* (in 

3163 degrees, either orientation: *angles* are those in [0, 180)). 

3164 

3165 Raises 

3166 ------ 

3167 ValueError 

3168 No bond of the shell points along *ang*. 

3169 ''' 

3170 if not np.any(np.isclose(ang, angles, atol=ATOL)) and \ 

3171 not np.any(np.isclose(ang, angles - 180., atol=ATOL)): 

3172 raise ValueError('\n\nNo bond of this neighbour shell has angle {} (angles: {}).\n' 

3173 .format(ang, np.unique(angles.round(4)))) 

3174 

3175 

3176def not_orbital_system(sys, orbital_cls): 

3177 ''' 

3178 Check that a System is not an *OrbitalSystem* (several rows per site). 

3179 

3180 Raises 

3181 ------ 

3182 TypeError 

3183 An OrbitalSystem is not supported. 

3184 ''' 

3185 if isinstance(sys, orbital_cls): 

3186 raise TypeError('\n\nAn OrbitalSystem (several orbitals per site) is not supported: ' 

3187 'build its KSpace with tbkit.slater_koster.sk_kspace.\n') 

3188 

3189 

3190def lattice_sites(matches): 

3191 ''' 

3192 Check that every site of a finite lattice is a copy of exactly one 

3193 orbital of *unit_cell*, translated by a lattice vector (*matches*: the 

3194 number of such orbitals, per site). 

3195 

3196 Raises 

3197 ------ 

3198 ValueError 

3199 A site is not on the periodic lattice. 

3200 ''' 

3201 bad = np.flatnonzero(np.asarray(matches) != 1) 

3202 if len(bad): 

3203 raise ValueError('\n\nSite {} is not a translate of exactly one unit-cell site (same tag, ' 

3204 'position r0 + n1 a1 + ...): was the lattice rotated, strained ' 

3205 'or shifted?\n'.format(int(bad[0]))) 

3206 

3207 

3208def translation_invariant(dev, tol): 

3209 ''' 

3210 Check that equivalent matrix elements (same orbitals, same lattice 

3211 vector) are equal in every cell, within *tol*. 

3212 

3213 Raises 

3214 ------ 

3215 ValueError 

3216 The model is not translation invariant. 

3217 ''' 

3218 if dev > tol: 

3219 raise ValueError('\n\nThe Hamiltonian is not translation invariant: equivalent hoppings ' 

3220 'or onsite energies differ by {:.3g} (> tol = {:.3g}), e.g. after ' 

3221 'disorder, defects or a magnetic field.\n'.format(dev, tol)) 

3222 

3223 

3224def torus_range(R, sizes): 

3225 ''' 

3226 Check that the lattice vectors of a torus's bonds (shortest images, in 

3227 [-N/2, N/2)) are unambiguous: at least 3 cells along every direction, 

3228 and no bond reaching half-way around. 

3229 

3230 Raises 

3231 ------ 

3232 ValueError 

3233 The torus is too small for the range of the model. 

3234 ''' 

3235 if np.any(sizes < 3) or np.any(2 * R == -sizes[None, :]): 

3236 raise ValueError('\n\nThe torus ({} cells) is too small for the range of the model: ' 

3237 'a bond reaches half-way around it.\n'.format(tuple(int(n) for n in sizes))) 

3238 

3239 

3240def saveable_model(model, lattice_cls, system_cls, kspace_cls, orbital_cls): 

3241 ''' 

3242 Check that *tbkit.io.save_model* can store *model*. 

3243 

3244 Raises 

3245 ------ 

3246 TypeError 

3247 model must be a Lattice, System or KSpace instance 

3248 (not an OrbitalSystem). 

3249 ''' 

3250 if isinstance(model, orbital_cls) or not isinstance(model, (lattice_cls, system_cls, kspace_cls)): 

3251 raise TypeError('\n\nParameter model must be a Lattice, System or KSpace instance ' 

3252 '(an OrbitalSystem or a driven model cannot be saved).\n') 

3253 

3254 

3255def file_path(path, var_name): 

3256 ''' 

3257 Check a file name. 

3258 

3259 Raises 

3260 ------ 

3261 TypeError 

3262 Parameter var_name must be a string or a path. 

3263 ''' 

3264 if not (isinstance(path, str) or hasattr(path, "__fspath__")): 

3265 raise TypeError('\n\nParameter {} must be a string or a path.\n'.format(var_name)) 

3266 

3267 

3268def model_archive(data, fmt, version): 

3269 ''' 

3270 Check an archive read by *tbkit.io.load_model*. 

3271 

3272 Raises 

3273 ------ 

3274 ValueError 

3275 Not a tbkit model archive, or a newer version. 

3276 ''' 

3277 if 'format' not in data.files or str(data['format']) != fmt: 

3278 raise ValueError('\n\nThis file is not a tbkit model archive (format {}).\n'.format(fmt)) 

3279 if int(data['version']) > version: 

3280 raise ValueError('\n\nThis archive has version {}; this tbkit reads versions up to {}: ' 

3281 'upgrade tbkit.\n'.format(int(data['version']), version)) 

3282 

3283 

3284def hr_header(tokens): 

3285 ''' 

3286 Check the header of a Wannier90 ``_hr.dat`` file: num_wann, nrpts and 

3287 the nrpts degeneracy weights, as positive integers. 

3288 

3289 Raises 

3290 ------ 

3291 ValueError 

3292 Not a Wannier90 _hr.dat file. 

3293 ''' 

3294 try: 

3295 num_wann, nrpts = int(tokens[0]), int(tokens[1]) 

3296 weights = [int(w) for w in tokens[2:2 + nrpts]] 

3297 except (IndexError, ValueError): 

3298 raise ValueError('\n\nNot a Wannier90 _hr.dat file: expected num_wann, nrpts and ' 

3299 'the degeneracy weights after the header line.\n') from None 

3300 if num_wann < 1 or nrpts < 1 or len(weights) != nrpts or min(weights) < 1: 

3301 raise ValueError('\n\nNot a Wannier90 _hr.dat file: num_wann, nrpts and the ' 

3302 'degeneracy weights must be positive integers.\n') 

3303 

3304 

3305def hr_body(n_tokens, nrpts, num_wann): 

3306 ''' 

3307 Check the number of entries of a Wannier90 ``_hr.dat`` file. 

3308 

3309 Raises 

3310 ------ 

3311 ValueError 

3312 The file must have nrpts * num_wann**2 lines of 7 numbers. 

3313 ''' 

3314 if n_tokens != 7 * nrpts * num_wann ** 2: 

3315 raise ValueError('\n\nThe _hr.dat file must have nrpts * num_wann**2 = {} lines ' 

3316 '"R1 R2 R3 m n Re Im" after the weights.\n'.format(nrpts * num_wann ** 2)) 

3317 

3318 

3319def hr_blocks(ok): 

3320 ''' 

3321 Check that the lines of a ``_hr.dat`` file come in blocks of num_wann**2 

3322 with the same lattice vector. 

3323 

3324 Raises 

3325 ------ 

3326 ValueError 

3327 Lines of a lattice vector must be consecutive. 

3328 ''' 

3329 if not ok: 

3330 raise ValueError('\n\nThe _hr.dat lines of each lattice vector R must be consecutive ' 

3331 '(num_wann**2 lines per R).\n') 

3332 

3333 

3334def win_block(found): 

3335 ''' 

3336 Check that a ``.win`` file has a ``unit_cell_cart`` block. 

3337 

3338 Raises 

3339 ------ 

3340 ValueError 

3341 No unit_cell_cart block. 

3342 ''' 

3343 if not found: 

3344 raise ValueError('\n\nThe .win file has no "begin unit_cell_cart ... end unit_cell_cart" block.\n') 

3345 

3346 

3347def win_cell(cell): 

3348 ''' 

3349 Check the ``unit_cell_cart`` block of a ``.win`` file: three lines of 

3350 three real numbers. 

3351 

3352 Raises 

3353 ------ 

3354 ValueError 

3355 unit_cell_cart must hold three vectors of three numbers. 

3356 ''' 

3357 try: 

3358 ok = len(cell) == 3 and all(len(v) == 3 for v in cell) and \ 

3359 all(np.isfinite([float(c) for v in cell for c in v])) 

3360 except ValueError: 

3361 ok = False 

3362 if not ok: 

3363 raise ValueError('\n\nThe unit_cell_cart block must hold three vectors of three real numbers.\n') 

3364 

3365 

3366def centres_count(n, num_wann): 

3367 ''' 

3368 Check that a ``_centres.xyz`` file lists num_wann Wannier centres (X). 

3369 

3370 Raises 

3371 ------ 

3372 ValueError 

3373 Too few Wannier centres. 

3374 ''' 

3375 if n < num_wann: 

3376 raise ValueError('\n\nThe _centres.xyz file lists {} Wannier centres ("X"), ' 

3377 'not {}.\n'.format(n, num_wann)) 

3378 

3379 

3380def wannier_dim(dim): 

3381 ''' 

3382 Check the dimension of a Wannier90 model. 

3383 

3384 Raises 

3385 ------ 

3386 ValueError 

3387 dim must be 2 or 3. 

3388 ''' 

3389 if isinstance(dim, bool) or dim not in (2, 3): 

3390 raise ValueError('\n\nParameter dim must be 2 or 3.\n') 

3391 

3392 

3393def win_given(win): 

3394 ''' 

3395 Check that the lattice vectors can be found. 

3396 

3397 Raises 

3398 ------ 

3399 ValueError 

3400 Give prim_vec or win. 

3401 ''' 

3402 if win is None: 

3403 raise ValueError('\n\nGive the primitive vectors: prim_vec, or the .win file (win).\n') 

3404 

3405 

3406def layer_cell(cell, dim): 

3407 ''' 

3408 Check that, for a 2D model, a1 and a2 lie in the (x, y) plane. 

3409 

3410 Raises 

3411 ------ 

3412 ValueError 

3413 a1 and a2 must have no z component. 

3414 ''' 

3415 if dim == 2 and np.any(np.abs(cell[:2, 2]) > 1e-8): 

3416 raise ValueError('\n\nFor dim=2, the first two lattice vectors must lie in the (x, y) plane.\n') 

3417 

3418 

3419def wannier_prim_vec(prim_vec, dim): 

3420 ''' 

3421 Check that the primitive vectors are *dim* vectors of *dim* components. 

3422 

3423 Raises 

3424 ------ 

3425 ValueError 

3426 prim_vec must hold dim vectors of dim components. 

3427 ''' 

3428 if len(prim_vec) != dim or len(prim_vec[0]) != dim: 

3429 raise ValueError('\n\nParameter prim_vec must hold {0} vectors of {0} components.\n'.format(dim)) 

3430 independent(prim_vec) 

3431 

3432 

3433def wannier_positions(shape, num_wann, dim): 

3434 ''' 

3435 Check the orbital positions of a Wannier90 model. 

3436 

3437 Raises 

3438 ------ 

3439 ValueError 

3440 positions must have shape (num_wann, dim) or (num_wann, 3). 

3441 ''' 

3442 if len(shape) != 2 or shape[0] != num_wann or shape[1] not in (dim, 3): 

3443 raise ValueError('\n\nParameter positions must have shape ({0}, {1}) or ({0}, 3).\n' 

3444 .format(num_wann, dim)) 

3445 

3446 

3447def wannier_tags(tags, num_wann): 

3448 ''' 

3449 Check the sublattice tags of a Wannier90 model. 

3450 

3451 Raises 

3452 ------ 

3453 TypeError 

3454 tags must be a list of one-character strings. 

3455 ValueError 

3456 tags must have num_wann elements. 

3457 ''' 

3458 if not isinstance(tags, list) or not all(isinstance(t, str) and len(t) == 1 for t in tags): 

3459 raise TypeError('\n\nParameter tags must be a list of one-character strings.\n') 

3460 if len(tags) != num_wann: 

3461 raise ValueError('\n\nParameter tags must have {} elements.\n'.format(num_wann)) 

3462 

3463 

3464def layer_hoppings(h_max, tol): 

3465 ''' 

3466 Check that a 2D model has no hopping between layers (R3 != 0). 

3467 

3468 Raises 

3469 ------ 

3470 ValueError 

3471 Hoppings with R3 != 0 must vanish for dim=2. 

3472 ''' 

3473 if h_max > tol: 

3474 raise ValueError('\n\nFor dim=2, the matrix elements with R3 != 0 must vanish ' 

3475 '(largest: {:.3g}).\n'.format(h_max)) 

3476 

3477 

3478def spinless(spin): 

3479 ''' 

3480 Check that a KSpace model has no spin. 

3481 

3482 Raises 

3483 ------ 

3484 ValueError 

3485 This needs a spinless model. 

3486 ''' 

3487 if spin: 

3488 raise ValueError('\n\nThis needs a spinless model (spin=False): a System has one ' 

3489 'orbital per site; use bridges.finite_model for spinful models.\n') 

3490# HIGHER-ORDER TOPOLOGY, MOIRE SUPERCELLS, SELF-CONSISTENT INTERACTIONS 

3491#################################### 

3492 

3493 

3494def wannier_gap(nu, sector, tol=1e-4): 

3495 ''' 

3496 Check that a Wannier sector is separated from the other Wannier bands 

3497 over the mesh (*nu*, shape (n1, n2, nb), sorted at each k): at every k, 

3498 the smallest distance on the circle between a Wannier band of the 

3499 sector and one outside it (the Wannier gap) exceeds *tol*, and the 

3500 Wannier bands of the sector change between neighbouring k-points by 

3501 less than the smallest gap (otherwise two Wannier bands crossed between 

3502 them, or the sector wrapped around the circle). 

3503 

3504 Raises 

3505 ------ 

3506 ValueError 

3507 The Wannier sector is not separated by a Wannier gap. 

3508 ''' 

3509 nu = np.asarray(nu) 

3510 rest = [n for n in range(nu.shape[-1]) if n not in sector] 

3511 if not rest: 

3512 return 

3513 

3514 def circ(d): 

3515 d = np.abs(d) % 1. 

3516 return np.minimum(d, 1. - d) 

3517 gap = np.min(circ(nu[..., sector][..., :, None] - nu[..., rest][..., None, :])) 

3518 jump = max(np.max(circ(nu[..., sector] - np.roll(nu[..., sector], 1, axis=a))) 

3519 for a in range(nu.ndim - 1)) 

3520 if gap < tol or jump >= gap: 

3521 raise ValueError('\n\nThe Wannier sector is not separated from the other Wannier ' 

3522 'bands by a Wannier gap (smallest distance {:.2e}, largest ' 

3523 'step {:.2e}).\n'.format(gap, jump)) 

3524 

3525 

3526def supercell_matrix(mat, dim): 

3527 ''' 

3528 Check a supercell matrix: integers, shape (dim, dim), nonzero determinant. 

3529 

3530 Raises 

3531 ------ 

3532 TypeError 

3533 The supercell matrix must contain integers. 

3534 ValueError 

3535 The supercell matrix must be (dim, dim), with a nonzero determinant. 

3536 ''' 

3537 if mat.shape != (dim, dim): 

3538 raise ValueError('\n\nThe supercell matrix must have the shape ({0}, {0}).\n'.format(dim)) 

3539 if not np.issubdtype(mat.dtype, np.integer): 

3540 raise TypeError('\n\nThe supercell matrix must contain integers.\n') 

3541 if round(abs(np.linalg.det(mat))) == 0: 

3542 raise ValueError('\n\nThe supercell matrix must have a nonzero determinant.\n') 

3543 

3544 

3545def supercell_model(sc, cls): 

3546 ''' 

3547 Check that *sc* is a supercell (made by *moire.supercell*). 

3548 

3549 Raises 

3550 ------ 

3551 TypeError 

3552 Parameter sc must be a SupercellKSpace instance. 

3553 ''' 

3554 if not isinstance(sc, cls): 

3555 raise TypeError('\n\nParameter sc must be a SupercellKSpace instance ' 

3556 '(see tbkit.moire.supercell).\n') 

3557 

3558 

3559def bond_vectors(d): 

3560 ''' 

3561 Check bond vectors: shape (n, 3), none of zero length. 

3562 

3563 Raises 

3564 ------ 

3565 ValueError 

3566 Bond vectors must have the shape (n, 3) and a nonzero length. 

3567 ''' 

3568 if d.ndim != 2 or d.shape[1] != 3: 

3569 raise ValueError('\n\nThe bond vectors must have the shape (n, 3).\n') 

3570 if np.any(np.linalg.norm(d, axis=1) == 0): 

3571 raise ValueError('\n\nThe bond vectors must have a nonzero length.\n') 

3572 

3573 

3574def twist_lattice(lattice): 

3575 ''' 

3576 Check the layer lattice of a twisted bilayer. 

3577 

3578 Raises 

3579 ------ 

3580 ValueError 

3581 Parameter lattice must be "honeycomb" or "square". 

3582 ''' 

3583 if lattice not in ('honeycomb', 'square'): 

3584 raise ValueError('\n\nParameter lattice must be "honeycomb" or "square".\n') 

3585 

3586 

3587def commensurate(moire_1, moire_2): 

3588 ''' 

3589 Check that the moire vectors of the two layers coincide. 

3590 

3591 Raises 

3592 ------ 

3593 ValueError 

3594 The two layers do not share the moire cell. 

3595 ''' 

3596 if not np.allclose(moire_1, moire_2, atol=1e-9 * max(1., np.abs(moire_1).max())): 

3597 raise ValueError('\n\nThe two layers do not share the moire cell.\n') 

3598 

3599 

3600def hopping_values(t, n): 

3601 ''' 

3602 Check the output of a hopping function: n values. 

3603 

3604 Raises 

3605 ------ 

3606 ValueError 

3607 The hopping function must return one value per bond vector. 

3608 ''' 

3609 if np.shape(t) != (n,): 

3610 raise ValueError('\n\nThe hopping function must return one value per bond vector, ' 

3611 'shape ({},).\n'.format(n)) 

3612 

3613 

3614def even_dimension(n): 

3615 ''' 

3616 Check that a spinful matrix has an even dimension (two spins per site). 

3617 

3618 Raises 

3619 ------ 

3620 ValueError 

3621 A spinful Hamiltonian must have an even dimension. 

3622 ''' 

3623 if n % 2: 

3624 raise ValueError('\n\nA spinful Hamiltonian must have an even dimension ' 

3625 '(rows site-major, spin up then down).\n') 

3626 

3627 

3628def magnetization(mag, n): 

3629 ''' 

3630 Check initial moments: a real array of shape (n, 3). 

3631 

3632 Raises 

3633 ------ 

3634 ValueError 

3635 Parameter magnetization must have the shape (n, 3). 

3636 ''' 

3637 if mag.shape != (n, 3): 

3638 raise ValueError('\n\nParameter magnetization must have the shape ({}, 3).\n'.format(n)) 

3639 

3640 

3641def interaction(V, n): 

3642 ''' 

3643 Check an attractive interaction: a positive number, or n non-negative numbers. 

3644 

3645 Raises 

3646 ------ 

3647 ValueError 

3648 The interaction must be positive (or n non-negative values). 

3649 ''' 

3650 V = np.asarray(V, dtype='f8') 

3651 if V.ndim == 0: 

3652 positive_real(float(V), 'V') 

3653 return 

3654 if V.shape != (n,) or np.any(V < 0): 

3655 raise ValueError('\n\nParameter V must be a positive number, or {} non-negative ' 

3656 'numbers (one per site).\n'.format(n)) 

3657 

3658 

3659def pairing_amplitudes(delta, n, var_name='delta0'): 

3660 ''' 

3661 Check pairing amplitudes: a number, or n numbers. 

3662 

3663 Raises 

3664 ------ 

3665 ValueError 

3666 Parameter *var_name* must be a number or n numbers. 

3667 ''' 

3668 if np.ndim(delta) == 0: 

3669 number(delta, var_name) 

3670 return 

3671 if np.shape(delta) != (n,): 

3672 raise ValueError('\n\nParameter {} must be a number, or {} numbers ' 

3673 '(one per site).\n'.format(var_name, n)) 

3674 

3675 

3676def spin_up_model(spin): 

3677 ''' 

3678 Check that a KSpace model is spinless (the spin-up electrons). 

3679 

3680 Raises 

3681 ------ 

3682 ValueError 

3683 This needs the spinless spin-up model. 

3684 ''' 

3685 if spin: 

3686 raise ValueError('\n\nThis needs a spinless model (spin=False): the spin-up ' 

3687 'electrons, whose time-reversed partners are the spin-down ones.\n') 

3688# OPTICAL RESPONSE AND TRANSPORT (tbkit.optics, tbkit.transport) 

3689#################################### 

3690 

3691 

3692def frequencies(omega, var_name): 

3693 ''' 

3694 Check photon energies (or energies): a real number, or a non-empty 

3695 array of finite real numbers. 

3696 

3697 Raises 

3698 ------ 

3699 TypeError 

3700 Parameter must be a real number or an array of real numbers. 

3701 ValueError 

3702 Parameter must contain at least one finite number. 

3703 ''' 

3704 arr = np.asarray(omega) 

3705 if arr.dtype.kind not in 'iuf': 

3706 raise TypeError('\n\nParameter {} must be a real number or an array ' 

3707 'of real numbers.\n'.format(var_name)) 

3708 if arr.size == 0 or not np.all(np.isfinite(arr)): 

3709 raise ValueError('\n\nParameter {} must contain at least one finite ' 

3710 'number.\n'.format(var_name)) 

3711 

3712 

3713def tensor_component(component, order): 

3714 ''' 

3715 Check a Cartesian tensor component of a 2D response: a string of 

3716 *order* letters among 'x' and 'y'. Returns the axis indices. 

3717 

3718 Raises 

3719 ------ 

3720 TypeError 

3721 Parameter component must be a string. 

3722 ValueError 

3723 Parameter component must have order letters among x, y. 

3724 ''' 

3725 if not isinstance(component, str): 

3726 raise TypeError('\n\nParameter component must be a string.\n') 

3727 if len(component) != order or any(c not in 'xy' for c in component): 

3728 raise ValueError('\n\nParameter component must be {} letters among ' 

3729 "'x' and 'y'.\n".format(order)) 

3730 return tuple('xy'.index(c) for c in component) 

3731 

3732 

3733def band_derivatives(en, vel, w): 

3734 ''' 

3735 Check the band energies (nk, n), velocities (2, nk, n, n) and second 

3736 derivatives (2, 2, nk, n, n) of *optics.generalized_derivative*. 

3737 

3738 Raises 

3739 ------ 

3740 ValueError 

3741 Inconsistent shapes. 

3742 ''' 

3743 if en.ndim != 2 or vel.shape != (2,) + en.shape + en.shape[-1:] or \ 

3744 w.shape != (2,) + vel.shape: 

3745 raise ValueError('\n\nThe shapes must be en (nk, n), vel (2, nk, n, n) and ' 

3746 'w (2, 2, nk, n, n).\n') 

3747 

3748 

3749def lead_pair(pair, n_leads, var_name, distinct): 

3750 ''' 

3751 Check a pair of lead indices (and, with *distinct*, that they differ). 

3752 

3753 Raises 

3754 ------ 

3755 TypeError 

3756 Parameter must be a tuple/list of two lead indices. 

3757 ValueError 

3758 No such lead, or the two leads must differ. 

3759 ''' 

3760 if not isinstance(pair, (tuple, list)) or len(pair) != 2: 

3761 raise TypeError('\n\nParameter {} must be a tuple of two lead indices.\n'.format(var_name)) 

3762 for lead in pair: 

3763 lead_index(lead, n_leads) 

3764 if distinct and pair[0] == pair[1]: 

3765 raise ValueError('\n\nThe two leads of parameter {} must differ.\n'.format(var_name)) 

3766 

3767 

3768def site_positions(positions, n_sites): 

3769 ''' 

3770 Check site positions: a real array of shape (n_sites, d). 

3771 

3772 Raises 

3773 ------ 

3774 ValueError 

3775 Parameter positions must have shape (n_sites, d). 

3776 ''' 

3777 if positions.ndim != 2 or len(positions) != n_sites: 

3778 raise ValueError('\n\nParameter positions must have shape ({}, d).\n'.format(n_sites)) 

3779 

3780 

3781def slices(slices, n_sites): 

3782 ''' 

3783 Check a partition of the device sites into non-empty slices. 

3784 

3785 Raises 

3786 ------ 

3787 TypeError 

3788 Parameter slices must be a list of lists of integers. 

3789 ValueError 

3790 The slices must contain every site exactly once. 

3791 ''' 

3792 if not isinstance(slices, list) or not all( 

3793 isinstance(s, (list, tuple)) and len(s) > 0 

3794 and all(isinstance(i, (int, np.integer)) for i in s) for s in slices): 

3795 raise TypeError('\n\nParameter slices must be a list of non-empty lists of integers.\n') 

3796 flat = sorted(int(i) for s in slices for i in s) 

3797 if flat != list(range(n_sites)): 

3798 raise ValueError('\n\nThe slices must contain every site (0 to {}) exactly ' 

3799 'once.\n'.format(n_sites - 1)) 

3800 

3801 

3802def block_tridiagonal(max_distance): 

3803 ''' 

3804 Check that hoppings only couple a slice to itself or to its neighbours. 

3805 

3806 Raises 

3807 ------ 

3808 ValueError 

3809 Hoppings must connect neighbouring slices only. 

3810 ''' 

3811 if max_distance > 1: 

3812 raise ValueError('\n\nThe Hamiltonian couples slices that are not neighbours: ' 

3813 'use thicker slices.\n') 

3814 

3815 

3816def lead_tuple(lead): 

3817 ''' 

3818 Check a lead given as a tuple (h0, v, coupling). 

3819 

3820 Raises 

3821 ------ 

3822 TypeError 

3823 A lead must be a tuple (h0, v, coupling). 

3824 ''' 

3825 if not isinstance(lead, (tuple, list)) or len(lead) != 3: 

3826 raise TypeError('\n\nA lead must be a tuple (h0, v, coupling).\n') 

3827 

3828 

3829#################################### 

3830# SECTOR CHERN NUMBERS, BDG, BOTT, WEYL 

3831#################################### 

3832 

3833 

3834def hermitian_operator(ops, tol=1e-10): 

3835 ''' 

3836 Check that an operator (shape (..., norb, norb)) is Hermitian. 

3837 

3838 Raises 

3839 ------ 

3840 ValueError 

3841 The operator must be Hermitian. 

3842 ''' 

3843 if np.max(np.abs(ops - ops.conj().swapaxes(-1, -2))) > tol: 

3844 raise ValueError('\n\nThe operator must be Hermitian.\n') 

3845 

3846 

3847def projected_gap(w, tol=1e-6): 

3848 ''' 

3849 Check that the projected operator POP (eigenvalues *w*, shape (nk, n), 

3850 sorted) has no eigenvalue near zero, and the same number of negative 

3851 eigenvalues at every k-point; return that number. 

3852 

3853 Raises 

3854 ------ 

3855 ValueError 

3856 The spectrum of POP must have a gap around zero. 

3857 ''' 

3858 n_minus = np.sum(w < 0., axis=1) 

3859 if np.min(np.abs(w)) < tol or np.any(n_minus != n_minus[0]): 

3860 raise ValueError('\n\nThe spectrum of the projected operator POP closes around zero ' 

3861 '(smallest |eigenvalue| {:.3g}): the two sectors are not ' 

3862 'defined.\n'.format(float(np.min(np.abs(w))))) 

3863 return int(n_minus[0]) 

3864 

3865 

3866def mirror_square(squares, tol): 

3867 ''' 

3868 Check that a mirror operator squares to +1 or -1 (*squares*, shape 

3869 (..., norb, norb)); return the sign. 

3870 

3871 Raises 

3872 ------ 

3873 ValueError 

3874 The mirror operator must square to +1 or -1. 

3875 ''' 

3876 sign = np.sign(squares[0, 0, 0].real) 

3877 eye = np.eye(squares.shape[-1]) 

3878 if sign == 0 or np.max(np.abs(squares - sign * eye)) > tol: 

3879 raise ValueError('\n\nThe mirror operator must be unitary and square to +1 or -1.\n') 

3880 return int(sign) 

3881 

3882 

3883def commutes(err, tol): 

3884 ''' 

3885 Check that an operator commutes with H(k) on the plane (*err*: largest 

3886 entry of the commutator). 

3887 

3888 Raises 

3889 ------ 

3890 ValueError 

3891 The operator does not commute with H(k) on this plane. 

3892 ''' 

3893 if err > tol: 

3894 raise ValueError('\n\nThe mirror operator does not commute with H(k) on this plane ' 

3895 '(largest commutator entry {:.3g}): choose a ' 

3896 'mirror-invariant plane.\n'.format(err)) 

3897 

3898 

3899def strong_index(strong): 

3900 ''' 

3901 Check that the strong Z2 index comes out the same from the three pairs 

3902 of time-reversal-invariant planes. 

3903 

3904 Raises 

3905 ------ 

3906 ValueError 

3907 The six Z2 invariants are inconsistent. 

3908 ''' 

3909 if len(set(int(s) for s in strong)) != 1: 

3910 raise ValueError('\n\nThe six planar Z2 invariants give different strong indices: ' 

3911 'increase nk or nk_perp (or the bands are not gapped).\n') 

3912 

3913 

3914def region(region, n): 

3915 ''' 

3916 Check a region: a non-empty list of distinct indices between 0 and n-1. 

3917 

3918 Raises 

3919 ------ 

3920 TypeError 

3921 Parameter region must be a non-empty list of integers. 

3922 ValueError 

3923 Parameter region must hold distinct indices between 0 and n-1. 

3924 ''' 

3925 if not isinstance(region, list) or not region or \ 

3926 not all(isinstance(i, (int, np.integer)) for i in region): 

3927 raise TypeError('\n\nParameter region must be a non-empty list of integers.\n') 

3928 if len(set(region)) != len(region) or not all(0 <= i < n for i in region): 

3929 raise ValueError('\n\nParameter region must hold distinct indices between 0 and ' 

3930 '{}.\n'.format(n - 1)) 

3931 

3932 

3933def torus_cell(cell, d): 

3934 ''' 

3935 Check the two vectors spanning a torus: shape (2, d), linearly independent. 

3936 

3937 Raises 

3938 ------ 

3939 ValueError 

3940 Parameter cell must hold two independent vectors of d components. 

3941 ''' 

3942 if cell.shape != (2, d) or np.linalg.matrix_rank(cell) < 2: 

3943 raise ValueError('\n\nParameter cell must hold two linearly independent vectors ' 

3944 'of {} components.\n'.format(d)) 

3945 

3946 

3947def band_below_top(n, norb): 

3948 ''' 

3949 Check that a band has a band above it. 

3950 

3951 Raises 

3952 ------ 

3953 ValueError 

3954 The highest band of bands must not be the top band. 

3955 ''' 

3956 if n >= norb - 1: 

3957 raise ValueError('\n\nThe highest band of bands must have a band above it.\n') 

3958 

3959 

3960def antisymmetric(a, tol=1e-10): 

3961 ''' 

3962 Check that a matrix is antisymmetric. 

3963 

3964 Raises 

3965 ------ 

3966 ValueError 

3967 The matrix must be antisymmetric. 

3968 ''' 

3969 if np.max(np.abs(a + a.T), initial=0.) > tol: 

3970 raise ValueError('\n\nThe matrix must be antisymmetric.\n') 

3971 

3972 

3973def bdg_orbitals(norb): 

3974 ''' 

3975 Check that a BdG model has an even number of orbitals (particles, then holes). 

3976 

3977 Raises 

3978 ------ 

3979 ValueError 

3980 A BdG model must have an even number of orbitals. 

3981 ''' 

3982 if norb % 2: 

3983 raise ValueError('\n\nA BdG model must have an even number of orbitals ' 

3984 '(particles, then holes).\n') 

3985 

3986 

3987def majorana_form(a, tol=1e-8): 

3988 ''' 

3989 Check that the Majorana form -i U H U^dagger of a BdG Hamiltonian at a 

3990 time-reversal-invariant momentum is real (particle-hole symmetry). 

3991 

3992 Raises 

3993 ------ 

3994 ValueError 

3995 The model is not particle-hole symmetric. 

3996 ''' 

3997 if np.max(np.abs(a.imag)) > tol: 

3998 raise ValueError('\n\nThe model is not particle-hole symmetric in the BdG basis ' 

3999 '(particles, then holes; see bdg.bdg_kspace).\n') 

4000 

4001 

4002def gapped_trim(pf, tol=1e-12): 

4003 ''' 

4004 Check that a Pfaffian is nonzero (the BdG gap is open at the momentum). 

4005 

4006 Raises 

4007 ------ 

4008 ValueError 

4009 The gap closes at a time-reversal-invariant momentum. 

4010 ''' 

4011 if abs(pf) < tol: 

4012 raise ValueError('\n\nThe gap closes at a time-reversal-invariant momentum: the ' 

4013 'Majorana number is not defined.\n') 

4014 

4015 

4016#################################### 

4017# PERIODIC BOUNDARIES (SYSTEM) 

4018#################################### 

4019 

4020 

4021def periodic(periodic, dim): 

4022 ''' 

4023 Check parameter *periodic* of *System*: a boolean, or a tuple of *dim* 

4024 booleans (one per primitive vector); return the tuple. 

4025 

4026 Raises 

4027 ------ 

4028 TypeError 

4029 Parameter periodic must be a bool or a tuple of bools. 

4030 ValueError 

4031 Parameter periodic must have one entry per primitive vector. 

4032 ''' 

4033 if isinstance(periodic, bool): 

4034 return (periodic,) * dim 

4035 if not isinstance(periodic, tuple) or not all(isinstance(p, bool) for p in periodic): 

4036 raise TypeError('\n\nParameter periodic must be a bool or a tuple of bools.\n') 

4037 if len(periodic) != dim: 

4038 raise ValueError('\n\nParameter periodic must have one entry per primitive vector ' 

4039 '({}).\n'.format(dim)) 

4040 return periodic 

4041 

4042 

4043def torus_lattice(n1): 

4044 ''' 

4045 Check that the lattice of a periodic System comes from *get_lattice*. 

4046 

4047 Raises 

4048 ------ 

4049 ValueError 

4050 Periodic boundaries need lat.get_lattice first. 

4051 ''' 

4052 if n1 == 0: 

4053 raise ValueError('\n\nPeriodic boundaries wrap the n1 x n2 (x n3) cells of ' 

4054 'lat.get_lattice: call it first.\n') 

4055 

4056 

4057def torus_size(dist, period, n): 

4058 ''' 

4059 Check that bonds of length *dist* (order *n*) reach less than half-way 

4060 around the torus (shortest period *period*), so that each one has a 

4061 single shortest image. 

4062 

4063 Raises 

4064 ------ 

4065 ValueError 

4066 The torus is too small for this neighbour order. 

4067 ''' 

4068 if 2 * dist >= period - ATOL: 

4069 raise ValueError('\n\nThe torus is too small for the neighbours of order {}: their ' 

4070 'length {:.3f} reaches half-way around it (shortest period ' 

4071 '{:.3f}). Use more cells.\n'.format(n, dist, period)) 

4072 

4073 

4074def not_periodic(periodic, method): 

4075 ''' 

4076 Check that a System has open boundaries. 

4077 

4078 Raises 

4079 ------ 

4080 ValueError 

4081 The method needs open boundaries. 

4082 ''' 

4083 if any(periodic): 

4084 raise ValueError('\n\n{} needs open boundaries (periodic=False); on a torus, see ' 

4085 'System.get_bott_index and set_peierls_phase.\n'.format(method)) 

4086 

4087 

4088def torus_2d(periodic): 

4089 ''' 

4090 Check that a System is a 2D torus (periodic along two primitive vectors). 

4091 

4092 Raises 

4093 ------ 

4094 ValueError 

4095 This calculation needs a 2D lattice periodic along both vectors. 

4096 ''' 

4097 if len(periodic) != 2 or not all(periodic): 

4098 raise ValueError('\n\nThis calculation needs a 2D lattice with periodic boundaries ' 

4099 'along both primitive vectors (periodic=True).\n') 

4100 

4101 

4102#################################### 

4103# ANALYSIS AND PLOTTING (projections, contours, k-paths) 

4104#################################### 

4105 

4106 

4107def projector(proj, norb, tags): 

4108 ''' 

4109 Check parameter *projector* of *kspace.band_weights*: an orbital index, 

4110 a non-empty list of distinct orbital indices, a sublattice tag, or a 

4111 (norb, norb) Hermitian matrix. 

4112 

4113 Raises 

4114 ------ 

4115 TypeError 

4116 Parameter projector must be an orbital index, a list of them, a tag or a matrix. 

4117 ValueError 

4118 Orbital indices must be distinct and between 0 and norb-1. 

4119 ValueError 

4120 Parameter projector must be a tag of the unit cell. 

4121 ValueError 

4122 The operator must be a (norb, norb) Hermitian matrix. 

4123 ''' 

4124 if isinstance(proj, str): 

4125 if proj not in tags: 

4126 raise ValueError('\n\nParameter projector must be a tag of the unit cell: ' 

4127 '{}.\n'.format(sorted(set(tags)))) 

4128 return 

4129 if isinstance(proj, np.ndarray) and proj.ndim == 2: 

4130 operator(proj, norb) 

4131 hermitian_operator(proj) 

4132 return 

4133 if isinstance(proj, int): 

4134 proj = [proj] 

4135 if not isinstance(proj, list) or not proj or not all(isinstance(p, int) for p in proj): 

4136 raise TypeError('\n\nParameter projector must be an orbital index, a list of ' 

4137 'orbital indices, a sublattice tag or a (norb, norb) matrix.\n') 

4138 if len(set(proj)) != len(proj) or not all(0 <= p < norb for p in proj): 

4139 raise ValueError('\n\nOrbital indices must be distinct and between 0 and ' 

4140 '{}.\n'.format(norb - 1)) 

4141 

4142 

4143def band_weights(weights, shape): 

4144 ''' 

4145 Check parameter *weights* of *kspace.plot_bands*: real values, one per 

4146 band and k-point of the stored band structure. 

4147 

4148 Raises 

4149 ------ 

4150 ValueError 

4151 Parameter weights must be a real array of the shape of the bands. 

4152 ''' 

4153 weights = np.asarray(weights) 

4154 if weights.shape != shape or np.iscomplexobj(weights) or not np.all(np.isfinite(weights)): 

4155 raise ValueError('\n\nParameter weights must be a finite real array of shape {}, ' 

4156 'the shape of the bands (see band_weights).\n'.format(shape)) 

4157 

4158 

4159def weight_style(style): 

4160 ''' 

4161 Check parameter *style* of *kspace.plot_bands*. 

4162 

4163 Raises 

4164 ------ 

4165 ValueError 

4166 Parameter style must be "color" or "size". 

4167 ''' 

4168 if style not in ('color', 'size'): 

4169 raise ValueError('\n\nParameter style must be "color" or "size".\n') 

4170 

4171 

4172def contour_found(n_elements, energy): 

4173 ''' 

4174 Check that a band crosses the energy of a constant-energy contour. 

4175 

4176 Raises 

4177 ------ 

4178 ValueError 

4179 The band does not cross the energy. 

4180 ''' 

4181 if n_elements == 0: 

4182 raise ValueError('\n\nThe band does not cross the energy {}: there is no ' 

4183 'constant-energy contour.\n'.format(energy)) 

4184 

4185 

4186def cubic_path(points, names, axes): 

4187 ''' 

4188 Check the parameters of *kspace.cubic_path*. 

4189 

4190 Raises 

4191 ------ 

4192 TypeError 

4193 Parameter points must be a dictionary. 

4194 ValueError 

4195 Each point must have 3 real coordinates. 

4196 ValueError 

4197 Parameter names must list at least two keys of points. 

4198 ValueError 

4199 Parameter axes must be 3 orthonormal rows. 

4200 ''' 

4201 if not isinstance(points, dict): 

4202 raise TypeError('\n\nParameter points must be a dictionary (label: point).\n') 

4203 for key, point in points.items(): 

4204 point = np.asarray(point) 

4205 if point.shape != (3,) or not np.isrealobj(point) or not np.all(np.isfinite(point)): 

4206 raise ValueError('\n\nPoint {} must have 3 real coordinates.\n'.format(key)) 

4207 if (not isinstance(names, (list, tuple)) or len(names) < 2 

4208 or any(n not in points for n in names)): 

4209 raise ValueError('\n\nParameter names must list at least two keys of points.\n') 

4210 if axes is not None: 

4211 axes = np.asarray(axes) 

4212 if axes.shape != (3, 3) or not np.isrealobj(axes) or not np.allclose(axes @ axes.T, np.eye(3)): 

4213 raise ValueError('\n\nParameter axes must be 3 orthonormal rows (shape (3, 3)).\n') 

4214 

4215 

4216def bravais_lattice(kind, dim): 

4217 ''' 

4218 Check that *kspace.high_symmetry_path* recognized the Bravais lattice. 

4219 

4220 Raises 

4221 ------ 

4222 ValueError 

4223 No standard path for this lattice. 

4224 ''' 

4225 if kind is None: 

4226 raise ValueError('\n\nNo standard high-symmetry path for this {}D lattice: ' 

4227 'high_symmetry_path knows the 1D and 2D Bravais lattices, ' 

4228 'and the simple cubic, fcc, bcc, simple tetragonal and ' 

4229 'hexagonal ones in 3D. Pass the k-points to k_path ' 

4230 'directly.\n'.format(dim)) 

4231 

4232 

4233############################### 

4234# CONTINUUM DISCRETIZATION 

4235############################### 

4236 

4237 

4238def sympy_module(module): 

4239 ''' 

4240 Check that sympy, the optional dependency of *tbkit.continuum*, is installed. 

4241 

4242 Raises 

4243 ------ 

4244 ImportError 

4245 sympy is not installed. 

4246 ''' 

4247 if module is None: 

4248 raise ImportError('\n\ntbkit.continuum needs sympy: pip install tbkit[continuum].\n') 

4249 

4250 

4251def continuum_type(ham, types): 

4252 ''' 

4253 Check the type of the Hamiltonian of *continuum.discretize*. 

4254 

4255 Raises 

4256 ------ 

4257 TypeError 

4258 The Hamiltonian must be a string, a sympy expression 

4259 or a sympy Matrix. 

4260 ''' 

4261 if not isinstance(ham, types): 

4262 raise TypeError('\n\nParameter hamiltonian must be a string, a sympy expression ' 

4263 'or a sympy Matrix.\n') 

4264 

4265 

4266def continuum_parsed(error, string): 

4267 ''' 

4268 Check that the string Hamiltonian of *continuum.discretize* was parsed. 

4269 

4270 Raises 

4271 ------ 

4272 ValueError 

4273 The string is not a valid expression. 

4274 ''' 

4275 if error is not None: 

4276 raise ValueError('\n\nParameter hamiltonian {!r} is not a valid sympy expression ' 

4277 '({}: {}).\n'.format(string, type(error).__name__, error)) 

4278 

4279 

4280def continuum_square(shape): 

4281 ''' 

4282 Check that the Hamiltonian of *continuum.discretize* is a square matrix. 

4283 

4284 Raises 

4285 ------ 

4286 ValueError 

4287 The Hamiltonian must be a square matrix. 

4288 ''' 

4289 if len(shape) != 2 or shape[0] != shape[1] or shape[0] == 0: 

4290 raise ValueError('\n\nParameter hamiltonian must be a scalar or a square matrix ' 

4291 '(shape {}).\n'.format(shape)) 

4292 

4293 

4294def continuum_symbols(names): 

4295 ''' 

4296 Check the free symbols of the Hamiltonian of *continuum.discretize*: 

4297 no position (the model is translation invariant), no symbol named 

4298 like the grid spacing *a*, and parameter names that can be passed as 

4299 keyword arguments. 

4300 

4301 Raises 

4302 ------ 

4303 ValueError 

4304 x, y, z are not supported. 

4305 ValueError 

4306 a is reserved for the grid spacing. 

4307 ValueError 

4308 Parameter names must be Python identifiers. 

4309 ''' 

4310 position = sorted(names & {'x', 'y', 'z'}) 

4311 if position: 

4312 raise ValueError('\n\nThe Hamiltonian depends on the position ({}): only translation-' 

4313 'invariant continuum models (constant coefficients) can be ' 

4314 'discretized into a KSpace.\n'.format(', '.join(position))) 

4315 if 'a' in names: 

4316 raise ValueError('\n\nThe symbol a is reserved for the grid spacing: rename the ' 

4317 'parameter.\n') 

4318 bad = sorted(n for n in names if not n.isidentifier() or n in ('site_i', 'site_j')) 

4319 if bad: 

4320 raise ValueError('\n\nParameter names must be Python identifiers (other than site_i, ' 

4321 'site_j), to be passed to get_ham: {}.\n'.format(', '.join(bad))) 

4322 

4323 

4324def continuum_dim(dim, used): 

4325 ''' 

4326 Check the dimension of *continuum.discretize*, given the momenta 

4327 k_x, k_y, k_z (axes 0, 1, 2) used by the Hamiltonian. 

4328 

4329 Raises 

4330 ------ 

4331 TypeError 

4332 Parameter dim must be an integer. 

4333 ValueError 

4334 Parameter dim must be 1, 2 or 3. 

4335 ValueError 

4336 The Hamiltonian uses a momentum beyond dim. 

4337 ''' 

4338 if not isinstance(dim, int): 

4339 raise TypeError('\n\nParameter dim must be an integer.\n') 

4340 if dim not in (1, 2, 3): 

4341 raise ValueError('\n\nParameter dim must be 1, 2 or 3.\n') 

4342 if used and max(used) >= dim: 

4343 raise ValueError('\n\nThe Hamiltonian uses {} in dimension {}.\n'.format( 

4344 ('k_x', 'k_y', 'k_z')[max(used)], dim)) 

4345 

4346 

4347def continuum_polynomial(entry, is_polynomial): 

4348 ''' 

4349 Check that a matrix element of *continuum.discretize* is a polynomial 

4350 in the momenta. 

4351 

4352 Raises 

4353 ------ 

4354 ValueError 

4355 The Hamiltonian must be a polynomial in k_x, k_y, k_z. 

4356 ''' 

4357 if not is_polynomial: 

4358 raise ValueError('\n\nThe Hamiltonian must be a polynomial in k_x, k_y, k_z ' 

4359 '(matrix element {}).\n'.format(entry)) 

4360 

4361 

4362def continuum_tags(tags, norb): 

4363 ''' 

4364 Check the orbital tags of *continuum.discretize*. 

4365 

4366 Raises 

4367 ------ 

4368 ValueError 

4369 More than 52 orbitals need explicit tags. 

4370 TypeError 

4371 Parameter tags must be a string or a list of strings. 

4372 ValueError 

4373 Parameter tags must hold one one-character tag per orbital. 

4374 ''' 

4375 if tags is None: 

4376 if norb > 52: 

4377 raise ValueError('\n\nThe Hamiltonian has {} orbitals: pass their tags (default ' 

4378 'tags are the 52 letters).\n'.format(norb)) 

4379 return 

4380 if not isinstance(tags, (str, list, tuple)) or not all(isinstance(t, str) for t in tags): 

4381 raise TypeError('\n\nParameter tags must be a string or a list of strings.\n') 

4382 if len(tags) != norb or not all(len(t) == 1 for t in tags): 

4383 raise ValueError('\n\nParameter tags must hold {} one-character tags, one per ' 

4384 'orbital.\n'.format(norb)) 

4385 

4386 

4387def continuum_spacing(a): 

4388 ''' 

4389 Check the grid spacing of *continuum.discretize*: a **Lattice** 

4390 refuses primitive vectors whose squared norm is below 0.1. 

4391 

4392 Raises 

4393 ------ 

4394 ValueError 

4395 Parameter a must be at least sqrt(0.1). 

4396 ''' 

4397 if a * a < 0.1: 

4398 raise ValueError('\n\nParameter a must be at least sqrt(0.1) = 0.316 (the shortest ' 

4399 'primitive vector of a Lattice): measure lengths in a smaller ' 

4400 'unit, and scale the coefficients of the Hamiltonian ' 

4401 'accordingly.\n') 

4402 

4403 

4404#################################### 

4405# WANNIER FUNCTIONS 

4406#################################### 

4407 

4408 

4409def trial_orbitals(trial, norb, n_bands): 

4410 ''' 

4411 Check the trial orbitals of *wannier.wannierize*: a list of orbital 

4412 indices, or an array of shape (norb, n_wann), with at most *n_bands* 

4413 trial orbitals (one per Wannier function; fewer than the bands 

4414 disentangles them). 

4415 

4416 Raises 

4417 ------ 

4418 TypeError 

4419 Parameter trial must be a list of orbital indices or an array. 

4420 ValueError 

4421 Parameter trial must hold between 1 and n_bands trial orbitals. 

4422 ''' 

4423 if isinstance(trial, list) and trial and all(isinstance(o, int) for o in trial): 

4424 if len(trial) > n_bands or not all(0 <= o < norb for o in trial): 

4425 raise ValueError('\n\nParameter trial must hold at most {} orbital indices (one per ' 

4426 'Wannier function, at most one per band) between 0 and {}.\n' 

4427 .format(n_bands, norb - 1)) 

4428 return 

4429 try: 

4430 g = np.asarray(trial, dtype='c16') 

4431 except (TypeError, ValueError): 

4432 raise TypeError('\n\nParameter trial must be a list of orbital indices or a complex ' 

4433 'array of shape (norb, n_wann).\n') from None 

4434 if g.ndim != 2 or g.shape[0] != norb or not 1 <= g.shape[1] <= n_bands: 

4435 raise ValueError('\n\nParameter trial must be an array of shape ({}, n_wann), with ' 

4436 '1 <= n_wann <= {}: one column (trial orbital) per Wannier ' 

4437 'function, at most one per band.\n'.format(norb, n_bands)) 

4438 

4439 

4440def energy_window(window, var_name): 

4441 ''' 

4442 Check an energy window of *wannier.wannierize*: a tuple of two 

4443 increasing real numbers. 

4444 

4445 Raises 

4446 ------ 

4447 TypeError 

4448 Parameter var_name must be a tuple of two real numbers. 

4449 ValueError 

4450 Parameter var_name must be increasing. 

4451 ''' 

4452 if not isinstance(window, tuple) or len(window) != 2 or \ 

4453 not all(isinstance(e, (int, float)) and not isinstance(e, bool) for e in window): 

4454 raise TypeError('\n\nParameter {} must be a tuple (E_min, E_max) of two real ' 

4455 'numbers.\n'.format(var_name)) 

4456 if window[0] >= window[1]: 

4457 raise ValueError('\n\nParameter {} must satisfy E_min < E_max.\n'.format(var_name)) 

4458 

4459 

4460def frozen_window(frozen, window): 

4461 ''' 

4462 Check that the frozen (inner) window of *wannier.wannierize* lies 

4463 inside the outer window. 

4464 

4465 Raises 

4466 ------ 

4467 ValueError 

4468 The frozen window must lie inside the outer window. 

4469 ''' 

4470 if frozen[0] < window[0] or frozen[1] > window[1]: 

4471 raise ValueError('\n\nParameter frozen {} must lie inside the outer window {}.\n' 

4472 .format(frozen, window)) 

4473 

4474 

4475def window_states(n_inside, n_frozen, n_wann): 

4476 ''' 

4477 Check the number of states of the disentanglement windows at every 

4478 k-point: at least *n_wann* in the outer window, at most *n_wann* in the 

4479 frozen window. 

4480 

4481 Raises 

4482 ------ 

4483 ValueError 

4484 The outer window holds fewer than n_wann states at some k-point. 

4485 ValueError 

4486 The frozen window holds more than n_wann states at some k-point. 

4487 ''' 

4488 if n_inside < n_wann: 

4489 raise ValueError('\n\nThe outer window holds only {} of the bands at some k-point, ' 

4490 'fewer than the {} Wannier functions: widen it, or add bands.\n' 

4491 .format(n_inside, n_wann)) 

4492 if n_frozen > n_wann: 

4493 raise ValueError('\n\nThe frozen window holds {} bands at some k-point, more than the ' 

4494 '{} Wannier functions: narrow it.\n'.format(n_frozen, n_wann)) 

4495 

4496 

4497def isolated_bands(en, bands, tol=1e-8): 

4498 ''' 

4499 Check that a group of bands is separated in energy from the other bands 

4500 at every point of the mesh (*en*, shape (nk, norb), sorted). 

4501 

4502 Raises 

4503 ------ 

4504 ValueError 

4505 The bands are not isolated. 

4506 ''' 

4507 inside = np.zeros(en.shape[1], bool) 

4508 inside[bands] = True 

4509 for n in range(en.shape[1] - 1): 

4510 if inside[n] != inside[n + 1] and np.min(en[:, n + 1] - en[:, n]) < tol: 

4511 raise ValueError('\n\nThe bands {} touch band {} on the k-mesh: Wannier functions ' 

4512 'need an isolated group of bands: add the bands they touch to the group, ' 

4513 'or disentangle them (more bands than trial orbitals).\n' 

4514 .format(bands, n + 1 if inside[n] else n)) 

4515 

4516 

4517def projection(s_min, tol=1e-10): 

4518 ''' 

4519 Check that the projection of the bands onto the trial orbitals has full 

4520 rank at every k-point. 

4521 

4522 Raises 

4523 ------ 

4524 ValueError 

4525 The trial orbitals miss the bands at some k-point. 

4526 ''' 

4527 if s_min < tol: 

4528 raise ValueError('\n\nThe trial orbitals have no overlap with the bands at some ' 

4529 'k-point (singular projection): choose other trial orbitals, or ' 

4530 'another mesh. A band group with a nonzero Chern number has no ' 

4531 'localized Wannier functions, and any projection vanishes ' 

4532 'somewhere.\n') 

4533