-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFermiDirac.f90
397 lines (324 loc) · 11.2 KB
/
FermiDirac.f90
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
subroutine Optimize_FD(iscf)
!.. Optimizes the total energy with respect to beta and mu
!.. under the constraint that the sum of occupations per spin is N/2
!..Global
use global; use orbocc, ONLY: occnum, ennat, beta_occ, xmu_occ
implicit none
!..Argument
integer :: iscf
!..Local
real(dp) :: sum_occ, sum_occ1, sum_occ2, Sgra, exp_mine
integer :: ia
!..FOR NAG MINIMIZATION
integer :: Nvar=2, nclin=0, ncnlin=1, lda=1, ldcj=1, ldr=2, iter, istate(3)
integer :: liwork=8, lwork=100, iuser(1), ifail
real(dp) :: A(1,1), BL(3), BU(3), C(1), cjac(1,2), clamda(3), objf, objgrd(2)
real(dp) :: R(2,2), X(2), iwork(8), work(100), user(1)
external :: E_OBJ_FERMI, confun_FD, e04uef, e04ucf !, e04djf, e04dgf
print*,'---------------------------------------------------------'
print*,'Minimizing occupations assuming Fermi Dirac distribution:'
!..Optional parameters for the NAG routine
!..Initialize mu and beta for first scf step
if(iscf == 1) then
! xmu_occ(1) = 0.5d0*(ennat(ibond(1)) + ennat(1+ibond(1)))
xmu_occ(1) = ennat(ibond(1))
Temperat= 1.e6_dp
beta_occ(1) = 1._dp/(Boltz_konst*Temperat)
Print*, 'Initial beta, mu:', beta_occ(1), xmu_occ(1)
endif
X(1) = beta_occ(1); X(2) = xmu_occ(1)
call E_OBJ_FERMI(2, 2, X, OBJF, OBJGRD, 0, IUSER, USER)
Sgra=0._dp
do ia=1,Nvar
Sgra = Sgra+ OBJGRD(ia)*OBJGRD(ia)
enddo
print*,'Sgra: ',Sgra
! if (Sgra > 1.d-8) then
print*,'Optimizing beta and mu...'
BL(1) = 1.d-10
BU(1) = 1d20
! BL(2) = ennat(ibond(1)-1)
! BU(2) = ennat(ibond(1)+2)
BL(2) = -1d20
BU(2) = 1d20
BL(3) = xnele(1)
BU(3) = xnele(1)
! e04ucf settings
! call e04uef("Derivative level = 3")
! call e04uef("Verify Level = 3")
! NOTE: difference interval *must* be smaller than 'rdmepsocc'
call e04uef("Difference interval = 1d-12")
call e04uef("Major Iteration Limit = 1000")
call e04uef("Minor Iteration Limit = 5000")
call e04uef("Central difference interval = 1d-5")
!..accuracy of solution
call e04uef("Function Precision = 1d-9")
call e04uef("Optimality Tolerance = 1d-8")
!..How accurately a variable should obey a constraint:
call e04uef("Feasibility Tolerance = 1d-12")
!..print level
call e04uef("Major print level = 0")
call e04uef("Minor print level = 0")
call e04ucf(Nvar, nclin, ncnlin, lda, ldcj, ldr, A, &
bl, bu, confun_FD, E_OBJ_FERMI, iter, istate, c, &
cjac, clamda, objf, objgrd, r, X, iwork, liwork, &
work, lwork, iuser, user, ifail)
beta_occ(1)=X(1)
xmu_occ(1)=X(2)
beta_occ(2)=X(1)
xmu_occ(2)=X(2)
do ia=1,nnatorb
occnum(ia,1) = 1._dp/(1._dp+exp_mine(X(1)*(ennat(ia)-X(2))))
occnum(ia,2) = occnum(ia,1)
occnum(ia,3) = occnum(ia,1)+occnum(ia,2)
enddo
! else
! print*,'Beta and mu already optimal no need to optimize further'
! endif
open(unit=121,file='n_vs_e',status='unknown')
do ia=1,nnatorb
write(121,'(2f25.10)')ennat(ia),occnum(ia,1)
enddo
close(121)
Temperat=1._dp/Boltz_konst/beta_occ(1)
print*,'Optimals:'
print*,'Effective T:', Temperat, 'Kelvin'
print*,'Effective beta:', beta_occ(1), '(a.u.)'
print*,'Effective mu:', xmu_occ(1), '(a.u.)'
print*,'-----------------------------------------------------'
sum_occ1=sum(occnum(:,1))
sum_occ2=sum(occnum(:,2))
sum_occ=sum_occ1+sum_occ2
print*,'Occupation numbers for spin up:'
write(6,'(5f12.8)')(occnum(ia,1),ia=1,nnatorb)
print*,'Mu for spin up (From Fermi Dirac):',xmu_occ(1)
print*,'Total spin up electrons: ', sum_occ1
print*,'Occupation numbers for spin down:'
write(6,'(5f12.8)')(occnum(ia,2),ia=1,nnatorb)
print*,'Mu for spin dn (From Fermi Dirac):',xmu_occ(2)
print*,'Total spin down electrons: ', sum_occ2
print*,'Total number of electrons: ', sum_occ
print*,'******************************************************'
end subroutine Optimize_FD
!-----------------------------------------------------------------------
subroutine E_OBJ_FERMI(MODE, N, X, OBJF, OBJGRD, NSTATE, IUSER, RUSER)
!..Global
use global; use functional_m; use energies; use orbocc
implicit none
!..Arguments
integer :: MODE, N, NSTATE, IUSER(*)
real(dp) :: X(N), OBJF, OBJGRD(N), RUSER(*)
!..Local
real(dp), allocatable :: occ(:,:), DE_Dn(:)
real(dp) :: dm, drvia, fcdrv, exp_mine, rdum
integer :: ia, i, idum
allocate (occ(lnnatorb,3), DE_Dn(lnnatorb))
idum=nstate; idum=iuser(1); rdum=ruser(1)
do ia=1,nnatorb
dm = X(1)*(ennat(ia)-X(2))
occ(ia,1) = 1._dp/(1._dp+exp_mine(dm))
occ(ia,1) = max(occ(ia,1),zero)
occ(ia,2) = occ(ia,1)
occ(ia,3) = occ(ia,1)+occ(ia,2)
enddo
call calc_occ_factors(occ, fac_h, fac_c, fac_e)
if (MODE == 0 .or. mode==2) then
call total_energy_MO(1)
OBJF = Tot_energy
endif
if (mode ==1 .or. mode==2) then
call Func_der_n_NAG(occ, DE_Dn, 1)
OBJGRD = 0._dp
do ia = 1,nnatorb
dm = exp_mine(X(1)*(ennat(ia)-X(2)))
drvia = dm/(1._dp+dm)**2
do i = 1,2
if(i == 1) then
fcdrv = (X(2) - ennat(ia))*drvia
else
fcdrv = X(1)*drvia
endif
OBJGRD(i) = OBJGRD(i) + 2._dp*fcdrv*DE_Dn(ia) !2 is for spin
enddo
enddo
endif
end subroutine E_OBJ_FERMI
!-----------------------------------------------------------------------
subroutine confun_FD(MODE,ncnln,N,LDCJ,needc,X,C,CJAC,nstate,iuser,ruser)
!..Global
use global; use functional_m; use energies; use orbocc
implicit none
!..Arguments
integer :: MODE, ncnln, n, ldcj, needc(ncnln), nstate, iuser(*)
real(dp) :: X(n), C(ncnln), CJAC(ldcj,n), ruser(*)
!..Local
real(dp) :: occ(lnnatorb)
real(dp) :: dm, drvia, exp_mine
integer :: ia
do ia=1,nnatorb
dm = exp_mine(X(1)*(ennat(ia)-X(2)))
occ(ia) = 1._dp/(1._dp+dm)
enddo
if(mode == 0 .or. mode == 2 ) then !Calculate c
if(needc(1) > 0) then
c(1) = 0._dp
do ia=1,nnatorb
c(1)=c(1) + occ(ia)
enddo
endif
endif
if(mode == 1 .or. mode == 2) then !calculate cjac
if(needc(1) > 0) then
cjac(1,1) = 0._dp; cjac(1,2) = 0._dp
do ia=1,nnatorb
dm = exp_mine(X(1)*(ennat(ia)-X(2)))
drvia = dm/(1._dp+dm)**2
cjac(1,1) = cjac(1,1) + (X(2) - ennat(ia))*drvia
cjac(1,2) = cjac(1,2) + X(1)*drvia
enddo
endif
endif
end subroutine confun_FD
!-----------------------------------------------------------------------
subroutine Fit_Fermi(iscf,xmu)
!..Given a set of occupation numbers occnum and a set of energies ennat
!..It fits a 2 Fermi functions (1 per spin) yielding the effecive beta and
!..mu per spin.
!..Global
use global; use orbocc, ONLY: occnum, ennat, beta_occ, xmu_occ
implicit none
!..Arguments
integer :: iscf
real(dp) :: xmu(2)
!..Local
real(dp) :: xin(2), Sgra, exp_mine
integer :: ia
!..FOR NAG MINIMIZATION
integer :: IWORK(3), IUSER(1), IFAIL, INFORM
real(dp) :: OBJF, OBJGRD(2), WORK(26), RUSER(1)
external :: OBJ_FERMI, e04djf, e04dgf
print*,'-----------------------------------------------------'
print*,'Effective Fermi Dirac Fitting:'
!..Optional parameters for the NAG routine
open(unit=8, file='optinals_nag', status='unknown')
write(8,'(a5)')'Begin'
write(8,*)' Print Level = 0'
write(8,*)' Optimality Tolerance = 1D-9'
write(8,*)' Verify = YES'
write(8,*)' Verify Level = 1'
write(8,'(a3)')'End'
rewind(8)
call e04djf(8,INFORM)
close(8)
!..Initialize mu and beta for first scf step
if(iscf == 1) then
xmu_occ=xmu
beta_occ(1) = log((1.d0/occnum(1+ibond(1),1))-1.d0) / (ennat(1+ibond(1))-xmu_occ(1))
endif
xin(1) = beta_occ(1); xin(2) = xmu_occ(1)
call OBJ_FERMI(2, 2, Xin, OBJF, OBJGRD, 0, IUSER, RUSER)
Sgra=0._dp
do ia=1,nnatorb
Sgra = Sgra+ OBJGRD(ia)*OBJGRD(ia)
enddo
print*,'Sgra: ',Sgra
if (Sgra > 1.e-8_dp) then
print*,'Optimizing beta and mu...'
! call e04dgf(2, OBJ_FERMI, ITER, OBJF, OBJGRD, Xin, IWORK, WORK, IUSER, RUSER, IFAIL)
beta_occ(1)=xin(1)
xmu_occ(1)=xin(2)
else
Print*,'Beta and mu already optimal no need to optimize further!'
endif
open(unit=121,file='n_vs_e',status='unknown')
do ia=1,nnatorb
write(121,'(2f25.10)')ennat(ia),occnum(ia,1)
enddo
close(121)
Temperat=1._dp/(Boltz_konst* beta_occ(1))
Temperat=1.e-3_dp
beta_occ(1) = 1.0_dp/(Boltz_konst*Temperat)
xmu_occ=xmu
do ia=1,nnatorb
occnum(ia,1) = 1.0_dp/(1.0_dp + exp_mine(beta_occ(1)*(ennat(ia)-xmu_occ(1))))
occnum(ia,2) = occnum(ia,1); occnum(ia,3)=2.0_dp*occnum(ia,1)
enddo
print*,'Effective T:', Temperat, 'Kelvin'
print*,'Effective beta:', beta_occ(1), '(a.u.)'
print*,'Effective mu:', xmu_occ(1), '(a.u.)'
print*,'-----------------------------------------------------'
end subroutine Fit_Fermi
!-----------------------------------------------------------------------
subroutine OBJ_FERMI(MODE, N, X, OBJF, OBJGRD, NSTATE, IUSER, RUSER)
!..Global
use global
implicit none
!..Arguments
integer :: MODE, N, NSTATE, IUSER(*)
real(dp) :: X(N), OBJF, OBJGRD(N), RUSER(*)
real(dp), external :: Fermi_LSQ
if (MODE == 0) then
OBJF = Fermi_LSQ(X)
elseif (MODE == 2) then
OBJF = Fermi_LSQ(X)
call DFermi_LSQ(X, OBJGRD)
else
stop 'OBJ_FERMI:wrong MODE'
endif
end subroutine OBJ_FERMI
!-----------------------------------------------------------------------
function Fermi_LSQ(xin)
!..Global
use global; use orbocc, ONLY: occnum, ennat
implicit none
!..Arguments
real(dp) :: xin(2)
!..The function
real(dp) :: Fermi_LSQ
!..Local
real(dp) :: sum_d, dm
integer :: ia
sum_d=0.0_dp
do ia=1,nnatorb
dm = xin(1)*(ennat(ia) - xin(2))
sum_d = sum_d + (occnum(ia,1) - 1.0_dp/(1.0_dp + exp(dm)))**2
enddo
Fermi_LSQ = sum_d
return
end function Fermi_LSQ
!-----------------------------------------------------------------------
subroutine DFermi_LSQ(xin, Der_x)
!..Global
use global; use orbocc, ONLY: occnum, ennat
implicit none
!..Arguments
real(dp) :: xin(2), Der_x(2)
!..Local
integer :: ia
real(dp) :: dm, ff
Der_x=0.0_dp
do ia=1,nnatorb
dm = xin(1)*(ennat(ia) - xin(2))
ff = (1.0_dp/(1.0_dp+exp(dm))-occnum(ia,1))/(cosh(dm)+1.0_dp)
Der_x(1) = Der_x(1) + ff*(xin(2)-ennat(ia))
Der_x(2) = Der_x(2) + ff*xin(1)
enddo
return
end subroutine DFermi_LSQ
!----------------------------------------------------------------------------
function Dn_De_FD(epsi)
! The derivative of Fermi dirac with respect to epsilon
!..Global
use global; use orbocc, ONLY: beta_occ, xmu_occ
implicit none
real(dp) :: Dn_De_FD, epsi
Dn_De_FD = -0.5_dp*beta_occ(1)/(cosh(beta_occ(1)*(epsi-xmu_occ(1))) + 1.0_dp)
end function Dn_De_FD
!----------------------------------------------------------------------------
function exp_mine(x)
use global
real(dp), intent(IN) :: x
real(dp) :: exp_mine, y
y=min(max(x,-200.0_dp),200.0_dp)
exp_mine=exp(y)
end function exp_mine