Line data Source code
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 :
15 : MODULE qs_mo_occupation
16 :
17 : USE cp_control_types, ONLY: hairy_probes_type
18 : USE cp_log_handling, ONLY: cp_to_string
19 : USE hairy_probes, ONLY: probe_occupancy
20 : USE input_constants, ONLY: smear_energy_window,&
21 : smear_fermi_dirac,&
22 : smear_gaussian,&
23 : smear_list,&
24 : smear_mp,&
25 : smear_mv
26 : USE kahan_sum, ONLY: accurate_sum
27 : USE kinds, ONLY: dp
28 : USE qs_mo_types, ONLY: get_mo_set,&
29 : has_uniform_occupation,&
30 : mo_set_type,&
31 : set_mo_set
32 : USE scf_control_types, ONLY: smear_type
33 : USE smearing_utils, ONLY: SmearFixed,&
34 : SmearFixedDerivMV
35 : USE util, ONLY: sort
36 : USE xas_env_types, ONLY: get_xas_env,&
37 : xas_environment_type
38 : #include "./base/base_uses.f90"
39 :
40 : IMPLICIT NONE
41 :
42 : PRIVATE
43 :
44 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_mo_occupation'
45 :
46 : PUBLIC :: set_mo_occupation
47 :
48 : INTERFACE set_mo_occupation
49 : MODULE PROCEDURE set_mo_occupation_1, set_mo_occupation_2
50 : END INTERFACE
51 :
52 : CONTAINS
53 :
54 : ! **************************************************************************************************
55 : !> \brief Occupation for smeared spin polarized electronic structures
56 : !> with relaxed multiplicity
57 : !>
58 : !> \param mo_array ...
59 : !> \param smear ...
60 : !> \date 10.03.2011 (MI)
61 : !> \author MI
62 : !> \version 1.0
63 : ! **************************************************************************************************
64 180 : SUBROUTINE set_mo_occupation_3(mo_array, smear)
65 :
66 : TYPE(mo_set_type), DIMENSION(2), INTENT(INOUT) :: mo_array
67 : TYPE(smear_type) :: smear
68 :
69 : CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_3'
70 :
71 : INTEGER :: all_nmo, handle, homo_a, homo_b, i, &
72 : lfomo_a, lfomo_b, nmo_a, nmo_b, &
73 : xas_estate
74 180 : INTEGER, ALLOCATABLE, DIMENSION(:) :: all_index
75 : LOGICAL :: is_large
76 : REAL(KIND=dp) :: all_nelec, kTS, mu, nelec_a, nelec_b, &
77 : occ_estate
78 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: all_eigval, all_occ
79 180 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b, occ_a, occ_b
80 :
81 180 : CALL timeset(routineN, handle)
82 :
83 180 : NULLIFY (eigval_a, eigval_b, occ_a, occ_b)
84 : CALL get_mo_set(mo_set=mo_array(1), nmo=nmo_a, eigenvalues=eigval_a, &
85 180 : occupation_numbers=occ_a)
86 : CALL get_mo_set(mo_set=mo_array(2), nmo=nmo_b, eigenvalues=eigval_b, &
87 180 : occupation_numbers=occ_b)
88 180 : all_nmo = nmo_a + nmo_b
89 540 : ALLOCATE (all_eigval(all_nmo))
90 360 : ALLOCATE (all_occ(all_nmo))
91 540 : ALLOCATE (all_index(all_nmo))
92 :
93 4628 : all_eigval(1:nmo_a) = eigval_a(1:nmo_a)
94 4628 : all_eigval(nmo_a + 1:all_nmo) = eigval_b(1:nmo_b)
95 :
96 180 : CALL sort(all_eigval, all_nmo, all_index)
97 :
98 180 : xas_estate = -1
99 180 : occ_estate = 0.0_dp
100 :
101 : nelec_a = 0.0_dp
102 : nelec_b = 0.0_dp
103 : all_nelec = 0.0_dp
104 180 : nelec_a = accurate_sum(occ_a(:))
105 180 : nelec_b = accurate_sum(occ_b(:))
106 180 : all_nelec = nelec_a + nelec_b
107 :
108 9076 : DO i = 1, all_nmo
109 9076 : IF (all_index(i) <= nmo_a) THEN
110 4448 : all_occ(i) = occ_a(all_index(i))
111 : ELSE
112 4448 : all_occ(i) = occ_b(all_index(i) - nmo_a)
113 : END IF
114 : END DO
115 :
116 : CALL SmearFixed(all_occ, mu, kTS, all_eigval, all_nelec, &
117 180 : smear%electronic_temperature, 1._dp, smear%method, xas_estate, occ_estate)
118 :
119 9256 : is_large = ABS(MAXVAL(all_occ) - 1.0_dp) > smear%eps_fermi_dirac
120 : ! this is not a real problem, but the temperature might be a bit large
121 180 : CPWARN_IF(is_large, "Fermi-Dirac smearing includes the first MO")
122 :
123 9256 : is_large = ABS(MINVAL(all_occ)) > smear%eps_fermi_dirac
124 180 : IF (is_large) &
125 : CALL cp_warn(__LOCATION__, &
126 : "Fermi-Dirac smearing includes the last MO => "// &
127 20 : "Add more MOs for proper smearing.")
128 :
129 : ! check that the total electron count is accurate
130 180 : is_large = (ABS(all_nelec - accurate_sum(all_occ(:))) > smear%eps_fermi_dirac*all_nelec)
131 180 : CPWARN_IF(is_large, "Total number of electrons is not accurate")
132 :
133 9076 : DO i = 1, all_nmo
134 9076 : IF (all_index(i) <= nmo_a) THEN
135 4448 : occ_a(all_index(i)) = all_occ(i)
136 4448 : eigval_a(all_index(i)) = all_eigval(i)
137 : ELSE
138 4448 : occ_b(all_index(i) - nmo_a) = all_occ(i)
139 4448 : eigval_b(all_index(i) - nmo_a) = all_eigval(i)
140 : END IF
141 : END DO
142 :
143 180 : nelec_a = accurate_sum(occ_a(:))
144 180 : nelec_b = accurate_sum(occ_b(:))
145 :
146 2058 : DO i = 1, nmo_a
147 2058 : IF (occ_a(i) < 1.0_dp) THEN
148 180 : lfomo_a = i
149 180 : EXIT
150 : END IF
151 : END DO
152 1782 : DO i = 1, nmo_b
153 1782 : IF (occ_b(i) < 1.0_dp) THEN
154 180 : lfomo_b = i
155 180 : EXIT
156 : END IF
157 : END DO
158 180 : homo_a = lfomo_a - 1
159 2250 : DO i = nmo_a, lfomo_a, -1
160 2250 : IF (occ_a(i) > smear%eps_fermi_dirac) THEN
161 88 : homo_a = i
162 88 : EXIT
163 : END IF
164 : END DO
165 180 : homo_b = lfomo_b - 1
166 2526 : DO i = nmo_b, lfomo_b, -1
167 2526 : IF (occ_b(i) > smear%eps_fermi_dirac) THEN
168 88 : homo_b = i
169 88 : EXIT
170 : END IF
171 : END DO
172 :
173 : CALL set_mo_set(mo_set=mo_array(1), kTS=kTS/2.0_dp, mu=mu, n_el_f=nelec_a, &
174 180 : lfomo=lfomo_a, homo=homo_a, uniform_occupation=.FALSE.)
175 : CALL set_mo_set(mo_set=mo_array(2), kTS=kTS/2.0_dp, mu=mu, n_el_f=nelec_b, &
176 180 : lfomo=lfomo_b, homo=homo_b, uniform_occupation=.FALSE.)
177 :
178 180 : CALL timestop(handle)
179 :
180 360 : END SUBROUTINE set_mo_occupation_3
181 :
182 : ! **************************************************************************************************
183 : !> \brief Prepare an occupation of alpha and beta MOs following an Aufbau
184 : !> principle, i.e. allowing a change in multiplicity.
185 : !> \param mo_array ...
186 : !> \param smear ...
187 : !> \param eval_deriv ...
188 : !> \param tot_zeff_corr ...
189 : !> \param probe ...
190 : !> \date 25.01.2010 (MK)
191 : !> \par History
192 : !> 10.2019 Added functionality to adjust mo occupation if the core
193 : !> charges are changed via CORE_CORRECTION during surface dipole
194 : !> calculation. Total number of electrons matches the total core
195 : !> charges if tot_zeff_corr is non-zero. Not yet implemented for
196 : !> OT type method. [Soumya Ghosh]
197 : !> \author Matthias Krack (MK)
198 : !> \version 1.0
199 : ! **************************************************************************************************
200 95479 : SUBROUTINE set_mo_occupation_2(mo_array, smear, eval_deriv, tot_zeff_corr, probe)
201 :
202 : TYPE(mo_set_type), DIMENSION(:), INTENT(INOUT) :: mo_array
203 : TYPE(smear_type) :: smear
204 : REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
205 : REAL(KIND=dp), OPTIONAL :: tot_zeff_corr
206 : TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
207 : POINTER :: probe
208 :
209 : CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_2'
210 :
211 : INTEGER :: handle, i, lumo_a, lumo_b, &
212 : multiplicity_new, multiplicity_old, &
213 : nelec
214 : REAL(KIND=dp) :: nelec_f, threshold
215 95479 : REAL(KIND=dp), DIMENSION(:), POINTER :: eigval_a, eigval_b
216 :
217 95479 : CALL timeset(routineN, handle)
218 :
219 : ! Fall back for the case that we have only one MO set
220 95479 : IF (SIZE(mo_array) == 1) THEN
221 85303 : IF (PRESENT(probe)) THEN
222 14 : CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
223 85289 : ELSE IF (PRESENT(eval_deriv)) THEN
224 : ! Change of MO occupancy to account for CORE_CORRECTION is not yet implemented
225 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
226 : ELSE
227 85289 : IF (PRESENT(tot_zeff_corr)) THEN
228 20 : CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
229 : ELSE
230 85269 : CALL set_mo_occupation_1(mo_array(1), smear=smear)
231 : END IF
232 : END IF
233 85303 : CALL timestop(handle)
234 : RETURN
235 : END IF
236 :
237 10176 : IF (PRESENT(probe)) THEN
238 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
239 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
240 : END IF
241 :
242 10176 : IF (smear%do_smear) THEN
243 1536 : IF (smear%fixed_mag_mom < 0.0_dp) THEN
244 180 : IF (PRESENT(tot_zeff_corr)) THEN
245 : CALL cp_warn(__LOCATION__, &
246 : "CORE_CORRECTION /= 0.0 might cause the cell to charge up "// &
247 : "that will lead to application of different background "// &
248 : "correction compared to the reference system. "// &
249 : "Use FIXED_MAGNETIC_MOMENT >= 0.0 if using SMEAR keyword "// &
250 0 : "to correct the electron density")
251 : END IF
252 180 : IF (smear%fixed_mag_mom /= -1.0_dp) THEN
253 180 : CPASSERT(.NOT. (PRESENT(eval_deriv)))
254 180 : CALL set_mo_occupation_3(mo_array, smear=smear)
255 180 : CALL timestop(handle)
256 180 : RETURN
257 : END IF
258 : ELSE
259 1356 : nelec_f = mo_array(1)%n_el_f + mo_array(2)%n_el_f
260 1356 : 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
261 2 : mo_array(1)%n_el_f = nelec_f/2.0_dp + smear%fixed_mag_mom/2.0_dp
262 2 : mo_array(2)%n_el_f = nelec_f/2.0_dp - smear%fixed_mag_mom/2.0_dp
263 : END IF
264 1356 : CPASSERT(.NOT. (PRESENT(eval_deriv)))
265 1356 : IF (PRESENT(tot_zeff_corr)) THEN
266 20 : CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
267 20 : CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
268 : ELSE
269 1336 : CALL set_mo_occupation_1(mo_array(1), smear=smear)
270 1336 : CALL set_mo_occupation_1(mo_array(2), smear=smear)
271 : END IF
272 : END IF
273 : END IF
274 :
275 9996 : IF (.NOT. ((mo_array(1)%flexible_electron_count > 0.0_dp) .AND. &
276 : (mo_array(2)%flexible_electron_count > 0.0_dp))) THEN
277 9830 : IF (PRESENT(probe)) THEN
278 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
279 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
280 9830 : ELSE IF (PRESENT(eval_deriv)) THEN
281 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
282 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
283 : ELSE
284 9830 : IF (PRESENT(tot_zeff_corr)) THEN
285 20 : CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
286 20 : CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
287 : ELSE
288 9810 : CALL set_mo_occupation_1(mo_array(1), smear=smear)
289 9810 : CALL set_mo_occupation_1(mo_array(2), smear=smear)
290 : END IF
291 : END IF
292 9830 : CALL timestop(handle)
293 9830 : RETURN
294 : END IF
295 :
296 166 : nelec = mo_array(1)%nelectron + mo_array(2)%nelectron
297 :
298 166 : multiplicity_old = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
299 :
300 166 : IF (mo_array(1)%nelectron >= mo_array(1)%nmo) &
301 : CALL cp_warn(__LOCATION__, &
302 : "All alpha MOs are occupied. Add more alpha MOs to "// &
303 0 : "allow for a higher multiplicity")
304 166 : IF ((mo_array(2)%nelectron >= mo_array(2)%nmo) .AND. (mo_array(2)%nelectron /= mo_array(1)%nelectron)) &
305 : CALL cp_warn(__LOCATION__, "All beta MOs are occupied. Add more beta MOs to "// &
306 0 : "allow for a lower multiplicity")
307 :
308 166 : eigval_a => mo_array(1)%eigenvalues
309 166 : eigval_b => mo_array(2)%eigenvalues
310 :
311 166 : lumo_a = 1
312 166 : lumo_b = 1
313 :
314 : ! Apply Aufbau principle
315 2046 : DO i = 1, nelec
316 : ! Threshold is needed to ensure a preference for alpha occupation in the case
317 : ! of degeneracy
318 1880 : threshold = MAX(mo_array(1)%flexible_electron_count, mo_array(2)%flexible_electron_count)
319 1880 : IF ((eigval_a(lumo_a) - threshold) < eigval_b(lumo_b)) THEN
320 1072 : lumo_a = lumo_a + 1
321 : ELSE
322 808 : lumo_b = lumo_b + 1
323 : END IF
324 1880 : IF (lumo_a > mo_array(1)%nmo) THEN
325 0 : IF (i /= nelec) &
326 : CALL cp_warn(__LOCATION__, &
327 : "All alpha MOs are occupied. Add more alpha MOs to "// &
328 0 : "allow for a higher multiplicity")
329 0 : IF (i < nelec) THEN
330 0 : lumo_a = lumo_a - 1
331 0 : lumo_b = lumo_b + 1
332 : END IF
333 : END IF
334 2046 : IF (lumo_b > mo_array(2)%nmo) THEN
335 34 : IF (lumo_b < lumo_a) &
336 : CALL cp_warn(__LOCATION__, &
337 : "All beta MOs are occupied. Add more beta MOs to "// &
338 0 : "allow for a lower multiplicity")
339 34 : IF (i < nelec) THEN
340 6 : lumo_a = lumo_a + 1
341 6 : lumo_b = lumo_b - 1
342 : END IF
343 : END IF
344 : END DO
345 :
346 166 : mo_array(1)%homo = lumo_a - 1
347 166 : mo_array(2)%homo = lumo_b - 1
348 :
349 166 : IF (mo_array(2)%homo > mo_array(1)%homo) THEN
350 : CALL cp_warn(__LOCATION__, &
351 : "More beta ("// &
352 : TRIM(ADJUSTL(cp_to_string(mo_array(2)%homo)))// &
353 : ") than alpha ("// &
354 : TRIM(ADJUSTL(cp_to_string(mo_array(1)%homo)))// &
355 0 : ") MOs are occupied. Resorting to low spin state")
356 0 : mo_array(1)%homo = nelec/2 + MODULO(nelec, 2)
357 0 : mo_array(2)%homo = nelec/2
358 : END IF
359 :
360 166 : mo_array(1)%nelectron = mo_array(1)%homo
361 166 : mo_array(2)%nelectron = mo_array(2)%homo
362 166 : multiplicity_new = mo_array(1)%nelectron - mo_array(2)%nelectron + 1
363 :
364 166 : IF (multiplicity_new /= multiplicity_old) &
365 : CALL cp_warn(__LOCATION__, &
366 : "Multiplicity changed from "// &
367 : TRIM(ADJUSTL(cp_to_string(multiplicity_old)))//" to "// &
368 8 : TRIM(ADJUSTL(cp_to_string(multiplicity_new))))
369 :
370 166 : IF (PRESENT(probe)) THEN
371 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, probe=probe)
372 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, probe=probe)
373 166 : ELSE IF (PRESENT(eval_deriv)) THEN
374 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, eval_deriv=eval_deriv)
375 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, eval_deriv=eval_deriv)
376 : ELSE
377 166 : IF (PRESENT(tot_zeff_corr)) THEN
378 0 : CALL set_mo_occupation_1(mo_array(1), smear=smear, tot_zeff_corr=tot_zeff_corr)
379 0 : CALL set_mo_occupation_1(mo_array(2), smear=smear, tot_zeff_corr=tot_zeff_corr)
380 : ELSE
381 166 : CALL set_mo_occupation_1(mo_array(1), smear=smear)
382 166 : CALL set_mo_occupation_1(mo_array(2), smear=smear)
383 : END IF
384 : END IF
385 :
386 166 : CALL timestop(handle)
387 :
388 95479 : END SUBROUTINE set_mo_occupation_2
389 :
390 : ! **************************************************************************************************
391 : !> \brief Smearing of the MO occupation with all kind of occupation numbers
392 : !> \param mo_set MO dataset structure
393 : !> \param smear optional smearing information
394 : !> \param eval_deriv on entry the derivative of the KS energy wrt to the occupation number
395 : !> on exit the derivative of the full free energy (i.e. KS and entropy) wrt to the eigenvalue
396 : !> \param xas_env ...
397 : !> \param tot_zeff_corr ...
398 : !> \param probe ...
399 : !> \date 17.04.2002 (v1.0), 26.08.2008 (v1.1)
400 : !> \par History
401 : !> 10.2019 Added functionality to adjust mo occupation if the core
402 : !> charges are changed via CORE_CORRECTION during surface dipole
403 : !> calculation. Total number of electrons matches the total core
404 : !> charges if tot_zeff_corr is non-zero. Not yet implemented for
405 : !> OT type method. [Soumya Ghosh]
406 : !> \author Matthias Krack
407 : !> \version 1.1
408 : ! **************************************************************************************************
409 226072 : SUBROUTINE set_mo_occupation_1(mo_set, smear, eval_deriv, xas_env, tot_zeff_corr, probe)
410 :
411 : TYPE(mo_set_type), INTENT(INOUT) :: mo_set
412 : TYPE(smear_type), OPTIONAL :: smear
413 : REAL(KIND=dp), DIMENSION(:), OPTIONAL, POINTER :: eval_deriv
414 : TYPE(xas_environment_type), OPTIONAL, POINTER :: xas_env
415 : REAL(KIND=dp), OPTIONAL :: tot_zeff_corr
416 : TYPE(hairy_probes_type), DIMENSION(:), OPTIONAL, &
417 : POINTER :: probe
418 :
419 : CHARACTER(LEN=*), PARAMETER :: routineN = 'set_mo_occupation_1'
420 :
421 : INTEGER :: handle, i_first, imo, ir, irmo, nmo, &
422 : nomo, xas_estate
423 : LOGICAL :: equal_size, is_large
424 : REAL(KIND=dp) :: delectron, e1, e2, edelta, edist, &
425 : el_count, nelec, occ_estate, &
426 : total_zeff_corr, xas_nelectron
427 226072 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: tmp_v
428 :
429 226072 : CALL timeset(routineN, handle)
430 :
431 226072 : CPASSERT(ASSOCIATED(mo_set%eigenvalues))
432 226072 : CPASSERT(ASSOCIATED(mo_set%occupation_numbers))
433 2454207 : mo_set%occupation_numbers(:) = 0.0_dp
434 :
435 : ! Quick return, if no electrons are available
436 226072 : IF (mo_set%nelectron == 0) THEN
437 1652 : CALL timestop(handle)
438 1652 : RETURN
439 : END IF
440 :
441 224420 : xas_estate = -1
442 224420 : occ_estate = 0.0_dp
443 224420 : IF (PRESENT(xas_env)) THEN
444 798 : CALL get_xas_env(xas_env=xas_env, xas_nelectron=xas_nelectron, occ_estate=occ_estate, xas_estate=xas_estate)
445 798 : nomo = CEILING(xas_nelectron + 1.0 - occ_estate - EPSILON(0.0_dp))
446 :
447 8102 : mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
448 798 : IF (xas_estate > 0) mo_set%occupation_numbers(xas_estate) = occ_estate
449 8102 : el_count = SUM(mo_set%occupation_numbers(1:nomo))
450 798 : IF (el_count > xas_nelectron) &
451 98 : mo_set%occupation_numbers(nomo) = mo_set%occupation_numbers(nomo) - (el_count - xas_nelectron)
452 8102 : el_count = SUM(mo_set%occupation_numbers(1:nomo))
453 798 : is_large = ABS(el_count - xas_nelectron) > xas_nelectron*EPSILON(el_count)
454 798 : CPASSERT(.NOT. is_large)
455 : ELSE
456 223622 : IF (MODULO(mo_set%nelectron, INT(mo_set%maxocc)) == 0) THEN
457 222580 : nomo = NINT(mo_set%nelectron/mo_set%maxocc)
458 : ! Initialize MO occupations
459 2131275 : mo_set%occupation_numbers(1:nomo) = mo_set%maxocc
460 : ELSE
461 1042 : nomo = INT(mo_set%nelectron/mo_set%maxocc) + 1
462 : ! Initialize MO occupations
463 6648 : mo_set%occupation_numbers(1:nomo - 1) = mo_set%maxocc
464 1042 : mo_set%occupation_numbers(nomo) = mo_set%nelectron - (nomo - 1)*mo_set%maxocc
465 : END IF
466 : ! introduce applied potential correction here
467 : ! electron density is adjusted according to applied core correction
468 : ! ref: SS, MT, MWF, JN PRL, 2018, 120, 246801
469 : ! see whether both surface dipole correction and core correction is present in
470 : ! the inputfile
471 223622 : IF (PRESENT(tot_zeff_corr)) THEN
472 : ! find the additional core charges
473 106 : total_zeff_corr = tot_zeff_corr
474 106 : IF (INT(mo_set%maxocc) == 1) total_zeff_corr = total_zeff_corr/2.0_dp
475 106 : delectron = 0.0_dp
476 106 : IF (total_zeff_corr < 0.0_dp) THEN
477 : ! remove electron density from the mos
478 106 : delectron = ABS(total_zeff_corr) - REAL(mo_set%maxocc, KIND=dp)
479 106 : IF (delectron > 0.0_dp) THEN
480 0 : mo_set%occupation_numbers(nomo) = 0.0_dp
481 0 : irmo = CEILING(delectron/REAL(mo_set%maxocc, KIND=dp))
482 0 : DO ir = 1, irmo
483 0 : delectron = delectron - REAL(mo_set%maxocc, KIND=dp)
484 0 : IF (delectron < 0.0_dp) THEN
485 0 : mo_set%occupation_numbers(nomo - ir) = -delectron
486 : ELSE
487 0 : mo_set%occupation_numbers(nomo - ir) = 0.0_dp
488 : END IF
489 : END DO
490 0 : nomo = nomo - irmo
491 0 : IF (mo_set%occupation_numbers(nomo) == 0.0_dp) nomo = nomo - 1
492 106 : ELSEIF (delectron < 0.0_dp) THEN
493 106 : mo_set%occupation_numbers(nomo) = -delectron
494 : ELSE
495 0 : mo_set%occupation_numbers(nomo) = 0.0_dp
496 0 : nomo = nomo - 1
497 : END IF
498 0 : ELSEIF (total_zeff_corr > 0.0_dp) THEN
499 : ! add electron density to the mos
500 0 : delectron = total_zeff_corr - REAL(mo_set%maxocc, KIND=dp)
501 0 : IF (delectron > 0.0_dp) THEN
502 0 : mo_set%occupation_numbers(nomo + 1) = REAL(mo_set%maxocc, KIND=dp)
503 0 : nomo = nomo + 1
504 0 : irmo = CEILING(delectron/REAL(mo_set%maxocc, KIND=dp))
505 0 : DO ir = 1, irmo
506 0 : delectron = delectron - REAL(mo_set%maxocc, KIND=dp)
507 0 : IF (delectron < 0.0_dp) THEN
508 0 : mo_set%occupation_numbers(nomo + ir) = delectron + REAL(mo_set%maxocc, KIND=dp)
509 : ELSE
510 0 : mo_set%occupation_numbers(nomo + ir) = REAL(mo_set%maxocc, KIND=dp)
511 : END IF
512 : END DO
513 0 : nomo = nomo + irmo
514 : ELSE
515 0 : mo_set%occupation_numbers(nomo + 1) = total_zeff_corr
516 0 : nomo = nomo + 1
517 : END IF
518 : END IF
519 : END IF
520 : END IF
521 224420 : nmo = SIZE(mo_set%eigenvalues)
522 :
523 224420 : CPASSERT(nmo >= nomo)
524 224420 : CPASSERT((SIZE(mo_set%occupation_numbers) == nmo))
525 :
526 224420 : mo_set%homo = nomo
527 224420 : mo_set%lfomo = nomo + 1
528 224420 : mo_set%mu = mo_set%eigenvalues(nomo)
529 :
530 : ! Check consistency of the array lengths
531 224420 : IF (PRESENT(eval_deriv)) THEN
532 0 : equal_size = (SIZE(mo_set%occupation_numbers, 1) == SIZE(eval_deriv, 1))
533 0 : CPASSERT(equal_size)
534 : END IF
535 :
536 : !calling of HP module HERE, before smear
537 224420 : IF (PRESENT(probe)) THEN
538 14 : i_first = 1
539 14 : IF (smear%fixed_mag_mom == -1.0_dp) THEN
540 0 : nelec = REAL(mo_set%nelectron, dp)
541 : ELSE
542 14 : nelec = mo_set%n_el_f
543 : END IF
544 :
545 294 : mo_set%occupation_numbers(:) = 0.0_dp
546 :
547 : CALL probe_occupancy(mo_set%occupation_numbers, mo_set%mu, mo_set%kTS, &
548 : mo_set%eigenvalues, mo_set%mo_coeff, mo_set%maxocc, &
549 14 : probe, N=nelec)
550 : !NB: mu and T are taken from the hairy_probe type (defined in cp_control_types.F); these values are set in the input
551 :
552 : ! Find the lowest fractional occupied MO (LFOMO)
553 198 : DO imo = i_first, nmo
554 198 : IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
555 14 : mo_set%lfomo = imo
556 14 : EXIT
557 : END IF
558 : END DO
559 308 : is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > probe(1)%eps_hp
560 : ! this is not a real problem, but the temperature might be a bit large
561 14 : IF (is_large) &
562 0 : CPWARN("Hair-probes occupancy distribution includes the first MO")
563 :
564 : ! Find the highest (fractional) occupied MO which will be now the HOMO
565 22 : DO imo = nmo, mo_set%lfomo, -1
566 22 : IF (mo_set%occupation_numbers(imo) > probe(1)%eps_hp) THEN
567 14 : mo_set%homo = imo
568 14 : EXIT
569 : END IF
570 : END DO
571 308 : is_large = ABS(MINVAL(mo_set%occupation_numbers)) > probe(1)%eps_hp
572 14 : IF (is_large) &
573 : CALL cp_warn(__LOCATION__, &
574 : "Hair-probes occupancy distribution includes the last MO => "// &
575 6 : "Add more MOs for proper smearing.")
576 :
577 : ! check that the total electron count is accurate
578 14 : is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > probe(1)%eps_hp*nelec)
579 14 : IF (is_large) &
580 0 : CPWARN("Total number of electrons is not accurate")
581 :
582 : END IF
583 :
584 : ! Quick return, if no smearing information is supplied (TO BE FIXED, smear should become non-optional...)
585 224420 : IF (.NOT. PRESENT(smear)) THEN
586 : ! there is no dependence of the energy on the eigenvalues
587 230 : mo_set%uniform_occupation = .TRUE.
588 230 : IF (PRESENT(eval_deriv)) THEN
589 0 : eval_deriv = 0.0_dp
590 : END IF
591 230 : CALL timestop(handle)
592 230 : RETURN
593 : END IF
594 :
595 : ! Check if proper eigenvalues are already available
596 224190 : IF (smear%method /= smear_list) THEN
597 224166 : IF ((ABS(mo_set%eigenvalues(1)) < 1.0E-12_dp) .AND. &
598 : (ABS(mo_set%eigenvalues(nmo)) < 1.0E-12_dp)) THEN
599 50884 : CALL timestop(handle)
600 50884 : RETURN
601 : END IF
602 : END IF
603 :
604 : ! Perform smearing
605 173306 : IF (smear%do_smear) THEN
606 19788 : IF (PRESENT(xas_env)) THEN
607 30 : i_first = xas_estate + 1
608 30 : nelec = xas_nelectron
609 : ELSE
610 19758 : i_first = 1
611 19758 : IF (smear%fixed_mag_mom == -1.0_dp) THEN
612 0 : nelec = REAL(mo_set%nelectron, dp)
613 : ELSE
614 19758 : nelec = mo_set%n_el_f
615 : END IF
616 : END IF
617 18220 : SELECT CASE (smear%method)
618 : CASE (smear_fermi_dirac)
619 18220 : IF (.NOT. PRESENT(eval_deriv)) THEN
620 : CALL SmearFixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
621 : mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
622 : smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
623 18220 : xas_estate, occ_estate)
624 : ELSE
625 0 : IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
626 0 : tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
627 : CALL SmearFixedDerivMV(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
628 : mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
629 : smear%electronic_temperature, mo_set%maxocc, smear_fermi_dirac, &
630 0 : tmp_v, xas_estate, occ_estate)
631 : END IF
632 :
633 : ! Find the lowest fractional occupied MO (LFOMO)
634 137354 : DO imo = i_first, nmo
635 137354 : IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
636 18220 : mo_set%lfomo = imo
637 18220 : EXIT
638 : END IF
639 : END DO
640 379230 : is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
641 : ! this is not a real problem, but the temperature might be a bit large
642 18220 : CPWARN_IF(is_large, "Fermi-Dirac smearing includes the first MO")
643 :
644 : ! Find the highest (fractional) occupied MO which will be now the HOMO
645 194598 : DO imo = nmo, mo_set%lfomo, -1
646 194598 : IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
647 10490 : mo_set%homo = imo
648 10490 : EXIT
649 : END IF
650 : END DO
651 379230 : is_large = ABS(MINVAL(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
652 18220 : IF (is_large) &
653 : CALL cp_warn(__LOCATION__, &
654 : "Fermi-Dirac smearing includes the last MO => "// &
655 684 : "Add more MOs for proper smearing.")
656 :
657 : ! check that the total electron count is accurate
658 18220 : is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
659 18220 : CPWARN_IF(is_large, "Total number of electrons is not accurate")
660 :
661 : CASE (smear_gaussian, smear_mp, smear_mv)
662 1386 : IF (.NOT. PRESENT(eval_deriv)) THEN
663 : CALL SmearFixed(mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, mo_set%kTS, &
664 : mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
665 : smear%smearing_width, mo_set%maxocc, smear%method, &
666 1386 : xas_estate, occ_estate)
667 : ELSE
668 0 : IF (.NOT. ALLOCATED(tmp_v)) ALLOCATE (tmp_v(SIZE(eval_deriv)))
669 0 : tmp_v(:) = eval_deriv - mo_set%eigenvalues + mo_set%mu
670 : CALL SmearFixedDerivMV(eval_deriv, mo_set%occupation_numbers(1:mo_set%nmo), mo_set%mu, &
671 : mo_set%kTS, mo_set%eigenvalues(1:mo_set%nmo), Nelec, &
672 : smear%smearing_width, mo_set%maxocc, smear%method, &
673 0 : tmp_v, xas_estate, occ_estate)
674 : END IF
675 :
676 : ! Find the lowest fractional occupied MO (LFOMO)
677 17480 : DO imo = i_first, nmo
678 17480 : IF (mo_set%occupation_numbers(imo) < mo_set%maxocc) THEN
679 1386 : mo_set%lfomo = imo
680 1386 : EXIT
681 : END IF
682 : END DO
683 36212 : is_large = ABS(MAXVAL(mo_set%occupation_numbers) - mo_set%maxocc) > smear%eps_fermi_dirac
684 1386 : CPWARN_IF(is_large, "Gaussian smearing includes the first MO")
685 :
686 : ! Find the highest (fractional) occupied MO which will be now the HOMO
687 16852 : DO imo = nmo, mo_set%lfomo, -1
688 16852 : IF (mo_set%occupation_numbers(imo) > smear%eps_fermi_dirac) THEN
689 1014 : mo_set%homo = imo
690 1014 : EXIT
691 : END IF
692 : END DO
693 36212 : is_large = ABS(MINVAL(mo_set%occupation_numbers)) > smear%eps_fermi_dirac
694 1386 : IF (is_large) &
695 : CALL cp_warn(__LOCATION__, &
696 : "Gaussian smearing includes the last MO => "// &
697 0 : "Add more MOs for proper smearing.")
698 :
699 : ! Check that the total electron count is accurate
700 1386 : is_large = (ABS(nelec - accurate_sum(mo_set%occupation_numbers(:))) > smear%eps_fermi_dirac*nelec)
701 1386 : CPWARN_IF(is_large, "Total number of electrons is not accurate")
702 :
703 : CASE (smear_energy_window)
704 : ! not implemented
705 158 : CPASSERT(.NOT. PRESENT(eval_deriv))
706 :
707 : ! Define the energy window for the eigenvalues
708 158 : e1 = mo_set%eigenvalues(mo_set%homo) - 0.5_dp*smear%window_size
709 158 : IF (e1 <= mo_set%eigenvalues(1)) THEN
710 0 : CPWARN("Energy window for smearing includes the first MO")
711 : END IF
712 :
713 158 : e2 = mo_set%eigenvalues(mo_set%homo) + 0.5_dp*smear%window_size
714 158 : IF (e2 >= mo_set%eigenvalues(nmo)) &
715 : CALL cp_warn(__LOCATION__, &
716 : "Energy window for smearing includes the last MO => "// &
717 0 : "Add more MOs for proper smearing.")
718 :
719 : ! Find the lowest fractional occupied MO (LFOMO)
720 2636 : DO imo = i_first, nomo
721 2636 : IF (mo_set%eigenvalues(imo) > e1) THEN
722 158 : mo_set%lfomo = imo
723 158 : EXIT
724 : END IF
725 : END DO
726 :
727 : ! Find the highest fractional occupied (non-zero) MO which will be the HOMO
728 1344 : DO imo = nmo, nomo, -1
729 1344 : IF (mo_set%eigenvalues(imo) < e2) THEN
730 158 : mo_set%homo = imo
731 158 : EXIT
732 : END IF
733 : END DO
734 :
735 : ! Get the number of electrons to be smeared
736 158 : edist = 0.0_dp
737 158 : nelec = 0.0_dp
738 :
739 390 : DO imo = mo_set%lfomo, mo_set%homo
740 232 : nelec = nelec + mo_set%occupation_numbers(imo)
741 390 : edist = edist + ABS(e2 - mo_set%eigenvalues(imo))
742 : END DO
743 :
744 : ! Smear electrons inside the energy window
745 390 : DO imo = mo_set%lfomo, mo_set%homo
746 232 : edelta = ABS(e2 - mo_set%eigenvalues(imo))
747 232 : mo_set%occupation_numbers(imo) = MIN(mo_set%maxocc, nelec*edelta/edist)
748 232 : nelec = nelec - mo_set%occupation_numbers(imo)
749 390 : edist = edist - edelta
750 : END DO
751 :
752 : CASE (smear_list)
753 24 : equal_size = SIZE(mo_set%occupation_numbers, 1) == SIZE(smear%list, 1)
754 24 : CPASSERT(equal_size)
755 168 : mo_set%occupation_numbers = smear%list
756 : ! there is no dependence of the energy on the eigenvalues
757 24 : IF (PRESENT(eval_deriv)) THEN
758 0 : eval_deriv = 0.0_dp
759 : END IF
760 : ! most general case
761 24 : mo_set%lfomo = 1
762 19812 : mo_set%homo = nmo
763 : END SELECT
764 :
765 : ! Check, if the smearing involves more than one MO
766 19788 : IF (mo_set%lfomo == mo_set%homo) THEN
767 1432 : mo_set%homo = nomo
768 1432 : mo_set%lfomo = nomo + 1
769 : ELSE
770 18356 : mo_set%uniform_occupation = .FALSE.
771 : END IF
772 :
773 : END IF ! do smear
774 :
775 : ! zeros don't count as uniform
776 173306 : mo_set%uniform_occupation = has_uniform_occupation(mo_set=mo_set)
777 :
778 173306 : IF (ALLOCATED(tmp_v)) DEALLOCATE (tmp_v)
779 173306 : CALL timestop(handle)
780 :
781 173306 : END SUBROUTINE set_mo_occupation_1
782 :
783 : END MODULE qs_mo_occupation
|