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
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-21 15:52 +0000
1import numpy as np
4def pretty_header(header, log):
5 bar = ' ' + '═' * (len(header) + 14)
6 log(bar)
7 log(' ' + header)
8 log(bar)
9 log()
12def pretty_subheader(header, log):
13 bar = ' ' * 2 + '─' * (len(header) + 10)
14 log(bar)
15 log(' ' * 4 + header)
16 log(bar)
19def atos(array, fmt):
20 return ' '.join(f'{x:{fmt}}' for x in array)
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)
37 # Remove signed zero
38 C_cv = np.where(C_cv == 0, 0.0, C_cv)
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()
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)
62def pretty_atomic_dofs(atoms, dof_zac, *, log):
63 C_cv = atoms.cell
64 log('Atomic degrees of freedom:')
66 from dataclasses import dataclass
68 @dataclass
69 class FakeAtom:
70 index: int
71 symbol: str
72 position: np.ndarray
73 scaled_position: np.ndarray
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()
90_header = (
91 ' id symbol Rx Ry Rz sx sy sz'
92)
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)
106def pretty_dofs(dM_zcc, M_cc, rot_vv, C_cv, eps=1e-8, *, log):
107 from ase._4.symopt.relax import chol_derivative
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)
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)