Coverage for tbkit/error_handling.py: 100%

1275 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 06:13 +0000

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 TypeError: Parameter *var* must be a bool. 

18 ''' 

19 if not isinstance(var, bool): 

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

21 

22 

23def positive_int(var, var_name): 

24 ''' 

25 Check if *var* is a positive integer. 

26 

27 :raises TypeError: Parameter *var* must be an integer. 

28 :raises ValueError: Parameter *var* must be a positive integer. 

29 ''' 

30 if not isinstance(var, int): 

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

32 if var < 1: 

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

34 

35 

36def positive_int_lim(var, var_name, nmax): 

37 ''' 

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

39 

40 :raises TypeError: Parameter *var* must be an integer. 

41 :raises ValueError: Parameter *var* must be a positive integer. 

42 :raises ValueError: Parameter *var* must be a positive integer 

43 smaller than nmax. 

44 ''' 

45 if not isinstance(var, int): 

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

47 if var < 1: 

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

49 if var > nmax: 

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

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

52 

53 

54def real_number(var, var_name): 

55 ''' 

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

57 

58 :raises TypeError: Parameter *var* must be a real number. 

59 ''' 

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

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

62 

63 

64def positive_real(var, var_name): 

65 ''' 

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

67 

68 :raises TypeError: Parameter *var* must be a real number. 

69 :raises ValueError: Parameter *var* must be a positive number. 

70 ''' 

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

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

73 if var <= 0: 

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

75 

76 

77def positive_real_zero(var, var_name): 

78 ''' 

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

80 

81 :raises TypeError: Parameter *var* must be a real number. 

82 :raises ValueError: Parameter *var* must be a positive number or zero. 

83 ''' 

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

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

86 if var < 0: 

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

88 

89 

90def negative_real(var, var_name): 

91 ''' 

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

93 

94 :raises TypeError: Parameter *var* must be a real number. 

95 :raises ValueError: Parameter *var* must be a negative number. 

96 

97 ''' 

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

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

100 if var >= 0: 

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

102 

103 

104def number(var, var_name): 

105 ''' 

106 Check if parameter *var* is a number. 

107 

108 :raises TypeError: Parameter *var* must be a real or complex number. 

109 ''' 

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

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

112 

113 

114def value_params(required, params, func): 

115 ''' 

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

117 needs (see *tbkit.values*). 

118 

119 :raises TypeError: The value function needs parameters missing from get_ham. 

120 ''' 

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

122 if missing: 

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

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

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

126 

127 

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

129 ''' 

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

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

132 matrix or one per entry). 

133 

134 :raises ValueError: The value function must return a number or an array of one value per bond/site. 

135 ''' 

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

137 if val.shape not in shapes: 

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

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

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

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

142 if spin else '')) 

143 

144 

145def no_value_functions(model, method): 

146 ''' 

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

148 energies), which *method* cannot represent. 

149 

150 :raises ValueError: method does not support value functions. 

151 ''' 

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

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

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

155 

156 

157def is_callable(var, var_name): 

158 ''' 

159 Check if parameter *var* is callable. 

160 

161 :raises TypeError: Parameter *var* must be callable. 

162 ''' 

163 if not callable(var): 

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

165 

166 

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

168 ''' 

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

170 

171 :raises ValueError: Parameter *var1* larger than *var2*. 

172 ''' 

173 if var1 >= var2: 

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

175 .format(var_name1, var_name2)) 

176 

177 

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

179 ''' 

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

181 

182 :raises ValueError: Parameter *var1* must be smaller than *var2*. 

183 ''' 

184 if var1 >= var2: 

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

186 .format(var_name1, var_name2)) 

187 

188 

189def string(var, var_name): 

190 ''' 

191 Check if parameter *var* is a string. 

192 

193 :raises TypeError: Parameter *var* must be a string. 

194 ''' 

195 if var is None: 

196 return 

197 if not isinstance(var, str): 

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

199 

200 

201def ndarray(var, var_name, length): 

202 ''' 

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

204 

205 :raises TypeError: Parameter *var* must be a numpy ndarray. 

206 :raises ValueError: length array must be equal to length. 

207 ''' 

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

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

210 if len(var) != length: 

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

212 ''.format(var_name, length)) 

213 

214 

215def ndarray_null(var, var_name): 

216 ''' 

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

218 

219 :raises ValueError: Parameter *var* must not be a null numpy ndarray. 

220 ''' 

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

222 if np.allclose(var, array_null): 

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

224 

225 

226def ndarray_empty(var, var_name): 

227 ''' 

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

229 

230 :raises ValueError: Parameter *var* must not be an emptynumpy ndarray. 

231 ''' 

232 if var.size == 0: 

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

234 

235 

236def list_tuple_2elem(var, var_name): 

237 ''' 

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

239 

240 :raises TypeError: Parameter *var* must be a list/tuple. 

241 :raises ValueError: Parameter *var* must contain 2 elements. 

242 ''' 

243 if var is None: 

244 return 

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

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

247 if len(var) != 2: 

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

249 

250 

251def tuple_2elem(var, var_name): 

252 ''' 

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

254 

255 :raises TypeError: Parameter *var* must be a tuple. 

256 :raises ValueError: Parameter *var* must contain 2 elements. 

257 ''' 

258 if var is None: 

259 return 

260 if not isinstance(var, tuple): 

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

262 if len(var) != 2: 

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

264 

265 

266############################### 

267# LATTICE EXCEPTION HANDLING 

268############################### 

269 

270 

271def lat(lat): 

272 ''' 

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

274 :raises TypeError: Parameter must be an instance of the class lattice. 

275 ''' 

276 names = inspect.getmro(lat.__class__) 

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

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

279 

280 

281def unit_cell(unit_cell): 

282 ''' 

283 Check parameter *unit_cell*. 

284 

285 :raises TypeError: Parameter unit_cell must be a list. 

286 :raises KeyError: Dictionaries must contain the key "tag". 

287 :raises KeyError: Dictionaries must contain the key "r0". 

288 :raises TypeError: Key "tags" must contain a one-character string. 

289 :raises ValueError: Key "tags" must contain a one-character string. 

290 :raises ValueError: Key "r0" must contain be a list. 

291 :raises TypeError: Key "r0" must contain be a tuple. 

292 :raises ValueError: Key "r0" must contain a tuple of length two. 

293 :raises ValueError: Key "r0" must contain a tuple of two real numbers. 

294 ''' 

295 if not isinstance(unit_cell, list): 

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

297 for dic in unit_cell: 

298 if 'tag' not in dic: 

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

300 if 'r0' not in dic: 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

315 

316 

317def prim_vec(prim_vec): 

318 ''' 

319 Check parameter *prim_vec*. 

320 

321 :raises TypeError: Parameter prim_vec must be a list. 

322 :raises ValueError: Parameter prim_vec must be a list 

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

324 :raises TypeError: List elements must be tuples. 

325 :raises ValueError: List elements must be 1 or 2 tuples. 

326 :raises ValueError: Tuples must be of length 2. 

327 :raises ValueError: Tuples must containt real numbers. 

328 :raises ValueError: Norm of coor should be larger than 0.1. 

329 ''' 

330 if not isinstance(prim_vec, list): 

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

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

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

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

335 for coor in prim_vec: 

336 if not isinstance(coor, tuple): 

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

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

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

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

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

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

343 'real numbers.\n') 

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

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

346 'than 0.1.\n') 

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

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

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

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

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

352 

353 

354def independent(prim_vec): 

355 ''' 

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

357 the reciprocal lattice). 

358 

359 :raises ValueError: The primitive vectors must be linearly independent. 

360 ''' 

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

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

363 

364 

365def space_dim(unit_cell, prim_vec): 

366 ''' 

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

368 same space (2D or 3D). 

369 

370 :raises ValueError: "r0" and prim_vec tuples must have the same length. 

371 ''' 

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

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

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

375 

376 

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

378 ''' 

379 Check method *get_lattice*. 

380 

381 :raises TypeError: Parameters n1, n2, n3 must be integers. 

382 :raises ValueError: Parameters n1, n2, n3 must be positive integers. 

383 :raises ValueError: n2 (n3) must be 1 with fewer than 2 (3) primitive vectors. 

384 ''' 

385 positive_int(n1, 'n1') 

386 positive_int(n2, 'n2') 

387 positive_int(n3, 'n3') 

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

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

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

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

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

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

394 

395 

396def coor(coor, dtype=None): 

397 ''' 

398 Check if *coor* is a structured array with 

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

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

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

402 ''' 

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

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

405 if dtype is not None: 

406 dtypes = [np.dtype(dtype)] 

407 if coor.dtype not in dtypes: 

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

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

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

411 

412 

413def coor_1d(coor): 

414 ''' 

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

416 :raises ValueError: *coor* must be 1d( coor['y'] = cst).. 

417 ''' 

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

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

420 

421 

422def remove_sites(index, sites): 

423 ''' 

424 Check method *remove_sites*. 

425 

426 :raises TypeError: Parameter index must be a list. 

427 :raises ValueError: Parameter index must be a list of integers. 

428 :raises ValueError: Indices must be between 0 and sites -1. 

429 of integers between 0 and sites 

430 ''' 

431 if not isinstance(index, list): 

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

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

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

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

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

437 

438 

439def shift(shift): 

440 ''' 

441 Check *shift_x* and *shift_y*. 

442 :raises TypeError: Parameter delta must be a real number. 

443 ''' 

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

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

446 

447 

448def boundary_line(cx, cy, co): 

449 ''' 

450 Check *boundary_line*. 

451 :raises TypeError: Parameter cx must be a real number. 

452 :raises TypeError: Parameter cy must be a real number. 

453 :raises TypeError: Parameter co must be a real number. 

454 ''' 

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

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

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

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

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

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

461 

462 

463def space_3d(space_dim): 

464 ''' 

465 Check that the lattice lives in 3D space. 

466 

467 :raises ValueError: This method requires a lattice in 3D space. 

468 ''' 

469 if space_dim != 3: 

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

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

472 

473 

474def sites(sites): 

475 ''' 

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

477 :raises RuntimeError: Run method lat.get_lattice first. 

478 ''' 

479 if sites == 0: 

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

481 

482 

483#################################### 

484# CLASS SYSTEM EXCEPTION HANDLING 

485#################################### 

486 

487 

488def sys(sys): 

489 ''' 

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

491 :raises TypeError: Parameter must be an instance of the class system. 

492 ''' 

493 names = inspect.getmro(sys.__class__) 

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

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

496 

497 

498def print_hopping(n, nmax): 

499 ''' 

500 Check method *print_vec_hopping*. 

501 

502 :raises TypeError: Parameter *nmax* must be an integer. 

503 :raises ValueError: Parameter *nmax* must be a positive integer. 

504 between 1 and n_max-1. 

505 ''' 

506 if not isinstance(n, int): 

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

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

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

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

511 

512 

513def set_onsite(onsite, tags): 

514 ''' 

515 Check method *set_onsite*. 

516 

517 :raises TypeError: Parameter onsite must be a dictionary. 

518 :raises ValueError: Parameter onsite keys must be a tag. 

519 :raises ValueError: Parameter onsite values must be 

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

521 ''' 

522 if not isinstance(onsite, dict): 

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

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

525 if tag not in tags: 

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

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

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

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

530 

531 

532def set_hopping(list_hop, n_max): 

533 ''' 

534 Check method *set_hopping*. 

535 

536 :raises TypeError: Parameter *list_hop* must be a list. 

537 :raises TypeError: Parameter *list_hop* must be a list of dictionary. 

538 :raises KeyError: "n" and "t" must be dictionary keys. 

539 :raises KeyError: "tag" or "ang" must be a key. 

540 :raises KeyError: "tag" and "ang" must be keys. 

541 :raises ValueError: Dictionaries must be of length 2, 3, or 4. 

542 :raises ValueError: "n" must be between 1 and nmax" 

543 

544 ''' 

545 if not isinstance(list_hop, list): 

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

547 for dic in list_hop: 

548 if not isinstance(dic, dict): 

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

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

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

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

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

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

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

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

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

558 if len(dic) == 3: 

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

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

561 elif len(dic) == 4: 

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

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

564 elif len(dic) > 4: 

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

566 if 'tag' in dic: 

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

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

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

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

571 if 'ang' in dic: 

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

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

574 

575 

576def index(ind, dic): 

577 ''' 

578 check if *ind* not empy. 

579 ''' 

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

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

582 

583 

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

585 ''' 

586 Check methods *set_hopping_def* and *set_hopping_manual*. 

587 

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

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

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

591 

592 :raises TypeError: Parameter *hopping_def* must be a dictionary 

593 :raises TypeError: *hopping_def* keys must be tuples. 

594 :raises TypeError: *hopping_def* keys must be tuples of length 2. 

595 :raises ValueError: *hopping_def* keys must be tuples of integers. 

596 :raises ValueError: *hopping_def* keys must be integers between 0 and sites-1. 

597 :raises ValueError: *hopping_def* keys must be different integers between 0 and sites-1. 

598 :raises TypeError: *hopping_def* values must be numbers. 

599 ''' 

600 if not isinstance(hopping_def, dict): 

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

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

603 if not isinstance(key, tuple): 

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

605 if len(key) != 2: 

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

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

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

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

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

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

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

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

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

615 

616 

617def set_onsite_def(onsite_def, sites): 

618 ''' 

619 Check method *set_ons_def*. 

620 

621 :raises TypeError: Parameter *onsite_def* must be a dictionary. 

622 :raises TypeError: *onsite_def* keys must be integers. 

623 :raises TypeError: *onsite_def* values must be numbers. 

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

625 ''' 

626 if not isinstance(onsite_def, dict): 

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

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

629 if not isinstance(key, int): 

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

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

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

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

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

635 

636 

637def hop_n1(hop): 

638 ''' 

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

640 

641 :raises ValueError: self.hop must contain nearest neighbours hoppings. 

642 ''' 

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

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

645 

646 

647def empty_onsite(onsite): 

648 ''' 

