Coverage for tests/test_structure_factor.py: 100%

92 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1import numpy as np 

2import matplotlib.pyplot as plt 

3 

4import gamdpy as gp 

5from object_lib import pairpot_LJ as pair_potential 

6 

7# The following environment variable fixes a warning appearing during testing 

8# The warning appears when the tbb library is installed with conda 

9# https://github.com/numba/numba/issues/6350 

10import os 

11os.environ['NUMBA_THREADING_LAYER']='omp' 

12 

13def test_structure_factor(): 

14 # verbose = False 

15 # plot = False 

16 

17 # Setup simulation of single-component Lennard-Jones liquid 

18 temperature = 2.0 

19 configuration = gp.Configuration(D=3) 

20 configuration.make_lattice(gp.unit_cells.FCC, [8, 8, 8], rho=0.973) 

21 configuration['m'] = 1.0 

22 configuration.randomize_velocities(temperature=temperature * 2) 

23 

24 integrator = gp.integrators.NVT(temperature=temperature, tau=0.2, dt=0.005) 

25 

26 runtime_actions = [ gp.TrajectorySaver(), 

27 gp.ScalarSaver(), 

28 gp.MomentumReset(100)] 

29 

30 sim = gp.Simulation(configuration, pair_potential, integrator, runtime_actions, 

31 steps_per_timeblock=1024, num_timeblocks=16, 

32 storage='memory') 

33 

34 # if verbose: 

35 # print('Equilibrating...') 

36 # for _ in sim.run_timeblocks(): 

37 # if verbose: 

38 # print(sim.status(per_particle=True)) 

39 # 

40 # if verbose: 

41 # print('Calculating structure factor in production run ...') 

42 

43 q_max: float = 16.0 

44 calc_struct_fact = gp.CalculatorStructureFactor(configuration) 

45 calc_struct_fact.generate_q_vectors(q_max=q_max) 

46 for _ in sim.run_timeblocks(): 

47 calc_struct_fact.update() 

48 # if verbose: 

49 # print(sim.status(per_particle=True)) 

50 

51 # Read the (binned) structure factor 

52 # if verbose: 

53 # print('Testing binned data output ...') 

54 struc_fact = calc_struct_fact.read(bins=128) 

55 

56 # Assert that the structure factor is a dictionary 

57 assert isinstance(struc_fact, dict) 

58 # Assert that the dictionary contains the keys '|q|' and 'S(|q|)' 

59 assert '|q|' in struc_fact, f"struc_fact.keys() = {struc_fact.keys()}" 

60 assert 'S(|q|)' in struc_fact, f"struc_fact.keys() = {struc_fact.keys()}" 

61 # Assert that the values of the dictionary are numpy arrays 

62 assert isinstance(struc_fact['|q|'], type(np.array([]))) 

63 assert isinstance(struc_fact['S(|q|)'], type(np.array([]))) 

64 # Assert that the length of the arrays are the same 

65 assert len(struc_fact['|q|']) == len(struc_fact['S(|q|)']) 

66 # Assert that q values are between 0 and max_q 

67 assert np.all(struc_fact['|q|'] >= 0) 

68 assert np.all(struc_fact['|q|'] <= q_max) 

69 # Assert that max S_q is between 2 and 3 

70 assert np.max(struc_fact['S(|q|)']) < 3, f"max(S(q)) = {np.max(struc_fact['S(|q|)'])}" 

71 assert np.max(struc_fact['S(|q|)']) > 2, f"max(S(q)) = {np.max(struc_fact['S(|q|)'])}" 

72 # Assert that min S_q is between 0 and 1 

73 assert np.min(struc_fact['S(|q|)']) < 1, f"min(S(q)) = {np.min(struc_fact['S(|q|)'])}" 

74 assert np.min(struc_fact['S(|q|)']) >= 0, f"min(S(q)) = {np.min(struc_fact['S(|q|)'])}" 

75 

76 # if verbose: 

77 # # Print shape of the arrays 

78 # for key in struc_fact.keys(): 

79 # print(f"Shape of {key}: {struc_fact[key].shape}") 

80 # print(f"Max of S(q): {np.max(struc_fact['S(|q|)'])}") 

81 # print(f"Length of q at max of S(q): {struc_fact['|q|'][np.argmax(struc_fact['S(|q|)'])]}") 

82 # 

83 # if plot: 

84 # plt.figure() 

85 # plt.title('Structure factor') 

86 # plt.plot(struc_fact['|q|'], struc_fact['S(|q|)'], 'o--') 

87 # plt.xlabel('|q|') 

88 # plt.ylabel('S(|q|)') 

89 # plt.xlim(0, None) 

90 # plt.ylim(0, None) 

91 # plt.show() 

92 # 

93 # if verbose: 

94 # print('Testing raw data output ...') 

95 

96 struc_fact_raw = calc_struct_fact.read(bins=None) 

97 # Assert that the output is a dictionary 

98 assert isinstance(struc_fact_raw, dict) 

99 # Assert that the dictionary contains the keys 'q', '|q|', 'S(|q|)', 'rho_q', 'n_vectors' 

100 assert 'q' in struc_fact_raw, f"struc_fact_raw.keys() = {struc_fact_raw.keys()}" 

101 assert '|q|' in struc_fact_raw, f"struc_fact_raw.keys() = {struc_fact_raw.keys()}" 

102 assert 'S(q)' in struc_fact_raw, f"struc_fact_raw.keys() = {struc_fact_raw.keys()}" 

