(git:9754b87)
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-2025 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 cpwarn_if(is_large, "Fermi-Dirac smearing includes the first MO")
117
118 is_large = abs(minval(all_occ)) > smear%eps_fermi_dirac
119 IF (is_large) &
120 CALL cp_warn(__location__, &
121 "Fermi-Dirac smearing includes the last MO => "// &
122 "Add more MOs for proper smearing.")
123
124 ! check that the total electron count is accurate
125 is_large = (abs(all_nelec - accurate_sum(all_occ(:))) > smear%eps_fermi_dirac*all_nelec)
126 cpwarn_if(is_large, "Total number of electrons is not accurate")
127
128 DO i = 1, all_nmo
129 IF (all_index(i) <= nmo_a) THEN
130 occ_a(all_index(i)) = all_occ(i)
131 eigval_a(all_index(i)) = all_eigval(i)
132 ELSE
133 occ_b(all_index(i) - nmo_a) = all_occ(i)
134 eigval_b(all_index(i) - nmo_a) = all_eigval(i)
135 END IF
136 END DO
137
138 nelec_a = accurate_sum(occ_a(:))
139 nelec_b = accurate_sum(occ_b(:))
140
141 DO i = 1, nmo_a
142 IF (occ_a(i) < 1.0_dp) THEN
143 lfomo_a = i
144 EXIT
145 END IF
146 END DO
147 DO i = 1, nmo_b
148 IF (occ_b(i) < 1.0_dp) THEN
149 lfomo_b = i
150 EXIT
151 END IF
152 END DO
153 homo_a = lfomo_a - 1
154 DO i = nmo_a, lfomo_a, -1
155 IF (occ_a(i) > smear%eps_fermi_dirac) THEN
156 homo_a = i
157 EXIT
158 END IF
159 END DO
160 homo_b = lfomo_b - 1
161 DO i = nmo_b, lfomo_b, -1
162 IF (occ_b(i) > smear%eps_fermi_dirac) THEN
163 homo_b = i
164 EXIT
165 END IF
166 END DO
167
168 CALL set_mo_set(mo_set=mo_array(1), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_a, &
169 lfomo=lfomo_a, homo=homo_a, uniform_occupation=.false.)
170 CALL set_mo_set(mo_set=mo_array(2), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_b, &
171 lfomo=lfomo_b, homo=homo_b, uniform_occupation=.false.)
172
173 CALL timestop(handle)
174
175 END SUBROUTINE set_mo_occupation_3
176
177! **************************************************************************************************
178!> \brief Prepare an occupation of alpha and beta MOs following an Aufbau
179!> principle, i.e. allowing a change in multiplicity.
180!> \param mo_array ...
181!> \param smear ...
182!> \param eval_deriv ...
183!> \param tot_zeff_corr ...
184!> \date 25.01.2010 (MK)
185!> \par History
186!> 10.2019 Added functionality to adjust mo occupation if the core
187!> charges are changed via CORE_CORRECTION during surface dipole
188!> calculation. Total number of electrons matches the total core
189!> charges if tot_zeff_corr is non-zero. Not yet implemented for
190!> OT type method. [Soumya Ghosh]
191!> \author Matthias Krack (MK)
192!> \version 1.0
193! **************************************************************************************************
194 SUBROUTINE set_mo_occupation_2(mo_array, smear, eval_deriv, tot_zeff_corr)
195
196 TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT) :: mo_array
197 TYPE(smear_type), POINTER :: smear
198 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
199 REAL(KIND=dp), OPTIONAL :: tot_zeff_corr
200
201 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_2'
202
203 INTEGER :: handle, i, lumo_a, lumo_b, &
204 multiplicity_new, multiplicity_old, &
205 nelec
206 REAL(KIND=dp) :: nelec_f, threshold
207 REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b
208
209 CALL timeset(routinen, handle)
210
211 ! Fall back for the case that we have only one MO set
212 IF (SIZE(mo_array) == 1) THEN
213 IF (PRESENT(eval_deriv)) THEN
214! Change of MO occupancy to account for CORE_CORRECTION is not yet implemented
215 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
216 ELSE
217 IF (PRESENT(tot_zeff_corr)) THEN
218 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
219 ELSE
220 CALL set_mo_occupation_1(mo_array(1), smear=smear)
221 END IF
222 END IF
223 CALL timestop(handle)
224 RETURN
225 END IF
226
227 IF (smear%do_smear) THEN
228 IF (smear%fixed_mag_mom < 0.0_dp) THEN
229 IF (PRESENT(tot_zeff_corr)) THEN
230 CALL cp_warn(__location__, &
231 "CORE_CORRECTION /= 0.0 might cause the cell to charge up "// &
232 "that will lead to application of different background "// &
233 "correction compared to the reference system. "// &
234 "Use FIXED_MAGNETIC_MOMENT >= 0.0 if using SMEAR keyword "// &
235 "to correct the electron density")
236 END IF
237 IF (smear%fixed_mag_mom /= -1.0_dp) THEN
238 cpassert(.NOT. (PRESENT(eval_deriv)))
239 CALL set_mo_occupation_3(mo_array, smear=smear)
240 CALL timestop(handle)
241 RETURN
242 END IF
243 ELSE
244 nelec_f = mo_array(1)%n_el_f + mo_array(2)%n_el_f
245 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
246 mo_array(1)%n_el_f = nelec_f/2.0_dp + smear%fixed_mag_mom/2.0_dp
247 mo_array(2)%n_el_f = nelec_f/2.0_dp - smear%fixed_mag_mom/2.0_dp
248 END IF
249 cpassert(.NOT. (PRESENT(eval_deriv)))
250 IF (PRESENT(tot_zeff_corr)) THEN
251 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
252 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
253 ELSE
254 CALL set_mo_occupation_1(mo_array(1), smear=smear)
255 CALL set_mo_occupation_1(mo_array(2), smear=smear)
256 END IF
257 END IF
258 END IF
259
260 IF (.NOT. ((mo_array(1)%flexible_electron_count > 0.0_dp) .AND. &
261 (mo_array(2)%flexible_electron_count > 0.0_dp))) THEN
262 IF (PRESENT(eval_deriv)) THEN
263 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
264 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
265 ELSE
266 IF (PRESENT(tot_zeff_corr)) THEN
267 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
268 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
269 ELSE
270 CALL set_mo_occupation_1(mo_array(1), smear=smear)
271 CALL set_mo_occupation_1(mo_array(2), smear=smear)
272 END IF
273 END IF
274 CALL timestop(handle)
275 RETURN
276 END IF
277
278 nelec = mo_array(1)%nelectron + mo_array(2)%nelectron
279
280 multiplicity_old = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
281
282 IF (mo_array(1)%nelectron >= mo_array(1)%nmo) &
283 CALL cp_warn(__location__, &
284 "All alpha MOs are occupied. Add more alpha MOs to "// &
285 "allow for a higher multiplicity")
286 IF ((mo_array(2)%nelectron >= mo_array(2)%nmo) .AND. (mo_array(2)%nelectron /= mo_array(1)%nelectron)) &
287 CALL cp_warn(__location__, "All beta MOs are occupied. Add more beta MOs to "// &
288 "allow for a lower multiplicity")
289
290 eigval_a => mo_array(1)%eigenvalues
291 eigval_b => mo_array(2)%eigenvalues
292
293 lumo_a = 1
294 lumo_b = 1
295
296 ! Apply Aufbau principle
297 DO i = 1, nelec
298 ! Threshold is needed to ensure a preference for alpha occupation in the case
299 ! of degeneracy
300 threshold = max(mo_array(1)%flexible_electron_count, mo_array(2)%flexible_electron_count)
301 IF ((eigval_a(lumo_a) - threshold) < eigval_b(lumo_b)) THEN
302 lumo_a = lumo_a + 1
303 ELSE
304 lumo_b = lumo_b + 1
305 END IF
306 IF (lumo_a > mo_array(1)%nmo) THEN
307 IF (i /= nelec) &
308 CALL cp_warn(__location__, &
309 "All alpha MOs are occupied. Add more alpha MOs to "// &
310 "allow for a higher multiplicity")
311 IF (i < nelec) THEN
312 lumo_a = lumo_a - 1
313 lumo_b = lumo_b + 1
314 END IF
315 END IF
316 IF (lumo_b > mo_array(2)%nmo) THEN
317 IF (lumo_b < lumo_a) &
318 CALL cp_warn(__location__, &
319 "All beta MOs are occupied. Add more beta MOs to "// &
320 "allow for a lower multiplicity")
321 IF (i < nelec) THEN
322 lumo_a = lumo_a + 1
323 lumo_b = lumo_b - 1
324 END IF
325 END IF
326 END DO
327
328 mo_array(1)%homo = lumo_a - 1
329 mo_array(2)%homo = lumo_b - 1
330
331 IF (mo_array(2)%homo > mo_array(1)%homo) THEN
332 CALL cp_warn(__location__, &
333 "More beta ("// &
334 trim(adjustl(cp_to_string(mo_array(2)%homo)))// &
335 ") than alpha ("// &
336 trim(adjustl(cp_to_string(mo_array(1)%homo)))// &
337 ") MOs are occupied. Resorting to low spin state")
338 mo_array(1)%homo = nelec/2 + modulo(nelec, 2)
339 mo_array(2)%homo = nelec/2
340 END IF
341
342 mo_array(1)%nelectron = mo_array(1)%homo
343 mo_array(2)%nelectron = mo_array(2)%homo
344 multiplicity_new = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
345
346 IF (multiplicity_new /= multiplicity_old) &
347 CALL cp_warn(__location__, &
348 "Multiplicity changed from "// &
349 trim(adjustl(cp_to_string(multiplicity_old)))//" to "// &
350 trim(adjustl(cp_to_string(multiplicity_new))))
351
352 IF (PRESENT(eval_deriv)) THEN
353 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
354 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
355 ELSE
356 IF (PRESENT(tot_zeff_corr)) THEN
357 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
358 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
359 ELSE
360 CALL set_mo_occupation_1(mo_array(1), smear=smear)
361 CALL set_mo_occupation_1(mo_array(2), smear=smear)
362 END IF
363 END IF
364
365 CALL timestop(handle)
366
367 END SUBROUTINE set_mo_occupation_2
368
369! **************************************************************************************************
370!> \brief Smearing of the MO occupation with all kind of occupation numbers
371!> \param mo_set MO dataset structure
372!> \param smear optional smearing information
373!> \param eval_deriv on entry the derivative of the KS energy wrt to the occupation number
374!> on exit the derivative of the full free energy (i.e. KS and entropy) wrt to the eigenvalue
375!> \param xas_env ...
376!> \param tot_zeff_corr ...
377!> \date 17.04.2002 (v1.0), 26.08.2008 (v1.1)
378!> \par History
379!> 10.2019 Added functionality to adjust mo occupation if the core
380!> charges are changed via CORE_CORRECTION during surface dipole
381!> calculation. Total number of electrons matches the total core
382!> charges if tot_zeff_corr is non-zero. Not yet implemented for
383!> OT type method. [Soumya Ghosh]
384!> \author Matthias Krack
385!> \version 1.1
386! **************************************************************************************************
387 SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr)
388
389 TYPE(mo_set_type), INTENT(INOUT) :: mo_set
390 TYPE(smear_type), OPTIONAL, POINTER :: smear
391 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
392 TYPE(xas_environment_type), OPTIONAL, POINTER :: xas_env
393 REAL(kind=dp), OPTIONAL :: tot_zeff_corr
394
395 CHARACTER(LEN=*), PARAMETER :: routinen = 'set_mo_occupation_1'
396
397 INTEGER :: handle, i_first, imo, ir, irmo, nmo, &
398 nomo, xas_estate
399 LOGICAL :: equal_size, is_large
400 REAL(kind=dp) :: delectron, e1, e2, edelta, edist, &
401 el_count, lengthscale, nelec, &
402 occ_estate, total_zeff_corr, &
403 xas_nelectron
404 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: dfde
405
406 CALL timeset(routinen, handle)
407
408 cpassert(ASSOCIATED(mo_set%eigenvalues))
409 cpassert(ASSOCIATED(mo_set%occupation_numbers))
410 mo_set%occupation_numbers(:) = 0.0_dp
411
412 ! Quick return, if no electrons are available
413 IF (mo_set%nelectron == 0) THEN
414 CALL timestop(handle)
415 RETURN
416 END IF
417
418 xas_estate = -1
419 occ_estate = 0.0_dp
420 IF (PRESENT(xas_env)) THEN
421 CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
422 nomo = ceiling(xas_nelectron + 1.0 - occ_estate - epsilon(0.0_dp))
423
424 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
425 IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
426 el_count = sum(mo_set%occupation_numbers(1:nomo))
427 IF (el_count > xas_nelectron) &
428 mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
429 el_count = sum(mo_set%occupation_numbers(1:nomo))
430 is_large = abs(el_count - xas_nelectron) > xas_nelectron*epsilon(el_count)
431 cpassert(.NOT. is_large)
432 ELSE
433 IF (modulo(mo_set%nelectron, int(mo_set%maxocc)) == 0) THEN
434 nomo = nint(mo_set%nelectron/mo_set%maxocc)
435 ! Initialize MO occupations
436 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
437 ELSE
438 nomo = int(mo_set%nelectron/mo_set%maxocc) + 1
439 ! Initialize MO occupations
440 mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
441 mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
442 END IF
443! introduce applied potential correction here
444! electron density is adjusted according to applied core correction
445! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
446! see whether both surface dipole correction and core correction is present in
447! the inputfile
448 IF (PRESENT(tot_zeff_corr)) THEN
449! find the additional core charges
450 total_zeff_corr = tot_zeff_corr
451 IF (int(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
452 delectron = 0.0_dp
453 IF (total_zeff_corr < 0.0_dp) THEN
454! remove electron density from the mos
455 delectron = abs(total_zeff_corr) - real(mo_set%maxocc, kind=dp)
456 IF (delectron > 0.0_dp) THEN
457 mo_set%occupation_numbers(nomo) = 0.0_dp
458 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
459 DO ir = 1, irmo
460 delectron = delectron - real(mo_set%maxocc, kind=dp)
461 IF (delectron < 0.0_dp) THEN
462 mo_set%occupation_numbers(nomo - ir) = -delectron
463 ELSE
464 mo_set%occupation_numbers(nomo - ir) = 0.0_dp
465 END IF
466 END DO
467 nomo = nomo - irmo
468 IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
469 ELSEIF (delectron < 0.0_dp) THEN
470 mo_set%occupation_numbers(nomo) = -delectron
471 ELSE
472 mo_set%occupation_numbers(nomo) = 0.0_dp
473 nomo = nomo - 1
474 END IF
475 ELSEIF (total_zeff_corr > 0.0_dp) THEN
476! add electron density to the mos
477 delectron = total_zeff_corr - real(mo_set%maxocc, kind=dp)
478 IF (delectron > 0.0_dp) THEN
479 mo_set%occupation_numbers(nomo + 1) = real(mo_set%maxocc, kind=dp)
480 nomo = nomo + 1
481 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
482 DO ir = 1, irmo
483 delectron = delectron - real(mo_set%maxocc, kind=dp)
484 IF (delectron < 0.0_dp) THEN
485 mo_set%occupation_numbers(nomo + ir) = delectron + real(mo_set%maxocc, kind=dp)
486 ELSE
487 mo_set%occupation_numbers(nomo + ir) = real(mo_set%maxocc, kind=dp)
488 END IF
489 END DO
490 nomo = nomo + irmo
491 ELSE
492 mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
493 nomo = nomo + 1
494 END IF
495 END IF
496 END IF
497 END IF
498 nmo = SIZE(mo_set%eigenvalues)
499
500 cpassert(nmo >= nomo)
501 cpassert((SIZE(mo_set%occupation_numbers) == nmo))
502
503 mo_set%homo = nomo
504 mo_set%lfomo = nomo + 1
505 mo_set%mu = mo_set%eigenvalues(nomo)
506
507 ! Check consistency of the array lengths
508 IF (PRESENT(eval_deriv)) THEN
509 equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
510 cpassert(equal_size)
511 END IF
512
513 ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
514 IF (.NOT. PRESENT(smear)) THEN
515 ! there is no dependence of the energy on the eigenvalues
516 mo_set%uniform_occupation = .true.
517 IF (PRESENT(eval_deriv)) THEN
518 eval_deriv = 0.0_dp
519 END IF
520 CALL timestop(handle)
521 RETURN
522 END IF
523
524 ! Check if proper eigenvalues are already available
525 IF (smear%method /= smear_list) THEN
526 IF ((abs(mo_set%eigenvalues(1)) < 1.0e-12_dp) .AND. &
527 (abs(mo_set%eigenvalues(nmo)) < 1.0e-12_dp)) THEN
528 CALL timestop(handle)
529 RETURN
530 END IF
531 END IF
532
533 ! Perform smearing
534 IF (smear%do_smear) THEN
535 IF (PRESENT(xas_env)) THEN
536 i_first = xas_estate + 1
537 nelec = xas_nelectron
538 ELSE
539 i_first = 1
540 IF (smear%fixed_mag_mom == -1.0_dp) THEN
541 nelec = real(mo_set%nelectron, dp)
542 ELSE
543 nelec = mo_set%n_el_f
544 END IF
545 END IF
546 SELECT CASE (smear%method)
547 CASE (smear_fermi_dirac)
548 IF (.NOT. PRESENT(eval_deriv)) THEN
549 CALL fermifixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
550 mo_set%eigenvalues(1:mo_set%nmo), nelec, &
551 smear%electronic_temperature, mo_set%maxocc, xas_estate, occ_estate)
552 ELSE
553 ! could be a relatively large matrix, but one could get rid of it by never storing it
554 ! we only need dE/df * df/de, one could equally parallelize over entries, this could become expensive
555 ALLOCATE (dfde(nmo, nmo))
556 ! lengthscale could become a parameter, but this is pretty good
557 lengthscale = 10*smear%electronic_temperature
558
559 CALL fermifixedderiv(dfde, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
560 mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), 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 cpwarn_if(is_large, "Fermi-Dirac smearing includes the first MO")
581
582 ! Find the highest (fractional) occupied MO which will be now the HOMO
583 DO imo = nmo, mo_set%lfomo, -1
584 IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
585 mo_set%homo = imo
586 EXIT
587 END IF
588 END DO
589 is_large = abs(minval(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
590 IF (is_large) &
591 CALL cp_warn(__location__, &
592 "Fermi-Dirac smearing includes the last MO => "// &
593 "Add more MOs for proper smearing.")
594
595 ! check that the total electron count is accurate
596 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
597 cpwarn_if(is_large, "Total number of electrons is not accurate")
598
600 ! not implemented
601 cpassert(.NOT. PRESENT(eval_deriv))
602
603 ! Define the energy window for the eigenvalues
604 e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
605 IF (e1 <= mo_set%eigenvalues(1)) THEN
606 cpwarn("Energy window for smearing includes the first MO")
607 END IF
608
609 e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
610 IF (e2 >= mo_set%eigenvalues(nmo)) &
611 CALL cp_warn(__location__, &
612 "Energy window for smearing includes the last MO => "// &
613 "Add more MOs for proper smearing.")
614
615 ! Find the lowest fractional occupied MO (LFOMO)
616 DO imo = i_first, nomo
617 IF (mo_set%eigenvalues(imo) > e1) THEN
618 mo_set%lfomo = imo
619 EXIT
620 END IF
621 END DO
622
623 ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
624 DO imo = nmo, nomo, -1
625 IF (mo_set%eigenvalues(imo) < e2) THEN
626 mo_set%homo = imo
627 EXIT
628 END IF
629 END DO
630
631 ! Get the number of electrons to be smeared
632 edist = 0.0_dp
633 nelec = 0.0_dp
634
635 DO imo = mo_set%lfomo, mo_set%homo
636 nelec = nelec + mo_set%occupation_numbers(imo)
637 edist = edist + abs(e2 - mo_set%eigenvalues(imo))
638 END DO
639
640 ! Smear electrons inside the energy window
641 DO imo = mo_set%lfomo, mo_set%homo
642 edelta = abs(e2 - mo_set%eigenvalues(imo))
643 mo_set%occupation_numbers(imo) = min(mo_set%maxocc, nelec*edelta/edist)
644 nelec = nelec - mo_set%occupation_numbers(imo)
645 edist = edist - edelta
646 END DO
647
648 CASE (smear_list)
649 equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
650 cpassert(equal_size)
651 mo_set%occupation_numbers = smear%list
652 ! there is no dependence of the energy on the eigenvalues
653 IF (PRESENT(eval_deriv)) THEN
654 eval_deriv = 0.0_dp
655 END IF
656 ! most general case
657 mo_set%lfomo = 1
658 mo_set%homo = nmo
659 END SELECT
660
661 ! Check, if the smearing involves more than one MO
662 IF (mo_set%lfomo == mo_set%homo) THEN
663 mo_set%homo = nomo
664 mo_set%lfomo = nomo + 1
665 ELSE
666 mo_set%uniform_occupation = .false.
667 END IF
668
669 END IF ! do smear
670
671 ! zeros don't count as uniform
672 mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
673
674 CALL timestop(handle)
675
676 END SUBROUTINE set_mo_occupation_1
677
678END 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