649 Check if *onsite* not empty. 

650 

651 :raises RuntimeError: Run method set_onsite first. 

652 ''' 

653 if onsite.size == 0: 

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

655 

656 

657def empty_hop(hop): 

658 ''' 

659 Check if *hop* not empty. 

660 

661 :raises RuntimeError: Run method set_hopping first. 

662 ''' 

663 if hop.size == 0: 

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

665 

666 

667def hop_sites(hop, sites): 

668 ''' 

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

670 

671 :raises ValueError: Run method sys.clear_hopping and redefine the hoppings. 

672 ''' 

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

674 if ind_max >= sites: 

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

676 'Run method system.clear_hopping ' 

677 'and redefine the hoppings.\n') 

678 

679 

680def empty_coor(coor): 

681 ''' 

682 Check if *coor* not empty. 

683 

684 :raises RuntimeError: Run method lattice.get_lattice first. 

685 ''' 

686 if coor.size == 0: 

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

688 

689 

690def empty_coor_hop(coor_hop): 

691 ''' 

692 Check if *coor_hop* not empty. 

693 

694 :raises RuntimeError: Run method system.get_coor_hop first. 

695 ''' 

696 if coor_hop.size == 0: 

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

698 

699 

700def empty_ham(ham): 

701 ''' 

702 Check if Hamiltonian not empty. 

703 

704 :raises RuntimeError: Run method system.get_ham first. 

705 ''' 

706 if not ham.nnz: 

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

708 

709 

710def empty_en(en): 

711 ''' 

712 Check if *en* not empty. 

713 

714 :raises RuntimeError: Run method get_ham first. 

715 ''' 

716 if en.size == 0: 

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

718 

719 

720def empty_pola(pola): 

721 ''' 

722 Check if *pola* not empty. 

723 

724 :raises RuntimeError: Run method get_eig(eigenvec=True) first. 

725 ''' 

726 if pola.size == 0: 

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

728 

729 

730def empty_vn(vn): 

731 ''' 

732 Check if *vn* not empty. 

733 

734 :raises RuntimeError: Run method get_eig(eigenvec=True) first. 

735 ''' 

736 if vn.size == 0: 

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

738 

739 

740def empty_ipr(ipr): 

741 ''' 

742 Check if *ipr* not empty. 

743 ''' 

744 if ipr.size == 0: 

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

746 

747 

748def empty_ndarray(arr, method): 

749 ''' 

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

751 ''' 

752 if arr.size == 0: 

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

754 

755 

756def tag(tag, tags): 

757 ''' 

758 Check tag. 

759 

760 :raises TypeError: Parameter *tag* must be a string. 

761 :raises ValueError: Parameter *tag* is not in tags. 

762 ''' 

763 if not isinstance(tag, str): 

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

765 if tag not in tags: 

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

767 

768 

769def angle(angle, angles, upper_part): 

770 ''' 

771 Check angle. 

772 

773 :raises TypeError: Parameter *angle* must be 

774 

775 * a positive number if *upper_part* is True 

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

777 :raises ValueError: Parameter *angle* is not in hop['ang']. 

778 ''' 

779 if upper_part: 

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

781 else: 

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

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

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

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

786 

787 

788def lims(lims): 

789 ''' 

790 Check parameter *lims*. 

791 

792 :raises TypeError: Parameter lims must be a list. 

793 :raises TypeError: Parameter *lims[0]* must be a real number. 

794 :raises TypeError: Parameter *lims[1]* must be a real number. 

795 :raises ValueError: *lims* must be a list of length 2. 

796 :raises ValueError: *lims[0]* must be smaller than *lims[1]*. 

797 ''' 

798 if lims is not None: 

799 list_tuple_2elem(lims, 'lims') 

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

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

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

803 

804 

805def lims_positive(lims): 

806 ''' 

807 Check parameter *lims*. 

808 

809 :raises TypeError: Parameter lims must be a list. 

810 :raises TypeError: Parameter *lims[0]* must be a positive real number. 

811 :raises TypeError: Parameter *lims[1]* must be a positive real number. 

812 :raises ValueError: *lims* must be a list of length 2. 

813 :raises ValueError: *lims[0]* must be smaller than *lims[1]*. 

814 ''' 

815 if lims is not None: 

816 list_tuple_2elem(lims, 'lims') 

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

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

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

820 

821 

822################################# 

823# CLASS PLOT EXCEPTION HANDLING 

824################################# 

825 

826 

827def fig(fig): 

828 ''' 

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

830 

831 :raises TypeError: fig must be an instance of *Figure*. 

832 ''' 

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

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

835 

836 

837def ani(ani): 

838 ''' 

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

840 

841 :raises TypeError: ani must be an instance of *FuncAnimation*. 

842 ''' 

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

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

845 

846 

847def file_format(file_format): 

848 ''' 

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

850 

851 :raises TypeError: file_format must be a string. 

852 :raises ValueError: file_format must be a string given by 'png', 'pdf', 'ps', 'eps', or 'svg'. 

853 

854 ''' 

855 if not isinstance(file_format, str): 

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

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

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

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

860 

861 

862#################################### 

863# PROPAGATION 

864#################################### 

865 

866 

867def get_pump(hams): 

868 ''' 

869 Check method *propagation.get_pumping*. 

870 

871 :raises TypeError: hams must be a non-empty list. 

872 :raises RuntimeError: Run method system.get_ham first. 

873 ''' 

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

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

876 for ham in hams: 

877 empty_ham(ham) 

878 

879def prop_type(prop_type): 

880 string(prop_type, 'prop_type') 

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

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

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

884 

885 

886#################################### 

887# CLASS KSPACE EXCEPTION HANDLING 

888#################################### 

889 

890 

891def k_vector(vec, var_name, ndim): 

892 ''' 

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

894 

895 :raises TypeError: Parameter *var_name* must be a tuple/list. 

896 :raises ValueError: Parameter *var_name* must be of length *ndim*. 

897 :raises TypeError: Parameter *var_name* must contain real numbers. 

898 ''' 

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

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

901 if len(vec) != ndim: 

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

903 for val in vec: 

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

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

906 

907 

908def ks(ks, ndim): 

909 ''' 

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

911 (nk, *ndim*). 

912 

913 :raises ValueError: Parameter ks must be of shape (nk, ndim). 

914 ''' 

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

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

917 

918 

919def spin_matrix(t, var_name): 

920 ''' 

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

922 complex matrix. 

923 

924 :raises TypeError: Parameter *var_name* must be a number or a 2x2 matrix. 

925 ''' 

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

927 return 

928 t = np.asarray(t) 

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

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

931 

932 

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

934 ''' 

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

936 value function (a callable). 

937 

938 :raises TypeError: Parameter *list_hop* must be a list of dictionaries. 

939 :raises KeyError: "i", "j", "R", and "t" must be dictionary keys. 

940 :raises ValueError: "i" and "j" must be site indices between 0 and n_sites-1. 

941 :raises ValueError: "R" must be a tuple of *ndim* integers. 

942 :raises TypeError: "t" must be a real or complex number (or, if *spin*, 

943 a 2x2 matrix). 

944 ''' 

945 if not isinstance(list_hop, list): 

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

947 for dic in list_hop: 

948 if not isinstance(dic, dict): 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

964 continue 

965 if spin: 

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

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

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

969 

970 

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

972 ''' 

973 Check method *kspace.set_onsite*. 

974 

975 :raises TypeError: Parameter *dict_onsite* must be a dictionary. 

976 :raises ValueError: keys must be tags. 

977 :raises TypeError: values must be real or complex numbers (or, if 

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

979 ''' 

980 if not isinstance(dict_onsite, dict): 

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

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

983 if tag not in tags: 

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

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

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

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

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

989 if not (is_pair or is_mat): 

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

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

992 '2x2 matrix.\n') 

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

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

995 

996 

997def k_path_points(points, ndim): 

998 ''' 

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

1000 

1001 :raises TypeError: Parameter points must be a list. 

1002 :raises ValueError: Parameter points must contain at least two k-points. 

1003 ''' 

1004 if not isinstance(points, list): 

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

1006 if len(points) < 2: 

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

1008 for i, pt in enumerate(points): 

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

1010 

1011 

1012#################################### 

1013# DENSITY OF STATES 

1014#################################### 

1015 

1016 

1017def dos_kernel(kernel): 

1018 ''' 

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

1020 

1021 :raises TypeError: Parameter kernel must be a string. 

1022 :raises ValueError: Parameter kernel must be "gaussian" or "lorentzian". 

1023 ''' 

1024 string(kernel, 'kernel') 

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

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

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

1028 

1029 

1030def kspace_dos_kernel(kernel): 

1031 ''' 

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

1033 

1034 :raises TypeError: Parameter kernel must be a string. 

1035 :raises ValueError: Parameter kernel must be "gaussian", "lorentzian" or "tetrahedron". 

1036 ''' 

1037 string(kernel, 'kernel') 

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

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

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

1041 

1042 

1043def mesh_energies(energies): 

1044 ''' 

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

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

1047 

1048 :raises ValueError: Parameter energies must be of shape (n1, ..., nbands), on a mesh of 1 to 3 dimensions. 

1049 ''' 

1050 energies = np.asarray(energies) 

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

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

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

1054 

1055 

1056def increasing_grid(grid, var_name): 

1057 ''' 

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

1059 

1060 :raises ValueError: Parameter *var_name* must be a 1D array of at least two increasing real values. 

1061 ''' 

1062 grid = np.asarray(grid) 

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

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

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

1066 

1067 

1068def nk(nk, ndim): 

1069 ''' 

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

1071 

1072 :raises TypeError: Parameter nk must be an integer or a tuple of integers. 

1073 :raises ValueError: Parameter nk (or each of its elements) must be a 

1074 positive integer. 

1075 :raises ValueError: Parameter nk must be a tuple of length *ndim*. 

1076 ''' 

1077 if isinstance(nk, int): 

1078 positive_int(nk, 'nk') 

1079 return 

1080 if not isinstance(nk, tuple): 

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

1082 if len(nk) != ndim: 

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

1084 for n in nk: 

1085 positive_int(n, 'nk') 

1086 

1087 

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

1089# TOPOLOGY 

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

1091 

1092 

1093def dim_2(dim): 

1094 ''' 

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

1096 defined for a 2D Brillouin zone. 

1097 

1098 :raises ValueError: This calculation requires a 2D lattice. 

1099 ''' 

1100 if dim != 2: 

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

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

1103 

1104 

1105def dim_min(dim, dmin): 

1106 ''' 

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

1108 

1109 :raises ValueError: This calculation requires a lattice with at least dmin primitive vectors. 

1110 ''' 

1111 if dim < dmin: 

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

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

1114 

1115 

1116def plane(plane, dim): 

1117 ''' 

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

1119 reciprocal-vector indices below *dim*. 

1120 

1121 :raises TypeError: Parameter plane must be a tuple of two integers. 

1122 :raises ValueError: Parameter plane must contain two distinct indices below dim. 

1123 ''' 

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

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

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

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

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

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

1130 

1131 

1132def direction(direction, dim=2): 

1133 ''' 

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

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

1136 

1137 :raises TypeError: Parameter direction must be an integer. 

1138 :raises ValueError: Parameter direction must be between 0 and dim-1. 

1139 ''' 

1140 if not isinstance(direction, int): 

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

1142 if not 0 <= direction < dim: 

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

1144 

1145 

1146def band_indices(bands, norb): 

1147 ''' 

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

1149 

1150 :raises TypeError: Parameter bands must be a non-empty list of integers. 

1151 :raises ValueError: Parameter bands must be a list of distinct band 

1152 indices between 0 and norb-1. 

1153 ''' 

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

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

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

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

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

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

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

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

1162 

1163 

1164#################################### 

1165# BERRY PHASES, Z2, SYMMETRIES 

1166#################################### 

1167 

1168 

1169def k_perp(k_perp, n): 

1170 ''' 

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

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

1173 

1174 :raises ValueError: Parameter k_perp must contain n real numbers. 

1175 ''' 

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

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

1178 

1179 

1180def flow(flow, direction, dim): 

1181 ''' 

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

1183 

1184 :raises TypeError: Parameter flow must be an integer. 

1185 :raises ValueError: Parameter flow must be a direction other than *direction*. 

1186 ''' 

1187 if not isinstance(flow, int): 

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

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

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

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

1192 

1193 

1194def even_bands(bands): 

1195 ''' 

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

1197 

1198 :raises TypeError: Parameter bands must be a list. 

1199 :raises ValueError: Parameter bands must contain an even number of bands. 

1200 ''' 

1201 if not isinstance(bands, list): 

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

1203 if len(bands) % 2: 

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

1205 '(Kramers pairs).\n') 

1206 

1207 

1208def operator(mat, norb): 

1209 ''' 

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

1211 

1212 :raises ValueError: The operator must be a (norb, norb) matrix. 

1213 ''' 

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

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

1216 

1217 

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

1219 ''' 

1220 Check that inversion eigenvalues are +-1. 

1221 

1222 :raises ValueError: The bands are not an inversion-invariant subspace. 

1223 ''' 

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

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

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

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

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

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

1230 'Kramers pairs.\n') 

1231 

1232 

1233def k_map(k_map): 

1234 ''' 

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

1236 

1237 :raises ValueError: Parameter k_map must be 'minus' or 'identity'. 

1238 ''' 

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

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

1241 

1242 

1243def is_symmetry(err, tol): 

1244 ''' 

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

1246 

1247 :raises ValueError: The operator is not a symmetry of the model. 

1248 ''' 

1249 if err > tol: 

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

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

1252 

1253 

1254def square(sq, tol): 

1255 ''' 

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

1257 

1258 :raises ValueError: The square of the operator must be +1 or -1. 

1259 ''' 

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

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

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

1263 

1264 

1265def hermitian(ham): 

1266 ''' 

1267 Check that a (sparse) Hamiltonian is Hermitian. 

1268 

1269 :raises ValueError: This calculation requires a Hermitian Hamiltonian. 

1270 ''' 

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

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

1273 

1274 

1275def planar_cell(prim_vec): 

1276 ''' 

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

1278 

1279 :raises ValueError: Give the area per site explicitly. 

1280 ''' 

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

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

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

1284 

1285 

1286def integer(var, var_name): 

1287 ''' 

1288 Check if *var* is an integer. 

1289 

1290 :raises TypeError: Parameter *var* must be an integer. 

1291 ''' 

1292 if not isinstance(var, int): 

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

1294 

1295 

1296def dim_exact(dim, d): 

1297 ''' 

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

1299 

1300 :raises ValueError: This calculation requires a lattice with d primitive vectors. 

1301 ''' 

1302 if dim != d: 

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

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

1305 

1306 

1307def nonzero_number(var, var_name): 

1308 ''' 

1309 Check if *var* is a nonzero number. 

1310 

1311 :raises TypeError: Parameter *var* must be a number. 

1312 :raises ValueError: Parameter *var* must be nonzero. 

1313 ''' 

1314 number(var, var_name) 

1315 if var == 0: 

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

1317 

1318 

1319def gbz_roots(n_roots, p): 

1320 ''' 

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

1322 

1323 :raises ValueError: The model has no hoppings in both directions. 

1324 ''' 

1325 if p < 1 or n_roots <= p: 

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

1327 'directions along the chain.\n') 

1328 

1329 

1330#################################### 

1331# EXCEPTIONAL POINTS 

1332#################################### 

1333 

1334 

1335def ham_model(model, cls): 

1336 ''' 

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

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

1339 

1340 :raises TypeError: Parameter model must be a KSpace instance or a callable. 

1341 :raises ValueError: A KSpace model must have two primitive vectors. 

1342 ''' 

1343 if isinstance(model, cls): 

1344 if model.dim != 2: 

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

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

1347 elif not callable(model): 

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

1349 'returning a square matrix.\n') 

1350 

1351 

1352def ham_matrix(ham): 

1353 ''' 

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

1355 square and finite. 

1356 

1357 :raises ValueError: The model must return a finite square matrix. 

1358 ''' 

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

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

1361 

1362 

1363def loop(loop): 

1364 ''' 

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

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

1367 

1368 :raises TypeError: Parameter loop must be a callable or an array of vertices. 

1369 :raises ValueError: A polygon needs at least 3 vertices with 2 real coordinates. 

1370 ''' 

1371 if callable(loop): 

1372 return 

1373 try: 

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

1375 except (TypeError, ValueError): 

1376 arr = np.zeros(()) 

1377 if arr.ndim == 0: 

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

1379 'array of polygon vertices.\n') 

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

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

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

1383 

1384 

1385def loop_point(point): 

1386 ''' 

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

1388 

1389 :raises ValueError: The loop must return points of 2 real coordinates. 

1390 ''' 

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

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

1393 'coordinates.\n') 

1394 

1395 

1396def closed_loop(ham_start, ham_end): 

1397 ''' 

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

1399 

1400 :raises ValueError: The loop is not closed. 

1401 ''' 

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

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

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

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

1406 

1407 

1408def band_pair(bands, norb): 

1409 ''' 

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

1411 

1412 :raises TypeError: Parameter bands must be a pair of integers. 

1413 :raises ValueError: Parameter bands must be two distinct band indices. 

1414 ''' 

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

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

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

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

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

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

1421 

1422 

1423def band_index(band, norb): 

1424 ''' 

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

1426 

1427 :raises TypeError: Parameter band must be an integer. 

1428 :raises ValueError: Parameter band must be between 0 and norb-1. 

1429 ''' 

1430 integer(band, 'band') 

1431 if not 0 <= band < norb: 

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

1433 

1434 

1435def tracking(k): 

1436 ''' 

1437 Eigenvalue continuation failed: the path meets a degeneracy. 

1438 

1439 :raises ValueError: Cannot follow the eigenvalues continuously near k. 

1440 ''' 

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

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

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

1444 

1445 

1446def winding_path(k): 

1447 ''' 

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

1449 

1450 :raises ValueError: The loop passes through a zero of the discriminant. 

1451 ''' 

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

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

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

1455 

1456 

1457def discriminant_nonzero(scale): 

1458 ''' 

1459 Check that the discriminant does not vanish identically. 

1460 

1461 :raises ValueError: The bands are degenerate everywhere. 

1462 ''' 

1463 if scale == 0.: 

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

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

1466 

1467 

1468def gap_kind(gap): 

1469 ''' 

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

1471 

1472 :raises ValueError: Parameter gap must be "real" or "imaginary". 

1473 ''' 

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

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

1476 

1477 

1478def biorthogonal_kind(kind): 

1479 ''' 

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

1481 

1482 :raises ValueError: Parameter kind must be "LR", "RL", "RR" or "LL". 

1483 ''' 

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

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

1486 

1487 

1488def line_gap(keys, bands, gap): 

1489 ''' 

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

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

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

1493 never overlap over the whole mesh. 

1494 

1495 :raises ValueError: The bands have no line gap. 

1496 ''' 

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

1498 inside[bands] = True 

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

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

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

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

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

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

1505 'For a non-Hermitian model, use ' 

1506 'KSpace.biorthogonal_chern_number with the other kind of ' 

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

1508 'tbkit.exceptional.find_exceptional_points.\n' 

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

1510 

1511 

1512def band_continuity(en, bands): 

1513 ''' 

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

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

1516 eigenvalues of neighbouring points by distance maps the group onto 

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

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

1519 

1520 :raises ValueError: The band labels jump between neighbouring k-points. 

1521 ''' 

1522 from scipy.optimize import linear_sum_assignment 

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

1524 inside[bands] = True 

1525 for axis in (0, 1): 

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

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

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

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

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

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

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

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

1534 'For a non-Hermitian model, try ' 

1535 'KSpace.biorthogonal_chern_number with the other kind of ' 

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

1537 .format(bands)) 

1538 

1539 

1540def no_overlap(overlap): 

1541 ''' 

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

1543 supported by the biorthogonal tools). 

