(git:374b731)
Loading...
Searching...
No Matches
qs_mo_occupation.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Set occupation of molecular orbitals
10!> \par History
11!> - set_mo_occupation subroutines moved from qs_mo_types (11.12.2014 MI)
12!> \author MI
13! **************************************************************************************************
14
16
18 USE fermi_utils, ONLY: fermifixed,&
23 USE kahan_sum, ONLY: accurate_sum
24 USE kinds, ONLY: dp
25 USE qs_mo_types, ONLY: get_mo_set,&
30 USE util, ONLY: sort
31 USE xas_env_types, ONLY: get_xas_env,&
33#include "./base/base_uses.f90"
34
35 IMPLICIT NONE
36
37 PRIVATE
38
39 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_mo_occupation'
40
41 PUBLIC :: set_mo_occupation
42
44 MODULE PROCEDURE set_mo_occupation_1, set_mo_occupation_2
45 END INTERFACE
46
47CONTAINS
48
49! **************************************************************************************************
50!> \brief Occupation for smeared spin polarized electronic structures
51!> with relaxed multiplicity
52!>
53!> \param mo_array ...
54!> \param smear ...
55!> \date 10.03.2011 (MI)
56!> \author MI
57!> \version 1.0
58! **************************************************************************************************
59 SUBROUTINE set_mo_occupation_3(mo_array, smear)
60
61 TYPE(mo_set_type), DIMENSION(2), INTENT(INOUT) :: mo_array
62 TYPE(smear_type), POINTER :: smear
63
64 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_3'
65
66 INTEGER :: all_nmo, handle, homo_a, homo_b, i, &
67 lfomo_a, lfomo_b, nmo_a, nmo_b, &
68 xas_estate
69 INTEGER, ALLOCATABLE, DIMENSION(:) :: all_index
70 LOGICAL :: is_large
71 REAL(KIND=dp) :: all_nelec, kts, mu, nelec_a, nelec_b, &
72 occ_estate
73 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: all_eigval, all_occ
74 REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b, occ_a, occ_b
75
76 CALL timeset(routinen, handle)
77
78 NULLIFY (eigval_a, eigval_b, occ_a, occ_b)
79 CALL get_mo_set(mo_set=mo_array(1), nmo=nmo_a, eigenvalues=eigval_a, &
80 occupation_numbers=occ_a)
81 CALL get_mo_set(mo_set=mo_array(2), nmo=nmo_b, eigenvalues=eigval_b, &
82 occupation_numbers=occ_b)
83 all_nmo = nmo_a + nmo_b
84 ALLOCATE (all_eigval(all_nmo))
85 ALLOCATE (all_occ(all_nmo))
86 ALLOCATE (all_index(all_nmo))
87
88 all_eigval(1:nmo_a) = eigval_a(1:nmo_a)
89 all_eigval(nmo_a + 1:all_nmo) = eigval_b(1:nmo_b)
90
91 CALL sort(all_eigval, all_nmo, all_index)
92
93 xas_estate = -1
94 occ_estate = 0.0_dp
95
96 nelec_a = 0.0_dp
97 nelec_b = 0.0_dp
98 all_nelec = 0.0_dp
99 nelec_a = accurate_sum(occ_a(:))
100 nelec_b = accurate_sum(occ_b(:))
101 all_nelec = nelec_a + nelec_b
102
103 DO i = 1, all_nmo
104 IF (all_index(i) <= nmo_a) THEN
105 all_occ(i) = occ_a(all_index(i))
106 ELSE
107 all_occ(i) = occ_b(all_index(i) - nmo_a)
108 END IF
109 END DO
110
111 CALL fermifixed(all_occ, mu, kts, all_eigval, all_nelec, &
112 smear%electronic_temperature, 1._dp, xas_estate, occ_estate)
113
114 is_large = abs(maxval(all_occ) - 1.0_dp) > smear%eps_fermi_dirac
115 ! this is not a real problem, but the temperature might be a bit large
116 IF (is_large) &
117 cpwarn("Fermi-Dirac smearing includes the first MO")
118
119 is_large = abs(minval(all_occ)) > smear%eps_fermi_dirac
120 IF (is_large) &
121 CALL cp_warn(__location__, &
122 "Fermi-Dirac smearing includes the last MO => "// &
123 "Add more MOs for proper smearing.")
124
125 ! check that the total electron count is accurate
126 is_large = (abs(all_nelec - accurate_sum(all_occ(:))) > smear%eps_fermi_dirac*all_nelec)
127 IF (is_large) &
128 cpwarn("Total number of electrons is not accurate")
129
130 DO i = 1, all_nmo
131 IF (all_index(i) <= nmo_a) THEN
132 occ_a(all_index(i)) = all_occ(i)
133 eigval_a(all_index(i)) = all_eigval(i)
134 ELSE
135 occ_b(all_index(i) - nmo_a) = all_occ(i)
136 eigval_b(all_index(i) - nmo_a) = all_eigval(i)
137 END IF
138 END DO
139
140 nelec_a = accurate_sum(occ_a(:))
141 nelec_b = accurate_sum(occ_b(:))
142
143 DO i = 1, nmo_a
144 IF (occ_a(i) < 1.0_dp) THEN
145 lfomo_a = i
146 EXIT
147 END IF
148 END DO
149 DO i = 1, nmo_b
150 IF (occ_b(i) < 1.0_dp) THEN
151 lfomo_b = i
152 EXIT
153 END IF
154 END DO
155 homo_a = lfomo_a - 1
156 DO i = nmo_a, lfomo_a, -1
157 IF (occ_a(i) > smear%eps_fermi_dirac) THEN
158 homo_a = i
159 EXIT
160 END IF
161 END DO
162 homo_b = lfomo_b - 1
163 DO i = nmo_b, lfomo_b, -1
164 IF (occ_b(i) > smear%eps_fermi_dirac) THEN
165 homo_b = i
166 EXIT
167 END IF
168 END DO
169
170 CALL set_mo_set(mo_set=mo_array(1), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_a, &
171 lfomo=lfomo_a, homo=homo_a, uniform_occupation=.false.)
172 CALL set_mo_set(mo_set=mo_array(2), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_b, &
173 lfomo=lfomo_b, homo=homo_b, uniform_occupation=.false.)
174
175 CALL timestop(handle)
176
177 END SUBROUTINE set_mo_occupation_3
178
179! **************************************************************************************************
180!> \brief Prepare an occupation of alpha and beta MOs following an Aufbau
181!> principle, i.e. allowing a change in multiplicity.
182!> \param mo_array ...
183!> \param smear ...
184!> \param eval_deriv ...
185!> \param tot_zeff_corr ...
186!> \date 25.01.2010 (MK)
187!> \par History
188!> 10.2019 Added functionality to adjust mo occupation if the core
189!> charges are changed via CORE_CORRECTION during surface dipole
190!> calculation. Total number of electrons matches the total core
191!> charges if tot_zeff_corr is non-zero. Not yet implemented for
192!> OT type method. [Soumya Ghosh]
193!> \author Matthias Krack (MK)
194!> \version 1.0
195! **************************************************************************************************
196 SUBROUTINE set_mo_occupation_2(mo_array, smear, eval_deriv, tot_zeff_corr)
197
198 TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT) :: mo_array
199 TYPE(smear_type), POINTER :: smear
200 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
201 REAL(KIND=dp), OPTIONAL :: tot_zeff_corr
202
203 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_2'
204
205 INTEGER :: handle, i, lumo_a, lumo_b, &
206 multiplicity_new, multiplicity_old, &
207 nelec
208 REAL(KIND=dp) :: nelec_f, threshold
209 REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b
210
211 CALL timeset(routinen, handle)
212
213 ! Fall back for the case that we have only one MO set
214 IF (SIZE(mo_array) == 1) THEN
215 IF (PRESENT(eval_deriv)) THEN
216! Change of MO occupancy to account for CORE_CORRECTION is not yet implemented
217 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
218 ELSE
219 IF (PRESENT(tot_zeff_corr)) THEN
220 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
221 ELSE
222 CALL set_mo_occupation_1(mo_array(1), smear=smear)
223 END IF
224 END IF
225 CALL timestop(handle)
226 RETURN
227 END IF
228
229 IF (smear%do_smear) THEN
230 IF (smear%fixed_mag_mom < 0.0_dp) THEN
231 IF (PRESENT(tot_zeff_corr)) THEN
232 CALL cp_warn(__location__, &
233 "CORE_CORRECTION /= 0.0 might cause the cell to charge up "// &
234 "that will lead to application of different background "// &
235 "correction compared to the reference system. "// &
236 "Use FIXED_MAGNETIC_MOMENT >= 0.0 if using SMEAR keyword "// &
237 "to correct the electron density")
238 END IF
239 IF (smear%fixed_mag_mom /= -1.0_dp) THEN
240 cpassert(.NOT. (PRESENT(eval_deriv)))
241 CALL set_mo_occupation_3(mo_array, smear=smear)
242 CALL timestop(handle)
243 RETURN
244 END IF
245 ELSE
246 nelec_f = mo_array(1)%n_el_f + mo_array(2)%n_el_f
247 IF (abs((mo_array(1)%n_el_f - mo_array(2)%n_el_f) - smear%fixed_mag_mom) > smear%eps_fermi_dirac*nelec_f) THEN
248 mo_array(1)%n_el_f = nelec_f/2.0_dp + smear%fixed_mag_mom/2.0_dp
249 mo_array(2)%n_el_f = nelec_f/2.0_dp - smear%fixed_mag_mom/2.0_dp
250 END IF
251 cpassert(.NOT. (PRESENT(eval_deriv)))
252 IF (PRESENT(tot_zeff_corr)) THEN
253 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
254 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
255 ELSE
256 CALL set_mo_occupation_1(mo_array(1), smear=smear)
257 CALL set_mo_occupation_1(mo_array(2), smear=smear)
258 END IF
259 END IF
260 END IF
261
262 IF (.NOT. ((mo_array(1)%flexible_electron_count > 0.0_dp) .AND. &
263 (mo_array(2)%flexible_electron_count > 0.0_dp))) THEN
264 IF (PRESENT(eval_deriv)) THEN
265 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
266 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
267 ELSE
268 IF (PRESENT(tot_zeff_corr)) THEN
269 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
270 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
271 ELSE
272 CALL set_mo_occupation_1(mo_array(1), smear=smear)
273 CALL set_mo_occupation_1(mo_array(2), smear=smear)
274 END IF
275 END IF
276 CALL timestop(handle)
277 RETURN
278 END IF
279
280 nelec = mo_array(1)%nelectron + mo_array(2)%nelectron
281
282 multiplicity_old = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
283
284 IF (mo_array(1)%nelectron >= mo_array(1)%nmo) &
285 CALL cp_warn(__location__, &
286 "All alpha MOs are occupied. Add more alpha MOs to "// &
287 "allow for a higher multiplicity")
288 IF ((mo_array(2)%nelectron >= mo_array(2)%nmo) .AND. (mo_array(2)%nelectron /= mo_array(1)%nelectron)) &
289 CALL cp_warn(__location__, "All beta MOs are occupied. Add more beta MOs to "// &
290 "allow for a lower multiplicity")
291
292 eigval_a => mo_array(1)%eigenvalues
293 eigval_b => mo_array(2)%eigenvalues
294
295 lumo_a = 1
296 lumo_b = 1
297
298 ! Apply Aufbau principle
299 DO i = 1, nelec
300 ! Threshold is needed to ensure a preference for alpha occupation in the case
301 ! of degeneracy
302 threshold = max(mo_array(1)%flexible_electron_count, mo_array(2)%flexible_electron_count)
303 IF ((eigval_a(lumo_a) - threshold) < eigval_b(lumo_b)) THEN
304 lumo_a = lumo_a + 1
305 ELSE
306 lumo_b = lumo_b + 1
307 END IF
308 IF (lumo_a > mo_array(1)%nmo) THEN
309 IF (i /= nelec) &
310 CALL cp_warn(__location__, &
311 "All alpha MOs are occupied. Add more alpha MOs to "// &
312 "allow for a higher multiplicity")
313 IF (i < nelec) THEN
314 lumo_a = lumo_a - 1
315 lumo_b = lumo_b + 1
316 END IF
317 END IF
318 IF (lumo_b > mo_array(2)%nmo) THEN
319 IF (lumo_b < lumo_a) &
320 CALL cp_warn(__location__, &
321 "All beta MOs are occupied. Add more beta MOs to "// &
322 "allow for a lower multiplicity")
323 IF (i < nelec) THEN
324 lumo_a = lumo_a + 1
325 lumo_b = lumo_b - 1
326 END IF
327 END IF
328 END DO
329
330 mo_array(1)%homo = lumo_a - 1
331 mo_array(2)%homo = lumo_b - 1
332
333 IF (mo_array(2)%homo > mo_array(1)%homo) THEN
334 CALL cp_warn(__location__, &
335 "More beta ("// &
336 trim(adjustl(cp_to_string(mo_array(2)%homo)))// &
337 ") than alpha ("// &
338 trim(adjustl(cp_to_string(mo_array(1)%homo)))// &
339 ") MOs are occupied. Resorting to low spin state")
340 mo_array(1)%homo = nelec/2 + modulo(nelec, 2)
341 mo_array(2)%homo = nelec/2
342 END IF
343
344 mo_array(1)%nelectron = mo_array(1)%homo
345 mo_array(2)%nelectron = mo_array(2)%homo
346 multiplicity_new = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
347
348 IF (multiplicity_new /= multiplicity_old) &
349 CALL cp_warn(__location__, &
350 "Multiplicity changed from "// &
351 trim(adjustl(cp_to_string(multiplicity_old)))//" to "// &
352 trim(adjustl(cp_to_string(multiplicity_new))))
353
354 IF (PRESENT(eval_deriv)) THEN
355 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
356 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
357 ELSE
358 IF (PRESENT(tot_zeff_corr)) THEN
359 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
360 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
361 ELSE
362 CALL set_mo_occupation_1(mo_array(1), smear=smear)
363 CALL set_mo_occupation_1(mo_array(2), smear=smear)
364 END IF
365 END IF
366
367 CALL timestop(handle)
368
369 END SUBROUTINE set_mo_occupation_2
370
371! **************************************************************************************************
372!> \brief Smearing of the MO occupation with all kind of occupation numbers
373!> \param mo_set MO dataset structure
374!> \param smear optional smearing information
375!> \param eval_deriv on entry the derivative of the KS energy wrt to the occupation number
376!> on exit the derivative of the full free energy (i.e. KS and entropy) wrt to the eigenvalue
377!> \param xas_env ...
378!> \param tot_zeff_corr ...
379!> \date 17.04.2002 (v1.0), 26.08.2008 (v1.1)
380!> \par History
381!> 10.2019 Added functionality to adjust mo occupation if the core
382!> charges are changed via CORE_CORRECTION during surface dipole
383!> calculation. Total number of electrons matches the total core
384!> charges if tot_zeff_corr is non-zero. Not yet implemented for
385!> OT type method. [Soumya Ghosh]
386!> \author Matthias Krack
387!> \version 1.1
388! **************************************************************************************************
389 SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr)
390
391 TYPE(mo_set_type), INTENT(INOUT) :: mo_set
392 TYPE(smear_type), OPTIONAL, POINTER :: smear
393 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
394 TYPE(xas_environment_type), OPTIONAL, POINTER :: xas_env
395 REAL(kind=dp), OPTIONAL :: tot_zeff_corr
396
397 CHARACTER(LEN=*), PARAMETER :: routinen = 'set_mo_occupation_1'
398
399 INTEGER :: handle, i_first, imo, ir, irmo, nmo, &
400 nomo, xas_estate
401 LOGICAL :: equal_size, is_large
402 REAL(kind=dp) :: delectron, e1, e2, edelta, edist, &
403 el_count, lengthscale, nelec, &
404 occ_estate, total_zeff_corr, &
405 xas_nelectron
406 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: dfde
407
408 CALL timeset(routinen, handle)
409
410 cpassert(ASSOCIATED(mo_set%eigenvalues))
411 cpassert(ASSOCIATED(mo_set%occupation_numbers))
412 mo_set%occupation_numbers(:) = 0.0_dp
413
414 ! Quick return, if no electrons are available
415 IF (mo_set%nelectron == 0) THEN
416 CALL timestop(handle)
417 RETURN
418 END IF
419
420 xas_estate = -1
421 occ_estate = 0.0_dp
422 IF (PRESENT(xas_env)) THEN
423 CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
424 nomo = ceiling(xas_nelectron + 1.0 - occ_estate - epsilon(0.0_dp))
425
426 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
427 IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
428 el_count = sum(mo_set%occupation_numbers(1:nomo))
429 IF (el_count > xas_nelectron) &
430 mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
431 el_count = sum(mo_set%occupation_numbers(1:nomo))
432 is_large = abs(el_count - xas_nelectron) > xas_nelectron*epsilon(el_count)
433 cpassert(.NOT. is_large)
434 ELSE
435 IF (modulo(mo_set%nelectron, int(mo_set%maxocc)) == 0) THEN
436 nomo = nint(mo_set%nelectron/mo_set%maxocc)
437 ! Initialize MO occupations
438 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
439 ELSE
440 nomo = int(mo_set%nelectron/mo_set%maxocc) + 1
441 ! Initialize MO occupations
442 mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
443 mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
444 END IF
445! introduce applied potential correction here
446! electron density is adjusted according to applied core correction
447! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
448! see whether both surface dipole correction and core correction is present in
449! the inputfile
450 IF (PRESENT(tot_zeff_corr)) THEN
451! find the additional core charges
452 total_zeff_corr = tot_zeff_corr
453 IF (int(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
454 delectron = 0.0_dp
455 IF (total_zeff_corr < 0.0_dp) THEN
456! remove electron density from the mos
457 delectron = abs(total_zeff_corr) - real(mo_set%maxocc, kind=dp)
458 IF (delectron > 0.0_dp) THEN
459 mo_set%occupation_numbers(nomo) = 0.0_dp
460 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
461 DO ir = 1, irmo
462 delectron = delectron - real(mo_set%maxocc, kind=dp)
463 IF (delectron < 0.0_dp) THEN
464 mo_set%occupation_numbers(nomo - ir) = -delectron
465 ELSE
466 mo_set%occupation_numbers(nomo - ir) = 0.0_dp
467 END IF
468 END DO
469 nomo = nomo - irmo
470 IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
471 ELSEIF (delectron < 0.0_dp) THEN
472 mo_set%occupation_numbers(nomo) = -delectron
473 ELSE
474 mo_set%occupation_numbers(nomo) = 0.0_dp
475 nomo = nomo - 1
476 END IF
477 ELSEIF (total_zeff_corr > 0.0_dp) THEN
478! add electron density to the mos
479 delectron = total_zeff_corr - real(mo_set%maxocc, kind=dp)
480 IF (delectron > 0.0_dp) THEN
481 mo_set%occupation_numbers(nomo + 1) = real(mo_set%maxocc, kind=dp)
482 nomo = nomo + 1
483 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
484 DO ir = 1, irmo
485 delectron = delectron - real(mo_set%maxocc, kind=dp)
486 IF (delectron < 0.0_dp) THEN
487 mo_set%occupation_numbers(nomo + ir) = delectron + real(mo_set%maxocc, kind=dp)
488 ELSE
489 mo_set%occupation_numbers(nomo + ir) = real(mo_set%maxocc, kind=dp)
490 END IF
491 END DO
492 nomo = nomo + irmo
493 ELSE
494 mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
495 nomo = nomo + 1
496 END IF
497 END IF
498 END IF
499 END IF
500 nmo = SIZE(mo_set%eigenvalues)
501
502 cpassert(nmo >= nomo)
503 cpassert((SIZE(mo_set%occupation_numbers) == nmo))
504
505 mo_set%homo = nomo
506 mo_set%lfomo = nomo + 1
507 mo_set%mu = mo_set%eigenvalues(nomo)
508
509 ! Check consistency of the array lengths
510 IF (PRESENT(eval_deriv)) THEN
511 equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
512 cpassert(equal_size)
513 END IF
514
515 ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
516 IF (.NOT. PRESENT(smear)) THEN
517 ! there is no dependence of the energy on the eigenvalues
518 mo_set%uniform_occupation = .true.
519 IF (PRESENT(eval_deriv)) THEN
520 eval_deriv = 0.0_dp
521 END IF
522 CALL timestop(handle)
523 RETURN
524 END IF
525
526 ! Check if proper eigenvalues are already available
527 IF (smear%method /= smear_list) THEN
528 IF ((abs(mo_set%eigenvalues(1)) < 1.0e-12_dp) .AND. &
529 (abs(mo_set%eigenvalues(nmo)) < 1.0e-12_dp)) THEN
530 CALL timestop(handle)
531 RETURN
532 END IF
533 END IF
534
535 ! Perform smearing
536 IF (smear%do_smear) THEN
537 IF (PRESENT(xas_env)) THEN
538 i_first = xas_estate + 1
539 nelec = xas_nelectron
540 ELSE
541 i_first = 1
542 IF (smear%fixed_mag_mom == -1.0_dp) THEN
543 nelec = real(mo_set%nelectron, dp)
544 ELSE
545 nelec = mo_set%n_el_f
546 END IF
547 END IF
548 SELECT CASE (smear%method)
549 CASE (smear_fermi_dirac)
550 IF (.NOT. PRESENT(eval_deriv)) THEN
551 CALL fermifixed(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, nelec, &
552 smear%electronic_temperature, mo_set%maxocc, xas_estate, occ_estate)
553 ELSE
554 ! could be a relatively large matrix, but one could get rid of it by never storing it
555 ! we only need dE/df * df/de, one could equally parallelize over entries, this could become expensive
556 ALLOCATE (dfde(nmo, nmo))
557 ! lengthscale could become a parameter, but this is pretty good
558 lengthscale = 10*smear%electronic_temperature
559
560 CALL fermifixedderiv(dfde, mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, nelec, &
561 smear%electronic_temperature, mo_set%maxocc, lengthscale, xas_estate, occ_estate)
562
563 ! deriv of E_{KS}-kT*S wrt to f_i
564 eval_deriv = eval_deriv - mo_set%eigenvalues + mo_set%mu
565 ! correspondingly the deriv of E_{KS}-kT*S wrt to e_i
566 eval_deriv = matmul(transpose(dfde), eval_deriv)
567
568 DEALLOCATE (dfde)
569 END IF
570
571 ! Find the lowest fractional occupied MO (LFOMO)
572 DO imo = i_first, nmo
573 IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
574 mo_set%lfomo = imo
575 EXIT
576 END IF
577 END DO
578 is_large = abs(maxval(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
579 ! this is not a real problem, but the temperature might be a bit large
580 IF (is_large) &
581 cpwarn("Fermi-Dirac smearing includes the first MO")
582
583 ! Find the highest (fractional) occupied MO which will be now the HOMO
584 DO imo = nmo, mo_set%lfomo, -1
585 IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
586 mo_set%homo = imo
587 EXIT
588 END IF
589 END DO
590 is_large = abs(minval(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
591 IF (is_large) &
592 CALL cp_warn(__location__, &
593 "Fermi-Dirac smearing includes the last MO => "// &
594 "Add more MOs for proper smearing.")
595
596 ! check that the total electron count is accurate
597 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
598 IF (is_large) &
599 cpwarn("Total number of electrons is not accurate")
600
602 ! not implemented
603 cpassert(.NOT. PRESENT(eval_deriv))
604
605 ! Define the energy window for the eigenvalues
606 e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
607 IF (e1 <= mo_set%eigenvalues(1)) &
608 cpwarn("Energy window for smearing includes the first MO")
609
610 e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
611 IF (e2 >= mo_set%eigenvalues(nmo)) &
612 CALL cp_warn(__location__, &
613 "Energy window for smearing includes the last MO => "// &
614 "Add more MOs for proper smearing.")
615
616 ! Find the lowest fractional occupied MO (LFOMO)
617 DO imo = i_first, nomo
618 IF (mo_set%eigenvalues(imo) > e1) THEN
619 mo_set%lfomo = imo
620 EXIT
621 END IF
622 END DO
623
624 ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
625 DO imo = nmo, nomo, -1
626 IF (mo_set%eigenvalues(imo) < e2) THEN
627 mo_set%homo = imo
628 EXIT
629 END IF
630 END DO
631
632 ! Get the number of electrons to be smeared
633 edist = 0.0_dp
634 nelec = 0.0_dp
635
636 DO imo = mo_set%lfomo, mo_set%homo
637 nelec = nelec + mo_set%occupation_numbers(imo)
638 edist = edist + abs(e2 - mo_set%eigenvalues(imo))
639 END DO
640
641 ! Smear electrons inside the energy window
642 DO imo = mo_set%lfomo, mo_set%homo
643 edelta = abs(e2 - mo_set%eigenvalues(imo))
644 mo_set%occupation_numbers(imo) = min(mo_set%maxocc, nelec*edelta/edist)
645 nelec = nelec - mo_set%occupation_numbers(imo)
646 edist = edist - edelta
647 END DO
648
649 CASE (smear_list)
650 equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
651 cpassert(equal_size)
652 mo_set%occupation_numbers = smear%list
653 ! there is no dependence of the energy on the eigenvalues
654 IF (PRESENT(eval_deriv)) THEN
655 eval_deriv = 0.0_dp
656 END IF
657 ! most general case
658 mo_set%lfomo = 1
659 mo_set%homo = nmo
660 END SELECT
661
662 ! Check, if the smearing involves more than one MO
663 IF (mo_set%lfomo == mo_set%homo) THEN
664 mo_set%homo = nomo
665 mo_set%lfomo = nomo + 1
666 ELSE
667 mo_set%uniform_occupation = .false.
668 END IF
669
670 END IF ! do smear
671
672 ! zeros don't count as uniform
673 mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
674
675 CALL timestop(handle)
676
677 END SUBROUTINE set_mo_occupation_1
678
679END MODULE qs_mo_occupation
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
various routines to log and control the output. The idea is that decisions about where to log should ...
deal with the Fermi distribution, compute it, fix mu, get derivs
Definition fermi_utils.F:13
subroutine, public fermifixedderiv(dfde, f, mu, kts, e, n, t, maxocc, l, estate, festate)
returns f and dfde for a given set of energies and number of electrons it is a numerical derivative,...
subroutine, public fermifixed(f, mu, kts, e, n, t, maxocc, estate, festate)
returns occupations according to Fermi-Dirac statistics for a given set of energies and number of ele...
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public smear_fermi_dirac
integer, parameter, public smear_energy_window
integer, parameter, public smear_list
sums arrays of real/complex numbers with much reduced round-off as compared to a naive implementation...
Definition kahan_sum.F:29
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Set occupation of molecular orbitals.
Definition and initialisation of the mo data type.
Definition qs_mo_types.F:22
subroutine, public set_mo_set(mo_set, maxocc, homo, lfomo, nao, nelectron, n_el_f, nmo, eigenvalues, occupation_numbers, uniform_occupation, kts, mu, flexible_electron_count)
Set the components of a MO set data structure.
logical function, public has_uniform_occupation(mo_set, first_mo, last_mo, occupation, tolerance)
Check if the set of MOs in mo_set specifed by the MO index range [first_mo,last_mo] an integer occupa...
subroutine, public get_mo_set(mo_set, maxocc, homo, lfomo, nao, nelectron, n_el_f, nmo, eigenvalues, occupation_numbers, mo_coeff, mo_coeff_b, uniform_occupation, kts, mu, flexible_electron_count)
Get the components of a MO set data structure.
parameters that control an scf iteration
All kind of helpful little routines.
Definition util.F:14
define create destroy get and put information in xas_env to calculate the x-ray absorption spectra
subroutine, public get_xas_env(xas_env, exc_state, nao, nvirtual, nvirtual2, centers_wfn, atom_of_state, exc_atoms, nexc_states, type_of_state, mykind_of_atom, mykind_of_kind, state_of_atom, spectrum, groundstate_coeff, ostrength_sm, dip_fm_set, excvec_coeff, excvec_overlap, unoccupied_orbs, unoccupied_evals, unoccupied_max_iter, unoccupied_eps, all_vectors, all_evals, my_gto_basis, qs_loc_env, stogto_overlap, occ_estate, xas_nelectron, xas_estate, nexc_atoms, nexc_search, spin_channel, scf_env, scf_control)
...
contains the parameters needed by a scf run