Coverage for ase / _4 / symopt / relax_print.py: 100.00%

80 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-21 15:52 +0000

1import numpy as np 

2 

3 

4def pretty_header(header, log): 

5 bar = ' ' + '═' * (len(header) + 14) 

6 log(bar) 

7 log(' ' + header) 

8 log(bar) 

9 log() 

10 

11 

12def pretty_subheader(header, log): 

13 bar = ' ' * 2 + '─' * (len(header) + 10) 

14 log(bar) 

15 log(' ' * 4 + header) 

16 log(bar) 

17 

18 

19def atos(array, fmt): 

20 return ' '.join(f'{x:{fmt}}' for x in array) 

21 

22 

23def pretty( 

24 C_cv, title=None, units=None, decimals=7, symbolize=False, eps=1e-4, *, log 

25): 

26 C_cv = C_cv.copy() 

27 if symbolize: 

28 # Find smallest non zero 

29 alpha = np.min(np.abs(C_cv[np.nonzero(np.abs(C_cv) > eps)])) 

30 C_cv = C_cv / alpha 

31 if np.allclose(C_cv - np.round(C_cv), 0, atol=1e-3): 

32 C_cv = np.round(C_cv) 

33 decimals = 0 

34 else: 

35 C_cv = np.round(C_cv, decimals=decimals) 

36 

37 # Remove signed zero 

38 C_cv = np.where(C_cv == 0, 0.0, C_cv) 

39 

40 if title: 

41 log(f'{title} [{units}]') 

42 for i in range(C_cv.shape[0]): 

43 for j in range(C_cv.shape[1]): 

44 log(f'{C_cv[i, j]:{decimals + 5}.{decimals}f} ', end='') 

45 log() 

46 

47 

48def pprint_atoms(atoms, log, units='Å'): 

49 cell = atoms.cell 

50 log(f'Unit cell ({units})') 

51 for i in range(3): 

52 log(f' a{i + 1} = [ ', end='') 

53 for j in range(3): 

54 log(f' {cell[i, j]:10.5f}', end='') 

55 log(' ]') 

56 log(f'Lengths ({units}): {atos(cell.lengths(), ".3f")}') 

57 log(f'Angles (°): {atos(cell.angles(), ".2f")}') 

58 log('Atoms:') 

59 atom_table(atoms, log=log) 

60 

61 

62def pretty_atomic_dofs(atoms, dof_zac, *, log): 

63 C_cv = atoms.cell 

64 log('Atomic degrees of freedom:') 

65 

66 from dataclasses import dataclass 

67 

68 @dataclass 

69 class FakeAtom: 

70 index: int 

71 symbol: str 

72 position: np.ndarray 

73 scaled_position: np.ndarray 

74 

75 for z, dof_ac in enumerate(dof_zac): 

76 log(f'Degree of freedom q{z:02d}') 

77 atoms = [ 

78 FakeAtom( 

79 atom.index, 

80 atom.symbol, 

81 dof_ac[atom.index] @ C_cv, 

82 dof_ac[atom.index], 

83 ) 

84 for atom in atoms 

85 ] 

86 atom_table(atoms, log=log) 

87 log() 

88 

89 

90_header = ( 

91 ' id symbol Rx Ry Rz sx sy sz' 

92) 

93 

94 

95def atom_table(atoms, *, log): 

96 log(_header) 

97 for a in atoms: 

98 s = f'{a.index:5d} {a.symbol:5s}' 

99 for v in range(3): 

100 s += f'{a.position[v]:10.5f} ' 

101 for v in range(3): 

102 s += f'{a.scaled_position[v]:20.15f} ' 

103 log(s) 

104 

105 

106def pretty_dofs(dM_zcc, M_cc, rot_vv, C_cv, eps=1e-8, *, log): 

107 from ase._4.symopt.relax import chol_derivative 

108 

109 log(f'Found {len(dM_zcc)} independent cell degrees of freedom') 

110 for z, dM_cc in enumerate(dM_zcc): 

111 log(f'Tangent {z} of cell') 

112 log('In metric space') 

113 pretty(dM_cc, symbolize=True, decimals=3, log=log) 

114 log('In cell space at C0_cv:') 

115 dC_cv = chol_derivative(M_cc, dM_cc) @ rot_vv.T 

116 pretty(dC_cv, symbolize=True, decimals=6, log=log) 

117 

118 log('In terms of unit cell vectors a1, a2, a3') 

119 # Deformation gradient, but in cc space 

120 F_cc = np.linalg.inv(C_cv.T) @ dC_cv.T 

121 pretty(F_cc, symbolize=True, decimals=3, log=log)