1544 

1545 :raises ValueError: Not available with an overlap matrix. 

1546 ''' 

1547 if overlap: 

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

1549 

1550 

1551def weights(weights, n): 

1552 ''' 

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

1554 

1555 :raises ValueError: Parameter weights must contain n positive numbers. 

1556 ''' 

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

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

1559 

1560 

1561def electrons(n_electrons, capacity): 

1562 ''' 

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

1564 

1565 :raises TypeError: Parameter n_electrons must be a real number. 

1566 :raises ValueError: Parameter n_electrons must be between 0 and capacity. 

1567 ''' 

1568 positive_real_zero(n_electrons, 'n_electrons') 

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

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

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

1572 

1573 

1574def n_eig(n_eig, sites): 

1575 ''' 

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

1577 

1578 :raises TypeError: Parameter n_eig must be an integer. 

1579 :raises ValueError: Parameter n_eig must be between 1 and sites-2. 

1580 ''' 

1581 positive_int(n_eig, 'n_eig') 

1582 if n_eig > sites - 2: 

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

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

1585 

1586 

1587#################################### 

1588# TRANSPORT 

1589#################################### 

1590 

1591 

1592def square_matrix(mat, var_name): 

1593 ''' 

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

1595 

1596 :raises ValueError: Parameter must be a square matrix. 

1597 ''' 

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

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

1600 

1601 

1602def lead(h0, v): 

1603 ''' 

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

1605 

1606 :raises ValueError: h0 and v must be square matrices of the same shape. 

1607 ''' 

1608 square_matrix(h0, 'h0') 

1609 if v.shape != h0.shape: 

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

1611 

1612 

1613def converged(flag, name): 

1614 ''' 

1615 Check that an iteration converged. 

1616 

1617 :raises RuntimeError: The iteration did not converge. 

1618 ''' 

1619 if not flag: 

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

1621 

1622 

1623def lead_direction(direction): 

1624 ''' 

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

1626 

1627 :raises ValueError: Parameter direction must be +1 or -1. 

1628 ''' 

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

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

1631 

1632 

1633def surface_side(side): 

1634 ''' 

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

1636 

1637 :raises ValueError: Parameter side must be +1 or -1. 

1638 ''' 

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

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

1641 

1642 

1643def nearest_cells(n): 

1644 ''' 

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

1646 

1647 :raises ValueError: Lead hoppings must connect neighbouring cells only. 

1648 ''' 

1649 if abs(n) > 1: 

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

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

1652 

1653 

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

1655 ''' 

1656 Check the coupling of a lead to the device. 

1657 

1658 :raises TypeError: Parameter sites must be a list of integers. 

1659 :raises ValueError: sites must be distinct device indices, coupling of shape (len(sites), m). 

1660 ''' 

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

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

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

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

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

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

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

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

1669 

1670 

1671def lead_device_size(sites, n_device): 

1672 ''' 

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

1674 

1675 :raises ValueError: The System must have as many sites as the device Hamiltonian has rows. 

1676 ''' 

1677 if sites != n_device: 

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

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

1680 

1681 

1682def lead_space_dim(lead_dim, device_dim): 

1683 ''' 

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

1685 

1686 :raises ValueError: The lead and the device must have the same space dimension. 

1687 ''' 

1688 if lead_dim != device_dim: 

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

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

1691 

1692 

1693def lead_overlap(found): 

1694 ''' 

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

1696 

1697 :raises ValueError: No device site matches a lead orbital. 

1698 ''' 

1699 if not found: 

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

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

1702 

1703 

1704def lead_interface(missing): 

1705 ''' 

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

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

1708 

1709 :raises ValueError: The interface between the device and the lead is incomplete. 

1710 ''' 

1711 if missing: 

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

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

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

1715 

1716 

1717def lead_extension(inside): 

1718 ''' 

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

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

1721 innermost lead cell holding a device site). 

1722 

1723 :raises ValueError: The lead cannot be closed against the device. 

1724 ''' 

1725 if not inside: 

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

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

1728 'off its edge).\n') 

1729 

1730 

1731def lead_index(lead, n_leads): 

1732 ''' 

1733 Check a lead index. 

1734 

1735 :raises TypeError: The lead index must be an integer. 

1736 :raises ValueError: No such lead. 

1737 ''' 

1738 if not isinstance(lead, int): 

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

1740 if not 0 <= lead < n_leads: 

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

1742 

1743 

1744def lead_band_edge(flag, energy): 

1745 ''' 

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

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

1748 

1749 :raises ValueError: Energy at a band edge of a lead. 

1750 ''' 

1751 if not flag: 

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

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

1754 

1755 

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

1757 ''' 

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

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

1760 

1761 :raises ValueError: Wrong shape, not Hermitian, or not conserved by the lead. 

1762 ''' 

1763 if law.shape != h0.shape: 

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

1765 hermitian_operator(law, tol) 

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

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

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

1769 

1770 

1771def lead_block(lead, n_blocks): 

1772 ''' 

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

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

1775 

1776 :raises TypeError: Not an integer or a tuple of two integers. 

1777 :raises ValueError: No such lead or block. 

1778 ''' 

1779 if isinstance(lead, tuple): 

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

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

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

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

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

1785 lead[0], n_blocks[lead[0]])) 

1786 else: 

1787 lead_index(lead, len(n_blocks)) 

1788 

1789 

1790 

1791#################################### 

1792# KERNEL POLYNOMIAL METHOD 

1793#################################### 

1794 

1795 

1796def kpm_kernel(kernel): 

1797 ''' 

1798 Check the KPM kernel name. 

1799 

1800 :raises ValueError: Parameter kernel must be "jackson" or "lorentz". 

1801 ''' 

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

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

1804 

1805 

1806def kpm_grid(eps): 

1807 ''' 

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

1809 

1810 :raises ValueError: e_grid must lie inside the spectral bounds. 

1811 ''' 

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

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

1814 'bounds.\n') 

1815 

1816 

1817def site_index(site, n): 

1818 ''' 

1819 Check a site (row) index. 

1820 

1821 :raises TypeError: Parameter site must be an integer. 

1822 :raises ValueError: Parameter site must be between 0 and n-1. 

1823 ''' 

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

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

1826 if not 0 <= site < n: 

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

1828 

1829 

1830def max_order(list_hop): 

1831 ''' 

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

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

1834 ''' 

1835 try: 

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

1837 except (TypeError, KeyError): 

1838 return 1 

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

1840 

1841 

1842#################################### 

1843# ORBITALS, SLATER-KOSTER, SPIN 

1844#################################### 

1845 

1846 

1847def orbitals(orbs): 

1848 ''' 

1849 Check a list of orbital names. 

1850 

1851 :raises TypeError: The orbitals must be a list of names. 

1852 :raises ValueError: Unknown or repeated orbital. 

1853 ''' 

1854 from tbkit.slater_koster import ORBITALS 

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

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

1857 for o in orbs: 

1858 if o not in ORBITALS: 

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

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

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

1862 

1863 

1864def orbital_dict(orbital_dict, tags): 

1865 ''' 

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

1867 

1868 :raises TypeError: Parameter orbitals must be a dictionary. 

1869 :raises ValueError: Every tag needs its orbitals. 

1870 ''' 

1871 if not isinstance(orbital_dict, dict): 

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

1873 for tag in tags: 

1874 if tag not in orbital_dict: 

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

1876 for orbs in orbital_dict.values(): 

1877 orbitals(orbs) 

1878 

1879 

1880def sk_params(params): 

1881 ''' 

1882 Check a dictionary of Slater-Koster bond integrals. 

1883 

1884 :raises TypeError: Parameter params must be a dictionary of numbers. 

1885 :raises ValueError: Unknown bond integral. 

1886 ''' 

1887 from tbkit.slater_koster import PARAMS 

1888 if not isinstance(params, dict): 

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

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

1891 if key not in PARAMS: 

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

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

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

1895 

1896 

1897def sk_pair_params(params, tags_i, tags_j): 

1898 ''' 

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

1900 per tag pair of the bonds. 

