Coverage for gpaw/test/response/test_cobalt_sf_gsspawALDA.py: 85%
100 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1import pytest
3import numpy as np
5from gpaw import GPAW
6from gpaw.response import ResponseGroundStateAdapter, ResponseContext
7from gpaw.response.frequencies import ComplexFrequencyDescriptor
8from gpaw.response.chiks import ChiKSCalculator, SelfEnhancementCalculator
9from gpaw.response.dyson import DysonSolver
10from gpaw.response.goldstone import (
11 NewFMGoldstoneScaling,
12 RefinedFMGoldstoneScaling,
13)
14from gpaw.response.susceptibility import (spectral_decomposition,
15 read_eigenmode_lineshapes)
17from gpaw.test import findpeak
18from gpaw.test.gpwfile import response_band_cutoff
21@pytest.mark.kspair
22@pytest.mark.response
23def test_response_cobalt_sf_gsspawALDA(in_tmp_dir, gpw_files):
24 # ---------- Inputs ---------- #
26 q_qc = [[0.0, 0.0, 0.0], [1. / 4., 0.0, 0.0]] # Two q-points along G-M
27 frq_w = np.linspace(-0.5, 2.0, 101)
28 eta = 0.2
30 rshelmax = 0
31 ecut = 150
32 pos_eigs = 5
33 nmodes = 2 # majority modes
34 nblocks = 'max'
36 # ---------- Script ---------- #
38 # Read ground state data
39 context = ResponseContext(txt='cobalt_susceptibility.txt')
40 calc = GPAW(gpw_files['co_pw'], parallel=dict(domain=1))
41 nbands = response_band_cutoff['co_pw']
42 gs = ResponseGroundStateAdapter(calc)
44 # Set up response calculators
45 calc_args = (gs,)
46 calc_kwargs = dict(context=context,
47 nbands=nbands,
48 ecut=ecut,
49 gammacentered=True,
50 bandsummation='pairwise',
51 nblocks=nblocks)
52 chiks_calc = ChiKSCalculator(*calc_args, **calc_kwargs)
53 xi_calc = SelfEnhancementCalculator(*calc_args,
54 rshelmax=rshelmax,
55 **calc_kwargs)
56 base_scaling = NewFMGoldstoneScaling.from_xi_calculator(xi_calc)
57 refi_scaling = RefinedFMGoldstoneScaling.from_xi_calculator(xi_calc)
58 dyson_solver = DysonSolver(context)
60 for q, q_c in enumerate(q_qc):
61 # Calculate χ_KS^+-(q,z) and Ξ^++(q,z)
62 zd = ComplexFrequencyDescriptor.from_array(frq_w + 1j * eta)
63 chiks = chiks_calc.calculate('+-', q_c, zd)
64 xi = xi_calc.calculate('+-', q_c, zd)
66 # Distribute frequencies and invert dyson equation
67 chiks = chiks.copy_with_global_frequency_distribution()
68 xi = xi.copy_with_global_frequency_distribution()
69 chi = dyson_solver(chiks, xi)
70 # Test ability to apply Goldstone scaling when inverting the dyson eq.
71 scaled_chi = dyson_solver(chiks, xi, hxc_scaling=base_scaling)
72 refined_chi = dyson_solver(chiks, xi, hxc_scaling=refi_scaling)
73 # Simulate a "restart" of the GoldstoneScaling
74 base_scaling = NewFMGoldstoneScaling(lambd=base_scaling.lambd)
75 refi_scaling = RefinedFMGoldstoneScaling(lambd=refi_scaling.lambd)
77 # Calculate majority spectral function
78 Amaj, _ = spectral_decomposition(chi, pos_eigs=pos_eigs)
79 Amaj.write_eigenmode_lineshapes(
80 f'cobalt_Amaj_q{q}.csv', nmodes=nmodes)
81 sAmaj, _ = spectral_decomposition(scaled_chi, pos_eigs=pos_eigs)
82 sAmaj.write_eigenmode_lineshapes(
83 f'cobalt_sAmaj_q{q}.csv', nmodes=nmodes)
84 rAmaj, _ = spectral_decomposition(refined_chi, pos_eigs=pos_eigs)
85 rAmaj.write_eigenmode_lineshapes(
86 f'cobalt_rAmaj_q{q}.csv', nmodes=nmodes)
88 # Store Re ξ^++(q=0,ω), to test the self-enhancement after scaling
89 if q == 0:
90 _, xi_mW = get_mode_projections(
91 chiks, xi, sAmaj, lambd=base_scaling.lambd, nmodes=nmodes)
92 xi0_w = xi_mW[0].real
93 _, xi_mW = get_mode_projections(
94 chiks, xi, sAmaj, lambd=refi_scaling.lambd, nmodes=nmodes)
95 rxi0_w = xi_mW[0].real
97 # plot_enhancement(chiks, xi, Amaj, sAmaj,
98 # lambd=base_scaling.lambd, nmodes=nmodes)
99 # plot_enhancement(chiks, xi, Amaj, rAmaj,
100 # lambd=refi_scaling.lambd, nmodes=nmodes)
102 context.write_timer()
104 # Compare scaling coefficient to reference
105 assert base_scaling.lambd == pytest.approx(1.0541, abs=0.001)
106 assert refi_scaling.lambd == pytest.approx(1.0526, abs=0.001)
107 # Test that Re ξ^++(q=0,ω) ≾ 1 at ω=0
108 w0 = np.argmin(np.abs(frq_w))
109 assert xi0_w[w0] == pytest.approx(0.987, abs=0.01)
110 assert rxi0_w[w0] == pytest.approx(0.986, abs=0.01)
112 # Compare magnon peaks to reference data
113 refs_mqa = [
114 # Acoustic
115 [
116 # q_Γ
117 [
118 # (wpeak, Apeak)
119 (0.085, 7.895), # unscaled
120 (-0.002, 7.980), # scaled
121 ],
122 # q_M / 2
123 [
124 # (wpeak, Apeak)
125 (0.320, 5.828), # unscaled
126 (0.245, 6.215), # scaled
127 ],
128 ],
129 # Optical
130 [
131 # q_Γ
132 [
133 # (wpeak, Apeak)
134 (0.904, 3.493), # unscaled
135 (0.860, 3.395), # scaled
136 ],
137 # q_M / 2
138 [
139 # (wpeak, Apeak)
140 (0.857, 2.988), # unscaled
141 (0.721, 3.163), # scaled
142 ],
143 ],
144 ]
145 for a, Astr in zip([0, 1, 1], ['Amaj', 'sAmaj', 'rAmaj']):
146 for q in range(len(q_qc)):
147 w_w, a_wm = read_eigenmode_lineshapes(f'cobalt_{Astr}_q{q}.csv')
148 for m in range(nmodes):
149 wpeak, Apeak = findpeak(w_w, a_wm[:, m])
150 refw, refA = refs_mqa[m][q][a]
151 print(m, q, a, wpeak, Apeak)
152 assert wpeak == pytest.approx(refw, abs=0.01) # eV
153 assert Apeak == pytest.approx(refA, abs=0.05) # a.u.
154 if q == 0 and m == 0 and Astr == 'rAmaj':
155 # Check that the gap error is completely removed when using
156 # the refined scaling
157 assert wpeak == pytest.approx(0.0, abs=1e-4) # eV
160def get_mode_projections(chiks, xi, Amaj, *, lambd, nmodes):
161 """Project χ_KS^+-(q,z) and Ξ^++(q,z) onto the magnon mode vectors."""
162 wm = Amaj.get_eigenmode_frequency(nmodes=nmodes)
163 v_Gm = Amaj.get_eigenvectors_at_frequency(wm, nmodes=nmodes)
164 chiks_wm = np.zeros((chiks.blocks1d.nlocal, nmodes), dtype=complex)
165 xi_wm = np.zeros((xi.blocks1d.nlocal, nmodes), dtype=complex)
166 for m, v_G in enumerate(v_Gm.T):
167 chiks_wm[:, m] = np.conj(v_G) @ chiks.array @ v_G # chiks_wGG
168 xi_wm[:, m] = np.conj(v_G) @ xi.array @ v_G # xi_wGG
169 chiks_mW = chiks.blocks1d.all_gather(chiks_wm * lambd).T
170 xi_mW = xi.blocks1d.all_gather(xi_wm * lambd).T
171 return chiks_mW, xi_mW
174def plot_enhancement(chiks, xi, Amaj0, sAmaj, *, lambd, nmodes):
175 import matplotlib.pyplot as plt
176 from gpaw.mpi import world
177 from ase.units import Ha
179 for Amaj, _lambd in zip([Amaj0, sAmaj], [1., lambd]):
180 a_mW = Amaj.get_eigenmode_lineshapes(nmodes=nmodes).T
181 chiks_mW, xi_mW = get_mode_projections(
182 chiks, xi, Amaj, lambd=_lambd, nmodes=nmodes)
183 for m in range(nmodes):
184 plt.subplot(1, nmodes, m + 1)
185 plt.plot(chiks.zd.omega_w * Ha, -chiks_mW[m].imag / np.pi)
186 plt.plot(xi.zd.omega_w * Ha, xi_mW[m].real)
187 plt.axhline(1., c='0.5')
188 plt.plot(Amaj.omega_w, a_mW[m])
190 if world.rank == 0:
191 plt.show()
192 world.barrier()