ABACUS develop
Atomic-orbital Based Ab-initio Computation at UStc
Loading...
Searching...
No Matches
dspin_force_stress.hpp
Go to the documentation of this file.
1#pragma once
2#include "dspin_lcao.h"
4#include "source_base/timer.h"
5
6namespace hamilt
7{
8
9template <typename TK, typename TR>
10void DeltaSpin<OperatorLCAO<TK, TR>>::cal_force_stress(const bool cal_force,
11 const bool cal_stress,
12 const HContainer<double>* dmR,
13 ModuleBase::matrix& force,
14 ModuleBase::matrix& stress)
15{
16 ModuleBase::TITLE("DeltaSpin", "cal_force_stress");
17
18 // begin the calculation of force and stress
19 ModuleBase::timer::tick("DeltaSpin", "cal_force_stress");
20
22 auto& constrain = sc.get_constrain();
23 this->cal_constraint_atom_list(constrain);
24 auto& lambda = sc.get_sc_lambda();
25
26 const Parallel_Orbitals* paraV = dmR->get_paraV();
27 const int npol = this->ucell->get_npol();
28 std::vector<double> stress_tmp(6, 0);
29 if (cal_force)
30 {
31 force.zero_out();
32 }
33 // 1. calculate <psi|beta> for each pair of atoms
34 // loop over all on-site atoms
35 #pragma omp parallel
36 {
37 std::vector<double> stress_local(6, 0);
38 ModuleBase::matrix force_local(force.nr, force.nc);
39 #pragma omp for schedule(dynamic)
40 for (int iat0 = 0; iat0 < this->ucell->nat; iat0++)
41 {
42 if(!this->constraint_atom_list[iat0])
43 {
44 continue;
45 }
46
47 // skip the atoms without plus-U
48 auto tau0 = ucell->get_tau(iat0);
49 int T0, I0;
50 ucell->iat2iait(iat0, &I0, &T0);
51
52 // first step: find the adjacent atoms and filter the real adjacent atoms
54 this->gridD->Find_atom(*ucell, tau0, T0, I0, &adjs);
55
56 std::vector<bool> is_adj(adjs.adj_num + 1, false);
57 for (int ad = 0; ad < adjs.adj_num + 1; ++ad)
58 {
59 const int T1 = adjs.ntype[ad];
60 const int I1 = adjs.natom[ad];
61 const int iat1 = ucell->itia2iat(T1, I1);
62 const ModuleBase::Vector3<int>& R_index1 = adjs.box[ad];
63 // choose the real adjacent atoms
64 // Note: the distance of atoms should less than the cutoff radius,
65 // When equal, the theoretical value of matrix element is zero,
66 // but the calculated value is not zero due to the numerical error, which would lead to result changes.
67 if (this->ucell->cal_dtau(iat0, iat1, R_index1).norm() * this->ucell->lat0
68 < this->orb_cutoff_[T1] + PARAM.inp.onsite_radius)
69 {
70 is_adj[ad] = true;
71 }
72 }
73 filter_adjs(is_adj, adjs);
74 const int max_l_plus_1 = this->ucell->atoms[T0].nwl + 1;
75 const int length = max_l_plus_1 * max_l_plus_1;
76 std::vector<std::unordered_map<int, std::vector<double>>> nlm_iat0(adjs.adj_num + 1);
77
78 for (int ad = 0; ad < adjs.adj_num + 1; ++ad)
79 {
80 const int T1 = adjs.ntype[ad];
81 const int I1 = adjs.natom[ad];
82 const int iat1 = ucell->itia2iat(T1, I1);
83 const ModuleBase::Vector3<double>& tau1 = adjs.adjacent_tau[ad];
84 const Atom* atom1 = &ucell->atoms[T1];
85
86 auto all_indexes = paraV->get_indexes_row(iat1);
87 auto col_indexes = paraV->get_indexes_col(iat1);
88 // insert col_indexes into all_indexes to get universal set with no repeat elements
89 all_indexes.insert(all_indexes.end(), col_indexes.begin(), col_indexes.end());
90 std::sort(all_indexes.begin(), all_indexes.end());
91 all_indexes.erase(std::unique(all_indexes.begin(), all_indexes.end()), all_indexes.end());
92 for (int iw1l = 0; iw1l < all_indexes.size(); iw1l += npol)
93 {
94 const int iw1 = all_indexes[iw1l] / npol;
95 std::vector<std::vector<double>> nlm;
96 // nlm is a vector of vectors, but size of outer vector is only 1 here
97 // If we are calculating force, we need also to store the gradient
98 // and size of outer vector is then 4
99 // inner loop : all projectors (L0,M0)
100 int L1 = atom1->iw2l[iw1];
101 int N1 = atom1->iw2n[iw1];
102 int m1 = atom1->iw2m[iw1];
103
104 // convert m (0,1,...2l) to M (-l, -l+1, ..., l-1, l)
105 int M1 = (m1 % 2 == 0) ? -m1 / 2 : (m1 + 1) / 2;
106
107 ModuleBase::Vector3<double> dtau = tau0 - tau1;
108 intor_->snap(T1, L1, N1, M1, T0, dtau * this->ucell->lat0, 1 /*cal_deri*/, nlm);
109 // select the elements of nlm with target_L
110 std::vector<double> nlm_target(length * 4);
111 // select the elements of nlm with target_L (0, 1, 2, 3 ...)
112 int target_L = 0, index=0;
113 for(int iw =0;iw < this->ucell->atoms[T0].nw; iw++)
114 {
115 const int L0 = this->ucell->atoms[T0].iw2l[iw];
116 // only the first zeta of each l-orbital is needed
117 if(L0 == target_L)
118 {
119 for(int m = 0; m < 2*L0+1; m++)
120 {
121 for (int n = 0; n < 4; n++) // value, deri_x, deri_y, deri_z
122 {
123 nlm_target[index + n * length] = nlm[n][iw + m];
124 }
125 index++;
126 }
127 target_L++;
128 }
129 }
130 nlm_iat0[ad].insert({all_indexes[iw1l], nlm_target});
131 }
132 }
133
134 // second iteration to calculate force and stress
135 for (int ad1 = 0; ad1 < adjs.adj_num + 1; ++ad1)
136 {
137 const int T1 = adjs.ntype[ad1];
138 const int I1 = adjs.natom[ad1];
139 const int iat1 = ucell->itia2iat(T1, I1);
140 double* force_tmp1 = (cal_force) ? &force_local(iat1, 0) : nullptr;
141 double* force_tmp2 = (cal_force) ? &force_local(iat0, 0) : nullptr;
142 ModuleBase::Vector3<int>& R_index1 = adjs.box[ad1];
143 ModuleBase::Vector3<double> dis1 = adjs.adjacent_tau[ad1] - tau0;
144 for (int ad2 = 0; ad2 < adjs.adj_num + 1; ++ad2)
145 {
146 const int T2 = adjs.ntype[ad2];
147 const int I2 = adjs.natom[ad2];
148 const int iat2 = ucell->itia2iat(T2, I2);
149 ModuleBase::Vector3<int>& R_index2 = adjs.box[ad2];
150 ModuleBase::Vector3<double> dis2 = adjs.adjacent_tau[ad2] - tau0;
151 ModuleBase::Vector3<int> R_vector(R_index2[0] - R_index1[0],
152 R_index2[1] - R_index1[1],
153 R_index2[2] - R_index1[2]);
154 const hamilt::BaseMatrix<double>* tmp = dmR->find_matrix(iat1, iat2, R_vector[0], R_vector[1], R_vector[2]);
155 int row_size = paraV->get_row_size();
156 int col_size = paraV->get_col_size();
157 if(row_size == 0 || col_size == 0)
158 {
159 continue;
160 }
161 // if not found , skip this pair of atoms
162 if (tmp != nullptr)
163 {
164 // calculate force
165 if (cal_force) {
166 this->cal_force_IJR(iat1,
167 iat2,
168 paraV,
169 nlm_iat0[ad1],
170 nlm_iat0[ad2],
171 tmp,
172 lambda[iat0],
173 this->nspin,
174 force_tmp1,
175 force_tmp2);
176 }
177
178 // calculate stress
179 if (cal_stress) {
180 this->cal_stress_IJR(iat1,
181 iat2,
182 paraV,
183 nlm_iat0[ad1],
184 nlm_iat0[ad2],
185 tmp,
186 lambda[iat0],
187 this->nspin,
188 dis1,
189 dis2,
190 stress_local.data());
191 }
192 }
193 }
194 }
195 }
196 #pragma omp critical
197 {
198 if(cal_force)
199 {
200 force += force_local;
201 }
202 if(cal_stress)
203 {
204 for(int i = 0; i < 6; i++)
205 {
206 stress_tmp[i] += stress_local[i];
207 }
208 }
209 }
210 }
211
212 if (cal_force)
213 {
214#ifdef __MPI
215 // sum up the occupation matrix
216 Parallel_Reduce::reduce_all(force.c, force.nr * force.nc);
217#endif
218 for (int i = 0; i < force.nr * force.nc; i++)
219 {
220 force.c[i] *= 2.0;
221 }
222 }
223
224 // stress renormalization
225 if (cal_stress)
226 {
227#ifdef __MPI
228 // sum up the occupation matrix
229 Parallel_Reduce::reduce_all(stress_tmp.data(), 6);
230#endif
231 const double weight = this->ucell->lat0 / this->ucell->omega;
232 for (int i = 0; i < 6; i++)
233 {
234 stress.c[i] = stress_tmp[i] * weight;
235 }
236 stress.c[8] = stress.c[5]; // stress(2,2)
237 stress.c[7] = stress.c[4]; // stress(2,1)
238 stress.c[6] = stress.c[2]; // stress(2,0)
239 stress.c[5] = stress.c[4]; // stress(1,2)
240 stress.c[4] = stress.c[3]; // stress(1,1)
241 stress.c[3] = stress.c[1]; // stress(1,0)
242 }
243
244 ModuleBase::timer::tick("DeltaSpin", "cal_force_stress");
245}
246
247template <typename TK, typename TR>
248void DeltaSpin<OperatorLCAO<TK, TR>>::cal_force_IJR(const int& iat1,
249 const int& iat2,
250 const Parallel_Orbitals* paraV,
251 const std::unordered_map<int, std::vector<double>>& nlm1_all,
252 const std::unordered_map<int, std::vector<double>>& nlm2_all,
253 const hamilt::BaseMatrix<double>* dmR_pointer,
254 const ModuleBase::Vector3<double>& lambda,
255 const int nspin,
256 double* force1,
257 double* force2)
258{
259 // npol is the number of polarizations,
260 // 1 for non-magnetic (one Hamiltonian matrix only has spin-up or spin-down),
261 // 2 for magnetic (one Hamiltonian matrix has both spin-up and spin-down)
262 const int npol = this->ucell->get_npol();
263 // ---------------------------------------------
264 // calculate the Nonlocal matrix for each pair of orbitals
265 // ---------------------------------------------
266 auto row_indexes = paraV->get_indexes_row(iat1);
267 auto col_indexes = paraV->get_indexes_col(iat2);
268 // step_trace = 0 for NSPIN=2; ={0, 1, local_col, local_col+1} for NSPIN=4
269 std::vector<int> step_trace(nspin, 0);
270 if (nspin == 4) {
271 step_trace[1] = 1;
272 step_trace[2] = col_indexes.size();
273 step_trace[3] = col_indexes.size() + 1;
274 }
275 double tmp[3];
276 // calculate the local matrix
277 for (int is = 1; is < nspin; is++)
278 {
279 const double lambda_tmp = nspin==2?lambda[2]:lambda[is-1];
280 const double* dm_pointer = dmR_pointer->get_pointer();
281 for (int iw1l = 0; iw1l < row_indexes.size(); iw1l += npol)
282 {
283 const std::vector<double>& nlm1 = nlm1_all.find(row_indexes[iw1l])->second;
284 for (int iw2l = 0; iw2l < col_indexes.size(); iw2l += npol)
285 {
286 const std::vector<double>& nlm2 = nlm2_all.find(col_indexes[iw2l])->second;
287#ifdef __DEBUG
288 assert(nlm1.size() == nlm2.size());
289#endif
290 const int length = nlm1.size() / 4;
291 const int lmax = sqrt(length);
292 int index = 0;
293 for(int l = 0; l<lmax; l++)
294 {
295 for (int m = 0; m < 2*l+1; m++)
296 {
297 index = l*l + m;
298 tmp[0] = lambda_tmp * nlm1[index + length] * nlm2[index] * dm_pointer[step_trace[is]];
299 tmp[1] = lambda_tmp * nlm1[index + length * 2] * nlm2[index] * dm_pointer[step_trace[is]];
300 tmp[2] = lambda_tmp * nlm1[index + length * 3] * nlm2[index] * dm_pointer[step_trace[is]];
301 // force1 = - VU * <d phi_{I,R1}/d R1|chi_m> * <chi_m'|phi_{J,R2}>
302 // force2 = - VU * <phi_{I,R1}|d chi_m/d R0> * <chi_m'|phi_{J,R2>}
303 force1[0] += tmp[0];
304 force1[1] += tmp[1];
305 force1[2] += tmp[2];
306 force2[0] -= tmp[0];
307 force2[1] -= tmp[1];
308 force2[2] -= tmp[2];
309 }
310 }
311 dm_pointer += npol;
312 }
313 dm_pointer += (npol - 1) * col_indexes.size();
314 }
315 }
316}
317
318template <typename TK, typename TR>
319void DeltaSpin<OperatorLCAO<TK, TR>>::cal_stress_IJR(const int& iat1,
320 const int& iat2,
321 const Parallel_Orbitals* paraV,
322 const std::unordered_map<int, std::vector<double>>& nlm1_all,
323 const std::unordered_map<int, std::vector<double>>& nlm2_all,
324 const hamilt::BaseMatrix<double>* dmR_pointer,
325 const ModuleBase::Vector3<double>& lambda,
326 const int nspin,
327 const ModuleBase::Vector3<double>& dis1,
328 const ModuleBase::Vector3<double>& dis2,
329 double* stress)
330{
331 // npol is the number of polarizations,
332 // 1 for non-magnetic (one Hamiltonian matrix only has spin-up or spin-down),
333 // 2 for magnetic (one Hamiltonian matrix has both spin-up and spin-down)
334 const int npol = this->ucell->get_npol();
335 // ---------------------------------------------
336 // calculate the Nonlocal matrix for each pair of orbitals
337 // ---------------------------------------------
338 auto row_indexes = paraV->get_indexes_row(iat1);
339 auto col_indexes = paraV->get_indexes_col(iat2);
340 // step_trace = 0 for NSPIN=2; ={0, 1, local_col, local_col+1} for NSPIN=4
341 std::vector<int> step_trace(nspin, 0);
342 if (nspin == 4) {
343 step_trace[1] = 1;
344 step_trace[2] = col_indexes.size();
345 step_trace[3] = col_indexes.size() + 1;
346 }
347 // calculate the local matrix
348 for (int is = 1; is < nspin; is++)
349 {
350 const double lambda_tmp = nspin==2?lambda[2]:lambda[is-1];
351 const double* dm_pointer = dmR_pointer->get_pointer();
352 for (int iw1l = 0; iw1l < row_indexes.size(); iw1l += npol)
353 {
354 const std::vector<double>& nlm1 = nlm1_all.find(row_indexes[iw1l])->second;
355 for (int iw2l = 0; iw2l < col_indexes.size(); iw2l += npol)
356 {
357 const std::vector<double>& nlm2 = nlm2_all.find(col_indexes[iw2l])->second;
358#ifdef __DEBUG
359 assert(nlm1.size() == nlm2.size());
360#endif
361 const int length = nlm1.size() / 4;
362 const int lmax = sqrt(length);
363 double tmp = lambda_tmp * dm_pointer[step_trace[is]];
364 int index = 0;
365 for(int l = 0; l<lmax; l++)
366 {
367 for (int m = 0; m < 2*l+1; m++)
368 {
369 index = l*l + m;
370 stress[0]
371 += tmp * (nlm1[index + length] * dis1.x * nlm2[index] + nlm1[index] * nlm2[index + length] * dis2.x);
372 stress[1]
373 += tmp * (nlm1[index + length] * dis1.y * nlm2[index] + nlm1[index] * nlm2[index + length] * dis2.y);
374 stress[2]
375 += tmp * (nlm1[index + length] * dis1.z * nlm2[index] + nlm1[index] * nlm2[index + length] * dis2.z);
376 stress[3] += tmp
377 * (nlm1[index + length * 2] * dis1.y * nlm2[index]
378 + nlm1[index] * nlm2[index + length * 2] * dis2.y);
379 stress[4] += tmp
380 * (nlm1[index + length * 2] * dis1.z * nlm2[index]
381 + nlm1[index] * nlm2[index + length * 2] * dis2.z);
382 stress[5] += tmp
383 * (nlm1[index + length * 3] * dis1.z * nlm2[index]
384 + nlm1[index] * nlm2[index + length * 3] * dis2.z);
385 }
386 }
387 dm_pointer += npol;
388 }
389 dm_pointer += (npol - 1) * col_indexes.size();
390 }
391 }
392}
393
394} // namespace hamilt
Definition sltk_grid_driver.h:20
int adj_num
Definition sltk_grid_driver.h:25
std::vector< ModuleBase::Vector3< double > > adjacent_tau
Definition sltk_grid_driver.h:28
std::vector< ModuleBase::Vector3< int > > box
Definition sltk_grid_driver.h:29
std::vector< int > ntype
Definition sltk_grid_driver.h:26
std::vector< int > natom
Definition sltk_grid_driver.h:27
Definition atom_spec.h:7
std::vector< int > iw2m
Definition atom_spec.h:18
std::vector< int > iw2l
Definition atom_spec.h:20
std::vector< int > iw2n
Definition atom_spec.h:19
3 elements vector
Definition vector3.h:22
T x
Definition vector3.h:24
T y
Definition vector3.h:25
T z
Definition vector3.h:26
Definition matrix.h:19
double * c
Definition matrix.h:25
static void tick(const std::string &class_name_in, const std::string &name_in)
Use twice at a time: the first time, set start_flag to false; the second time, calculate the time dur...
Definition timer.cpp:57
Definition parallel_orbitals.h:9
std::vector< int > get_indexes_col() const
Definition parallel_orbitals.cpp:154
int get_col_size() const
dimension getters for 2D-block-cyclic division of Hamiltonian matrix get_col_size() : total number of...
Definition parallel_orbitals.h:76
int get_row_size() const
Definition parallel_orbitals.h:77
std::vector< int > get_indexes_row() const
gather global indexes of orbitals in this processor get_indexes_row() : global indexes (~NLOCAL) of r...
Definition parallel_orbitals.cpp:140
const Input_para & inp
Definition parameter.h:26
Definition base_matrix.h:20
T * get_pointer() const
get pointer of value from a submatrix
Definition base_matrix.h:86
Definition dspin_lcao.h:20
Definition hcontainer.h:144
BaseMatrix< T > * find_matrix(int i, int j, int rx, int ry, int rz)
find BaseMatrix with atom index atom_i and atom_j and R index (rx, ry, rz) This interface can be used...
Definition hcontainer.cpp:261
const Parallel_Orbitals * get_paraV() const
get parallel orbital pointer to check parallel information
Definition hcontainer.h:192
Definition spin_constrain.h:24
static SpinConstrain & getScInstance()
Public method to access the Singleton instance.
Definition spin_constrain.cpp:12
const std::vector< ModuleBase::Vector3< int > > & get_constrain() const
get constrain
Definition spin_constrain.cpp:357
const std::vector< ModuleBase::Vector3< double > > & get_sc_lambda() const
get sc_lambda
Definition spin_constrain.cpp:344
void TITLE(const std::string &class_name, const std::string &function_name, const bool disable)
Definition tool_title.cpp:18
void reduce_all(T &object)
reduce in all process
Definition depend_mock.cpp:14
Definition hamilt.h:12
Parameter PARAM
Definition parameter.cpp:3
void filter_adjs(const std::vector< bool > &is_adj, AdjacentAtomInfo &adjs)
Definition sltk_grid_driver.cpp:67
double onsite_radius
radius of the sphere for onsite projection (Bohr)
Definition input_parameter.h:560