1901 

1902 :returns: True if keyed by tag pair. 

1903 :raises ValueError: A tag pair of the bonds has no bond integrals. 

1904 ''' 

1905 from tbkit.slater_koster import PARAMS 

1906 if not isinstance(params, dict): 

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

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

1909 if not by_pair: 

1910 sk_params(params) 

1911 return False 

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

1913 if a + b not in params: 

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

1915 sk_params(params[a + b]) 

1916 return True 

1917 

1918 

1919def bond_vector(d): 

1920 ''' 

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

1922 

1923 :raises ValueError: The bond vector must have 2 or 3 components and be nonzero. 

1924 ''' 

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

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

1927 

1928 

1929def orbital_of_site(orbital, orbs): 

1930 ''' 

1931 Check that a site carries an orbital. 

1932 

1933 :raises ValueError: The site has no such orbital. 

1934 ''' 

1935 if orbital not in orbs: 

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

1937 

1938 

1939def spin_index(spin, ns): 

1940 ''' 

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

1942 

1943 :raises ValueError: Parameter spin must be 0 (or 1 with spin). 

1944 ''' 

1945 if spin not in range(ns): 

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

1947 

1948 

1949def set_onsite_orb(dict_onsite, tags, orbital_dict): 

1950 ''' 

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

1952 

1953 :raises TypeError: Onsite energies must be numbers or dictionaries. 

1954 :raises ValueError: Unknown tag or orbital. 

1955 ''' 

1956 if not isinstance(dict_onsite, dict): 

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

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

1959 if tag not in tags: 

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

1961 if isinstance(val, dict): 

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

1963 orbital_of_site(o, orbital_dict[tag]) 

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

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

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

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

1968 

1969 

1970def spinful(spin): 

1971 ''' 

1972 Check that the model has spin. 

1973 

1974 :raises ValueError: This term needs spin=True. 

1975 ''' 

1976 if not spin: 

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

1978 

1979 

1980def vector3(vec, var_name): 

1981 ''' 

1982 Check a tuple of three real numbers. 

1983 

1984 :raises ValueError: Parameter must be a tuple of three real numbers. 

1985 ''' 

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

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

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

1989 

1990 

1991def p_shell(tags, orbital_dict): 

1992 ''' 

1993 Check that the tags carry the three p orbitals. 

1994 

1995 :raises ValueError: The tags must carry px, py and pz. 

1996 ''' 

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

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

1999 for tag in tags: 

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

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

2002 

2003 

2004def block_shape(mat, shape): 

2005 ''' 

2006 Check the shape of a hopping block. 

2007 

2008 :raises ValueError: The block must have the given shape. 

2009 ''' 

2010 if mat.shape != shape: 

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

2012 

2013 

2014def orbital_terms(hop, blocks): 

2015 ''' 

2016 Check that an *OrbitalSystem* has hoppings. 

2017 

2018 :raises RuntimeError: Run set_hopping or set_slater_koster first. 

2019 ''' 

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

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

2022 

2023 

2024def no_left(left): 

2025 ''' 

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

2027 

2028 :raises ValueError: left=True needs an orthogonal basis. 

2029 ''' 

2030 if left: 

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

2032 

2033 

2034def orthogonal(overlap): 

2035 ''' 

2036 Check that the basis is orthogonal. 

2037 

2038 :raises ValueError: This calculation needs an orthogonal basis. 

2039 ''' 

2040 if overlap is not None: 

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

2042 

2043 

2044#################################### 

2045# MEAN FIELD, SUPERCONDUCTIVITY 

2046#################################### 

2047 

2048 

2049def hermitian_dense(ham): 

2050 ''' 

2051 Check that a dense matrix is Hermitian. 

2052 

2053 :raises ValueError: This calculation requires a Hermitian Hamiltonian. 

2054 ''' 

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

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

2057 

2058 

2059def integer_electrons(n_electrons): 

2060 ''' 

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

2062 

2063 :raises ValueError: At zero temperature, n_electrons must be an integer. 

2064 ''' 

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

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

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

2068 

2069 

2070def mixing(mixing): 

2071 ''' 

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

2073 

2074 :raises ValueError: Parameter mixing must be in (0, 1]. 

2075 ''' 

2076 positive_real(mixing, 'mixing') 

2077 if mixing > 1: 

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

2079 

2080 

2081def pairing(delta, n): 

2082 ''' 

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

2084 

2085 :raises ValueError: The pairing matrix must be (n, n) and antisymmetric. 

2086 ''' 

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

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

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

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

2091 

2092 

2093def hermitian_kspace(hermitian, overlap): 

2094 ''' 

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

2096 

2097 :raises ValueError: The model must be Hermitian, without overlap. 

2098 ''' 

2099 if not hermitian or overlap: 

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

2101 

2102 

2103def pairing_kspace(pairing, norb, ndim): 

2104 ''' 

2105 Check the pairings of *bdg.bdg_kspace*. 

2106 

2107 :raises TypeError: Parameter pairing must be a list of dictionaries. 

2108 :raises KeyError: "i", "j", "R" and "delta" must be keys. 

2109 :raises ValueError: Orbital indices below norb; R a tuple of ndim integers. 

2110 ''' 

2111 if not isinstance(pairing, list): 

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

2113 for dic in pairing: 

2114 if not isinstance(dic, dict): 

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

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

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

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

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

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

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

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

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

2124 

2125 

2126#################################### 

2127# FLOQUET 

2128#################################### 

2129 

2130 

2131def positive_int_zero(var, var_name): 

2132 ''' 

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

2134 

2135 :raises TypeError: Parameter *var* must be an integer. 

2136 :raises ValueError: Parameter *var* must be a positive integer or zero. 

2137 ''' 

2138 if not isinstance(var, int): 

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

2140 if var < 0: 

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

2142 

2143 

2144def harmonics(harm): 

2145 ''' 

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

2147 

2148 :raises TypeError: The harmonics must be a dictionary {m: matrix}. 

2149 :raises ValueError: The harmonics must contain m = 0 and square matrices of one shape. 

2150 ''' 

2151 if not isinstance(harm, dict): 

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

2153 if 0 not in harm: 

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

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

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

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

2158 

2159 

2160def kspace(ks, cls): 

2161 ''' 

2162 Check that *ks* is a KSpace instance. 

2163 

2164 :raises TypeError: Parameter ks must be a KSpace instance. 

2165 ''' 

2166 if not isinstance(ks, cls): 

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

2168 

2169 

2170def not_floquet(): 

2171 ''' 

2172 :raises ValueError: Not available for a Floquet (driven) model. 

2173 ''' 

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

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

2176 

2177 

2178def sk_bonds(bonds): 

2179 ''' 

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

2181 

2182 :raises TypeError: Parameter bonds must be a dictionary {n: bond integrals}. 

2183 ''' 

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

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

2186 'n a positive integer.\n') 

2187 for params in bonds.values(): 

2188 sk_params(params) 

2189 

2190 

2191#################################### 

2192# HALL CONDUCTIVITIES 

2193#################################### 

2194 

2195 

2196def fermi_energies(e_fermi): 

2197 ''' 

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

2199 non-empty array of finite real numbers. 

2200 

2201 :raises TypeError: Parameter e_fermi must be a real number or an array of real numbers. 

2202 :raises ValueError: Parameter e_fermi must be non-empty and finite. 

2203 ''' 

2204 arr = np.asarray(e_fermi) 

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

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

2207 'of real numbers.\n') 

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

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

2210 

2211 

2212def hermitian_model(hermitian): 

2213 ''' 

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

2215 bands and orthonormal eigenstates). 

2216 

2217 :raises ValueError: This calculation requires a Hermitian model. 

2218 ''' 

2219 if not hermitian: 

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

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

2222 

2223 

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

2225 ''' 

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

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

2228 

2229 :raises TypeError: Parameter k_fixed must be None or a real number. 

2230 :raises ValueError: A 3D spin Hall conductivity needs a plane (k_fixed). 

2231 ''' 

2232 if k_fixed is not None: 

2233 real_number(k_fixed, 'k_fixed') 

2234 elif dim == 3 and not full_allowed: 

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

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

2237 

2238 

2239def spin_axis(axis): 

2240 ''' 

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

2242 

2243 :raises ValueError: Parameter spin_axis must be "x", "y" or "z". 

2244 ''' 

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

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

2247 

2248 

2249def refine_fraction(fraction): 

2250 ''' 

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

2252 

2253 :raises TypeError: Parameter refine_fraction must be a real number. 

2254 :raises ValueError: Parameter refine_fraction must be in (0, 1]. 

2255 ''' 

2256 positive_real(fraction, 'refine_fraction') 

2257 if fraction > 1: 

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

2259 

2260 

2261def path_parameter(param): 

2262 ''' 

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

2264 

2265 :raises TypeError: Parameter param must be a string (the name of a value-function parameter). 

2266 ''' 

2267 if not isinstance(param, str): 

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

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

2270 

2271 

2272def path_values(values): 

2273 ''' 

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

2275 real numbers, strictly increasing or strictly decreasing. 

2276 

2277 :raises TypeError: Parameter values must be a 1D array of real numbers. 

2278 :raises ValueError: Parameter values must hold at least two finite, strictly monotonic values. 

2279 ''' 

2280 arr = np.asarray(values) 

2281 if arr.ndim != 1 or arr.dtype.kind not in 'iuf': 

2282 raise TypeError('\n\nParameter values must be a 1D array of real numbers.\n') 

2283 step = np.diff(arr) 

2284 if len(arr) < 2 or not np.all(np.isfinite(arr)) or not (np.all(step > 0) or np.all(step < 0)): 

2285 raise ValueError('\n\nParameter values must hold at least two finite values, ' 

2286 'strictly increasing or strictly decreasing.\n') 

2287 

2288 

2289def path_gap(gap, param, value, tol=1e-6): 

2290 ''' 

2291 Check that the bands stay separated from the others along a path. 

2292 

2293 :raises ValueError: The bands touch the others at param = value. 

2294 ''' 

2295 if gap < tol: 

2296 raise ValueError('\n\nThe bands touch the other bands at {} = {} (gap {:.1e}): ' 

2297 'the path must stay gapped.\n'.format(param, value, gap)) 

2298 

2299 

2300def velocity(vel, n, var_name): 

2301 ''' 

2302 Check a velocity operator: a square (sparse or dense) matrix of the 

2303 Hamiltonian's shape. 

2304 

2305 :raises ValueError: The velocity must be a (n, n) matrix. 

2306 ''' 

2307 if getattr(vel, 'shape', None) != (n, n): 

2308 raise ValueError('\n\nParameter {} must be a ({}, {}) matrix, like ham.\n'.format(var_name, n, n)) 

2309 

2310 

2311def positive_overlap(s_min): 

2312 ''' 

2313 Check that the overlap matrix S(k) is positive definite. 

2314 

2315 :raises ValueError: The overlap matrix must be positive definite. 

2316 ''' 

2317 if s_min <= 0: 

2318 raise ValueError('\n\nThe overlap matrix S(k) must be positive definite ' 

2319 '(smallest eigenvalue {:.3g}).\n'.format(s_min)) 

2320 

2321 

2322#################################### 

2323# FLOQUET: STEP DRIVES, WINDING NUMBER 

2324#################################### 

2325 

2326 

2327def branch_cut(epsilon): 

2328 ''' 

2329 Check the branch cut of a Floquet Hamiltonian: None or a real number. 

2330 

2331 :raises TypeError: Parameter epsilon must be None or a real number. 

2332 ''' 

2333 if epsilon is not None and (isinstance(epsilon, bool) 

2334 or not isinstance(epsilon, (int, float, np.integer, np.floating))): 

2335 raise TypeError('\n\nParameter epsilon must be None or a real number.\n') 

2336 

2337 

2338def durations(durations, n): 

2339 ''' 

2340 Check the step durations of a step drive: n positive reals. 

2341 

2342 :raises TypeError: Parameter durations must be a list of real numbers. 

2343 :raises ValueError: Parameter durations must hold one positive number per step. 

2344 ''' 

2345 if not isinstance(durations, (list, tuple, np.ndarray)): 

2346 raise TypeError('\n\nParameter durations must be a list of real numbers.\n') 

2347 if len(durations) != n: 

2348 raise ValueError('\n\nParameter durations must hold one duration per step ({}).\n'.format(n)) 

2349 for d in durations: 

2350 if isinstance(d, bool) or not isinstance(d, (int, float, np.integer, np.floating)): 

2351 raise TypeError('\n\nParameter durations must be a list of real numbers.\n') 

2352 if d <= 0: 

2353 raise ValueError('\n\nParameter durations must be positive numbers.\n') 

2354 

2355 

2356def step_models(models, kspace_cls, system_cls): 

2357 ''' 

2358 Check the models of a step drive: a non-empty list, either of KSpace 

2359 instances (Hermitian, orthogonal basis, one lattice size) or of System 

2360 instances and square matrices. Returns 'kspace' or 'matrix'. 

2361 

2362 :raises TypeError: Parameter models must be a non-empty list of KSpace, System or matrices. 

2363 :raises ValueError: The KSpace models must be Hermitian, with no overlap, of one size. 

2364 ''' 

2365 if not isinstance(models, (list, tuple)) or len(models) == 0: 

2366 raise TypeError('\n\nParameter models must be a non-empty list of KSpace instances, ' 

2367 'or of System instances and matrices.\n') 

2368 if all(isinstance(m, kspace_cls) for m in models): 

2369 for m in models: 

2370 if (m.norb, m.dim) != (models[0].norb, models[0].dim): 

2371 raise ValueError('\n\nThe KSpace models must have the same orbitals and dimension.\n') 

2372 if not m.is_hermitian(): 

2373 raise ValueError('\n\nThe KSpace models must be Hermitian.\n') 

2374 if m._overlap_hop: 

2375 raise ValueError('\n\nThe KSpace models need an orthogonal basis (no overlap).\n') 

2376 return 'kspace' 

2377 for m in models: 

2378 if not isinstance(m, system_cls) and getattr(m, 'ndim', None) != 2: 

2379 raise TypeError('\n\nParameter models must be a list of KSpace instances only, ' 

2380 'or of System instances and square matrices.\n') 

2381 return 'matrix' 

2382 

2383 

2384def step_matrices(hams): 

2385 ''' 