103 assert 'rho_q' in struc_fact_raw, f"struc_fact_raw.keys() = {struc_fact_raw.keys()}" 

104 assert 'n_vectors' in struc_fact_raw, f"struc_fact_raw.keys() = {struc_fact_raw.keys()}" 

105 # Assert that the values of the dictionary are numpy arrays 

106 assert isinstance(struc_fact_raw['q'], type(np.array([]))) 

107 assert isinstance(struc_fact_raw['|q|'], type(np.array([]))) 

108 assert isinstance(struc_fact_raw['S(q)'], type(np.array([]))) 

109 assert isinstance(struc_fact_raw['rho_q'], type(np.array([]))) 

110 assert isinstance(struc_fact_raw['n_vectors'], type(np.array([]))) 

111 # Assert that the length of the arrays are the same 

112 

113 # if verbose: 

114 # # Print dimensions of the arrays 

115 # for key in struc_fact_raw.keys(): 

116 # print(f"Shape of {key}: {struc_fact_raw[key].shape}") 

117 # 

118 # if plot: 

119 # # Plot image of 2D S(q) for q_z = 0 

120 # q: np.ndarray = struc_fact_raw['q'] 

121 # S_q: np.ndarray = struc_fact_raw['S(q)'] 

122 # # Select q_vectors where q_z = 0 

123 # mask = np.isclose(q[:, 2], 0) 

124 # q = q[mask] 

125 # q_x = q[:, 0] 

126 # q_y = q[:, 1] 

127 # S_q = S_q[mask] 

128 # # Plot image of 2D S(q) 

129 # plt.figure() 

130 # plt.title('Structure factor') 

131 # plt.scatter(q_x, q_y, c=S_q, s=50, alpha=0.8, cmap='viridis') 

132 # plt.xlabel('q_x') 

133 # plt.ylabel('q_y') 

134 # plt.colorbar() 

135 # plt.show() 

136 # 

137 # if verbose: 

138 # print('All tests passed successfully') 

139 

140def test_structure_factor_backends(): 

141 # Test structure factor calculation with different backends 

142 backends = ['CPU multi core', 'CPU single core'] 

143 sim = gp.get_default_sim() 

144 for backend in backends: 

145 print(f'Testing backend: {backend}') 

146 

147 # Test direct  

148 n_vectors = [[5,6,7], [0, 0, 1], [2, -2, -5]] 

149 calc_struct_fact = gp.CalculatorStructureFactor(sim.configuration, backend=backend, n_vectors=n_vectors) 

150 calc_struct_fact.update() 

151 struc_fact = calc_struct_fact.read(bins=8) 

152 

153 # Test generated q_vectors 

154 calc_struct_fact = gp.CalculatorStructureFactor(sim.configuration, backend=backend) 

155 calc_struct_fact.generate_q_vectors(q_max=10.0) 

156 calc_struct_fact.update() 

157 struc_fact = calc_struct_fact.read(bins=128) 

158 

159def test_atomic_form_factors(): 

160 D = 3 

161 rho = 1.0 

162 number_of_particles = 10_000 

163 conf = gp.Configuration(D=D) 

164 conf.make_positions(N=number_of_particles, rho=rho) 

165 conf['m'] = 1.0 

166 

167 n_vectors = np.array([[5,6,7], [0, 0, 1], [2, -2, -5]]) 

168 atomic_form_factors = np.random.random(number_of_particles) 

169 

170 calc_single = gp.CalculatorStructureFactor(conf, n_vectors, atomic_form_factors, 'CPU single core') 

171 number_of_updates = 4 

172 for _ in range(number_of_updates): 

173 conf['r'] = (np.random.rand(number_of_particles, D)-0.5) * conf.simbox.get_lengths() # Ideal gas configuration 

174 conf.copy_to_device() 

175 calc_single.update() 

176 calc_multi = gp.CalculatorStructureFactor(conf, n_vectors, atomic_form_factors, 'CPU multi core') 

177 for _ in range(number_of_updates): 

178 conf['r'] = (np.random.rand(number_of_particles, D)-0.5) * conf.simbox.get_lengths() # Ideal gas configuration 

179 conf.copy_to_device() 

180 calc_multi.update() 

181 

182def test_structure_factor_gpu(): 

183 D = 3 

184 rho = 1.0 

185 number_of_particles = 10_000 

186 conf = gp.Configuration(D=D) 

187 conf.make_positions(N=number_of_particles, rho=rho) 

188 conf['m'] = 1.0 

189 

190 # Ideal gas 

191 conf['r'] = (np.random.rand(number_of_particles, D)-0.5) * conf.simbox.get_lengths() 

192 

193 # Test direct 

194 calc_struct_fact = gp.CalculatorStructureFactor(conf, backend='GPU') 

195 calc_struct_fact.generate_q_vectors(q_max=10.0) 

196 print(f'{calc_struct_fact.q_vectors.shape = }') 

197 calc_struct_fact.update() 

198 struc_fact = calc_struct_fact.read(bins=32) 

199 

200 assert np.isclose(sum(struc_fact['S(|q|)'])/len(struc_fact['S(|q|)']), 1.0, atol=0.2) 

201 

202 

203if __name__ == '__main__': # pragma: no cover 

204 test_structure_factor() 

205 test_structure_factor_backends() 

206 test_atomic_form_factors() 

207 test_structure_factor_gpu() 

208