Merge lp:~albertog/siesta/trunk-nlso into lp:siesta

Proposed by Alberto Garcia
Status: Merged
Merged at revision: 700
Proposed branch: lp:~albertog/siesta/trunk-nlso
Merge into: lp:siesta
Diff against target: 470 lines (+167/-43)
7 files modified
Src/Makefile (+2/-1)
Src/atom.F (+1/-1)
Src/nlefsm.f (+128/-35)
Src/read_options.F90 (+12/-1)
Src/siesta_options.F90 (+1/-0)
Src/write_subs.F (+22/-4)
version.info (+1/-1)
To merge this branch: bzr merge lp:~albertog/siesta/trunk-nlso
Reviewer Review Type Date Requested Status
Nick Papior Approve
Review via email: mp+347385@code.launchpad.net

Commit message

Implement option to avoid the splitting of the SR and SO contributions in SO 'offsite' calculations.

The recipe for splitting of SR and SO contributions when full lj projectors are used in a SOC calculation is quite fragile. It mostly works when the projectors are computed from semilocal potentials in a .psf file, but even then there might be problems when semicore states (and thus multiple projectors) are used.

An option _not_ to perform the splitting is implemented by this patch. If the line

    soc-split-sr-so F

is included in the fdf file, the program will avoid the split code in nlefsm_SO_off, will
assign to Enl the full lj contribution (done at the time of printing in write_subs), and
will set Eso to zero. New tags 'Enl(+so)' and 'Eso(nil)' are used for printing.

A new 'option' line is printing in the output file, and a new 'Split-SR-SO' parameter added to the CML file.

For generality, the same option is available for SO+onsite calculations.

To post a comment you must log in.
lp:~albertog/siesta/trunk-nlso updated
701. By Alberto Garcia

Sync to trunk-699

Revision history for this message
Alberto Garcia (albertog) wrote :

Sorry. I had forgotten to sync to trunk...

I should add that this patch is really needed for the PSML work, as there the projectors might not come from a simple semilocal source.

Revision history for this message
Nick Papior (nickpapior) wrote :

The code looks good to me. However, having looked closer at the code there are some points I particularly don't like. E.g. nint on 0.5, the facpm as you mention.

In general these routines could do with a careful re-coding. I would e.g. be cautious on non-IEEE standard optimizations and this code? Does it actually do the right thing?

review: Approve
lp:~albertog/siesta/trunk-nlso updated
702. By Alberto Garcia