2386 Check the Hamiltonians of a real-space step drive: Hermitian square 

2387 matrices of one shape. 

2388 

2389 :raises TypeError: Parameter hams must be a non-empty list of matrices. 

2390 :raises ValueError: The matrices must be square, Hermitian and of one shape. 

2391 ''' 

2392 if not isinstance(hams, (list, tuple)) or len(hams) == 0: 

2393 raise TypeError('\n\nParameter hams must be a non-empty list of matrices.\n') 

2394 shape = np.shape(hams[0]) 

2395 for h in hams: 

2396 h = np.asarray(h) 

2397 if h.ndim != 2 or h.shape[0] != h.shape[1] or h.shape != shape: 

2398 raise ValueError('\n\nThe step Hamiltonians must be square matrices of one shape.\n') 

2399 hermitian_dense(h) 

2400 

2401 

2402def drive_matrix(ham, norb): 

2403 ''' 

2404 Check the output of a Bloch drive ham_kt(k, t): a Hermitian (norb, norb) matrix. 

2405 

2406 :raises ValueError: ham_kt(k, t) must return a Hermitian (norb, norb) matrix. 

2407 ''' 

2408 if np.shape(ham) != (norb, norb): 

2409 raise ValueError('\n\nham_kt(k, t) must return a ({}, {}) matrix.\n'.format(norb, norb)) 

2410 hermitian_dense(np.asarray(ham)) 

2411 

2412 

2413def time_in_period(t, period): 

2414 ''' 

2415 Check a time within the period: None or a real in [0, period]. 

2416 

2417 :raises TypeError: Parameter t must be None or a real number. 

2418 :raises ValueError: Parameter t must be in [0, period]. 

2419 ''' 

2420 if t is None: 

2421 return 

2422 real_number(t, 't') 

2423 if not 0 <= t <= period: 

2424 raise ValueError('\n\nParameter t must be in [0, {}].\n'.format(period)) 

2425 

2426 

2427def nk_min(nk, n_min): 

2428 ''' 

2429 Check that every k-mesh size is at least n_min. 

2430 

2431 :raises ValueError: Parameter nk must be at least n_min. 

2432 ''' 

2433 if min(nk) < n_min: 

2434 raise ValueError('\n\nParameter nk must be at least {}.\n'.format(n_min)) 

2435 

2436 

2437#################################### 

2438# MODEL BUILDING, BRIDGES AND I/O 

2439# (neighbour-order hoppings in k-space, System <-> KSpace, tbkit.io) 

2440#################################### 

2441 

2442 

2443def hopping_form(list_hop): 

2444 ''' 

2445 Tell the two forms of *KSpace.set_hopping* apart: the explicit one 

2446 (keys 'i', 'j', 'R', 't') and the neighbour-order one of 

2447 *System.set_hopping* (keys 'n', 't', and optionally 'ang', 'tag'). 

2448 

2449 :raises TypeError: Parameter list_hop must be a list of dictionaries. 

2450 :raises ValueError: list_hop mixes the two forms. 

2451 

2452 :returns: 

2453 * **neighbour** -- Boolean. True for the neighbour-order form. 

2454 ''' 

2455 if not isinstance(list_hop, list): 

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

2457 if not all(isinstance(dic, dict) for dic in list_hop): 

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

2459 by_order = ['n' in dic for dic in list_hop] 

2460 if any(by_order) and not all(by_order): 

2461 raise ValueError('\n\nParameter list_hop mixes explicit hoppings (keys "i", "j", "R", "t") ' 

2462 'and neighbour-order ones (keys "n", "t", "ang", "tag"): ' 

2463 'make one call per form.\n') 

2464 return bool(list_hop) and all(by_order) 

2465 

2466 

2467def set_hopping_neighbours(list_hop, spin=False): 

2468 ''' 

2469 Check the neighbour-order form of *KSpace.set_hopping*. 

2470 

2471 :raises KeyError: "n" and "t" must be keys, and only "ang" and "tag" may be added. 

2472 :raises TypeError: "n" must be an integer, "t" a number (or, if *spin*, 

2473 a 2x2 matrix) or a value function, "ang" a real number and "tag" a string. 

2474 :raises ValueError: "n" must be positive, "ang" in [-180, 180), "tag" of length 2. 

2475 ''' 

2476 hopping_form(list_hop) 

2477 for dic in list_hop: 

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

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

2480 if not set(dic) <= {'n', 't', 'ang', 'tag'}: 

2481 raise KeyError('\n\nOnly the keys "n", "t", "ang" and "tag" are allowed.\n') 

2482 if isinstance(dic['n'], bool) or not isinstance(dic['n'], int): 

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

2484 if dic['n'] < 1: 

2485 raise ValueError('\n\n"n" value must be a positive integer.\n') 

2486 if callable(dic['t']): 

2487 pass 

2488 elif spin: 

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

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

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

2492 if 'ang' in dic: 

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

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

2495 if not -180. <= dic['ang'] < 180.: 

2496 raise ValueError('\n\n"ang" value must be in [-180, 180).\n') 

2497 if 'tag' in dic: 

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

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

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

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

2502 

2503 

2504def shell_angle(ang, angles): 

2505 ''' 

2506 Check that a neighbour shell has bonds along the angle *ang* (in 

2507 degrees, either orientation: *angles* are those in [0, 180)). 

2508 

2509 :raises ValueError: No bond of the shell points along *ang*. 

2510 ''' 

2511 if not np.any(np.isclose(ang, angles, atol=ATOL)) and \ 

2512 not np.any(np.isclose(ang, angles - 180., atol=ATOL)): 

2513 raise ValueError('\n\nNo bond of this neighbour shell has angle {} (angles: {}).\n' 

2514 .format(ang, np.unique(angles.round(4)))) 

2515 

2516 

2517def not_orbital_system(sys, orbital_cls): 

2518 ''' 

2519 Check that a System is not an *OrbitalSystem* (several rows per site). 

2520 

2521 :raises TypeError: An OrbitalSystem is not supported. 

2522 ''' 

2523 if isinstance(sys, orbital_cls): 

2524 raise TypeError('\n\nAn OrbitalSystem (several orbitals per site) is not supported: ' 

2525 'build its KSpace with tbkit.slater_koster.sk_kspace.\n') 

2526 

2527 

2528def lattice_sites(matches): 

2529 ''' 

2530 Check that every site of a finite lattice is a copy of exactly one 

2531 orbital of *unit_cell*, translated by a lattice vector (*matches*: the 

2532 number of such orbitals, per site). 

2533 

2534 :raises ValueError: A site is not on the periodic lattice. 

2535 ''' 

2536 bad = np.flatnonzero(np.asarray(matches) != 1) 

2537 if len(bad): 

2538 raise ValueError('\n\nSite {} is not a translate of exactly one unit-cell site (same tag, ' 

2539 'position r0 + n1 a1 + ...): was the lattice rotated, strained ' 

2540 'or shifted?\n'.format(int(bad[0]))) 

2541 

2542 

2543def translation_invariant(dev, tol): 

2544 ''' 

2545 Check that equivalent matrix elements (same orbitals, same lattice 

2546 vector) are equal in every cell, within *tol*. 

2547 

2548 :raises ValueError: The model is not translation invariant. 

2549 ''' 

2550 if dev > tol: 

2551 raise ValueError('\n\nThe Hamiltonian is not translation invariant: equivalent hoppings ' 

2552 'or onsite energies differ by {:.3g} (> tol = {:.3g}), e.g. after ' 

2553 'disorder, defects or a magnetic field.\n'.format(dev, tol)) 

2554 

2555 

2556def torus_range(R, sizes): 

2557 ''' 

2558 Check that the lattice vectors of a torus's bonds (shortest images, in 

2559 [-N/2, N/2)) are unambiguous: at least 3 cells along every direction, 

2560 and no bond reaching half-way around. 

2561 

2562 :raises ValueError: The torus is too small for the range of the model. 

2563 ''' 

2564 if np.any(sizes < 3) or np.any(2 * R == -sizes[None, :]): 

2565 raise ValueError('\n\nThe torus ({} cells) is too small for the range of the model: ' 

2566 'a bond reaches half-way around it.\n'.format(tuple(int(n) for n in sizes))) 

2567 

2568 

2569def saveable_model(model, lattice_cls, system_cls, kspace_cls, orbital_cls): 

2570 ''' 

2571 Check that *tbkit.io.save_model* can store *model*. 

2572 

2573 :raises TypeError: model must be a Lattice, System or KSpace instance 

2574 (not an OrbitalSystem). 

2575 ''' 

2576 if isinstance(model, orbital_cls) or not isinstance(model, (lattice_cls, system_cls, kspace_cls)): 

2577 raise TypeError('\n\nParameter model must be a Lattice, System or KSpace instance ' 

2578 '(an OrbitalSystem or a driven model cannot be saved).\n') 

2579 

2580 

2581def file_path(path, var_name): 

2582 ''' 

2583 Check a file name. 

2584 

2585 :raises TypeError: Parameter var_name must be a string or a path. 

2586 ''' 

2587 if not (isinstance(path, str) or hasattr(path, "__fspath__")): 

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

2589 

2590 

2591def model_archive(data, fmt, version): 

2592 ''' 

2593 Check an archive read by *tbkit.io.load_model*. 

2594 

2595 :raises ValueError: Not a tbkit model archive, or a newer version. 

2596 ''' 

2597 if 'format' not in data.files or str(data['format']) != fmt: 

2598 raise ValueError('\n\nThis file is not a tbkit model archive (format {}).\n'.format(fmt)) 

2599 if int(data['version']) > version: 

2600 raise ValueError('\n\nThis archive has version {}; this tbkit reads versions up to {}: ' 

2601 'upgrade tbkit.\n'.format(int(data['version']), version)) 

2602 

2603 

2604def hr_header(tokens): 

2605 ''' 

2606 Check the header of a Wannier90 ``_hr.dat`` file: num_wann, nrpts and 

2607 the nrpts degeneracy weights, as positive integers. 

2608 

2609 :raises ValueError: Not a Wannier90 _hr.dat file. 

2610 ''' 

2611 try: 

2612 num_wann, nrpts = int(tokens[0]), int(tokens[1]) 

2613 weights = [int(w) for w in tokens[2:2 + nrpts]] 

2614 except (IndexError, ValueError): 

2615 raise ValueError('\n\nNot a Wannier90 _hr.dat file: expected num_wann, nrpts and ' 

2616 'the degeneracy weights after the header line.\n') from None 

2617 if num_wann < 1 or nrpts < 1 or len(weights) != nrpts or min(weights) < 1: 

2618 raise ValueError('\n\nNot a Wannier90 _hr.dat file: num_wann, nrpts and the ' 

2619 'degeneracy weights must be positive integers.\n') 

2620 

2621 

2622def hr_body(n_tokens, nrpts, num_wann): 

2623 ''' 

2624 Check the number of entries of a Wannier90 ``_hr.dat`` file. 

2625 

2626 :raises ValueError: The file must have nrpts * num_wann**2 lines of 7 numbers. 

2627 ''' 

2628 if n_tokens != 7 * nrpts * num_wann ** 2: 

2629 raise ValueError('\n\nThe _hr.dat file must have nrpts * num_wann**2 = {} lines ' 

2630 '"R1 R2 R3 m n Re Im" after the weights.\n'.format(nrpts * num_wann ** 2)) 

2631 

2632 

2633def hr_blocks(ok): 

2634 ''' 

2635 Check that the lines of a ``_hr.dat`` file come in blocks of num_wann**2 

2636 with the same lattice vector. 

2637 

2638 :raises ValueError: Lines of a lattice vector must be consecutive. 

2639 ''' 

2640 if not ok: 

2641 raise ValueError('\n\nThe _hr.dat lines of each lattice vector R must be consecutive ' 

2642 '(num_wann**2 lines per R).\n') 

2643 

2644 

2645def win_block(found): 

2646 ''' 

2647 Check that a ``.win`` file has a ``unit_cell_cart`` block. 

2648 

2649 :raises ValueError: No unit_cell_cart block. 

2650 ''' 

2651 if not found: 

2652 raise ValueError('\n\nThe .win file has no "begin unit_cell_cart ... end unit_cell_cart" block.\n') 

2653 

2654 

2655def win_cell(cell): 

2656 ''' 

2657 Check the ``unit_cell_cart`` block of a ``.win`` file: three lines of 

2658 three real numbers. 

2659 

2660 :raises ValueError: unit_cell_cart must hold three vectors of three numbers. 

2661 ''' 

2662 try: 

2663 ok = len(cell) == 3 and all(len(v) == 3 for v in cell) and \ 

2664 all(np.isfinite([float(c) for v in cell for c in v])) 

2665 except ValueError: 

2666 ok = False 

2667 if not ok: 

2668 raise ValueError('\n\nThe unit_cell_cart block must hold three vectors of three real numbers.\n') 

2669 

2670 

2671def centres_count(n, num_wann): 

2672 ''' 

2673 Check that a ``_centres.xyz`` file lists num_wann Wannier centres (X). 

2674 

2675 :raises ValueError: Too few Wannier centres. 

2676 ''' 

2677 if n < num_wann: 

2678 raise ValueError('\n\nThe _centres.xyz file lists {} Wannier centres ("X"), ' 

2679 'not {}.\n'.format(n, num_wann)) 

2680 

2681 

2682def wannier_dim(dim): 

2683 ''' 

2684 Check the dimension of a Wannier90 model. 

2685 

2686 :raises ValueError: dim must be 2 or 3. 

