Coverage for gpaw/response/jdos.py: 100%
41 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
1import numpy as np
3from gpaw.response import ResponseContext
4from gpaw.response.frequencies import ComplexFrequencyDescriptor
5from gpaw.response.pair_integrator import (PairFunctionIntegrator,
6 DynamicPairFunction)
7from gpaw.response.chiks import get_temporal_part
10class JDOS(DynamicPairFunction):
12 def __init__(self, spincomponent, q_c, zd):
13 self.spincomponent = spincomponent
14 super().__init__(q_c, zd)
16 def zeros(self):
17 nz = len(self.zd)
18 return np.zeros(nz, float)
21class JDOSCalculator(PairFunctionIntegrator):
22 r"""Joint density of states calculator.
24 Here, the joint density of states of collinear systems is defined as the
25 spectral part of the four-component Kohn-Sham susceptibility,
26 see [PRB 103, 245110 (2021)]:
28 __ __
29 1 \ \ /
30 g^μν(q,ω) = ‾ / / | σ^μ_ss' σ^ν_s's (f_nks - f_n'k+qs')
31 V ‾‾ ‾‾ \
32 k t \
33 x δ(ħω - [ε_n'k's'-ε_nks]) |
34 /
36 where t is a composite band and spin transition index: (n, s) -> (n', s').
37 """
39 def __init__(self, gs, context=None,
40 nbands=None, bandsummation='pairwise',
41 **kwargs):
42 """Contruct the JDOSCalculator
44 Parameters
45 ----------
46 gs : ResponseGroundStateAdapter
47 context : ResponseContext
48 nbands : int
49 Number of bands to include in the sum over states
50 bandsummation : str
51 Band summation strategy (does not change the result, but can affect
52 the run-time).
53 'pairwise': sum over pairs of bands
54 'double': double sum over band indices.
55 kwargs : see gpaw.response.pair_integrator.PairFunctionIntegrator
56 """
57 if context is None:
58 context = ResponseContext()
59 assert isinstance(context, ResponseContext)
61 super().__init__(gs, context, **kwargs)
63 self.nbands = nbands
64 self.bandsummation = bandsummation
66 def calculate(self, spincomponent, q_c, zd):
67 """Calculate g^μν(q,ω) using a lorentzian broadening of the δ-function
69 Parameters
70 ----------
71 spincomponent : str
72 Spin component (μν) of the joint density of states.
73 Currently, '00', 'uu', 'dd', '+-' and '-+' are implemented.
74 q_c : list or np.array
75 Wave vector in relative coordinates
76 zd : ComplexFrequencyDescriptor
77 Complex frequencies ħz to evaluate g^μν(q,ω) at, where z = ω + iη
78 and η > 0 yields the HWHM of the resulting lorentzian broadening.
79 """
80 assert isinstance(zd, ComplexFrequencyDescriptor)
81 assert zd.upper_half_plane
83 # Prepare to sum over bands and spins
84 transitions = self.get_band_and_spin_transitions(
85 spincomponent, nbands=self.nbands,
86 bandsummation=self.bandsummation)
88 self.context.print(self.get_info_string(
89 q_c, len(zd), spincomponent, self.nbands, len(transitions)))
91 # Set up output data structure
92 jdos = JDOS(spincomponent, q_c, zd)
94 # Perform actual in-place integration
95 self.context.print('Integrating the joint density of states:')
96 self._integrate(jdos, transitions)
98 return jdos
100 def add_integrand(self, kptpair, weight, jdos):
101 r"""Add the g^μν(q,ω) integrand of the outer k-point integral:
102 __
103 -1 \ σ^μ_ss' σ^ν_s's (f_nks - f_n'k's')
104 (...)_k = ‾‾ Im / ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
105 π ‾‾ ħω - (ε_n'k's' - ε_nks) + iħη
106 t
108 NB: Since the implemented spin matrices are real, the dissipative part
109 is equal to the imaginary part (up to a factor of π) of the full
110 integrand.
111 """
112 # Construct jdos integrand via the imaginary part of the frequency
113 # dependence in χ_KS^μν(q,z)
114 if jdos.spincomponent == '00' and self.gs.nspins == 1:
115 weight = 2 * weight
116 x_mytz = get_temporal_part(jdos.spincomponent, jdos.zd.hz_z,
117 kptpair, self.bandsummation)
118 integrand_mytz = -x_mytz.imag / np.pi
120 with self.context.timer('Perform sum over t-transitions'):
121 # Sum over local transitions
122 integrand_z = np.sum(integrand_mytz, axis=0)
123 # Sum over distributed transitions
124 kptpair.tblocks.blockcomm.sum(integrand_z)
125 jdos.array[:] += weight * integrand_z
127 def get_info_string(self, q_c, nz, spincomponent, nbands, nt):
128 """Get information about the joint density of states calculation"""
129 info_list = ['',
130 'Calculating the joint density of states with:',
131 f' q_c: [{q_c[0]}, {q_c[1]}, {q_c[2]}]',
132 f' Number of frequency points: {nz}',
133 f' Spin component: {spincomponent}',
134 self.get_band_and_transitions_info_string(nbands, nt),
135 '',
136 self.get_basic_info_string()]
137 return '\n'.join(info_list)