forked from deepmodeling/abacus-develop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgint_atom.cpp
208 lines (180 loc) · 8.04 KB
/
gint_atom.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include "module_base/ylm.h"
#include "module_base/array_pool.h"
#include "gint_atom.h"
#include "gint_helper.h"
namespace ModuleGint
{
template <typename T>
void GintAtom::set_phi(const std::vector<Vec3d>& coords, const int stride, T* phi) const
{
const int num_mgrids = coords.size();
// orb_ does not have the member variable dr_uniform
const double dr_uniform = orb_->PhiLN(0, 0).dr_uniform;
// store the pointer to reduce repeated address fetching
std::vector<const double*> p_psi_uniform(atom_->nw);
std::vector<const double*> p_dpsi_uniform(atom_->nw);
for(int iw = 0; iw < atom_->nw; iw++)
{
if(atom_->iw2_new[iw])
{
int l = atom_->iw2l[iw];
int n = atom_->iw2n[iw];
p_psi_uniform[iw] = orb_->PhiLN(l, n).psi_uniform.data();
p_dpsi_uniform[iw] = orb_->PhiLN(l, n).dpsi_uniform.data();
}
}
// store the spherical harmonics
// it's outside the loop to reduce the vector allocation overhead
std::vector<double> ylma;
for(int im = 0; im < num_mgrids; im++)
{
const Vec3d& coord = coords[im];
// 1e-9 is to avoid division by zero
const double dist = coord.norm() < 1e-9 ? 1e-9 : coord.norm();
if(dist > orb_->getRcut())
{
// if the distance is larger than the cutoff radius,
// the wave function values are all zeros
ModuleBase::GlobalFunc::ZEROS(phi + im * stride, atom_->nw);
}
else
{
// spherical harmonics
// TODO: vectorize the sph_harm function,
// the vectorized function can be called once for all meshgrids in a biggrid
ModuleBase::Ylm::sph_harm(atom_->nwl, coord.x/dist, coord.y/dist, coord.z/dist, ylma);
// interpolation
// these parameters are related to interpolation
// because once the distance from atom to grid point is known,
// we can obtain the parameters for interpolation and
// store them first! these operations can save lots of efforts.
const double position = dist / dr_uniform;
const int ip = static_cast<int>(position);
const double dx = position - ip;
const double dx2 = dx * dx;
const double dx3 = dx2 * dx;
const double c3 = 3.0 * dx2 - 2.0 * dx3;
const double c1 = 1.0 - c3;
const double c2 = (dx - 2.0 * dx2 + dx3) * dr_uniform;
const double c4 = (dx3 - dx2) * dr_uniform;
// I'm not sure if the variable name 'psi' is appropriate
double psi = 0;
for(int iw = 0; iw < atom_->nw; iw++)
{
if(atom_->iw2_new[iw])
{
auto psi_uniform = p_psi_uniform[iw];
auto dpsi_uniform = p_dpsi_uniform[iw];
psi = c1 * psi_uniform[ip] + c2 * dpsi_uniform[ip]
+ c3 * psi_uniform[ip + 1] + c4 * dpsi_uniform[ip + 1];
}
phi[im * stride + iw] = psi * ylma[atom_->iw2_ylm[iw]];
}
}
}
}
template <typename T>
void GintAtom::set_phi_dphi(
const std::vector<Vec3d>& coords, const int stride,
T* phi, T* dphi_x, T* dphi_y, T* dphi_z) const
{
const int num_mgrids = coords.size();
// orb_ does not have the member variable dr_uniform
const double dr_uniform = orb_->PhiLN(0, 0).dr_uniform;
// store the pointer to reduce repeated address fetching
std::vector<const double*> p_psi_uniform(atom_->nw);
std::vector<const double*> p_dpsi_uniform(atom_->nw);
std::vector<int> phi_nr_uniform(atom_->nw);
for (int iw=0; iw< atom_->nw; ++iw)
{
if ( atom_->iw2_new[iw] )
{
int l = atom_->iw2l[iw];
int n = atom_->iw2n[iw];
p_psi_uniform[iw] = orb_->PhiLN(l, n).psi_uniform.data();
p_dpsi_uniform[iw] = orb_->PhiLN(l, n).dpsi_uniform.data();
phi_nr_uniform[iw] = orb_->PhiLN(l, n).nr_uniform;
}
}
std::vector<double> rly(std::pow(atom_->nwl + 1, 2));
// TODO: replace array_pool with std::vector
ModuleBase::Array_Pool<double> grly(std::pow(atom_->nwl + 1, 2), 3);
for(int im = 0; im < num_mgrids; im++)
{
const Vec3d& coord = coords[im];
// 1e-9 is to avoid division by zero
const double dist = coord.norm() < 1e-9 ? 1e-9 : coord.norm();
if(dist > orb_->getRcut())
{
// if the distance is larger than the cutoff radius,
// the wave function values are all zeros
if(phi != nullptr)
{
ModuleBase::GlobalFunc::ZEROS(phi + im * stride, atom_->nw);
}
ModuleBase::GlobalFunc::ZEROS(dphi_x + im * stride, atom_->nw);
ModuleBase::GlobalFunc::ZEROS(dphi_y + im * stride, atom_->nw);
ModuleBase::GlobalFunc::ZEROS(dphi_z + im * stride, atom_->nw);
}
else
{
// spherical harmonics
// TODO: vectorize the sph_harm function,
// the vectorized function can be called once for all meshgrids in a biggrid
ModuleBase::Ylm::grad_rl_sph_harm(atom_->nwl, coord.x, coord.y, coord.z, rly.data(), grly.get_ptr_2D());
// interpolation
const double position = dist / dr_uniform;
const int ip = static_cast<int>(position);
const double x0 = position - ip;
const double x1 = 1.0 - x0;
const double x2 = 2.0 - x0;
const double x3 = 3.0 - x0;
const double x12 = x1 * x2 / 6;
const double x03 = x0 * x3 / 2;
double tmp, dtmp;
for(int iw = 0; iw < atom_->nw; ++iw)
{
// this is a new 'l', we need 1D orbital wave
// function from interpolation method.
if(atom_->iw2_new[iw])
{
auto psi_uniform = p_psi_uniform[iw];
auto dpsi_uniform = p_dpsi_uniform[iw];
if(ip >= phi_nr_uniform[iw] - 4)
{
tmp = dtmp = 0.0;
}
else
{
// use Polynomia Interpolation method to get the
// wave functions
tmp = x12 * (psi_uniform[ip] * x3 + psi_uniform[ip + 3] * x0)
+ x03 * (psi_uniform[ip + 1] * x2 - psi_uniform[ip + 2] * x1);
dtmp = x12 * (dpsi_uniform[ip] * x3 + dpsi_uniform[ip + 3] * x0)
+ x03 * (dpsi_uniform[ip + 1] * x2 - dpsi_uniform[ip + 2] * x1);
}
} // new l is used.
// get the 'l' of this localized wave function
const int ll = atom_->iw2l[iw];
const int idx_lm = atom_->iw2_ylm[iw];
const double rl = pow_int(dist, ll);
const double tmprl = tmp / rl;
// 3D wave functions
if(phi != nullptr)
{
phi[im * stride + iw] = tmprl * rly[idx_lm];
}
// derivative of wave functions with respect to atom positions.
const double tmpdphi_rly = (dtmp - tmp * ll / dist) / rl * rly[idx_lm] / dist;
dphi_x[im * stride + iw] = tmpdphi_rly * coord.x + tmprl * grly[idx_lm][0];
dphi_y[im * stride + iw] = tmpdphi_rly * coord.y + tmprl * grly[idx_lm][1];
dphi_z[im * stride + iw] = tmpdphi_rly * coord.z + tmprl * grly[idx_lm][2];
}
}
}
}
// explicit instantiation
template void GintAtom::set_phi(const std::vector<Vec3d>& coords, const int stride, double* phi) const;
template void GintAtom::set_phi(const std::vector<Vec3d>& coords, const int stride, std::complex<double>* phi) const;
template void GintAtom::set_phi_dphi(const std::vector<Vec3d>& coords, const int stride, double* phi, double* dphi_x, double* dphi_y, double* dphi_z) const;
}