(git:6344619)
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 (abs(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 (abs(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!> \param emit_warnings emit first/last-MO and electron-count warnings
457!> \date 17.04.2002 (v1.0), 26.08.2008 (v1.1)
458!> \par History
459!> 10.2019 Added functionality to adjust mo occupation if the core
460!> charges are changed via CORE_CORRECTION during surface dipole
461!> calculation. Total number of electrons matches the total core
462!> charges if tot_zeff_corr is non-zero. Not yet implemented for
463!> OT type method. [Soumya Ghosh]
464!> \author Matthias Krack
465!> \version 1.1
466! **************************************************************************************************
467 SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr, probe, gce, &
468 emit_warnings)
469
470 TYPE(mo_set_type), INTENT(INOUT) :: mo_set
471 TYPE(smear_type), OPTIONAL :: smear
472 REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
473 TYPE(xas_environment_type), OPTIONAL, POINTER :: xas_env
474 REAL(kind=dp), OPTIONAL :: tot_zeff_corr
475 TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
476 POINTER :: probe
477 TYPE(gce_type), OPTIONAL, POINTER :: gce
478 LOGICAL, INTENT(IN), OPTIONAL :: emit_warnings
479
480 CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_1'
481
482 CHARACTER(LEN=20) :: method_label
483 INTEGER :: handle, i, i_first, imo, ir, irmo, nmo, &
484 nomo, xas_estate
485 LOGICAL :: do_gce, equal_size, is_large, &
486 my_emit_warnings
487 REAL(KIND=dp) :: delectron, e1, e2, edelta, edist, el_count, gce_mu, my_nelec, nelec, &
488 occ_estate, smear_width, total_zeff_corr, xas_nelectron
489 REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: tmp_v
490
491 CALL timeset(routinen, handle)
492
493 my_emit_warnings = .true.
494 IF (PRESENT(emit_warnings)) my_emit_warnings = emit_warnings
495
496 cpassert(ASSOCIATED(mo_set%eigenvalues))
497 cpassert(ASSOCIATED(mo_set%occupation_numbers))
498 mo_set%occupation_numbers(:) = 0.0_dp
499
500 ! Quick return, if no electrons are available
501 IF (mo_set%nelectron == 0) THEN
502 CALL timestop(handle)
503 RETURN
504 END IF
505
506 xas_estate = -1
507 occ_estate = 0.0_dp
508 IF (PRESENT(xas_env)) THEN
509 CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
510 nomo = ceiling(xas_nelectron + 1.0_dp - occ_estate - epsilon(0.0_dp))
511
512 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
513 IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
514 el_count = sum(mo_set%occupation_numbers(1:nomo))
515 IF (el_count > xas_nelectron) THEN
516 mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
517 END IF
518 el_count = sum(mo_set%occupation_numbers(1:nomo))
519 is_large = abs(el_count - xas_nelectron) > xas_nelectron*epsilon(el_count)
520 cpassert(.NOT. is_large)
521 ELSE
522 IF (PRESENT(gce)) THEN
523 do_gce = gce%do_gce
524 ELSE
525 do_gce = .false.
526 END IF
527 ! GCE workfunction (Fermi energy) mixing
528 IF (do_gce) THEN
529 smear_width = smear%smearing_width
530 IF (smear%method == smear_fermi_dirac) smear_width = smear%electronic_temperature
531 IF (gce%prev_workfunction < -1000.0_dp) THEN
532 my_nelec = real(mo_set%nelectron, dp)
533 CALL smearfixed(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, my_nelec, &
534 smear_width, mo_set%maxocc, smear%method)
535 gce%prev_workfunction = -501.0_dp
536 ELSE IF (gce%prev_workfunction < -500.0_dp) THEN
537 my_nelec = real(mo_set%nelectron, dp)
538 CALL smearfixed(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, mo_set%eigenvalues, my_nelec, &
539 smear_width, mo_set%maxocc, smear%method)
540 gce%prev_workfunction = gce%ref_esp - mo_set%mu
541 ELSE
542 gce_mu = gce%ref_esp - ((1.0_dp - gce%mixing_coef)*gce%prev_workfunction &
543 + gce%mixing_coef*gce%target_workfunction)
544 CALL smearocc(mo_set%occupation_numbers, my_nelec, mo_set%kTS, mo_set%eigenvalues, gce_mu, &
545 smear_width, mo_set%maxocc, smear%method)
546 mo_set%mu = gce_mu
547 gce%prev_workfunction = gce%ref_esp - mo_set%mu
548 is_large = abs(mo_set%occupation_numbers(1) - mo_set%maxocc) > smear%eps_fermi_dirac
549 cpwarn_if(is_large .AND. my_emit_warnings, "Fermi-Dirac smearing includes the first MO")
550 END IF
551 DO i = 1, SIZE(mo_set%occupation_numbers)
552 IF (mo_set%occupation_numbers(i) < mo_set%maxocc) THEN
553 mo_set%lfomo = i
554 EXIT
555 END IF
556 END DO
557 DO i = SIZE(mo_set%occupation_numbers), 1, -1
558 IF (abs(mo_set%occupation_numbers(i)) > smear%eps_fermi_dirac) THEN
559 mo_set%homo = i
560 EXIT
561 END IF
562 END DO
563 mo_set%uniform_occupation = .false.
564 mo_set%n_el_f = my_nelec
565 CALL timestop(handle)
566 RETURN
567 END IF
568
569 IF (modulo(mo_set%nelectron, int(mo_set%maxocc)) == 0) THEN
570 nomo = nint(mo_set%nelectron/mo_set%maxocc)
571 ! Initialize MO occupations
572 mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
573 ELSE
574 nomo = int(mo_set%nelectron/mo_set%maxocc) + 1
575 ! Initialize MO occupations
576 mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
577 mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
578 END IF
579! introduce applied potential correction here
580! electron density is adjusted according to applied core correction
581! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
582! see whether both surface dipole correction and core correction is present in
583! the inputfile
584 IF (PRESENT(tot_zeff_corr)) THEN
585! find the additional core charges
586 total_zeff_corr = tot_zeff_corr
587 IF (int(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
588 delectron = 0.0_dp
589 IF (total_zeff_corr < 0.0_dp) THEN
590! remove electron density from the mos
591 delectron = abs(total_zeff_corr) - real(mo_set%maxocc, kind=dp)
592 IF (delectron > 0.0_dp) THEN
593 mo_set%occupation_numbers(nomo) = 0.0_dp
594 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
595 DO ir = 1, irmo
596 delectron = delectron - real(mo_set%maxocc, kind=dp)
597 IF (delectron < 0.0_dp) THEN
598 mo_set%occupation_numbers(nomo - ir) = -delectron
599 ELSE
600 mo_set%occupation_numbers(nomo - ir) = 0.0_dp
601 END IF
602 END DO
603 nomo = nomo - irmo
604 IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
605 ELSE IF (delectron < 0.0_dp) THEN
606 mo_set%occupation_numbers(nomo) = -delectron
607 ELSE
608 mo_set%occupation_numbers(nomo) = 0.0_dp
609 nomo = nomo - 1
610 END IF
611 ELSE IF (total_zeff_corr > 0.0_dp) THEN
612! add electron density to the mos
613 delectron = total_zeff_corr - real(mo_set%maxocc, kind=dp)
614 IF (delectron > 0.0_dp) THEN
615 mo_set%occupation_numbers(nomo + 1) = real(mo_set%maxocc, kind=dp)
616 nomo = nomo + 1
617 irmo = ceiling(delectron/real(mo_set%maxocc, kind=dp))
618 DO ir = 1, irmo
619 delectron = delectron - real(mo_set%maxocc, kind=dp)
620 IF (delectron < 0.0_dp) THEN
621 mo_set%occupation_numbers(nomo + ir) = delectron + real(mo_set%maxocc, kind=dp)
622 ELSE
623 mo_set%occupation_numbers(nomo + ir) = real(mo_set%maxocc, kind=dp)
624 END IF
625 END DO
626 nomo = nomo + irmo
627 ELSE
628 mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
629 nomo = nomo + 1
630 END IF
631 END IF
632 END IF
633 END IF
634 nmo = SIZE(mo_set%eigenvalues)
635
636 cpassert(nmo >= nomo)
637 cpassert((SIZE(mo_set%occupation_numbers) == nmo))
638
639 mo_set%homo = nomo
640 mo_set%lfomo = nomo + 1
641 mo_set%mu = mo_set%eigenvalues(nomo)
642
643 ! Check consistency of the array lengths
644 IF (PRESENT(eval_deriv)) THEN
645 equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
646 cpassert(equal_size)
647 END IF
648
649!calling of HP module HERE, before smear
650 IF (PRESENT(probe)) THEN
651 i_first = 1
652 IF (smear%fixed_mag_mom == -1.0_dp) THEN
653 nelec = real(mo_set%nelectron, dp)
654 ELSE
655 nelec = mo_set%n_el_f
656 END IF
657
658 mo_set%occupation_numbers(:) = 0.0_dp
659
660 CALL probe_occupancy(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, &
661 mo_set%eigenvalues, mo_set%mo_coeff, mo_set%maxocc, &
662 probe, n=nelec)
663 !NB: mu and T are taken from the hairy_probe type (defined in cp_control_types.F); these values are set in the input
664
665 ! Find the lowest fractional occupied MO (LFOMO)
666 DO imo = i_first, nmo
667 IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
668 mo_set%lfomo = imo
669 EXIT
670 END IF
671 END DO
672 is_large = abs(maxval(mo_set%occupation_numbers) - mo_set%maxocc) > probe(1)%eps_hp
673 ! this is not a real problem, but the temperature might be a bit large
674 IF (is_large .AND. my_emit_warnings) THEN
675 cpwarn("Hair-probes occupancy distribution includes the first MO")
676 END IF
677
678 ! Find the highest (fractional) occupied MO which will be now the HOMO
679 DO imo = nmo, mo_set%lfomo, -1
680 IF (mo_set%occupation_numbers(imo) > probe(1)%eps_hp) THEN
681 mo_set%homo = imo
682 EXIT
683 END IF
684 END DO
685 is_large = abs(minval(mo_set%occupation_numbers)) > probe(1)%eps_hp
686 IF (is_large .AND. my_emit_warnings) THEN
687 CALL cp_warn(__location__, &
688 "Hair-probes occupancy distribution includes the last MO => "// &
689 "Add more MOs for proper smearing.")
690 END IF
691
692 ! check that the total electron count is accurate
693 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > probe(1)%eps_hp*nelec)
694 IF (is_large .AND. my_emit_warnings) THEN
695 cpwarn("Total number of electrons is not accurate")
696 END IF
697
698 END IF
699
700 ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
701 IF (.NOT. PRESENT(smear)) THEN
702 ! there is no dependence of the energy on the eigenvalues
703 mo_set%uniform_occupation = .true.
704 IF (PRESENT(eval_deriv)) THEN
705 eval_deriv = 0.0_dp
706 END IF
707 CALL timestop(handle)
708 RETURN
709 END IF
710
711 ! Check if proper eigenvalues are already available
712 IF (smear%method /= smear_list) THEN
713 IF ((abs(mo_set%eigenvalues(1)) < 1.0e-12_dp) .AND. &
714 (abs(mo_set%eigenvalues(nmo)) < 1.0e-12_dp)) THEN
715 CALL timestop(handle)
716 RETURN
717 END IF
718 END IF
719
720 ! Perform smearing
721 IF (smear%do_smear) THEN
722 IF (PRESENT(xas_env)) THEN
723 i_first = xas_estate + 1
724 nelec = xas_nelectron
725 ELSE
726 i_first = 1
727 IF (smear%fixed_mag_mom == -1.0_dp) THEN
728 nelec = real(mo_set%nelectron, dp)
729 ELSE
730 nelec = mo_set%n_el_f
731 END IF
732 END IF
733 SELECT CASE (smear%method)
734 CASE (smear_fermi_dirac)
735 IF (.NOT. PRESENT(eval_deriv)) THEN
736 CALL smearfixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
737 mo_set%eigenvalues(1:mo_set%nmo), nelec, &
738 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
739 xas_estate, occ_estate)
740 ELSE
741 IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
742 tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
743 CALL smearfixedderivmv(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
744 mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), nelec, &
745 smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
746 tmp_v, xas_estate, occ_estate)
747 END IF
748
749 ! Find the lowest fractional occupied MO (LFOMO)
750 DO imo = i_first, nmo
751 IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
752 mo_set%lfomo = imo
753 EXIT
754 END IF
755 END DO
756 IF (i_first <= nmo) THEN
757 is_large = abs(mo_set%occupation_numbers(i_first) - mo_set%maxocc) > smear%eps_fermi_dirac
758 ELSE
759 is_large = .false.
760 END IF
761 ! this is not a real problem, but the temperature might be a bit large
762 cpwarn_if(is_large .AND. my_emit_warnings, "Fermi-Dirac smearing includes the first MO")
763
764 ! Find the highest (fractional) occupied MO which will be now the HOMO
765 DO imo = nmo, mo_set%lfomo, -1
766 IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
767 mo_set%homo = imo
768 EXIT
769 END IF
770 END DO
771 is_large = abs(minval(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
772 IF (is_large .AND. my_emit_warnings) THEN
773 CALL cp_warn(__location__, &
774 "Fermi-Dirac smearing includes the last MO => "// &
775 "Add more MOs for proper smearing.")
776 END IF
777
778 ! check that the total electron count is accurate
779 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
780 cpwarn_if(is_large .AND. my_emit_warnings, "Total number of electrons is not accurate")
781
783 IF (.NOT. PRESENT(eval_deriv)) THEN
784 CALL smearfixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
785 mo_set%eigenvalues(1:mo_set%nmo), nelec, &
786 smear%smearing_width, mo_set%maxocc, smear%method, &
787 xas_estate, occ_estate)
788 ELSE
789 IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
790 tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
791 CALL smearfixedderivmv(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
792 mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), nelec, &
793 smear%smearing_width, mo_set%maxocc, smear%method, &
794 tmp_v, xas_estate, occ_estate)
795 END IF
796
797 ! Method label for warnings
798 SELECT CASE (smear%method)
799 CASE (smear_gaussian)
800 method_label = "Gaussian"
801 CASE (smear_mp)
802 method_label = "Methfessel-Paxton"
803 CASE (smear_mv)
804 method_label = "Marzari-Vanderbilt"
805 END SELECT
806
807 ! Find the lowest fractional occupied MO (LFOMO)
808 DO imo = i_first, nmo
809 IF (abs(mo_set%occupation_numbers(imo) - mo_set%maxocc) > smear%eps_fermi_dirac) THEN
810 mo_set%lfomo = imo
811 EXIT
812 END IF
813 END DO
814 IF (i_first <= nmo) THEN
815 is_large = abs(mo_set%occupation_numbers(i_first) - mo_set%maxocc) > smear%eps_fermi_dirac
816 ELSE
817 is_large = .false.
818 END IF
819 IF (is_large .AND. my_emit_warnings) THEN
820 cpwarn(trim(method_label)//" smearing includes the first MO")
821 END IF
822
823 ! Find the highest (fractional) occupied MO which will be now the HOMO
824 DO imo = nmo, mo_set%lfomo, -1
825 IF (abs(mo_set%occupation_numbers(imo)) > smear%eps_fermi_dirac) THEN
826 mo_set%homo = imo
827 EXIT
828 END IF
829 END DO
830 is_large = abs(mo_set%occupation_numbers(nmo)) > smear%eps_fermi_dirac
831 IF (is_large .AND. my_emit_warnings) THEN
832 CALL cp_warn(__location__, &
833 trim(method_label)//" smearing includes the last MO => "// &
834 "Add more MOs for proper smearing.")
835 END IF
836
837 ! Check that the total electron count is accurate
838 is_large = (abs(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
839 cpwarn_if(is_large .AND. my_emit_warnings, "Total number of electrons is not accurate")
840
842 ! not implemented
843 cpassert(.NOT. PRESENT(eval_deriv))
844
845 ! Define the energy window for the eigenvalues
846 e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
847 IF (e1 <= mo_set%eigenvalues(1) .AND. my_emit_warnings) THEN
848 cpwarn("Energy window for smearing includes the first MO")
849 END IF
850
851 e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
852 IF (e2 >= mo_set%eigenvalues(nmo) .AND. my_emit_warnings) THEN
853 CALL cp_warn(__location__, &
854 "Energy window for smearing includes the last MO => "// &
855 "Add more MOs for proper smearing.")
856 END IF
857
858 ! Find the lowest fractional occupied MO (LFOMO)
859 DO imo = i_first, nomo
860 IF (mo_set%eigenvalues(imo) > e1) THEN
861 mo_set%lfomo = imo
862 EXIT
863 END IF
864 END DO
865
866 ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
867 DO imo = nmo, nomo, -1
868 IF (mo_set%eigenvalues(imo) < e2) THEN
869 mo_set%homo = imo
870 EXIT
871 END IF
872 END DO
873
874 ! Get the number of electrons to be smeared
875 edist = 0.0_dp
876 nelec = 0.0_dp
877
878 DO imo = mo_set%lfomo, mo_set%homo
879 nelec = nelec + mo_set%occupation_numbers(imo)
880 edist = edist + abs(e2 - mo_set%eigenvalues(imo))
881 END DO
882
883 ! Smear electrons inside the energy window
884 DO imo = mo_set%lfomo, mo_set%homo
885 edelta = abs(e2 - mo_set%eigenvalues(imo))
886 mo_set%occupation_numbers(imo) = min(mo_set%maxocc, nelec*edelta/edist)
887 nelec = nelec - mo_set%occupation_numbers(imo)
888 edist = edist - edelta
889 END DO
890
891 CASE (smear_list)
892 equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
893 cpassert(equal_size)
894 mo_set%occupation_numbers = smear%list
895 ! there is no dependence of the energy on the eigenvalues
896 IF (PRESENT(eval_deriv)) THEN
897 eval_deriv = 0.0_dp
898 END IF
899 ! most general case
900 mo_set%lfomo = 1
901 mo_set%homo = nmo
902 END SELECT
903
904 ! Check, if the smearing involves more than one MO
905 IF (mo_set%lfomo == mo_set%homo) THEN
906 mo_set%homo = nomo
907 mo_set%lfomo = nomo + 1
908 ELSE
909 mo_set%uniform_occupation = .false.
910 END IF
911
912 END IF ! do smear
913
914 ! zeros don't count as uniform
915 mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
916
917 IF (ALLOCATED(tmp_v)) DEALLOCATE (tmp_v)
918 CALL timestop(handle)
919
920 END SUBROUTINE set_mo_occupation_1
921
922END 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.
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, cmo_coeff)
Get 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...
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