(git:71c3ab0)
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-2026 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
24 smear_mp,&
26 USE kahan_sum, ONLY: accurate_sum
27 USE kinds, ONLY: dp
28 USE qs_mo_types, ONLY: get_mo_set,&
32 USE scf_control_types, ONLY: gce_type,&
34 USE smearing_utils, ONLY: smearfixed,&
37 USE util, ONLY: sort
38 USE xas_env_types, ONLY: get_xas_env,&
40#include "./base/base_uses.f90"
41
42 IMPLICIT NONE
43
44 PRIVATE
45
46 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_mo_occupation'
47
48 PUBLIC :: set_mo_occupation
49
51 MODULE PROCEDURE set_mo_occupation_1, set_mo_occupation_2
52 END INTERFACE
53
54CONTAINS
55
56! **************************************************************************************************
57!> \brief Occupation for smeared spin polarized electronic structures
58!> with relaxed multiplicity
59!>
60!> \param mo_array ...
61!> \param smear ...
62!> \param gce ...
63!> \date 10.03.2011 (MI)
64!> \author MI
65!> \version 1.0
66! **************************************************************************************************
67 SUBROUTINE set_mo_occupation_3(mo_array, smear, gce)
68
69 TYPE(mo_set_type), DIMENSION(2), INTENT(INOUT) :: mo_array
70 TYPE(smear_type) :: smear
71 TYPE(gce_type), OPTIONAL, POINTER :: gce
72
73 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_3'
74
75 CHARACTER(LEN=32) :: method_label
76 INTEGER :: all_nmo, handle, homo_a, homo_b, i, &
77 lfomo_a, lfomo_b, nmo_a, nmo_b, &
78 xas_estate
79 INTEGER, ALLOCATABLE, DIMENSION(:) :: all_index
80 LOGICAL :: do_gce, is_large
81 REAL(KIND=dp) :: all_nelec, kts, mu, nelec_a, nelec_b, &
82 occ_estate, smear_width
83 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: all_eigval, all_occ
84 REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b, occ_a, occ_b
85
86 CALL timeset(routinen, handle)
87
88 NULLIFY (eigval_a, eigval_b, occ_a, occ_b)
89 CALL get_mo_set(mo_set=mo_array(1), nmo=nmo_a, eigenvalues=eigval_a, &
90 occupation_numbers=occ_a)
91 CALL get_mo_set(mo_set=mo_array(2), nmo=nmo_b, eigenvalues=eigval_b, &
92 occupation_numbers=occ_b)
93 all_nmo = nmo_a + nmo_b
94 ALLOCATE (all_eigval(all_nmo))
95 ALLOCATE (all_occ(all_nmo))
96 ALLOCATE (all_index(all_nmo))
97
98 all_eigval(1:nmo_a) = eigval_a(1:nmo_a)
99 all_eigval(nmo_a + 1:all_nmo) = eigval_b(1:nmo_b)
100
101 CALL sort(all_eigval, all_nmo, all_index)
102
103 IF (PRESENT(gce)) THEN
104 do_gce = gce%do_gce
105 ELSE
106 do_gce = .false.
107 END IF
108
109 SELECT CASE (smear%method)
110 CASE (smear_fermi_dirac)
111 smear_width = smear%electronic_temperature
112 method_label = "Fermi-Dirac"
113 CASE (smear_gaussian)
114 smear_width = smear%smearing_width
115 method_label = "Gaussian"
116 CASE (smear_mp)
117 smear_width = smear%smearing_width
118 method_label = "Methfessel-Paxton"
119 CASE (smear_mv)
120 smear_width = smear%smearing_width
121 method_label = "Marzari-Vanderbilt"
122 CASE DEFAULT
123 cpabort("set_mo_occupation_3: unsupported smearing method")
124 END SELECT
125
126 IF (.NOT. do_gce) THEN
127 xas_estate = -1
128 occ_estate = 0.0_dp
129
130 nelec_a = 0.0_dp
131 nelec_b = 0.0_dp
132 all_nelec = 0.0_dp
133 nelec_a = accurate_sum(occ_a(:))
134 nelec_b = accurate_sum(occ_b(:))
135 all_nelec = nelec_a + nelec_b
136
137 DO i = 1, all_nmo
138 IF (all_index(i) <= nmo_a) THEN
139 all_occ(i) = occ_a(all_index(i))
140 ELSE
141 all_occ(i) = occ_b(all_index(i) - nmo_a)
142 END IF
143 END DO
144
145 CALL smearfixed(all_occ, mu, kts, all_eigval, all_nelec, &
146 smear_width, 1._dp, smear%method, xas_estate, occ_estate)
147 ELSE
148 gce%prev_workfunction = gce%ref_esp - mo_array(1)%mu
149 mu = gce%ref_esp - ((1.0_dp - gce%mixing_coef)*gce%prev_workfunction + gce%mixing_coef*gce%target_workfunction)
150 CALL smearocc(all_occ, all_nelec, kts, all_eigval, mu, smear_width, 1._dp, smear%method)
151 END IF
152
153 is_large = abs(all_occ(1) - 1.0_dp) > smear%eps_fermi_dirac
154 ! this is not a real problem, but the smearing width might be a bit large
155 cpwarn_if(is_large, trim(method_label)//" smearing includes the first MO")
156
157 is_large = abs(all_occ(all_nmo)) > smear%eps_fermi_dirac
158 IF (is_large) THEN
159 CALL cp_warn(__location__, &
160 trim(method_label)//" smearing includes the last MO => "// &
161 "Add more MOs for proper smearing.")
162 END IF
163 IF (.NOT. do_gce) THEN
164 ! check that the total electron count is accurate
165 is_large = (abs(all_nelec - accurate_sum(all_occ(:))) > smear%eps_fermi_dirac*all_nelec)
166 cpwarn_if(is_large, "Total number of electrons is not accurate")
167 END IF
168
169 DO i = 1, all_nmo
170 IF (all_index(i) <= nmo_a) THEN
171 occ_a(all_index(i)) = all_occ(i)
172 eigval_a(all_index(i)) = all_eigval(i)
173 ELSE
174 occ_b(all_index(i) - nmo_a) = all_occ(i)
175 eigval_b(all_index(i) - nmo_a) = all_eigval(i)
176 END IF
177 END DO
178
179 nelec_a = accurate_sum(occ_a(:))
180 nelec_b = accurate_sum(occ_b(:))
181
182 lfomo_a = nmo_a + 1
183 lfomo_b = nmo_b + 1
184 homo_a = 0
185 homo_b = 0
186
187 DO i = 1, nmo_a
188 IF (occ_a(i) < 1.0_dp) THEN
189 lfomo_a = i
190 EXIT
191 END IF
192 END DO
193 DO i = 1, nmo_b
194 IF (occ_b(i) < 1.0_dp) THEN
195 lfomo_b = i
196 EXIT
197 END IF
198 END DO
199 homo_a = lfomo_a - 1
200 DO i = nmo_a, lfomo_a, -1
201 IF (occ_a(i) > smear%eps_fermi_dirac) THEN
202 homo_a = i
203 EXIT
204 END IF
205 END DO
206 homo_b = lfomo_b - 1
207 DO i = nmo_b, lfomo_b, -1
208 IF (occ_b(i) > smear%eps_fermi_dirac) THEN
209 homo_b = i
210 EXIT
211 END IF
212 END DO
213
214 CALL set_mo_set(mo_set=mo_array(1), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_a, &
215 lfomo=lfomo_a, homo=homo_a, uniform_occupation=.false.)
216 CALL set_mo_set(mo_set=mo_array(2), kts=kts/2.0_dp, mu=mu, n_el_f=nelec_b, &
217 lfomo=lfomo_b, homo=homo_b, uniform_occupation=.false.)
218
219 CALL timestop(handle)
220
221 END SUBROUTINE set_mo_occupation_3
222
223! **************************************************************************************************
224!> \brief Prepare an occupation of alpha and beta MOs following an Aufbau
225!> principle, i.e. allowing a change in multiplicity.
226!> \param mo_array ...
227!> \param smear ...
228!> \param eval_deriv ...
229!> \param tot_zeff_corr ...
230!> \param probe ...
231!> \param gce ...
232!> \date 25.01.2010 (MK)
233!> \par History
234!> 10.2019 Added functionality to adjust mo occupation if the core
235!> charges are changed via CORE_CORRECTION during surface dipole
236!> calculation. Total number of electrons matches the total core
237!> charges if tot_zeff_corr is non-zero. Not yet implemented for
238!> OT type method. [Soumya Ghosh]
239!> \author Matthias Krack (MK)
240!> \version 1.0
241! **************************************************************************************************
242 SUBROUTINE set_mo_occupation_2(mo_array, smear, eval_deriv, tot_zeff_corr, probe, gce)
243
244 TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT) :: mo_array
245 TYPE(smear_type) :: smear
246 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
247 REAL(KIND=dp), OPTIONAL :: tot_zeff_corr
248 TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
249 POINTER :: probe
250 TYPE(gce_type), OPTIONAL, POINTER :: gce
251
252 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_2'
253
254 INTEGER :: handle, i, lumo_a, lumo_b, &
255 multiplicity_new, multiplicity_old, &
256 nelec
257 REAL(KIND=dp) :: nelec_f, threshold
258 REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b
259
260 CALL timeset(routinen, handle)
261
262 ! Fall back for the case that we have only one MO set
263 IF (SIZE(mo_array) == 1) THEN
264 IF (PRESENT(probe)) THEN
265 CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
266 ELSE IF (PRESENT(eval_deriv)) THEN
267! Change of MO occupancy to account for CORE_CORRECTION is not yet implemented
268 IF (PRESENT(gce)) THEN
269 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv, gce=gce)
270 ELSE
271 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
272 END IF
273 ELSE
274 IF (PRESENT(tot_zeff_corr)) THEN
275 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
276 ELSE
277 IF (PRESENT(gce)) THEN
278 CALL set_mo_occupation_1(mo_array(1), smear=smear, gce=gce)
279 ELSE
280 CALL set_mo_occupation_1(mo_array(1), smear=smear)
281 END IF
282 END IF
283 END IF
284 CALL timestop(handle)
285 RETURN
286 END IF
287
288 IF (PRESENT(probe)) THEN
289 CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
290 CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
291 END IF
292
293 IF (smear%do_smear) THEN
294 IF (smear%fixed_mag_mom < 0.0_dp) THEN
295 IF (PRESENT(tot_zeff_corr)) THEN
296 CALL cp_warn(__location__, &
297 "CORE_CORRECTION /= 0.0 might cause the cell to charge up "// &
298 "that will lead to application of different background "// &
299 "correction compared to the reference system. "// &
300 "Use FIXED_MAGNETIC_MOMENT >= 0.0 if using SMEAR keyword "// &
301 "to correct the electron density")
302 END IF
303 IF (smear%fixed_mag_mom /= -1.0_dp) THEN
304 cpassert(.NOT. (PRESENT(eval_deriv)))
305 CALL set_mo_occupation_3(mo_array, smear=smear, gce=gce)
306 CALL timestop(handle)
307 RETURN
308 END IF
309 ELSE
310 nelec_f = mo_array(1)%n_el_f + mo_array(2)%n_el_f
311 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
312 mo_array(1)%n_el_f = nelec_f/2.0_dp + smear%fixed_mag_mom/2.0_dp
313 mo_array(2)%n_el_f = nelec_f/2.0_dp - smear%fixed_mag_mom/2.0_dp
314 END IF
315 cpassert(.NOT. (PRESENT(eval_deriv)))
316 IF (PRESENT(tot_zeff_corr)) THEN
317 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
318 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
319 ELSE
320 CALL set_mo_occupation_1(mo_array(1), smear=smear)
321 CALL set_mo_occupation_1(mo_array(2), smear=smear)
322 END IF
323 END IF
324 END IF
325
326 IF (.NOT. ((mo_array(1)%flexible_electron_count > 0.0_dp) .AND. &
327 (mo_array(2)%flexible_electron_count > 0.0_dp))) THEN
328 IF (PRESENT(probe)) THEN
329 CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
330 CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
331 ELSE IF (PRESENT(eval_deriv)) THEN
332 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
333 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
334 ELSE
335 IF (PRESENT(tot_zeff_corr)) THEN
336 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
337 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
338 ELSE
339 CALL set_mo_occupation_1(mo_array(1), smear=smear)
340 CALL set_mo_occupation_1(mo_array(2), smear=smear)
341 END IF
342 END IF
343 CALL timestop(handle)
344 RETURN
345 END IF
346
347 nelec = mo_array(1)%nelectron + mo_array(2)%nelectron
348
349 multiplicity_old = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
350
351 IF (mo_array(1)%nelectron >= mo_array(1)%nmo) THEN
352 CALL cp_warn(__location__, &
353 "All alpha MOs are occupied. Add more alpha MOs to "// &
354 "allow for a higher multiplicity")
355 END IF
356 IF ((mo_array(2)%nelectron >= mo_array(2)%nmo) .AND. (mo_array(2)%nelectron /= mo_array(1)%nelectron)) THEN
357 CALL cp_warn(__location__, "All beta MOs are occupied. Add more beta MOs to "// &
358 "allow for a lower multiplicity")
359 END IF
360
361 eigval_a => mo_array(1)%eigenvalues
362 eigval_b => mo_array(2)%eigenvalues
363
364 lumo_a = 1
365 lumo_b = 1
366
367 ! Apply Aufbau principle
368 DO i = 1, nelec
369 ! Threshold is needed to ensure a preference for alpha occupation in the case
370 ! of degeneracy
371 threshold = max(mo_array(1)%flexible_electron_count, mo_array(2)%flexible_electron_count)
372 IF ((eigval_a(lumo_a) - threshold) < eigval_b(lumo_b)) THEN
373 lumo_a = lumo_a + 1
374 ELSE
375 lumo_b = lumo_b + 1
376 END IF
377 IF (lumo_a > mo_array(1)%nmo) THEN
378 IF (i /= nelec) THEN
379 CALL cp_warn(__location__, &
380 "All alpha MOs are occupied. Add more alpha MOs to "// &
381 "allow for a higher multiplicity")
382 END IF
383 IF (i < nelec) THEN
384 lumo_a = lumo_a - 1
385 lumo_b = lumo_b + 1
386 END IF
387 END IF
388 IF (lumo_b > mo_array(2)%nmo) THEN
389 IF (lumo_b < lumo_a) THEN
390 CALL cp_warn(__location__, &
391 "All beta MOs are occupied. Add more beta MOs to "// &
392 "allow for a lower multiplicity")
393 END IF
394 IF (i < nelec) THEN
395 lumo_a = lumo_a + 1
396 lumo_b = lumo_b - 1
397 END IF
398 END IF
399 END DO
400
401 mo_array(1)%homo = lumo_a - 1
402 mo_array(2)%homo = lumo_b - 1
403
404 IF (mo_array(2)%homo > mo_array(1)%homo) THEN
405 CALL cp_warn(__location__, &
406 "More beta ("// &
407 trim(adjustl(cp_to_string(mo_array(2)%homo)))// &
408 ") than alpha ("// &
409 trim(adjustl(cp_to_string(mo_array(1)%homo)))// &
410 ") MOs are occupied. Resorting to low spin state")
411 mo_array(1)%homo = nelec/2 + modulo(nelec, 2)
412 mo_array(2)%homo = nelec/2
413 END IF
414
415 mo_array(1)%nelectron = mo_array(1)%homo
416 mo_array(2)%nelectron = mo_array(2)%homo
417 multiplicity_new = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
418
419 IF (multiplicity_new /= multiplicity_old) THEN
420 CALL cp_warn(__location__, &
421 "Multiplicity changed from "// &
422 trim(adjustl(cp_to_string(multiplicity_old)))//" to "// &
423 trim(adjustl(cp_to_string(multiplicity_new))))
424 END IF
425
426 IF (PRESENT(probe)) THEN
427 CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
428 CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
429 ELSE IF (PRESENT(eval_deriv)) THEN
430 CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
431 CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
432 ELSE
433 IF (PRESENT(tot_zeff_corr)) THEN
434 CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
435 CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
436 ELSE
437 CALL set_mo_occupation_1(mo_array(1), smear=smear)
438 CALL set_mo_occupation_1(mo_array(2), smear=smear)
439 END IF
440 END IF
441
442 CALL timestop(handle)
443
444 END SUBROUTINE set_mo_occupation_2
445
446! **************************************************************************************************
447!> \brief Smearing of the MO occupation with all kind of occupation numbers
448!> \param mo_set MO dataset structure
449!> \param smear optional smearing information
450!> \param eval_deriv on entry the derivative of the KS energy wrt to the occupation number
451!> on exit the derivative of the full free energy (i.e. KS and entropy) wrt to the eigenvalue
452!> \param xas_env ...
453!> \param tot_zeff_corr ...
454!> \param probe ...
455!> \param gce ...
456!> \date 17.04.2002 (v1.0), 26.08.2008 (v1.1)
457!> \par History
458!> 10.2019 Added functionality to adjust mo occupation if the core
459!> charges are changed via CORE_CORRECTION during surface dipole
460!> calculation. Total number of electrons matches the total core
461!> charges if tot_zeff_corr is non-zero. Not yet implemented for
462!> OT type method. [Soumya Ghosh]
463!> \author Matthias Krack
464!> \version 1.1
465! **************************************************************************************************
466 SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr, probe, gce)
467
468 TYPE(mo_set_type), INTENT(INOUT) :: mo_set
469 TYPE(smear_type), OPTIONAL :: smear
470 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
471 TYPE(xas_environment_type), OPTIONAL, POINTER :: xas_env
472 REAL(kind=dp), OPTIONAL :: tot_zeff_corr
473 TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
474 POINTER :: probe
475 TYPE(gce_type), OPTIONAL, POINTER :: gce
476
477 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_1'
478
479 CHARACTER(LEN=20) :: method_label
480 INTEGER :: handle, i, i_first, imo, ir, irmo, nmo, &
481 nomo, xas_estate
482 LOGICAL :: do_gce, equal_size, is_large
483 REAL(KIND=dp) :: delectron, e1, e2, edelta, edist, &
484 el_count, gce_mu, my_nelec, nelec, &
485 occ_estate, total_zeff_corr, &
486 xas_nelectron
487 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: tmp_v
488
489 CALL timeset(routinen, handle)
490
491 cpassert(ASSOCIATED(mo_set%eigenvalues))
492 cpassert(ASSOCIATED(mo_set%occupation_numbers))
493 mo_set%occupation_numbers(:) = 0.0_dp
494
495 ! Quick return, if no electrons are available
496 IF (mo_set%nelectron == 0) THEN
497 CALL timestop(handle)
498 RETURN
499 END IF
500
501 xas_estate = -1
502 occ_estate = 0.0_dp
503 IF (PRESENT(xas_env)) THEN
504 CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
505 nomo = ceiling(xas_nelectron + 1.0 - occ_estate - epsilon(0.0_dp))
506
507 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
508 IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
509 el_count = sum(mo_set%occupation_numbers(1:nomo))
510 IF (el_count > xas_nelectron) THEN
511 mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
512 END IF
513 el_count = sum(mo_set%occupation_numbers(1:nomo))
514 is_large = abs(el_count - xas_nelectron) > xas_nelectron*epsilon(el_count)
515 cpassert(.NOT. is_large)
516 ELSE
517 IF (PRESENT(gce)) THEN
518 do_gce = gce%do_gce
519 ELSE
520 do_gce = .false.
521 END IF
522 ! GCE workfunction (Fermi energy) mixing
523 IF (do_gce) THEN
524 IF (smear%method /= smear_fermi_dirac) THEN
525 cpabort("Grand canonical ensemble DFT SCF now only support Fermi Dirac smearing.")
526 END IF
527 IF (gce%prev_workfunction < -1000.0_dp) THEN
528 my_nelec = real(mo_set%nelectron, dp)
529 CALL smearfixed(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, my_nelec, &
530 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac)
531 gce%prev_workfunction = -501.0_dp
532 ELSE IF (gce%prev_workfunction < -500.0_dp) THEN
533 my_nelec = real(mo_set%nelectron, dp)
534 CALL smearfixed(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, my_nelec, &
535 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac)
536 gce%prev_workfunction = gce%ref_esp - mo_set%mu
537 ELSE
538 gce_mu = gce%ref_esp - ((1.0_dp - gce%mixing_coef)*gce%prev_workfunction &
539 + gce%mixing_coef*gce%target_workfunction)
540 CALL smearocc(mo_set%occupation_numbers, my_nelec, mo_set%kTS, mo_set%eigenvalues, gce_mu, &
541 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac)
542 mo_set%mu = gce_mu
543 gce%prev_workfunction = gce%ref_esp - mo_set%mu
544 is_large = abs(maxval(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
545 cpwarn_if(is_large, "Fermi-Dirac smearing includes the first MO")
546 END IF
547 DO i = 1, SIZE(mo_set%occupation_numbers)
548 IF (mo_set%occupation_numbers(i) < mo_set%maxocc) THEN
549 mo_set%lfomo = i
550 EXIT
551 END IF
552 END DO
553 DO i = SIZE(mo_set%occupation_numbers), 1, -1
554 IF (mo_set%occupation_numbers(i) > smear%eps_fermi_dirac) THEN
555 mo_set%homo = i
556 EXIT
557 END IF
558 END DO
559 mo_set%uniform_occupation = .false.
560 mo_set%n_el_f = my_nelec
561 CALL timestop(handle)
562 RETURN
563 END IF
564
565 IF (modulo(mo_set%nelectron, int(mo_set%maxocc)) == 0) THEN
566 nomo = nint(mo_set%nelectron/mo_set%maxocc)
567 ! Initialize MO occupations
568 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
569 ELSE
570 nomo = int(mo_set%nelectron/mo_set%maxocc) + 1
571 ! Initialize MO occupations
572 mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
573 mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
574 END IF
575! introduce applied potential correction here
576! electron density is adjusted according to applied core correction
577! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
578! see whether both surface dipole correction and core correction is present in
579! the inputfile
580 IF (PRESENT(tot_zeff_corr)) THEN
581! find the additional core charges
582 total_zeff_corr = tot_zeff_corr
583 IF (int(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
584 delectron = 0.0_dp
585 IF (total_zeff_corr < 0.0_dp) THEN
586! remove electron density from the mos
587 delectron = abs(total_zeff_corr) - real(mo_set%maxocc, kind=dp)
588 IF (delectron > 0.0_dp) THEN
589 mo_set%occupation_numbers(nomo) = 0.0_dp
590 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
591 DO ir = 1, irmo
592 delectron = delectron - real(mo_set%maxocc, kind=dp)
593 IF (delectron < 0.0_dp) THEN
594 mo_set%occupation_numbers(nomo - ir) = -delectron
595 ELSE
596 mo_set%occupation_numbers(nomo - ir) = 0.0_dp
597 END IF
598 END DO
599 nomo = nomo - irmo
600 IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
601 ELSE IF (delectron < 0.0_dp) THEN
602 mo_set%occupation_numbers(nomo) = -delectron
603 ELSE
604 mo_set%occupation_numbers(nomo) = 0.0_dp
605 nomo = nomo - 1
606 END IF
607 ELSE IF (total_zeff_corr > 0.0_dp) THEN
608! add electron density to the mos
609 delectron = total_zeff_corr - real(mo_set%maxocc, kind=dp)
610 IF (delectron > 0.0_dp) THEN
611 mo_set%occupation_numbers(nomo + 1) = real(mo_set%maxocc, kind=dp)
612 nomo = nomo + 1
613 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
614 DO ir = 1, irmo
615 delectron = delectron - real(mo_set%maxocc, kind=dp)
616 IF (delectron < 0.0_dp) THEN
617 mo_set%occupation_numbers(nomo + ir) = delectron + real(mo_set%maxocc, kind=dp)
618 ELSE
619 mo_set%occupation_numbers(nomo + ir) = real(mo_set%maxocc, kind=dp)
620 END IF
621 END DO
622 nomo = nomo + irmo
623 ELSE
624 mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
625 nomo = nomo + 1
626 END IF
627 END IF
628 END IF
629 END IF
630 nmo = SIZE(mo_set%eigenvalues)
631
632 cpassert(nmo >= nomo)
633 cpassert((SIZE(mo_set%occupation_numbers) == nmo))
634
635 mo_set%homo = nomo
636 mo_set%lfomo = nomo + 1
637 mo_set%mu = mo_set%eigenvalues(nomo)
638
639 ! Check consistency of the array lengths
640 IF (PRESENT(eval_deriv)) THEN
641 equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
642 cpassert(equal_size)
643 END IF
644
645!calling of HP module HERE, before smear
646 IF (PRESENT(probe)) THEN
647 i_first = 1
648 IF (smear%fixed_mag_mom == -1.0_dp) THEN
649 nelec = real(mo_set%nelectron, dp)
650 ELSE
651 nelec = mo_set%n_el_f
652 END IF
653
654 mo_set%occupation_numbers(:) = 0.0_dp
655
656 CALL probe_occupancy(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, &
657 mo_set%eigenvalues, mo_set%mo_coeff, mo_set%maxocc, &
658 probe, n=nelec)
659 !NB: mu and T are taken from the hairy_probe type (defined in cp_control_types.F); these values are set in the input
660
661 ! Find the lowest fractional occupied MO (LFOMO)
662 DO imo = i_first, nmo
663 IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
664 mo_set%lfomo = imo
665 EXIT
666 END IF
667 END DO
668 is_large = abs(maxval(mo_set%occupation_numbers) - mo_set%maxocc) > probe(1)%eps_hp
669 ! this is not a real problem, but the temperature might be a bit large
670 IF (is_large) THEN
671 cpwarn("Hair-probes occupancy distribution includes the first MO")
672 END IF
673
674 ! Find the highest (fractional) occupied MO which will be now the HOMO
675 DO imo = nmo, mo_set%lfomo, -1
676 IF (mo_set%occupation_numbers(imo) > probe(1)%eps_hp) THEN
677 mo_set%homo = imo
678 EXIT
679 END IF
680 END DO
681 is_large = abs(minval(mo_set%occupation_numbers)) > probe(1)%eps_hp
682 IF (is_large) THEN
683 CALL cp_warn(__location__, &
684 "Hair-probes occupancy distribution includes the last MO => "// &
685 "Add more MOs for proper smearing.")
686 END IF
687
688 ! check that the total electron count is accurate
689 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > probe(1)%eps_hp*nelec)
690 IF (is_large) THEN
691 cpwarn("Total number of electrons is not accurate")
692 END IF
693
694 END IF
695
696 ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
697 IF (.NOT. PRESENT(smear)) THEN
698 ! there is no dependence of the energy on the eigenvalues
699 mo_set%uniform_occupation = .true.
700 IF (PRESENT(eval_deriv)) THEN
701 eval_deriv = 0.0_dp
702 END IF
703 CALL timestop(handle)
704 RETURN
705 END IF
706
707 ! Check if proper eigenvalues are already available
708 IF (smear%method /= smear_list) THEN
709 IF ((abs(mo_set%eigenvalues(1)) < 1.0e-12_dp) .AND. &
710 (abs(mo_set%eigenvalues(nmo)) < 1.0e-12_dp)) THEN
711 CALL timestop(handle)
712 RETURN
713 END IF
714 END IF
715
716 ! Perform smearing
717 IF (smear%do_smear) THEN
718 IF (PRESENT(xas_env)) THEN
719 i_first = xas_estate + 1
720 nelec = xas_nelectron
721 ELSE
722 i_first = 1
723 IF (smear%fixed_mag_mom == -1.0_dp) THEN
724 nelec = real(mo_set%nelectron, dp)
725 ELSE
726 nelec = mo_set%n_el_f
727 END IF
728 END IF
729 SELECT CASE (smear%method)
730 CASE (smear_fermi_dirac)
731 IF (.NOT. PRESENT(eval_deriv)) THEN
732 CALL smearfixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
733 mo_set%eigenvalues(1:mo_set%nmo), nelec, &
734 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
735 xas_estate, occ_estate)
736 ELSE
737 IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
738 tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
739 CALL smearfixedderivmv(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
740 mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), nelec, &
741 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
742 tmp_v, xas_estate, occ_estate)
743 END IF
744
745 ! Find the lowest fractional occupied MO (LFOMO)
746 DO imo = i_first, nmo
747 IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
748 mo_set%lfomo = imo
749 EXIT
750 END IF
751 END DO
752 IF (i_first <= nmo) THEN
753 is_large = abs(mo_set%occupation_numbers(i_first) - mo_set%maxocc) > smear%eps_fermi_dirac
754 ELSE
755 is_large = .false.
756 END IF
757 ! this is not a real problem, but the temperature might be a bit large
758 cpwarn_if(is_large, "Fermi-Dirac smearing includes the first MO")
759
760 ! Find the highest (fractional) occupied MO which will be now the HOMO
761 DO imo = nmo, mo_set%lfomo, -1
762 IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
763 mo_set%homo = imo
764 EXIT
765 END IF
766 END DO
767 is_large = abs(minval(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
768 IF (is_large) THEN
769 CALL cp_warn(__location__, &
770 "Fermi-Dirac smearing includes the last MO => "// &
771 "Add more MOs for proper smearing.")
772 END IF
773
774 ! check that the total electron count is accurate
775 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
776 cpwarn_if(is_large, "Total number of electrons is not accurate")
777
779 IF (.NOT. PRESENT(eval_deriv)) THEN
780 CALL smearfixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
781 mo_set%eigenvalues(1:mo_set%nmo), nelec, &
782 smear%smearing_width, mo_set%maxocc, smear%method, &
783 xas_estate, occ_estate)
784 ELSE
785 IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
786 tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
787 CALL smearfixedderivmv(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
788 mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), nelec, &
789 smear%smearing_width, mo_set%maxocc, smear%method, &
790 tmp_v, xas_estate, occ_estate)
791 END IF
792
793 ! Method label for warnings
794 SELECT CASE (smear%method)
795 CASE (smear_gaussian)
796 method_label = "Gaussian"
797 CASE (smear_mp)
798 method_label = "Methfessel-Paxton"
799 CASE (smear_mv)
800 method_label = "Marzari-Vanderbilt"
801 END SELECT
802
803 ! Find the lowest fractional occupied MO (LFOMO)
804 DO imo = i_first, nmo
805 IF (abs(mo_set%occupation_numbers(imo) - mo_set%maxocc) > smear%eps_fermi_dirac) THEN
806 mo_set%lfomo = imo
807 EXIT
808 END IF
809 END DO
810 IF (i_first <= nmo) THEN
811 is_large = abs(mo_set%occupation_numbers(i_first) - mo_set%maxocc) > smear%eps_fermi_dirac
812 ELSE
813 is_large = .false.
814 END IF
815 cpwarn_if(is_large, trim(method_label)//" smearing includes the first MO")
816
817 ! Find the highest (fractional) occupied MO which will be now the HOMO
818 DO imo = nmo, mo_set%lfomo, -1
819 IF (abs(mo_set%occupation_numbers(imo)) > smear%eps_fermi_dirac) THEN
820 mo_set%homo = imo
821 EXIT
822 END IF
823 END DO
824 is_large = abs(mo_set%occupation_numbers(nmo)) > smear%eps_fermi_dirac
825 IF (is_large) THEN
826 CALL cp_warn(__location__, &
827 trim(method_label)//" smearing includes the last MO => "// &
828 "Add more MOs for proper smearing.")
829 END IF
830
831 ! Check that the total electron count is accurate
832 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
833 cpwarn_if(is_large, "Total number of electrons is not accurate")
834
836 ! not implemented
837 cpassert(.NOT. PRESENT(eval_deriv))
838
839 ! Define the energy window for the eigenvalues
840 e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
841 IF (e1 <= mo_set%eigenvalues(1)) THEN
842 cpwarn("Energy window for smearing includes the first MO")
843 END IF
844
845 e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
846 IF (e2 >= mo_set%eigenvalues(nmo)) THEN
847 CALL cp_warn(__location__, &
848 "Energy window for smearing includes the last MO => "// &
849 "Add more MOs for proper smearing.")
850 END IF
851
852 ! Find the lowest fractional occupied MO (LFOMO)
853 DO imo = i_first, nomo
854 IF (mo_set%eigenvalues(imo) > e1) THEN
855 mo_set%lfomo = imo
856 EXIT
857 END IF
858 END DO
859
860 ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
861 DO imo = nmo, nomo, -1
862 IF (mo_set%eigenvalues(imo) < e2) THEN
863 mo_set%homo = imo
864 EXIT
865 END IF
866 END DO
867
868 ! Get the number of electrons to be smeared
869 edist = 0.0_dp
870 nelec = 0.0_dp
871
872 DO imo = mo_set%lfomo, mo_set%homo
873 nelec = nelec + mo_set%occupation_numbers(imo)
874 edist = edist + abs(e2 - mo_set%eigenvalues(imo))
875 END DO
876
877 ! Smear electrons inside the energy window
878 DO imo = mo_set%lfomo, mo_set%homo
879 edelta = abs(e2 - mo_set%eigenvalues(imo))
880 mo_set%occupation_numbers(imo) = min(mo_set%maxocc, nelec*edelta/edist)
881 nelec = nelec - mo_set%occupation_numbers(imo)
882 edist = edist - edelta
883 END DO
884
885 CASE (smear_list)
886 equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
887 cpassert(equal_size)
888 mo_set%occupation_numbers = smear%list
889 ! there is no dependence of the energy on the eigenvalues
890 IF (PRESENT(eval_deriv)) THEN
891 eval_deriv = 0.0_dp
892 END IF
893 ! most general case
894 mo_set%lfomo = 1
895 mo_set%homo = nmo
896 END SELECT
897
898 ! Check, if the smearing involves more than one MO
899 IF (mo_set%lfomo == mo_set%homo) THEN
900 mo_set%homo = nomo
901 mo_set%lfomo = nomo + 1
902 ELSE
903 mo_set%uniform_occupation = .false.
904 END IF
905
906 END IF ! do smear
907
908 ! zeros don't count as uniform
909 mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
910
911 IF (ALLOCATED(tmp_v)) DEALLOCATE (tmp_v)
912 CALL timestop(handle)
913
914 END SUBROUTINE set_mo_occupation_1
915
916END 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....
Defines control structures, which contain the parameters and the settings for the DFT-based calculati...
various routines to log and control the output. The idea is that decisions about where to log should ...
subroutine, public probe_occupancy(occ, fermi, kts, energies, coeff, maxocc, probe, n)
subroutine to calculate occupation number and 'Fermi' level using the
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
integer, parameter, public smear_gaussian
integer, parameter, public smear_mv
integer, parameter, public smear_mp
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
Unified smearing module supporting four methods: smear_fermi_dirac — Fermi-Dirac distribution smear_g...
subroutine, public smearocc(f, n, kts, e, mu, sigma, maxocc, method, estate, festate)
Returns occupations and smearing correction for a given set of energies and chemical potential,...
subroutine, public smearfixed(f, mu, kts, e, n, sigma, maxocc, method, estate, festate)
Bisection search for the chemical potential mu such that the total electron count equals N,...
subroutine, public smearfixedderivmv(result, f, mu, kts, e, n_el, sigma, maxocc, method, v, estate, festate)
Apply TRANSPOSE(df/de) to a vector WITHOUT forming the full N x N Jacobian. O(N) time and O(N) memory...
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