Coverage for gpaw/test/test_berryphase.py: 100%

60 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-08 00:17 +0000

1import numpy as np 

2import pytest 

3 

4import gpaw.mpi as mpi 

5from gpaw import GPAW 

6from gpaw.berryphase import (get_berry_phases, 

7 polarization_phase, 

8 parallel_transport) 

9 

10# Values from an earlier test 

11ref_phi_mos2_km = np.array( 

12 [[2.72907676e-04, 2.99369724e+00, 4.51932187e+00, 5.94725651e+00], 

13 [4.84334561e-03, 2.42519044e+00, 4.43335136e+00, 5.75115262e+00], 

14 [2.99682618e-02, 2.26119678e+00, 4.30480687e+00, 5.78042986e+00], 

15 [4.84334561e-03, 2.42519044e+00, 4.43335136e+00, 5.75115262e+00], 

16 [2.72907676e-04, 2.99369724e+00, 4.51932187e+00, 5.94725651e+00], 

17 [3.75847658e-03, 2.67197983e+00, 4.36511629e+00, 5.60446187e+00]]) 

18 

19 

20def test_parallel_transport_mos2(in_tmp_dir, gpw_files): 

21 # Calculate the berry phases and spin projections 

22 gpw = gpw_files['mos2_pw_nosym'] 

23 parallel_transport(str(gpw), name='mos2', scale=1) 

24 

25 # Load phase-ordered data 

26 phi_km, S_km = load_renormalized_data('mos2') 

27 

28 # Test that the berry phases do not change (assuming that they 

29 # were correct to begin with) 

30 print(phi_km[:, ::7]) # we slice the bands to make output readable 

31 assert phi_km[:, ::7] == pytest.approx(ref_phi_mos2_km, abs=0.05) 

32 

33 

34def test_parallel_transport_i2sb2(in_tmp_dir, gpw_files): 

35 # Calculate the berry phases and spin projections 

36 calc = GPAW(gpw_files['i2sb2_pw_nosym'], txt=None, 

37 communicator=mpi.serial_comm) 

38 nelec = int(calc.get_number_of_electrons()) 

39 parallel_transport(calc, name='i2sb2', scale=1, 

40 # To calculate the valence bands berry 

41 # phases, we only need the top valence 

42 # group of bands. This corresponds to 2x8 

43 # bands, see c2db (x2 for spin) 

44 bands=range(nelec - 2 * 8, nelec)) 

45 

46 # Load phase-ordered data 

47 phi_km, S_km = load_renormalized_data('i2sb2') 

48 

49 # # For the spin test below to make sense, please compare this 

50 # # plot to the berry phase plot at the c2db website 

51 # import matplotlib.pyplot as plt 

52 # plt.scatter(np.tile(np.arange(len(phi_km)), len(phi_km.T)), 

53 # phi_km.T.reshape(-1), 

54 # cmap=plt.get_cmap('viridis'), 

55 # c=S_km.T.reshape(-1), 

56 # s=25, 

57 # marker='o') 

58 # plt.ylim((0, 2 * np.pi)) 

59 # plt.show() 

60 

61 # We test the spin for bands we are in control of, that is, 

62 # avoid high-symmetry points and look only at the winding 

63 # bands above a phase of ~pi, see the c2db berry phase plot 

64 bands = [0, 1, 3, 4] 

65 phi_qm = phi_km[bands] 

66 S_qm = S_km[bands] 

67 Svalues = S_qm[phi_qm > 3.0] 

68 assert Svalues == pytest.approx(np.array([-1, 1, # k=0 

69 -1, 1, # k=1 

70 1, -1, # k=2 

71 1, -1]), # k=3 

72 abs=0.01) 

73 # Test also the berry phases for the same bands 

74 phivalues = phi_qm[phi_qm > 3.0] 

75 print(phivalues) 

76 # We test that the values don't change too much. This will 

77 # also guarantee that the results agree qualitatively with 

78 # the c2db plot 

79 assert phivalues == pytest.approx([3.115, 5.309, 3.970, 4.455, 

80 3.970, 4.455, 3.115, 5.309], abs=0.05) 

81 

82 

83def load_renormalized_data(name): 

84 data = np.load(f'phases_{name}.npz') 

85 phi_km = data['phi_km'] 

86 S_km = data['S_km'] 

87 

88 # Phases are only well-defined modulo 2pi 

89 phi_km %= 2 * np.pi 

90 

91 # Sort bands by the berry phase 

92 indices = np.argsort(phi_km, axis=1) 

93 phi_km = np.take_along_axis(phi_km, indices, axis=1) 

94 S_km = np.take_along_axis(S_km, indices, axis=1) 

95 

96 return phi_km, S_km 

97 

98 

99def test_polarization_phase(in_tmp_dir, gpw_files): 

100 pi2 = 2.0 * np.pi 

101 phases_c = polarization_phase(gpw_files['mos2_pw_nosym'], 

102 comm=mpi.world) 

103 

104 phases_t = { 

105 'phase_c': pi2 * np.array([8.66037602, 3.33962524, 8.54861146e-15]), 

106 'electronic_phase_c': pi2 * np.array([0.66037602, -0.66037476, 1.0]), 

107 'atomic_phase_c': pi2 * np.array([8.0, 4.0, 13.0]), 

108 'dipole_phase_c': pi2 

109 * np.array([7.23912394e-01, -7.23912423e-01, 8.54861146e-15])} 

110 

111 # test all components 

112 # apply modulo 

113 for key in phases_c: 

114 # only should test modulo 2pi 

115 dphi = phases_c[key] - phases_t[key] 

116 phases_c[key] -= np.rint(dphi / pi2) * pi2 

117 print(key) 

118 assert phases_c[key] == pytest.approx(phases_t[key], abs=1e-6) 

119 

120 

121def test_berry_phases(in_tmp_dir, gpw_files): 

122 calc = GPAW(gpw_files['mos2_pw_nosym'], communicator=mpi.serial_comm) 

123 

124 ind, phases = get_berry_phases(calc) 

125 

126 indtest = np.array([[0, 6, 12, 18, 24, 30], 

127 [1, 7, 13, 19, 25, 31], 

128 [2, 8, 14, 20, 26, 32], 

129 [3, 9, 15, 21, 27, 33], 

130 [4, 10, 16, 22, 28, 34], 

131 [5, 11, 17, 23, 29, 35]]) 

132 

133 phasetest = [1.66179, 2.54985, 3.10069, 2.54985, 1.66179, 0.92385] 

134 assert ind == pytest.approx(indtest) 

135 assert phases == pytest.approx(phasetest, abs=1e-3) 

136 

137 

138# only master will raise, so this test will hang in parallel 

139@pytest.mark.serial 

140def test_assertions(in_tmp_dir, gpw_files): 

141 """ 

142 Functions should only work without symmetry 

143 Tests so that proper assertion is raised for calculator 

144 with symmetry enabled 

145 """ 

146 

147 gpw_file = gpw_files['mos2_pw'] 

148 with pytest.raises(AssertionError): 

149 polarization_phase(gpw_file, comm=mpi.serial_comm) 

150 

151 calc = GPAW(gpw_file, communicator=mpi.serial_comm) 

152 

153 with pytest.raises(AssertionError): 

154 ind, phases = get_berry_phases(calc) 

155 

156 with pytest.raises(AssertionError): 

157 phi_km, S_km = parallel_transport(calc, direction=0, 

158 name='mos2', scale=0)