2687 ''' 

2688 if isinstance(dim, bool) or dim not in (2, 3): 

2689 raise ValueError('\n\nParameter dim must be 2 or 3.\n') 

2690 

2691 

2692def win_given(win): 

2693 ''' 

2694 Check that the lattice vectors can be found. 

2695 

2696 :raises ValueError: Give prim_vec or win. 

2697 ''' 

2698 if win is None: 

2699 raise ValueError('\n\nGive the primitive vectors: prim_vec, or the .win file (win).\n') 

2700 

2701 

2702def layer_cell(cell, dim): 

2703 ''' 

2704 Check that, for a 2D model, a1 and a2 lie in the (x, y) plane. 

2705 

2706 :raises ValueError: a1 and a2 must have no z component. 

2707 ''' 

2708 if dim == 2 and np.any(np.abs(cell[:2, 2]) > 1e-8): 

2709 raise ValueError('\n\nFor dim=2, the first two lattice vectors must lie in the (x, y) plane.\n') 

2710 

2711 

2712def wannier_prim_vec(prim_vec, dim): 

2713 ''' 

2714 Check that the primitive vectors are *dim* vectors of *dim* components. 

2715 

2716 :raises ValueError: prim_vec must hold dim vectors of dim components. 

2717 ''' 

2718 if len(prim_vec) != dim or len(prim_vec[0]) != dim: 

2719 raise ValueError('\n\nParameter prim_vec must hold {0} vectors of {0} components.\n'.format(dim)) 

2720 independent(prim_vec) 

2721 

2722 

2723def wannier_positions(shape, num_wann, dim): 

2724 ''' 

2725 Check the orbital positions of a Wannier90 model. 

2726 

2727 :raises ValueError: positions must have shape (num_wann, dim) or (num_wann, 3). 

2728 ''' 

2729 if len(shape) != 2 or shape[0] != num_wann or shape[1] not in (dim, 3): 

2730 raise ValueError('\n\nParameter positions must have shape ({0}, {1}) or ({0}, 3).\n' 

2731 .format(num_wann, dim)) 

2732 

2733 

2734def wannier_tags(tags, num_wann): 

2735 ''' 

2736 Check the sublattice tags of a Wannier90 model. 

2737 

2738 :raises TypeError: tags must be a list of one-character strings. 

2739 :raises ValueError: tags must have num_wann elements. 

2740 ''' 

2741 if not isinstance(tags, list) or not all(isinstance(t, str) and len(t) == 1 for t in tags): 

2742 raise TypeError('\n\nParameter tags must be a list of one-character strings.\n') 

2743 if len(tags) != num_wann: 

2744 raise ValueError('\n\nParameter tags must have {} elements.\n'.format(num_wann)) 

2745 

2746 

2747def layer_hoppings(h_max, tol): 

2748 ''' 

2749 Check that a 2D model has no hopping between layers (R3 != 0). 

2750 

2751 :raises ValueError: Hoppings with R3 != 0 must vanish for dim=2. 

2752 ''' 

2753 if h_max > tol: 

2754 raise ValueError('\n\nFor dim=2, the matrix elements with R3 != 0 must vanish ' 

2755 '(largest: {:.3g}).\n'.format(h_max)) 

2756 

2757 

2758def spinless(spin): 

2759 ''' 

2760 Check that a KSpace model has no spin. 

2761 

2762 :raises ValueError: This needs a spinless model. 

2763 ''' 

2764 if spin: 

2765 raise ValueError('\n\nThis needs a spinless model (spin=False): a System has one ' 

2766 'orbital per site; use bridges.finite_model for spinful models.\n') 

2767# HIGHER-ORDER TOPOLOGY, MOIRE SUPERCELLS, SELF-CONSISTENT INTERACTIONS 

2768#################################### 

2769 

2770 

2771def wannier_gap(nu, sector, tol=1e-4): 

2772 ''' 

2773 Check that a Wannier sector is separated from the other Wannier bands 

2774 over the mesh (*nu*, shape (n1, n2, nb), sorted at each k): at every k, 

2775 the smallest distance on the circle between a Wannier band of the 

2776 sector and one outside it (the Wannier gap) exceeds *tol*, and the 

2777 Wannier bands of the sector change between neighbouring k-points by 

2778 less than the smallest gap (otherwise two Wannier bands crossed between 

2779 them, or the sector wrapped around the circle). 

2780 

2781 :raises ValueError: The Wannier sector is not separated by a Wannier gap. 

2782 ''' 

2783 nu = np.asarray(nu) 

2784 rest = [n for n in range(nu.shape[-1]) if n not in sector] 

2785 if not rest: 

2786 return 

2787 

2788 def circ(d): 

2789 d = np.abs(d) % 1. 

2790 return np.minimum(d, 1. - d) 

2791 gap = np.min(circ(nu[..., sector][..., :, None] - nu[..., rest][..., None, :])) 

2792 jump = max(np.max(circ(nu[..., sector] - np.roll(nu[..., sector], 1, axis=a))) 

2793 for a in range(nu.ndim - 1)) 

2794 if gap < tol or jump >= gap: 

2795 raise ValueError('\n\nThe Wannier sector is not separated from the other Wannier ' 

2796 'bands by a Wannier gap (smallest distance {:.2e}, largest ' 

2797 'step {:.2e}).\n'.format(gap, jump)) 

2798 

2799 

2800def supercell_matrix(mat, dim): 

2801 ''' 

2802 Check a supercell matrix: integers, shape (dim, dim), nonzero determinant. 

2803 

2804 :raises TypeError: The supercell matrix must contain integers. 

2805 :raises ValueError: The supercell matrix must be (dim, dim), with a nonzero determinant. 

2806 ''' 

2807 if mat.shape != (dim, dim): 

2808 raise ValueError('\n\nThe supercell matrix must have the shape ({0}, {0}).\n'.format(dim)) 

2809 if not np.issubdtype(mat.dtype, np.integer): 

2810 raise TypeError('\n\nThe supercell matrix must contain integers.\n') 

2811 if round(abs(np.linalg.det(mat))) == 0: 

2812 raise ValueError('\n\nThe supercell matrix must have a nonzero determinant.\n') 

2813 

2814 

2815def supercell_model(sc, cls): 

2816 ''' 

2817 Check that *sc* is a supercell (made by *moire.supercell*). 

2818 

2819 :raises TypeError: Parameter sc must be a SupercellKSpace instance. 

2820 ''' 

2821 if not isinstance(sc, cls): 

2822 raise TypeError('\n\nParameter sc must be a SupercellKSpace instance ' 

2823 '(see tbkit.moire.supercell).\n') 

2824 

2825 

2826def bond_vectors(d): 

2827 ''' 

2828 Check bond vectors: shape (n, 3), none of zero length. 

2829 

2830 :raises ValueError: Bond vectors must have the shape (n, 3) and a nonzero length. 

2831 ''' 

2832 if d.ndim != 2 or d.shape[1] != 3: 

2833 raise ValueError('\n\nThe bond vectors must have the shape (n, 3).\n') 

2834 if np.any(np.linalg.norm(d, axis=1) == 0): 

2835 raise ValueError('\n\nThe bond vectors must have a nonzero length.\n') 

2836 

2837 

2838def twist_lattice(lattice): 

2839 ''' 

2840 Check the layer lattice of a twisted bilayer. 

2841 

2842 :raises ValueError: Parameter lattice must be "honeycomb" or "square". 

2843 ''' 

2844 if lattice not in ('honeycomb', 'square'): 

2845 raise ValueError('\n\nParameter lattice must be "honeycomb" or "square".\n') 

2846 

2847 

2848def commensurate(moire_1, moire_2): 

2849 ''' 

2850 Check that the moire vectors of the two layers coincide. 

2851 

2852 :raises ValueError: The two layers do not share the moire cell. 

2853 ''' 

2854 if not np.allclose(moire_1, moire_2, atol=1e-9 * max(1., np.abs(moire_1).max())): 

2855 raise ValueError('\n\nThe two layers do not share the moire cell.\n') 

2856 

2857 

2858def hopping_values(t, n): 

2859 ''' 

2860 Check the output of a hopping function: n values. 

2861 

2862 :raises ValueError: The hopping function must return one value per bond vector. 

2863 ''' 

2864 if np.shape(t) != (n,): 

2865 raise ValueError('\n\nThe hopping function must return one value per bond vector, ' 

2866 'shape ({},).\n'.format(n)) 

2867 

2868 

2869def even_dimension(n): 

2870 ''' 

2871 Check that a spinful matrix has an even dimension (two spins per site). 

2872 

2873 :raises ValueError: A spinful Hamiltonian must have an even dimension. 

2874 ''' 

2875 if n % 2: 

2876 raise ValueError('\n\nA spinful Hamiltonian must have an even dimension ' 

2877 '(rows site-major, spin up then down).\n') 

2878 

2879 

2880def magnetization(mag, n): 

2881 ''' 

2882 Check initial moments: a real array of shape (n, 3). 

2883 

2884 :raises ValueError: Parameter magnetization must have the shape (n, 3). 

2885 ''' 

2886 if mag.shape != (n, 3): 

2887 raise ValueError('\n\nParameter magnetization must have the shape ({}, 3).\n'.format(n)) 

2888 

2889 

2890def interaction(V, n): 

2891 ''' 

2892 Check an attractive interaction: a positive number, or n non-negative numbers. 

2893 

2894 :raises ValueError: The interaction must be positive (or n non-negative values). 

2895 ''' 

2896 V = np.asarray(V, dtype='f8') 

2897 if V.ndim == 0: 

2898 positive_real(float(V), 'V') 

2899 return 

2900 if V.shape != (n,) or np.any(V < 0): 

2901 raise ValueError('\n\nParameter V must be a positive number, or {} non-negative ' 

2902 'numbers (one per site).\n'.format(n)) 

2903 

2904 

2905def pairing_amplitudes(delta, n): 

2906 ''' 

2907 Check initial pairing amplitudes: a number, or n numbers. 

2908 

2909 :raises ValueError: Parameter delta0 must be a number or n numbers. 

2910 ''' 

2911 if np.ndim(delta) == 0: 

2912 number(delta, 'delta0') 

2913 return 

2914 if np.shape(delta) != (n,): 

2915 raise ValueError('\n\nParameter delta0 must be a number, or {} numbers ' 

2916 '(one per site).\n'.format(n)) 

2917# OPTICAL RESPONSE AND TRANSPORT (tbkit.optics, tbkit.transport) 

2918#################################### 

2919 

2920 

2921def frequencies(omega, var_name): 

2922 ''' 

2923 Check photon energies (or energies): a real number, or a non-empty 

2924 array of finite real numbers. 

2925 

2926 :raises TypeError: Parameter must be a real number or an array of real numbers. 

2927 :raises ValueError: Parameter must contain at least one finite number. 

2928 ''' 

2929 arr = np.asarray(omega) 

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

2931 raise TypeError('\n\nParameter {} must be a real number or an array ' 

2932 'of real numbers.\n'.format(var_name)) 

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

2934 raise ValueError('\n\nParameter {} must contain at least one finite ' 

2935 'number.\n'.format(var_name)) 

2936 

2937 

2938def tensor_component(component, order): 

2939 ''' 

2940 Check a Cartesian tensor component of a 2D response: a string of 

2941 *order* letters among 'x' and 'y'. Returns the axis indices. 

2942 

2943 :raises TypeError: Parameter component must be a string. 

2944 :raises ValueError: Parameter component must have order letters among x, y. 

2945 ''' 

2946 if not isinstance(component, str): 

2947 raise TypeError('\n\nParameter component must be a string.\n') 

2948 if len(component) != order or any(c not in 'xy' for c in component): 

2949 raise ValueError('\n\nParameter component must be {} letters among ' 

2950 "'x' and 'y'.\n".format(order)) 

2951 return tuple('xy'.index(c) for c in component) 

2952 

2953 

2954def band_derivatives(en, vel, w): 

2955 ''' 

2956 Check the band energies (nk, n), velocities (2, nk, n, n) and second 

2957 derivatives (2, 2, nk, n, n) of *optics.generalized_derivative*. 

2958 

2959 :raises ValueError: Inconsistent shapes. 

2960 ''' 

2961 if en.ndim != 2 or vel.shape != (2,) + en.shape + en.shape[-1:] or \ 

2962 w.shape != (2,) + vel.shape: 

2963 raise ValueError('\n\nThe shapes must be en (nk, n), vel (2, nk, n, n) and ' 

2964 'w (2, 2, nk, n, n).\n') 

2965 

2966 

2967def lead_pair(pair, n_leads, var_name, distinct): 

2968 ''' 

2969 Check a pair of lead indices (and, with *distinct*, that they differ). 

2970 

2971 :raises TypeError: Parameter must be a tuple/list of two lead indices. 

2972 :raises ValueError: No such lead, or the two leads must differ. 

2973 ''' 

2974 if not isinstance(pair, (tuple, list)) or len(pair) != 2: 

2975 raise TypeError('\n\nParameter {} must be a tuple of two lead indices.\n'.format(var_name)) 

2976 for lead in pair: 

2977 lead_index(lead, n_leads) 

2978 if distinct and pair[0] == pair[1]: 

2979 raise ValueError('\n\nThe two leads of parameter {} must differ.\n'.format(var_name)) 

2980 

2981 

2982def site_positions(positions, n_sites): 

2983 ''' 

2984 Check site positions: a real array of shape (n_sites, d). 

2985 

2986 :raises ValueError: Parameter positions must have shape (n_sites, d). 

2987 ''' 

2988 if positions.ndim != 2 or len(positions) != n_sites: 

2989 raise ValueError('\n\nParameter positions must have shape ({}, d).\n'.format(n_sites)) 

2990 

2991 

2992def slices(slices, n_sites): 

2993 ''' 

2994 Check a partition of the device sites into non-empty slices. 

2995 

2996 :raises TypeError: Parameter slices must be a list of lists of integers. 

2997 :raises ValueError: The slices must contain every site exactly once. 

