SIRIUS 7.5.0
Electronic structure library and applications
hamiltonian.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2019 Anton Kozhevnikov, Mathieu Taillefumier, Thomas Schulthess
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
5// the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8// following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10// and the following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
13// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
15// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20/** \file hamiltonian.cpp
21 *
22 * \brief Contains definition of sirius::Hamiltonian0 class.
23 */
24
26#include "local_operator.hpp"
27#include "hamiltonian.hpp"
28
29namespace sirius {
30
31// TODO: radial integrals for the potential should be computed here; the problem is that they also can be set
32// externally by the host code
33
34template <typename T>
35Hamiltonian0<T>::Hamiltonian0(Potential& potential__, bool precompute_lapw__)
36 : ctx_(potential__.ctx())
37 , potential_(&potential__)
38 , unit_cell_(potential__.ctx().unit_cell())
39{
40 PROFILE("sirius::Hamiltonian0");
41
42 local_op_ = std::unique_ptr<Local_operator<T>>(
43 new Local_operator<T>(ctx_, ctx_.spfft_coarse<T>(), ctx_.gvec_coarse_fft_sptr(), &potential__));
44
45 if (!ctx_.full_potential()) {
46 d_op_ = std::unique_ptr<D_operator<T>>(new D_operator<T>(ctx_));
47 q_op_ = std::unique_ptr<Q_operator<T>>(new Q_operator<T>(ctx_));
48 }
49 if (ctx_.full_potential()) {
50 if (precompute_lapw__) {
52 potential_->update_atomic_potential();
53 ctx_.unit_cell().generate_radial_functions(ctx_.out());
54 ctx_.unit_cell().generate_radial_integrals();
55 }
56 hmt_ = std::vector<sddk::mdarray<std::complex<T>, 2>>(ctx_.unit_cell().num_atoms());
57 auto pu = ctx_.processing_unit();
58 #pragma omp parallel
59 {
60 int tid = omp_get_thread_num();
61 #pragma omp for
62 for (int ia = 0; ia < ctx_.unit_cell().num_atoms(); ia++) {
63 auto& atom = ctx_.unit_cell().atom(ia);
64 auto& type = atom.type();
65
66 int nmt = type.mt_basis_size();
67
68 hmt_[ia] = sddk::mdarray<std::complex<T>, 2>(nmt, nmt, sddk::memory_t::host, "hmt");
69
70 /* compute muffin-tin Hamiltonian */
71 for (int j2 = 0; j2 < nmt; j2++) {
72 int lm2 = type.indexb(j2).lm;
73 int idxrf2 = type.indexb(j2).idxrf;
74 for (int j1 = 0; j1 <= j2; j1++) {
75 int lm1 = type.indexb(j1).lm;
76 int idxrf1 = type.indexb(j1).idxrf;
77 hmt_[ia](j1, j2) = atom.radial_integrals_sum_L3<spin_block_t::nm>(idxrf1, idxrf2,
78 type.gaunt_coefs().gaunt_vector(lm1, lm2));
79 hmt_[ia](j2, j1) = std::conj(hmt_[ia](j1, j2));
80 }
81 }
82 if (pu == sddk::device_t::GPU) {
83 hmt_[ia].allocate(sddk::memory_t::device).copy_to(sddk::memory_t::device, acc::stream_id(tid));
84 }
85 }
86 if (pu == sddk::device_t::GPU) {
88 }
89 }
90 }
91}
92
93template <typename T>
95{
96}
97
98template <typename T> template <spin_block_t sblock>
99void
100Hamiltonian0<T>::apply_hmt_to_apw(Atom const& atom__, int ngv__, sddk::mdarray<std::complex<T>, 2>& alm__,
101 sddk::mdarray<std::complex<T>, 2>& halm__) const
102{
103 auto& type = atom__.type();
105 // TODO: this is k-independent and can in principle be precomputed together with radial integrals if memory is
106 // available
107 // TODO: for spin-collinear case hmt is Hermitian; compute upper triangular part and use zhemm
108 sddk::mdarray<std::complex<T>, 2> hmt(type.mt_aw_basis_size(), type.mt_aw_basis_size());
109 /* compute the muffin-tin Hamiltonian */
110 for (int j2 = 0; j2 < type.mt_aw_basis_size(); j2++) {
111 int lm2 = type.indexb(j2).lm;
112 int idxrf2 = type.indexb(j2).idxrf;
113 for (int j1 = 0; j1 < type.mt_aw_basis_size(); j1++) {
114 int lm1 = type.indexb(j1).lm;
115 int idxrf1 = type.indexb(j1).idxrf;
116 hmt(j1, j2) = atom__.radial_integrals_sum_L3<sblock>(idxrf1, idxrf2,
117 type.gaunt_coefs().gaunt_vector(lm1, lm2));
118 }
119 }
120 la::wrap(la::lib_t::blas)
121 .gemm('N', 'T', ngv__, type.mt_aw_basis_size(), type.mt_aw_basis_size(), &la::constant<std::complex<T>>::one(),
122 alm__.at(sddk::memory_t::host), alm__.ld(), hmt.at(sddk::memory_t::host), hmt.ld(),
123 &la::constant<std::complex<T>>::zero(), halm__.at(sddk::memory_t::host), halm__.ld());
124}
125
126template <typename T>
127void
128Hamiltonian0<T>::add_o1mt_to_apw(Atom const& atom__, int num_gkvec__, sddk::mdarray<std::complex<T>, 2>& alm__) const
129{
130 // TODO: optimize for the loop layout using blocks of G-vectors
131 auto& type = atom__.type();
132 std::vector<std::complex<T>> alm(type.mt_aw_basis_size());
133 std::vector<std::complex<T>> oalm(type.mt_aw_basis_size());
134 for (int ig = 0; ig < num_gkvec__; ig++) {
135 for (int j = 0; j < type.mt_aw_basis_size(); j++) {
136 alm[j] = oalm[j] = alm__(ig, j);
137 }
138 for (int j = 0; j < type.mt_aw_basis_size(); j++) {
139 int l = type.indexb(j).am.l();
140 int lm = type.indexb(j).lm;
141 int idxrf = type.indexb(j).idxrf;
142 for (int order = 0; order < type.aw_order(l); order++) {
143 int j1 = type.indexb().index_by_lm_order(lm, order);
144 int idxrf1 = type.indexr().index_of(angular_momentum(l), order);
145 oalm[j] += static_cast<const T>(atom__.symmetry_class().o1_radial_integral(idxrf, idxrf1)) * alm[j1];
146 }
147 }
148 for (int j = 0; j < type.mt_aw_basis_size(); j++) {
149 alm__(ig, j) = oalm[j];
150 }
151 }
152}
153
154template <typename T>
155void
157{
158 sddk::mdarray<std::complex<T>, 3> zm(unit_cell_.max_mt_basis_size(), unit_cell_.max_mt_basis_size(), ctx_.num_mag_dims());
159
160 for (auto it : psi__.spl_num_atoms()) {
161 auto ia = it.i;
162 auto& atom = unit_cell_.atom(ia);
163 int mt_basis_size = atom.type().mt_basis_size();
164
165 zm.zero();
166
167 /* only upper triangular part of zm is computed because it is a hermitian matrix */
168 #pragma omp parallel for default(shared)
169 for (int xi2 = 0; xi2 < mt_basis_size; xi2++) {
170 int lm2 = atom.type().indexb(xi2).lm;
171 int idxrf2 = atom.type().indexb(xi2).idxrf;
172
173 for (int i = 0; i < ctx_.num_mag_dims(); i++) {
174 for (int xi1 = 0; xi1 <= xi2; xi1++) {
175 int lm1 = atom.type().indexb(xi1).lm;
176 int idxrf1 = atom.type().indexb(xi1).idxrf;
177
178 zm(xi1, xi2, i) = atom.type().gaunt_coefs().sum_L3_gaunt(lm1, lm2, atom.b_radial_integrals(idxrf1, idxrf2, i));
179 }
180 }
181 }
182 /* compute bwf = B_z*|wf_j> */
183 la::wrap(la::lib_t::blas).hemm(
184 'L', 'U', mt_basis_size, ctx_.num_fv_states(), &la::constant<std::complex<T>>::one(),
185 zm.at(sddk::memory_t::host), zm.ld(), &psi__.mt_coeffs(0, it.li, wf::spin_index(0), wf::band_index(0)),
186 psi__.ld(), &la::constant<std::complex<T>>::zero(),
187 &bpsi__[0].mt_coeffs(0, it.li, wf::spin_index(0), wf::band_index(0)), bpsi__[0].ld());
188
189 /* compute bwf = (B_x - iB_y)|wf_j> */
190 if (bpsi__.size() == 3) {
191 /* reuse first (z) component of zm matrix to store (B_x - iB_y) */
192 for (int xi2 = 0; xi2 < mt_basis_size; xi2++) {
193 for (int xi1 = 0; xi1 <= xi2; xi1++) {
194 zm(xi1, xi2, 0) = zm(xi1, xi2, 1) - std::complex<T>(0, 1) * zm(xi1, xi2, 2);
195 }
196
197 /* remember: zm for x,y,z, components of magnetic field is hermitian and we computed
198 * only the upper triangular part */
199 for (int xi1 = xi2 + 1; xi1 < mt_basis_size; xi1++) {
200 zm(xi1, xi2, 0) = std::conj(zm(xi2, xi1, 1)) - std::complex<T>(0, 1) * std::conj(zm(xi2, xi1, 2));
201 }
202 }
203
204 la::wrap(la::lib_t::blas).gemm(
205 'N', 'N', mt_basis_size, ctx_.num_fv_states(), mt_basis_size, &la::constant<std::complex<T>>::one(),
206 zm.at(sddk::memory_t::host), zm.ld(), &psi__.mt_coeffs(0, it.li, wf::spin_index(0), wf::band_index(0)),
207 psi__.ld(), &la::constant<std::complex<T>>::zero(),
208 &bpsi__[2].mt_coeffs(0, it.li, wf::spin_index(0), wf::band_index(0)), bpsi__[2].ld());
209 }
210 }
211}
212
213template <typename T>
214void
216{
217 PROFILE("sirius::Hamiltonian0::apply_so_correction");
218
219 wf::spin_index s(0);
220
221 for (auto it : psi__.spl_num_atoms()) {
222 auto ia = it.i;
223 auto& atom = unit_cell_.atom(ia);
224 auto a = it.li;
225
226 for (int l = 0; l <= atom.type().lmax_apw(); l++) {
227 /* number of radial functions for this l */
228 int nrf = atom.type().indexr().max_order(l);
229
230 for (int order1 = 0; order1 < nrf; order1++) {
231 for (int order2 = 0; order2 < nrf; order2++) {
232 T sori = atom.symmetry_class().so_radial_integral(l, order1, order2);
233
234 for (int m = -l; m <= l; m++) {
235 int idx1 = atom.type().indexb_by_l_m_order(l, m, order1);
236 int idx2 = atom.type().indexb_by_l_m_order(l, m, order2);
237 int idx3 = (m + l != 0) ? atom.type().indexb_by_l_m_order(l, m - 1, order2) : 0;
238 // int idx4 = (m - l != 0) ? atom.type().indexb_by_l_m_order(l, m + 1, order2) : 0;
239
240 for (int ist = 0; ist < ctx_.num_fv_states(); ist++) {
241 wf::band_index b(ist);
242 auto z1 = psi__.mt_coeffs(idx2, a, s, b) * T(m) * sori;
243 /* u-u part */
244 hpsi__[0].mt_coeffs(idx1, a, s, b) += z1;
245 /* d-d part */
246 hpsi__[1].mt_coeffs(idx1, a, s, b) -= z1;
247 /* apply L_{-} operator; u-d part */
248 if (m + l) {
249 hpsi__[2].mt_coeffs(idx1, a, s, b) += psi__.mt_coeffs(idx3, a, s, b) * sori *
250 std::sqrt(T(l * (l + 1) - m * (m - 1)));
251 }
252 /* for the d-u part */
253 ///* apply L_{+} operator */
254 // if (m - l) {
255 // hpsi[3].mt_coeffs(0).prime().at(memory_t::host, offset + idx1, ist) +=
256 // fv_states__.mt_coeffs(0).prime().at(memory_t::host, offset + idx4, ist) *
257 // sori * std::sqrt(double(l * (l + 1) - m * (m + 1)));
258 //}
259 }
260 }
261 }
262 }
263 }
264 }
265}
266
267template class Hamiltonian0<double>;
268
269template
270void
271Hamiltonian0<double>::apply_hmt_to_apw<spin_block_t::nm>(Atom const& atom__, int ngv__, sddk::mdarray<std::complex<double>, 2>& alm__,
272 sddk::mdarray<std::complex<double>, 2>& halm__) const;
273
274#ifdef SIRIUS_USE_FP32
275template class Hamiltonian0<float>;
276
277template
278void
279Hamiltonian0<float>::apply_hmt_to_apw<spin_block_t::nm>(Atom const& atom__, int ngv__, sddk::mdarray<std::complex<float>, 2>& alm__,
280 sddk::mdarray<std::complex<float>, 2>& halm__) const;
281#endif
282
283} // namespace
Data and methods specific to the actual atom in the unit cell.
Definition: atom.hpp:37
Atom_symmetry_class & symmetry_class()
Return reference to corresponding atom symmetry class.
Definition: atom.hpp:324
std::complex< double > radial_integrals_sum_L3(int idxrf1__, int idxrf2__, std::vector< gaunt_L3< std::complex< double > > > const &gnt__) const
Definition: atom.hpp:420
Atom_type const & type() const
Return const reference to corresponding atom type object.
Definition: atom.hpp:318
Represent the k-point independent part of Hamiltonian.
Definition: hamiltonian.hpp:75
std::unique_ptr< Q_operator< T > > q_op_
Q operator (non-local part of S-operator).
Definition: hamiltonian.hpp:93
void apply_hmt_to_apw(Atom const &atom__, int ngv__, sddk::mdarray< std::complex< T >, 2 > &alm__, sddk::mdarray< std::complex< T >, 2 > &halm__) const
Apply the muffin-tin part of the Hamiltonian to the apw basis functions of an atom.
void apply_so_correction(wf::Wave_functions< T > &psi__, std::vector< wf::Wave_functions< T > > &hpsi__) const
Apply SO correction to the first-variational LAPW wave-functions.
std::unique_ptr< D_operator< T > > d_op_
D operator (non-local part of Hamiltonian).
Definition: hamiltonian.hpp:90
Potential * potential_
Alias for the potential.
Definition: hamiltonian.hpp:81
void add_o1mt_to_apw(Atom const &atom__, int num_gkvec__, sddk::mdarray< std::complex< T >, 2 > &alm__) const
Add correction to LAPW overlap arising in the infinite-order relativistic approximation (IORA).
Simulation_context & ctx_
Simulation context.
Definition: hamiltonian.hpp:78
void apply_bmt(wf::Wave_functions< T > &psi__, std::vector< wf::Wave_functions< T > > &bpsi__) const
Apply muffin-tin part of magnetic filed to the wave-functions.
std::unique_ptr< Local_operator< T > > local_op_
Local part of the Hamiltonian operator.
Definition: hamiltonian.hpp:87
Representation of the local operator.
Generate effective potential from charge density and magnetization.
Definition: potential.hpp:46
void generate_pw_coefs()
Generate plane-wave coefficients of the potential in the interstitial region.
std::ostream & out() const
Return output stream.
Helper class to wrap stream id (integer number).
Definition: acc.hpp:132
Angular momentum quantum number.
Multidimensional array with the column-major (Fortran) order.
Definition: memory.hpp:660
auto ld() const
Return leading dimensions of the wave-functions coefficients array.
auto const & spl_num_atoms() const
Return a split index for the number of atoms.
auto & mt_coeffs(int xi__, atom_index_t::local ia__, spin_index ispn__, band_index i__)
Return reference to the coefficient by atomic orbital index, atom, spin and band indices.
Wave-functions representation.
Contains declaration and definition of sirius::Hamiltonian class.
Declaration of sirius::Local_operator class.
void sync_stream(stream_id sid__)
Synchronize a single stream.
Definition: acc.hpp:234
void zero(T *ptr__, size_t n__)
Zero the device memory.
Definition: acc.hpp:397
int lm(int l, int m)
Get composite lm index by angular index l and azimuthal index m.
Definition: specfunc.hpp:50
Namespace of the SIRIUS library.
Definition: sirius.f90:5
@ nm
Non-magnetic case.
auto conj(double x__)
Return complex conjugate of a number. For a real value this is the number itself.
Definition: math_tools.hpp:165
Contains declaration and partial implementation of sirius::Potential class.