Safer arithmetic and more comments in nlefsm_SO_off

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Src/Makefile'
2--- Src/Makefile 2018-05-30 09:15:30 +0000
3+++ Src/Makefile 2018-06-07 08:38:56 +0000
4@@ -1136,7 +1136,8 @@
5 new_matel.o: alloc.o errorf.o interpolation.o matel_registry.o parallel.o
6 new_matel.o: precision.o radfft.o spher_harm.o sys.o
7 nlefsm.o: alloc.o atm_types.o atmfuncs.o atomlist.o chemical.o mneighb.o
8-nlefsm.o: new_matel.o parallel.o parallelsubs.o precision.o sparse_matrices.o
9+nlefsm.o: new_matel.o parallel.o parallelsubs.o precision.o siesta_options.o
10+nlefsm.o: sparse_matrices.o
11 normalize_dm.o: atomlist.o m_mpi_utils.o m_spin.o parallel.o precision.o
12 normalize_dm.o: siesta_options.o sparse_matrices.o sys.o
13 obc.o: alloc.o precision.o
14
15=== modified file 'Src/atom.F'
16--- Src/atom.F 2018-04-26 13:23:03 +0000
17+++ Src/atom.F 2018-06-07 08:38:56 +0000
18@@ -3136,7 +3136,7 @@
19 if (lj_projs .and. l>0 ) then
20 ! Set up the appropriate potential and label
21 if (jk == 1) then
22- ! V(l-j/2) = Vdn - (l+1)/2 Vup = Vion - (l+1)/2 Vso
23+ ! V(l-1/2) = Vdn - (l+1)/2 Vup = Vion - (l+1)/2 Vso
24 vp(:) = vps(:,l) - 0.5_dp*(l+1)*vps_u(:,l)
25 jstr = 'j-'
26 else
27
28=== modified file 'Src/nlefsm.f'
29--- Src/nlefsm.f 2018-04-24 14:13:24 +0000
30+++ Src/nlefsm.f 2018-06-07 08:38:56 +0000
31@@ -15,6 +15,7 @@
32
33 private
34
35+
36 CONTAINS
37
38 subroutine nlefsm( scell, nua, na, isa, xa, indxua,
39@@ -515,7 +516,10 @@
40 use m_new_matel, only : new_matel
41 use atm_types, only: species_info, species
42 use sparse_matrices, only: Dscf, xijo
43-
44+ use siesta_options, only: split_sr_so
45+
46+ use fdf
47+
48 integer, intent(in) ::
49 . maxnh, na, maxnd, nspin, nua
50
51@@ -568,14 +572,15 @@
52 logical, dimension(:), pointer :: listed, listedall
53
54 complex(dp) :: E_offsiteSO(4)
55-
56+
57 type(species_info), pointer :: spp
58
59 integer :: nd, ndn
60+ real(dp) :: Vit_saved
61
62 C ------------------------------------------------------------
63
64-C Start time counte
65+C Start time counter
66 call timer( 'nlefsm', 1 )
67
68 C Find unit cell volume
69@@ -659,11 +664,16 @@
70 C Initialize neighb subroutine
71 call mneighb( scell, rmax, na, xa, 0, 0, nna )
72
73- nd= 0; ndn= 0
74- Enl = 0.0d0; E_offsiteSO(1:4)=dcmplx(0.0d0,0.0d0)
75+ nd= 0
76+ ndn= 0
77+ Enl = 0.0d0
78+ E_offsiteSO(1:4)=dcmplx(0.0d0,0.0d0)
79 Enl_offsiteSO = 0.0d0
80 C Loop on atoms with KB projectors
81- do ka = 1,na ! Supercell atoms
82+
83+ ! Use info from hsparse to remove "far" atoms, as in nlefsm
84+
85+ do ka = 1,na ! Supercell atoms
86 kua = indxua(ka) ! Equivalent atom in the UC
87 ks = isa(ka) ! Specie index of atom ka
88 nkb = lastkb(ka) - lastkb(ka-1) ! number of KB projs of atom ka
89@@ -673,12 +683,16 @@
90
91 C Find neighbour orbitals
92 Ski(:,:) = 0.0_dp
93- nno = 0; ; iano(:)=0; iono(:)=0
94+ nno = 0
95+ iano(:)=0
96+ iono(:)=0
97 do ina = 1,nna ! Neighbour atoms
98 ia = iana(ina) ! Atom index of ina (the neighbour to ka)
99 is = isa(ia) ! Specie index of atom ia
100 rki = sqrt(r2ki(ina)) ! Square distance
101-
102+
103+ ! Use this to further filter
104+ !! if (rki - rkbmax(ks) - rorbmax(is) > 0.d0) CYCLE
105 do io = lasto(ia-1)+1,lasto(ia) ! Orbitals of atom ia
106
107 C Only calculate if needed locally
108@@ -756,7 +770,8 @@
109 do ispin = 1,min(2,nspin) ! Only diagonal parts
110 Di(jo) = Di(jo) + Dscf(ind,ispin)
111 enddo
112- Ds(1,1,jo) = dcmplx(Dscf(ind,1), Dscf(ind,5)) ! D(ju,iu)
113+ ! Should we *add to* Ds, as above for Di?
114+ Ds(1,1,jo) = dcmplx(Dscf(ind,1), Dscf(ind,5)) ! D(ju,iu)
115 Ds(2,2,jo) = dcmplx(Dscf(ind,2), Dscf(ind,6)) ! D(jd,id)
116 Ds(1,2,jo) = dcmplx(Dscf(ind,3), Dscf(ind,4)) ! D(ju,id)
117 Ds(2,1,jo) = dcmplx(Dscf(ind,7),-Dscf(ind,8)) ! D(jd,iu)
118@@ -789,10 +804,24 @@
119 c----------- Compute Vion
120 if ( l.eq.0 ) then
121 epsk(1) = epskb(ks,koa)
122- Vit = epsk(1) * Ski(koa,ino) * Ski(koa,jno)
123+ Vit_saved = epsk(1) * Ski(koa,ino) * Ski(koa,jno)
124+ if (split_sr_so) then
125+ Vit = Vit_saved
126+ else
127+ ! Move 'ionic' part to V_so diagonal
128+ V_so(1,1,jo)= V_so(1,1,jo) + Vit_saved
129+ V_so(2,2,jo)= V_so(2,2,jo) + Vit_saved
130+ Vit = 0.0_dp
131+ endif
132 Vi(jo) = Vi(jo) + Vit
133 if (.not. matrix_elements_only) then
134- Enl = Enl + Di(jo) * Vit
135+ if (split_sr_so) then
136+ Enl = Enl + Di(jo) * Vit
137+ else
138+ ! Move energy contribution to "Eso"
139+ E_offsiteSO(1) = E_offsiteSO(1) + Vit_saved*Ds(1,1,jo)
140+ E_offsiteSO(2) = E_offsiteSO(2) + Vit_saved*Ds(2,2,jo)
141+ endif
142 CVj = epsk(1) * Ski(koa,jno)
143 Cijk = 2.0_dp * Di(jo) * CVj
144 do ix = 1,3
145@@ -808,15 +837,18 @@
146 ko = ko + 1
147
148 c----------- Compute Vion from j+/-1/2 and V_so
149- else
150+ else ! l /= 0
151+ ! First proj of l-1/2 block
152 koa1 = -iphKB(ko+1)
153+ ! Last proj of l+1/2 block
154 koa2 = -iphKB(ko+2*(2*l+1))
155 epsk(1) = epskb(ks,koa1)
156 epsk(2) = epskb(ks,koa2)
157
158 call calc_Vj_offsiteSO( l, epsk, Ski(koa1:koa2,ino),
159 & Ski(koa1:koa2,jno), grSki(:,koa1:koa2,ino),
160- & grSki(:,koa1:koa2,jno), Vit, V_sot, F_so )
161+ & grSki(:,koa1:koa2,jno), Vit, V_sot, F_so,
162+ & split_sr_so)
163 Vi(jo) = Vi(jo) + Vit
164 V_so(1:2,1:2,jo)= V_so(1:2,1:2,jo) + V_sot(1:2,1:2)
165
166@@ -843,7 +875,7 @@
167 enddo
168 enddo
169 endif
170- ko = ko+2*(2*l+1)
171+ ko = ko+2*(2*l+1) ! Point to next group of l-/+ 1/2 blocks
172 endif
173 if ( ko.ge.lastkb(ka) ) exit KB_loop
174 enddo KB_loop
175@@ -906,10 +938,21 @@
176 c
177 c-----------------------------------------------------------------------
178 subroutine calc_Vj_offsiteSO( l, epskb, Ski, Skj, grSki, grSkj,
179- & V_ion, V_so, F_so )
180+ & V_ion, V_so, F_so, split_sr_so )
181
182 implicit none
183
184+ ! Constructs the NL operator from the l+/- 1/2 information, which
185+ ! is passed in two blocks. Hence the re-dimensioning of the dummy
186+ ! arguments:
187+ ! Ski(-l:l,2) means that there are '2' blocks of 2l+1 values
188+ ! and so on.
189+ ! The two epskb values correspond to the l-1/2 and l+1/2 blocks,
190+ ! respectively (all the projectors in a block have the same value)
191+
192+ ! On output, V_so and V_sr (V_ion) are separated, unless split_sr_so is .false.
193+ ! The force contributions are not separated
194+
195 integer , intent(in) :: l
196 real(dp) , intent(in) :: epskb(2)
197 real(dp) , intent(in) :: Ski(-l:l,2), Skj(-l:l,2)
198@@ -917,17 +960,17 @@
199 real(dp) , intent(out) :: V_ion
200 complex(dp) , intent(out) :: F_so(3,2,2)
201 complex(dp) , intent(out) :: V_so(2,2)
202-
203-
204- integer :: J, ij, imj, m, is
205+ logical , intent(in) :: split_sr_so
206+
207+ integer :: J, ij, imj, m, is, facpm
208 real(dp) :: aj, amj, al, a2l1, fac, facm,
209- & epskpm, V_iont, cp, cm, facpm
210+ & epskpm, V_iont, cp, cm
211
212 real(dp) :: cg(2*(2*l+1),2)
213 complex(dp):: u(-l:l,-l:l)
214 complex(dp):: SVi(2), SVj(2), grSVi(3,2)
215
216- external :: die
217+ external :: die, message
218 c-----------------------------------------------------------------------
219
220 c---- set constants and factors
221@@ -935,14 +978,29 @@
222 a2l1 = dble( 2*l+1 )
223
224 c---- load Clebsch-Gordan coefficients; cg(J,+-)
225+ !
226+ ! NOTE that this code will not work for l=0, since in this case
227+ ! there is a single j subspace (j=0.5)
228+ if ( l == 0 ) then
229+ call die("Code in calc_Vj_offsiteSO does not work for l=0")
230+ endif
231+
232 J = 0
233 cg(:,:) = 0.0_dp
234+
235+ ! ij ranges over the two j subspaces
236+ ! The loop could have used: 'do ik = -1,1,2' for easier reading
237+
238 do ij = 1, 2
239 aj = al + (2*ij-3)*0.5d0 ! j(ij=1)=l-1/2; j(ij=2)=l+1/2
240- facpm= (-1.0d0)**(aj-al-0.5d0) ! +/- sign
241- do imj = 1, nint(2*aj)+1 ! Degeneracy for j
242- amj = -aj + dfloat(imj-1) ! mj value
243- J = J+1 ! (j,mj) index
244+
245+ ! This way of computing a sign is very fragile. Better below (and integer)
246+ !facpm= (-1.0d0)**(aj-al-0.5d0) ! +/- sign: j=l-1/2: (-1)**(-1)=-1 ; j = l+1/2: (-1)**0 = 1
247+ facpm = 2*ij - 3
248+
249+ do imj = 1, nint(2*aj)+1 ! Degeneracy for j: 2j+1. Safe nint as aj is always half integer
250+ amj = -aj + (imj-1) ! mj value: -j, -j+1, ..., j-1, j
251+ J = J+1 ! Combined (j,mj) index (great notation!)
252
253 cp = sqrt( (al+0.5d0+amj)/a2l1 )
254 cm = sqrt( (al+0.5d0-amj)/a2l1 )
255@@ -969,26 +1027,28 @@
256 enddo
257
258 c---- Load V_so
259- V_so= cmplx(0.0d0,0.0d0); F_so= cmplx(0.0d0,0.0d0)
260+ V_so= cmplx(0.0d0,0.0d0)
261+ F_so= cmplx(0.0d0,0.0d0)
262 J = 0
263 do ij = 1, 2
264 aj = al + (2*ij-3)*0.5d0 ! j value
265 do imj = 1, nint(2*aj)+1 ! Degeneracy for j
266- amj = -aj + dfloat(imj-1) ! mj value
267- J = J+1 ! (j,mj) index
268+ amj = -aj + imj-1 ! mj value
269+ J = J+1 ! Combined (j,mj) index
270
271- SVi(1:2)= cmplx(0.0d0,0.0d0); SVj(1:2)= cmplx(0.0d0,0.0d0)
272+ SVi(1:2)= cmplx(0.0d0,0.0d0)
273+ SVj(1:2)= cmplx(0.0d0,0.0d0)
274 grSVi(1:3,1:2)= cmplx(0.0d0,0.0d0)
275 do is = 1, 2 ! spin loop
276
277-c select correct m
278+ ! select correct m. Safe nint as amj is always a half-integer
279 if ( is.eq.1 ) then
280 m = nint(amj-0.5d0) ! up => m=mj-1/2
281 else
282 m = nint(amj+0.5d0) ! down => m=mj+1/2
283 endif
284
285- if ( iabs(m).le.l ) then
286+ if ( abs(m).le.l ) then
287 SVi(is)= Ski(+M,ij)*u(+m,M)
288 SVj(is)= Skj(+M,ij)*u(+m,M)
289 grSVi(1:3,is)= grSki(1:3,+M,ij)*u(+m,M)
290@@ -1005,6 +1065,8 @@
291 endif
292 enddo ! is
293
294+ ! Note that these involve just one epskb at a time.
295+
296 c up-up = <i,+|V,J><V,J|j,+>
297 V_so(1,1) = V_so(1,1) + SVi(1) * epskb(ij) * conjg(SVj(1))
298 F_so(:,1,1)= F_so(:,1,1)+ grSVi(:,1) * epskb(ij) * conjg(SVj(1))
299@@ -1031,10 +1093,36 @@
300 call die('calc_Vj_LS: ERROR')
301 endif
302
303-c---- substract out V_ion
304- epskpm = sqrt( epskb(1)*epskb(2) )
305- epskpm = sign(epskpm,epskb(1))
306-
307+ if (split_sr_so) then
308+
309+ ! Attempt to get SR and SO parts of the total lj non-local contribution
310+ ! (Note that there is an extra contribution to SR coming from l=0, computed
311+ ! directly in the caller routine)
312+
313+ ! This is some kind of average, following Hemstreet, of the l+/- 1/2
314+ ! components. (In contrast to Hamann's more involved procedure)
315+
316+ ! v_sr = ( (l+1) v_j+ + l v_j- ) / (2l+1)
317+ ! and the v_j+ and v_j- are "square roots" of the KB projectors
318+
319+ !! epskpm = sqrt( epskb(1)*epskb(2) ) ! This sqrt should be guarded with an abs()
320+ !! epskpm = sign(epskpm,epskb(1)) ! The value of epskpm with the sign of epskb(1) WHY?
321+
322+ ! This is the only acceptable solution from symmetry arguments
323+ ! (and within the fragility of the approach)
324+ epskpm = sqrt(abs(epskb(1)*epskb(2)))
325+ if (epskb(1)*epskb(2) > 0) then
326+ ! same sign
327+ epskpm = epskpm * sign(1.0_dp,epskb(1))
328+ else
329+ ! different sign: this possibility was not taken into account in previous versions,
330+ ! and probably implies that the Hemstreet ansatz is ill-defined
331+ ! We should flag this instead of accepting this heuristic blindly.
332+ epskpm = - epskpm
333+ call message("WARNING",
334+ $ "The Enl-Eso split in energies might not be accurate")
335+ endif
336+
337 V_ion = 0.0d0
338 do M = -l, l
339 V_iont = ( l**2 * Ski(M,1)*epskb(1)*Skj(M,1)
340@@ -1044,10 +1132,15 @@
341 V_ion = V_ion + V_iont
342 enddo
343
344+ !---- substract out V_ion from V_so
345 V_so(1,1) = V_so(1,1) - cmplx(1.0d0,0.0d0)*V_ion
346 V_so(2,2) = V_so(2,2) - cmplx(1.0d0,0.0d0)*V_ion
347
348- return
349+ else
350+ !---- Keep all the NL contribution in V_so
351+ V_ion = 0.0_dp
352+ endif
353+
354 end subroutine calc_Vj_offsiteSO
355
356 end module m_nlefsm
357
358=== modified file 'Src/read_options.F90'
359--- Src/read_options.F90 2018-05-15 13:01:22 +0000
360+++ Src/read_options.F90 2018-06-07 08:38:56 +0000
361@@ -27,7 +27,7 @@
362 use units, only : eV, Ang, Kelvin
363 use siesta_cml
364 use m_target_stress, only: set_target_stress
365- use m_spin, only: print_spin_options
366+ use m_spin, only: print_spin_options, spin
367
368 use m_charge_add, only : read_charge_add
369 use m_hartree_add, only : read_hartree_add
370@@ -843,6 +843,17 @@
371 units='siestaUnits:eSpin' )
372 endif
373
374+ ! For SOC calculations: If .false., Enl will contain
375+ ! the SO part of the energy.
376+ if (spin%SO) then
377+ split_sr_so = fdf_get('SOC.Split.SR.SO',.true.)
378+ write(6,1) 'redata: Split SR and SO contributions', split_sr_so
379+ if (cml_p) then
380+ call cmlAddParameter( xf=mainXML, name='Split-SR-SO', &
381+ value=split_sr_so, dictref='siesta:split_sr_so' )
382+ endif
383+ endif
384+
385 ! Order-N solution parameters ...
386 ! Maximum number of CG minimization iterations
387 ncgmax = fdf_get('ON.MaxNumIter',1000)
388
389=== modified file 'Src/siesta_options.F90'
390--- Src/siesta_options.F90 2018-05-15 13:01:22 +0000
391+++ Src/siesta_options.F90 2018-06-07 08:38:56 +0000
392@@ -126,6 +126,7 @@
393 logical :: muldeb ! Write Mulliken populations at every SCF step?
394 logical :: spndeb ! Write spin-polarization information at every SCF step?
395 logical :: orbmoms ! Write orbital moments?
396+ logical :: split_sr_so ! Cosmetic: split full lj NL energies into SR and SO parts
397
398 ! Convergence options
399 logical :: converge_FreeE ! free Energy conv. to finish SCF iteration?
400
401=== modified file 'Src/write_subs.F'
402--- Src/write_subs.F 2018-04-19 10:08:47 +0000
403+++ Src/write_subs.F 2018-06-07 08:38:56 +0000
404@@ -304,7 +304,8 @@
405 real(dp), intent(in) :: dHmax ! Max. change in H elements
406
407 character(len=64) :: fmt
408- character(len=6) :: scf_name
409+ character(len=6) :: scf_name
410+ character(len=17) :: enl_str, eso_str
411
412 logical :: first_scf_step
413 integer :: i
414@@ -313,6 +314,16 @@
415 if ( TSrun ) then
416 scf_name = 'ts-scf'
417 end if
418+
419+ if (spin%SO .and. (.not. split_sr_so)) then
420+ Enl = Enl + Eso
421+ Eso = 0.0_dp
422+ enl_str = 'siesta: Enl(+so)='
423+ eso_str = 'siesta: Eso(nil)='
424+ else
425+ enl_str = 'siesta: Enl ='
426+ eso_str = 'siesta: Eso ='
427+ endif
428
429 first_scf_step = (iscf == 1)
430 ! Only print out full decomposition at very beginning and end.
431@@ -323,8 +334,8 @@
432 . 'siesta: Eions =', Eions/eV,
433 . 'siesta: Ena =', Ena/eV,
434 . 'siesta: Ekin =', Ekin/eV,
435- . 'siesta: Enl =', Enl/eV,
436- . 'siesta: Eso =', Eso/eV,
437+ . enl_str, Enl/eV,
438+ . eso_str, Eso/eV,
439 . 'siesta: Eldau =', Eldau/eV,
440 . 'siesta: DEna =', DEna/eV,
441 . 'siesta: DUscf =', DUscf/eV,
442@@ -496,13 +507,20 @@
443
444 else !final
445 ! Print out additional information in finalization.
446+
447+ if (spin%SO .and. (.not. split_sr_so)) then
448+ eso_str = ' Eso(nil) ='
449+ else
450+ eso_str = ' Eso ='
451+ endif
452+
453 write(6,'(/,a)') 'siesta: Final energy (eV):'
454 write(6,'(a,a15,f15.6)')
455 . 'siesta: ', 'Band Struct. =', Ebs/eV,
456 . 'siesta: ', 'Kinetic =', Ekin/eV,
457 . 'siesta: ', 'Hartree =', Uscf/eV,
458 . 'siesta: ', 'Eldau =', Eldau/eV,
459- . 'siesta: ', 'Eso =', Eso/eV,
460+ . 'siesta: ', eso_str , Eso/eV,
461 . 'siesta: ', 'Ext. field =', DUext/eV,
462 . 'siesta: ', 'Enegf =', DE_NEGF/eV,
463 . 'siesta: ', 'Exch.-corr. =', Exc/eV,
464
465=== modified file 'version.info'
466--- version.info 2018-05-30 09:15:30 +0000
467+++ version.info 2018-06-07 08:38:56 +0000
468@@ -1,1 +1,1 @@
469-trunk-699
470+trunk-699--nlso-702

Subscribers

People subscribed via source and target branches