2998 ''' 

2999 if not isinstance(slices, list) or not all( 

3000 isinstance(s, (list, tuple)) and len(s) > 0 

3001 and all(isinstance(i, (int, np.integer)) for i in s) for s in slices): 

3002 raise TypeError('\n\nParameter slices must be a list of non-empty lists of integers.\n') 

3003 flat = sorted(int(i) for s in slices for i in s) 

3004 if flat != list(range(n_sites)): 

3005 raise ValueError('\n\nThe slices must contain every site (0 to {}) exactly ' 

3006 'once.\n'.format(n_sites - 1)) 

3007 

3008 

3009def block_tridiagonal(max_distance): 

3010 ''' 

3011 Check that hoppings only couple a slice to itself or to its neighbours. 

3012 

3013 :raises ValueError: Hoppings must connect neighbouring slices only. 

3014 ''' 

3015 if max_distance > 1: 

3016 raise ValueError('\n\nThe Hamiltonian couples slices that are not neighbours: ' 

3017 'use thicker slices.\n') 

3018 

3019 

3020def lead_tuple(lead): 

3021 ''' 

3022 Check a lead given as a tuple (h0, v, coupling). 

3023 

3024 :raises TypeError: A lead must be a tuple (h0, v, coupling). 

3025 ''' 

3026 if not isinstance(lead, (tuple, list)) or len(lead) != 3: 

3027 raise TypeError('\n\nA lead must be a tuple (h0, v, coupling).\n') 

3028 

3029 

3030#################################### 

3031# SECTOR CHERN NUMBERS, BDG, BOTT, WEYL 

3032#################################### 

3033 

3034 

3035def hermitian_operator(ops, tol=1e-10): 

3036 ''' 

3037 Check that an operator (shape (..., norb, norb)) is Hermitian. 

3038 

3039 :raises ValueError: The operator must be Hermitian. 

3040 ''' 

3041 if np.max(np.abs(ops - ops.conj().swapaxes(-1, -2))) > tol: 

3042 raise ValueError('\n\nThe operator must be Hermitian.\n') 

3043 

3044 

3045def projected_gap(w, tol=1e-6): 

3046 ''' 

3047 Check that the projected operator POP (eigenvalues *w*, shape (nk, n), 

3048 sorted) has no eigenvalue near zero, and the same number of negative 

3049 eigenvalues at every k-point; return that number. 

3050 

3051 :raises ValueError: The spectrum of POP must have a gap around zero. 

3052 ''' 

3053 n_minus = np.sum(w < 0., axis=1) 

3054 if np.min(np.abs(w)) < tol or np.any(n_minus != n_minus[0]): 

3055 raise ValueError('\n\nThe spectrum of the projected operator POP closes around zero ' 

3056 '(smallest |eigenvalue| {:.3g}): the two sectors are not ' 

3057 'defined.\n'.format(float(np.min(np.abs(w))))) 

3058 return int(n_minus[0]) 

3059 

3060 

3061def mirror_square(squares, tol): 

3062 ''' 

3063 Check that a mirror operator squares to +1 or -1 (*squares*, shape 

3064 (..., norb, norb)); return the sign. 

3065 

3066 :raises ValueError: The mirror operator must square to +1 or -1. 

3067 ''' 

3068 sign = np.sign(squares[0, 0, 0].real) 

3069 eye = np.eye(squares.shape[-1]) 

3070 if sign == 0 or np.max(np.abs(squares - sign * eye)) > tol: 

3071 raise ValueError('\n\nThe mirror operator must be unitary and square to +1 or -1.\n') 

3072 return int(sign) 

3073 

3074 

3075def commutes(err, tol): 

3076 ''' 

3077 Check that an operator commutes with H(k) on the plane (*err*: largest 

3078 entry of the commutator). 

3079 

3080 :raises ValueError: The operator does not commute with H(k) on this plane. 

3081 ''' 

3082 if err > tol: 

3083 raise ValueError('\n\nThe mirror operator does not commute with H(k) on this plane ' 

3084 '(largest commutator entry {:.3g}): choose a ' 

3085 'mirror-invariant plane.\n'.format(err)) 

3086 

3087 

3088def strong_index(strong): 

3089 ''' 

3090 Check that the strong Z2 index comes out the same from the three pairs 

3091 of time-reversal-invariant planes. 

3092 

3093 :raises ValueError: The six Z2 invariants are inconsistent. 

3094 ''' 

3095 if len(set(int(s) for s in strong)) != 1: 

3096 raise ValueError('\n\nThe six planar Z2 invariants give different strong indices: ' 

3097 'increase nk or nk_perp (or the bands are not gapped).\n') 

3098 

3099 

3100def region(region, n): 

3101 ''' 

3102 Check a region: a non-empty list of distinct indices between 0 and n-1. 

3103 

3104 :raises TypeError: Parameter region must be a non-empty list of integers. 

3105 :raises ValueError: Parameter region must hold distinct indices between 0 and n-1. 

3106 ''' 

3107 if not isinstance(region, list) or not region or \ 

3108 not all(isinstance(i, (int, np.integer)) for i in region): 

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

3110 if len(set(region)) != len(region) or not all(0 <= i < n for i in region): 

3111 raise ValueError('\n\nParameter region must hold distinct indices between 0 and ' 

3112 '{}.\n'.format(n - 1)) 

3113 

3114 

3115def torus_cell(cell, d): 

3116 ''' 

3117 Check the two vectors spanning a torus: shape (2, d), linearly independent. 

3118 

3119 :raises ValueError: Parameter cell must hold two independent vectors of d components. 

3120 ''' 

3121 if cell.shape != (2, d) or np.linalg.matrix_rank(cell) < 2: 

3122 raise ValueError('\n\nParameter cell must hold two linearly independent vectors ' 

3123 'of {} components.\n'.format(d)) 

3124 

3125 

3126def band_below_top(n, norb): 

3127 ''' 

3128 Check that a band has a band above it. 

3129 

3130 :raises ValueError: The highest band of bands must not be the top band. 

3131 ''' 

3132 if n >= norb - 1: 

3133 raise ValueError('\n\nThe highest band of bands must have a band above it.\n') 

3134 

3135 

3136def antisymmetric(a, tol=1e-10): 

3137 ''' 

3138 Check that a matrix is antisymmetric. 

3139 

3140 :raises ValueError: The matrix must be antisymmetric. 

3141 ''' 

3142 if np.max(np.abs(a + a.T), initial=0.) > tol: 

3143 raise ValueError('\n\nThe matrix must be antisymmetric.\n') 

3144 

3145 

3146def bdg_orbitals(norb): 

3147 ''' 

3148 Check that a BdG model has an even number of orbitals (particles, then holes). 

3149 

3150 :raises ValueError: A BdG model must have an even number of orbitals. 

3151 ''' 

3152 if norb % 2: 

3153 raise ValueError('\n\nA BdG model must have an even number of orbitals ' 

3154 '(particles, then holes).\n') 

3155 

3156 

3157def majorana_form(a, tol=1e-8): 

3158 ''' 

3159 Check that the Majorana form -i U H U^dagger of a BdG Hamiltonian at a 

3160 time-reversal-invariant momentum is real (particle-hole symmetry). 

3161 

3162 :raises ValueError: The model is not particle-hole symmetric. 

3163 ''' 

3164 if np.max(np.abs(a.imag)) > tol: 

3165 raise ValueError('\n\nThe model is not particle-hole symmetric in the BdG basis ' 

3166 '(particles, then holes; see bdg.bdg_kspace).\n') 

3167 

3168 

3169def gapped_trim(pf, tol=1e-12): 

3170 ''' 

3171 Check that a Pfaffian is nonzero (the BdG gap is open at the momentum). 

3172 

3173 :raises ValueError: The gap closes at a time-reversal-invariant momentum. 

3174 ''' 

3175 if abs(pf) < tol: 

3176 raise ValueError('\n\nThe gap closes at a time-reversal-invariant momentum: the ' 

3177 'Majorana number is not defined.\n') 

3178 

3179 

3180#################################### 

3181# PERIODIC BOUNDARIES (SYSTEM) 

3182#################################### 

3183 

3184 

3185def periodic(periodic, dim): 

3186 ''' 

3187 Check parameter *periodic* of *System*: a boolean, or a tuple of *dim* 

3188 booleans (one per primitive vector); return the tuple. 

3189 

3190 :raises TypeError: Parameter periodic must be a bool or a tuple of bools. 

3191 :raises ValueError: Parameter periodic must have one entry per primitive vector. 

3192 ''' 

3193 if isinstance(periodic, bool): 

3194 return (periodic,) * dim 

3195 if not isinstance(periodic, tuple) or not all(isinstance(p, bool) for p in periodic): 

3196 raise TypeError('\n\nParameter periodic must be a bool or a tuple of bools.\n') 

3197 if len(periodic) != dim: 

3198 raise ValueError('\n\nParameter periodic must have one entry per primitive vector ' 

3199 '({}).\n'.format(dim)) 

3200 return periodic 

3201 

3202 

3203def torus_lattice(n1): 

3204 ''' 

3205 Check that the lattice of a periodic System comes from *get_lattice*. 

3206 

3207 :raises ValueError: Periodic boundaries need lat.get_lattice first. 

3208 ''' 

3209 if n1 == 0: 

3210 raise ValueError('\n\nPeriodic boundaries wrap the n1 x n2 (x n3) cells of ' 

3211 'lat.get_lattice: call it first.\n') 

3212 

3213 

3214def torus_size(dist, period, n): 

3215 ''' 

3216 Check that bonds of length *dist* (order *n*) reach less than half-way 

3217 around the torus (shortest period *period*), so that each one has a 

3218 single shortest image. 

3219 

3220 :raises ValueError: The torus is too small for this neighbour order. 

3221 ''' 

3222 if 2 * dist >= period - ATOL: 

3223 raise ValueError('\n\nThe torus is too small for the neighbours of order {}: their ' 

3224 'length {:.3f} reaches half-way around it (shortest period ' 

3225 '{:.3f}). Use more cells.\n'.format(n, dist, period)) 

3226 

3227 

3228def not_periodic(periodic, method): 

3229 ''' 

3230 Check that a System has open boundaries. 

3231 

3232 :raises ValueError: The method needs open boundaries. 

3233 ''' 

3234 if any(periodic): 

3235 raise ValueError('\n\n{} needs open boundaries (periodic=False); on a torus, see ' 

3236 'System.get_bott_index and set_peierls_phase.\n'.format(method)) 

3237 

3238 

3239def torus_2d(periodic): 

3240 ''' 

3241 Check that a System is a 2D torus (periodic along two primitive vectors). 

3242 

3243 :raises ValueError: This calculation needs a 2D lattice periodic along both vectors. 

3244 ''' 

3245 if len(periodic) != 2 or not all(periodic): 

3246 raise ValueError('\n\nThis calculation needs a 2D lattice with periodic boundaries ' 

3247 'along both primitive vectors (periodic=True).\n') 

3248 

3249 

3250#################################### 

3251# ANALYSIS AND PLOTTING (projections, contours, k-paths) 

3252#################################### 

3253 

3254 

3255def projector(proj, norb, tags): 

3256 ''' 

3257 Check parameter *projector* of *kspace.band_weights*: an orbital index, 

3258 a non-empty list of distinct orbital indices, a sublattice tag, or a 

3259 (norb, norb) Hermitian matrix. 

3260 

3261 :raises TypeError: Parameter projector must be an orbital index, a list of them, a tag or a matrix. 

3262 :raises ValueError: Orbital indices must be distinct and between 0 and norb-1. 

3263 :raises ValueError: Parameter projector must be a tag of the unit cell. 

3264 :raises ValueError: The operator must be a (norb, norb) Hermitian matrix. 

3265 ''' 

3266 if isinstance(proj, str): 

3267 if proj not in tags: 

3268 raise ValueError('\n\nParameter projector must be a tag of the unit cell: ' 

3269 '{}.\n'.format(sorted(set(tags)))) 

3270 return 

3271 if isinstance(proj, np.ndarray) and proj.ndim == 2: 

3272 operator(proj, norb) 

3273 hermitian_operator(proj) 

3274 return 

3275 if isinstance(proj, int): 

3276 proj = [proj] 

3277 if not isinstance(proj, list) or not proj or not all(isinstance(p, int) for p in proj): 

3278 raise TypeError('\n\nParameter projector must be an orbital index, a list of ' 

3279 'orbital indices, a sublattice tag or a (norb, norb) matrix.\n') 

3280 if len(set(proj)) != len(proj) or not all(0 <= p < norb for p in proj): 

3281 raise ValueError('\n\nOrbital indices must be distinct and between 0 and ' 

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

3283 

3284 

3285def band_weights(weights, shape): 

3286 ''' 

3287 Check parameter *weights* of *kspace.plot_bands*: real values, one per 

3288 band and k-point of the stored band structure. 

3289 

3290 :raises ValueError: Parameter weights must be a real array of the shape of the bands. 

3291 ''' 

3292 weights = np.asarray(weights) 

3293 if weights.shape != shape or np.iscomplexobj(weights) or not np.all(np.isfinite(weights)): 

3294 raise ValueError('\n\nParameter weights must be a finite real array of shape {}, ' 

3295 'the shape of the bands (see band_weights).\n'.format(shape)) 

3296 

3297 

3298def weight_style(style): 

3299 ''' 

3300 Check parameter *style* of *kspace.plot_bands*. 

3301 

3302 :raises ValueError: Parameter style must be "color" or "size". 

3303 ''' 

3304 if style not in ('color', 'size'): 

3305 raise ValueError('\n\nParameter style must be "color" or "size".\n') 

3306 

3307 

3308def contour_found(n_elements, energy): 

3309 ''' 

3310 Check that a band crosses the energy of a constant-energy contour. 

3311 

3312 :raises ValueError: The band does not cross the energy. 

3313 ''' 

3314 if n_elements == 0: 

3315 raise ValueError('\n\nThe band does not cross the energy {}: there is no ' 

3316 'constant-energy contour.\n'.format(energy)) 

3317 

3318 

3319def cubic_path(points, names, axes): 

3320 ''' 

3321 Check the parameters of *kspace.cubic_path*. 

3322 

3323 :raises TypeError: Parameter points must be a dictionary. 

3324 :raises ValueError: Each point must have 3 real coordinates. 

3325 :raises ValueError: Parameter names must list at least two keys of points. 

3326 :raises ValueError: Parameter axes must be 3 orthonormal rows. 

3327 ''' 

3328 if not isinstance(points, dict): 

3329 raise TypeError('\n\nParameter points must be a dictionary (label: point).\n') 

3330 for key, point in points.items(): 

3331 point = np.asarray(point) 

3332 if point.shape != (3,) or not np.isrealobj(point) or not np.all(np.isfinite(point)): 

3333 raise ValueError('\n\nPoint {} must have 3 real coordinates.\n'.format(key)) 

3334 if (not isinstance(names, (list, tuple)) or len(names) < 2 

3335 or any(n not in points for n in names)): 

3336 raise ValueError('\n\nParameter names must list at least two keys of points.\n') 

3337 if axes is not None: 

3338 axes = np.asarray(axes) 

3339 if axes.shape != (3, 3) or not np.isrealobj(axes) or not np.allclose(axes @ axes.T, np.eye(3)): 

3340 raise ValueError('\n\nParameter axes must be 3 orthonormal rows (shape (3, 3)).\n') 

3341 

3342 

3343def bravais_lattice(kind, dim): 

3344 ''' 

3345 Check that *kspace.high_symmetry_path* recognized the Bravais lattice. 

3346 

3347 :raises ValueError: No standard path for this lattice. 

3348 ''' 

3349 if kind is None: 

3350 raise ValueError('\n\nNo standard high-symmetry path for this {}D lattice: ' 

3351 'high_symmetry_path knows the 1D and 2D Bravais lattices, ' 

3352 'and the simple cubic, fcc, bcc, simple tetragonal and ' 

3353 'hexagonal ones in 3D. Pass the k-points to k_path ' 

3354 'directly.\n'.format(dim)) 

3355 

3356 

3357############################### 

3358# CONTINUUM DISCRETIZATION 

3359############################### 

3360 

3361 

3362def sympy_module(module): 

3363 ''' 

3364 Check that sympy, the optional dependency of *tbkit.continuum*, is installed. 

3365 

3366 :raises ImportError: sympy is not installed. 

3367 ''' 

3368 if module is None: 

3369 raise ImportError('\n\ntbkit.continuum needs sympy: pip install tbkit[continuum].\n') 

3370 

3371 

3372def continuum_type(ham, types): 

3373 ''' 

3374 Check the type of the Hamiltonian of *continuum.discretize*. 

3375 

3376 :raises TypeError: The Hamiltonian must be a string, a sympy expression 

3377 or a sympy Matrix. 

3378 ''' 

3379 if not isinstance(ham, types): 

3380 raise TypeError('\n\nParameter hamiltonian must be a string, a sympy expression ' 

3381 'or a sympy Matrix.\n') 

3382 

3383 

3384def continuum_parsed(error, string): 

3385 ''' 

3386 Check that the string Hamiltonian of *continuum.discretize* was parsed. 

3387 

3388 :raises ValueError: The string is not a valid expression. 

3389 ''' 

3390 if error is not None: 

3391 raise ValueError('\n\nParameter hamiltonian {!r} is not a valid sympy expression ' 

3392 '({}: {}).\n'.format(string, type(error).__name__, error)) 

3393 

3394 

3395def continuum_square(shape): 

3396 ''' 

3397 Check that the Hamiltonian of *continuum.discretize* is a square matrix. 

3398 

3399 :raises ValueError: The Hamiltonian must be a square matrix. 

3400 ''' 

3401 if len(shape) != 2 or shape[0] != shape[1] or shape[0] == 0: 

3402 raise ValueError('\n\nParameter hamiltonian must be a scalar or a square matrix ' 

3403 '(shape {}).\n'.format(shape)) 

3404 

3405 

3406def continuum_symbols(names): 

3407 ''' 

3408 Check the free symbols of the Hamiltonian of *continuum.discretize*: 

3409 no position (the model is translation invariant), no symbol named 

3410 like the grid spacing *a*, and parameter names that can be passed as 

3411 keyword arguments. 

3412 

3413 :raises ValueError: x, y, z are not supported. 

3414 :raises ValueError: a is reserved for the grid spacing. 

3415 :raises ValueError: Parameter names must be Python identifiers. 

3416 ''' 

3417 position = sorted(names & {'x', 'y', 'z'}) 

3418 if position: 

3419 raise ValueError('\n\nThe Hamiltonian depends on the position ({}): only translation-' 

3420 'invariant continuum models (constant coefficients) can be ' 

3421 'discretized into a KSpace.\n'.format(', '.join(position))) 

3422 if 'a' in names: 

3423 raise ValueError('\n\nThe symbol a is reserved for the grid spacing: rename the ' 

3424 'parameter.\n') 

3425 bad = sorted(n for n in names if not n.isidentifier() or n in ('site_i', 'site_j')) 

3426 if bad: 

3427 raise ValueError('\n\nParameter names must be Python identifiers (other than site_i, ' 

3428 'site_j), to be passed to get_ham: {}.\n'.format(', '.join(bad))) 

3429 

3430 

3431def continuum_dim(dim, used): 

3432 ''' 

3433 Check the dimension of *continuum.discretize*, given the momenta 

3434 k_x, k_y, k_z (axes 0, 1, 2) used by the Hamiltonian. 

3435 

3436 :raises TypeError: Parameter dim must be an integer. 

3437 :raises ValueError: Parameter dim must be 1, 2 or 3. 

3438 :raises ValueError: The Hamiltonian uses a momentum beyond dim. 

3439 ''' 

3440 if not isinstance(dim, int): 

3441 raise TypeError('\n\nParameter dim must be an integer.\n') 

3442 if dim not in (1, 2, 3): 

3443 raise ValueError('\n\nParameter dim must be 1, 2 or 3.\n') 

3444 if used and max(used) >= dim: 

3445 raise ValueError('\n\nThe Hamiltonian uses {} in dimension {}.\n'.format( 

3446 ('k_x', 'k_y', 'k_z')[max(used)], dim)) 

3447 

3448 

3449def continuum_polynomial(entry, is_polynomial): 

3450 ''' 

3451 Check that a matrix element of *continuum.discretize* is a polynomial 

3452 in the momenta. 

3453 

3454 :raises ValueError: The Hamiltonian must be a polynomial in k_x, k_y, k_z. 

3455 ''' 

3456 if not is_polynomial: 

3457 raise ValueError('\n\nThe Hamiltonian must be a polynomial in k_x, k_y, k_z ' 

3458 '(matrix element {}).\n'.format(entry)) 

3459 

3460 

3461def continuum_tags(tags, norb): 

3462 ''' 

3463 Check the orbital tags of *continuum.discretize*. 

3464 

3465 :raises ValueError: More than 52 orbitals need explicit tags. 

3466 :raises TypeError: Parameter tags must be a string or a list of strings. 

3467 :raises ValueError: Parameter tags must hold one one-character tag per orbital. 

3468 ''' 

3469 if tags is None: 

3470 if norb > 52: 

3471 raise ValueError('\n\nThe Hamiltonian has {} orbitals: pass their tags (default ' 

3472 'tags are the 52 letters).\n'.format(norb)) 

3473 return 

3474 if not isinstance(tags, (str, list, tuple)) or not all(isinstance(t, str) for t in tags): 

3475 raise TypeError('\n\nParameter tags must be a string or a list of strings.\n') 

3476 if len(tags) != norb or not all(len(t) == 1 for t in tags): 

3477 raise ValueError('\n\nParameter tags must hold {} one-character tags, one per ' 

3478 'orbital.\n'.format(norb)) 

3479 

3480 

3481def continuum_spacing(a): 

3482 ''' 

3483 Check the grid spacing of *continuum.discretize*: a **Lattice** 

3484 refuses primitive vectors whose squared norm is below 0.1. 

3485 

3486 :raises ValueError: Parameter a must be at least sqrt(0.1). 

3487 ''' 

3488 if a * a < 0.1: 

3489 raise ValueError('\n\nParameter a must be at least sqrt(0.1) = 0.316 (the shortest ' 

3490 'primitive vector of a Lattice): measure lengths in a smaller ' 

3491 'unit, and scale the coefficients of the Hamiltonian ' 

3492 'accordingly.\n') 

3493 

3494 

3495#################################### 

3496# WANNIER FUNCTIONS 

3497#################################### 

3498 

3499 

3500def trial_orbitals(trial, norb, n_bands): 

3501 ''' 

3502 Check the trial orbitals of *wannier.wannierize*: a list of orbital 

3503 indices, or an array of shape (norb, n_wann), with at most *n_bands* 

3504 trial orbitals (one per Wannier function; fewer than the bands 

3505 disentangles them). 

3506 

3507 :raises TypeError: Parameter trial must be a list of orbital indices or an array. 

3508 :raises ValueError: Parameter trial must hold between 1 and n_bands trial orbitals. 

3509 ''' 

3510 if isinstance(trial, list) and trial and all(isinstance(o, int) for o in trial): 

3511 if len(trial) > n_bands or not all(0 <= o < norb for o in trial): 

3512 raise ValueError('\n\nParameter trial must hold at most {} orbital indices (one per ' 

3513 'Wannier function, at most one per band) between 0 and {}.\n' 

3514 .format(n_bands, norb - 1)) 

3515 return 

3516 try: 

3517 g = np.asarray(trial, dtype='c16') 

3518 except (TypeError, ValueError): 

3519 raise TypeError('\n\nParameter trial must be a list of orbital indices or a complex ' 

3520 'array of shape (norb, n_wann).\n') from None 

3521 if g.ndim != 2 or g.shape[0] != norb or not 1 <= g.shape[1] <= n_bands: 

3522 raise ValueError('\n\nParameter trial must be an array of shape ({}, n_wann), with ' 

3523 '1 <= n_wann <= {}: one column (trial orbital) per Wannier ' 

3524 'function, at most one per band.\n'.format(norb, n_bands)) 

3525 

3526 

3527def energy_window(window, var_name): 

3528 ''' 

3529 Check an energy window of *wannier.wannierize*: a tuple of two 

3530 increasing real numbers. 

3531 

3532 :raises TypeError: Parameter var_name must be a tuple of two real numbers. 

3533 :raises ValueError: Parameter var_name must be increasing. 

3534 ''' 

3535 if not isinstance(window, tuple) or len(window) != 2 or \ 

3536 not all(isinstance(e, (int, float)) and not isinstance(e, bool) for e in window): 

3537 raise TypeError('\n\nParameter {} must be a tuple (E_min, E_max) of two real ' 

3538 'numbers.\n'.format(var_name)) 

3539 if window[0] >= window[1]: 

3540 raise ValueError('\n\nParameter {} must satisfy E_min < E_max.\n'.format(var_name)) 

3541 

3542 

3543def frozen_window(frozen, window): 

3544 ''' 

3545 Check that the frozen (inner) window of *wannier.wannierize* lies 

3546 inside the outer window. 

3547 

3548 :raises ValueError: The frozen window must lie inside the outer window. 

3549 ''' 

3550 if frozen[0] < window[0] or frozen[1] > window[1]: 

3551 raise ValueError('\n\nParameter frozen {} must lie inside the outer window {}.\n' 

3552 .format(frozen, window)) 

3553 

3554 

3555def window_states(n_inside, n_frozen, n_wann): 

3556 ''' 

3557 Check the number of states of the disentanglement windows at every 

3558 k-point: at least *n_wann* in the outer window, at most *n_wann* in the 

3559 frozen window. 

3560 

3561 :raises ValueError: The outer window holds fewer than n_wann states at some k-point. 

3562 :raises ValueError: The frozen window holds more than n_wann states at some k-point. 

3563 ''' 

3564 if n_inside < n_wann: 

3565 raise ValueError('\n\nThe outer window holds only {} of the bands at some k-point, ' 

3566 'fewer than the {} Wannier functions: widen it, or add bands.\n' 

3567 .format(n_inside, n_wann)) 

3568 if n_frozen > n_wann: 

3569 raise ValueError('\n\nThe frozen window holds {} bands at some k-point, more than the ' 

3570 '{} Wannier functions: narrow it.\n'.format(n_frozen, n_wann)) 

3571 

3572 

3573def isolated_bands(en, bands, tol=1e-8): 

3574 ''' 

3575 Check that a group of bands is separated in energy from the other bands 

3576 at every point of the mesh (*en*, shape (nk, norb), sorted). 

3577 

3578 :raises ValueError: The bands are not isolated. 

3579 ''' 

3580 inside = np.zeros(en.shape[1], bool) 

3581 inside[bands] = True 

3582 for n in range(en.shape[1] - 1): 

3583 if inside[n] != inside[n + 1] and np.min(en[:, n + 1] - en[:, n]) < tol: 

3584 raise ValueError('\n\nThe bands {} touch band {} on the k-mesh: Wannier functions ' 

3585 'need an isolated group of bands: add the bands they touch to the group, ' 

3586 'or disentangle them (more bands than trial orbitals).\n' 

3587 .format(bands, n + 1 if inside[n] else n)) 

3588 

3589 

3590def projection(s_min, tol=1e-10): 

3591 ''' 

3592 Check that the projection of the bands onto the trial orbitals has full 

3593 rank at every k-point. 

3594 

3595 :raises ValueError: The trial orbitals miss the bands at some k-point. 

3596 ''' 

3597 if s_min < tol: 

3598 raise ValueError('\n\nThe trial orbitals have no overlap with the bands at some ' 

3599 'k-point (singular projection): choose other trial orbitals, or ' 

3600 'another mesh. A band group with a nonzero Chern number has no ' 

3601 'localized Wannier functions, and any projection vanishes ' 

3602 'somewhere.\n') 

3603