(git:f2099e5)
Loading...
Searching...
No Matches
bse_properties.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 Routines for computing excitonic properties, e.g. exciton diameter, from the BSE
10!> \par History
11!> 10.2024 created [Maximilian Graml]
12! **************************************************************************************************
14 USE bse_types, ONLY: bse_env_type
15 USE bse_util, ONLY: fm_general_add_bse,&
19 USE cp_files, ONLY: close_file,&
23 USE cp_fm_diag, ONLY: cp_fm_svd
27 USE cp_fm_types, ONLY: &
33 USE kinds, ONLY: dp
34 USE mathconstants, ONLY: pi
36 USE physcon, ONLY: c_light_au,&
37 evolt
38 USE qs_mo_types, ONLY: allocate_mo_set,&
42#include "./base/base_uses.f90"
43
44 IMPLICIT NONE
45
46 PRIVATE
47
48 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bse_properties'
49
50 PUBLIC :: exciton_descr_type
51
54
55! TYPE definitions for exciton wavefunction descriptors
56
58 REAL(kind=dp), DIMENSION(3) :: r_e = 0.0_dp, &
59 r_h = 0.0_dp, &
60 r_e_sq = 0.0_dp, &
61 r_h_sq = 0.0_dp, &
62 r_e_shift = 0.0_dp, &
63 r_h_shift = 0.0_dp, &
64 d_eh_dir = 0.0_dp, &
65 sigma_e_dir = 0.0_dp, &
66 sigma_h_dir = 0.0_dp, &
67 d_exc_dir = 0.0_dp
68 REAL(kind=dp), DIMENSION(3, 3) :: r_e_h = 0.0_dp, &
69 cov_e_h = 0.0_dp, &
70 corr_e_h_matrix = 0.0_dp
71 REAL(kind=dp) :: sigma_e = 0.0_dp, &
72 sigma_h = 0.0_dp, &
73 cov_e_h_sum = 0.0_dp, &
74 corr_e_h = 0.0_dp, &
75 diff_r_abs = 0.0_dp, &
76 diff_r_sqr = 0.0_dp, &
77 norm_xpy = 0.0_dp
78 LOGICAL :: flag_tda = .false.
79 END TYPE exciton_descr_type
80
81CONTAINS
82
83! **************************************************************************************************
84!> \brief Compute and return BSE dipoles d_r^n = sqrt(2) sum_ia < ψ_i | r | ψ_a > ( X_ia^n + Y_ia^n )
85!> and oscillator strengths f^n = 2/3 * Ω^n sum_r∈(x,y,z) ( d_r^n )^2
86!> Prelim Ref.: Eqs. (23), (24)
87!> in J. Chem. Phys. 152, 044105 (2020); https://doi.org/10.1063/1.5123290
88!> \param fm_eigvec_X ...
89!> \param Exc_ens ...
90!> \param fm_dipole_ai_trunc ...
91!> \param trans_mom_bse BSE dipole vectors in real space per excitation level
92!> \param oscill_str Oscillator strength per excitation level
93!> \param polarizability_residues Residues of polarizability ("tensorial oscillator strength")
94!> per excitation level
95!> \param bse_env the BSE environment (settings)
96!> \param homo_red ...
97!> \param virtual_red ...
98!> \param unit_nr ...
99!> \param fm_eigvec_Y ...
100! **************************************************************************************************
101 SUBROUTINE get_oscillator_strengths(fm_eigvec_X, Exc_ens, fm_dipole_ai_trunc, &
102 trans_mom_bse, oscill_str, polarizability_residues, &
103 bse_env, homo_red, virtual_red, unit_nr, &
104 fm_eigvec_Y)
105
106 TYPE(cp_fm_type), INTENT(IN) :: fm_eigvec_x
107 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
108 INTENT(IN) :: exc_ens
109 TYPE(cp_fm_type), DIMENSION(3) :: fm_dipole_ai_trunc
110 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :), &
111 INTENT(OUT) :: trans_mom_bse
112 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
113 INTENT(OUT) :: oscill_str
114 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :), &
115 INTENT(OUT) :: polarizability_residues
116 TYPE(bse_env_type), INTENT(IN) :: bse_env
117 INTEGER, INTENT(IN) :: homo_red, virtual_red, unit_nr
118 TYPE(cp_fm_type), INTENT(IN), OPTIONAL :: fm_eigvec_y
119
120 CHARACTER(LEN=*), PARAMETER :: routinen = 'get_oscillator_strengths'
121
122 INTEGER :: handle, idir, jdir, n, n_exc
123 TYPE(cp_fm_struct_type), POINTER :: fm_struct_dipole_mo_trunc_reordered, &
124 fm_struct_trans_mom_bse
125 TYPE(cp_fm_type) :: fm_eigvec_xysum
126 TYPE(cp_fm_type), DIMENSION(3) :: fm_dipole_mo_trunc_reordered, &
127 fm_dipole_per_dir, fm_trans_mom_bse
128
129 CALL timeset(routinen, handle)
130
131 ! an iterative solver provides fewer excitations than transitions
132 CALL cp_fm_get_info(fm_eigvec_x, ncol_global=n_exc)
133
134 CALL cp_fm_struct_create(fm_struct_dipole_mo_trunc_reordered, fm_eigvec_x%matrix_struct%para_env, &
135 fm_eigvec_x%matrix_struct%context, 1, homo_red*virtual_red)
136 CALL cp_fm_struct_create(fm_struct_trans_mom_bse, fm_eigvec_x%matrix_struct%para_env, &
137 fm_eigvec_x%matrix_struct%context, 1, n_exc)
138
139 ! Include excitonic amplitudes in dipoles, i.e. obtain "BSE dipoles":
140 ! \vec{D}_n = sqrt(2) * sum_{i,a} \vec{D}_ai (X_{ai}^{(n)} + Y_{ai}^{(n)})
141
142 ! Reorder dipoles in order to execute the sum over i and a by parallel gemm
143 DO idir = 1, 3
144 CALL cp_fm_create(fm_dipole_mo_trunc_reordered(idir), matrix_struct=fm_struct_dipole_mo_trunc_reordered, &
145 name="dipoles_mo_reordered")
146 CALL cp_fm_set_all(fm_dipole_mo_trunc_reordered(idir), 0.0_dp)
147 CALL fm_general_add_bse(fm_dipole_mo_trunc_reordered(idir), fm_dipole_ai_trunc(idir), 1.0_dp, &
148 1, 1, &
149 1, virtual_red, &
150 unit_nr, [2, 4, 3, 1], bse_env)
151 CALL cp_fm_release(fm_dipole_per_dir(idir))
152 END DO
153
154 DO idir = 1, 3
155 CALL cp_fm_create(fm_trans_mom_bse(idir), matrix_struct=fm_struct_trans_mom_bse, &
156 name="excitonic_dipoles")
157 CALL cp_fm_set_all(fm_trans_mom_bse(idir), 0.0_dp)
158 END DO
159
160 ! If TDA is invoked, Y is not present as it is simply 0
161 CALL cp_fm_create(fm_eigvec_xysum, matrix_struct=fm_eigvec_x%matrix_struct, name="excit_amplitude_sum")
162 CALL cp_fm_set_all(fm_eigvec_xysum, 0.0_dp)
163 CALL cp_fm_to_fm(fm_eigvec_x, fm_eigvec_xysum)
164 IF (PRESENT(fm_eigvec_y)) THEN
165 CALL cp_fm_scale_and_add(1.0_dp, fm_eigvec_xysum, 1.0_dp, fm_eigvec_y)
166 END IF
167 DO idir = 1, 3
168 CALL parallel_gemm('N', 'N', 1, n_exc, homo_red*virtual_red, sqrt(2.0_dp), &
169 fm_dipole_mo_trunc_reordered(idir), fm_eigvec_xysum, 0.0_dp, fm_trans_mom_bse(idir))
170 END DO
171
172 ! Get oscillator strengths themselves
173 ALLOCATE (oscill_str(n_exc))
174 ! trans_mom_bse needs to be a 2D array per direction idir, such that cp_fm_get_submatrix can directly
175 ! write to it
176 ALLOCATE (trans_mom_bse(3, 1, n_exc))
177 ALLOCATE (polarizability_residues(3, 3, n_exc))
178 trans_mom_bse(:, :, :) = 0.0_dp
179
180 ! Sum over all directions
181 DO idir = 1, 3
182 CALL cp_fm_get_submatrix(fm_trans_mom_bse(idir), trans_mom_bse(idir, :, :))
183 END DO
184
185 DO n = 1, n_exc
186 DO idir = 1, 3
187 DO jdir = 1, 3
188 polarizability_residues(idir, jdir, n) = 2.0_dp*exc_ens(n)*trans_mom_bse(idir, 1, n)*trans_mom_bse(jdir, 1, n)
189 END DO
190 END DO
191 oscill_str(n) = 2.0_dp/3.0_dp*exc_ens(n)*sum(abs(trans_mom_bse(:, 1, n))**2)
192 END DO
193
194 CALL cp_fm_struct_release(fm_struct_dipole_mo_trunc_reordered)
195 CALL cp_fm_struct_release(fm_struct_trans_mom_bse)
196 DO idir = 1, 3
197 CALL cp_fm_release(fm_dipole_mo_trunc_reordered(idir))
198 CALL cp_fm_release(fm_trans_mom_bse(idir))
199 CALL cp_fm_release(fm_dipole_ai_trunc(idir))
200 END DO
201 CALL cp_fm_release(fm_eigvec_xysum)
202
203 CALL timestop(handle)
204
205 END SUBROUTINE get_oscillator_strengths
206
207! **************************************************************************************************
208!> \brief Computes and returns absorption spectrum for the frequency range and broadening
209!> provided by the user.
210!> Prelim Ref.: C. Ullrich, Time-Dependent Density-Functional Theory: Concepts and Applications
211!> (Oxford University Press, Oxford, 2012), Eq. 7.51
212!> \param oscill_str ...
213!> \param polarizability_residues ...
214!> \param Exc_ens ...
215!> \param info_approximation ...
216!> \param unit_nr ...
217!> \param bse_env the BSE environment (settings; para_env, whose ionode writes the files)
218! **************************************************************************************************
219 SUBROUTINE compute_and_print_absorption_spectrum(oscill_str, polarizability_residues, Exc_ens, &
220 info_approximation, unit_nr, bse_env)
221
222 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
223 INTENT(IN) :: oscill_str
224 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :), &
225 INTENT(IN) :: polarizability_residues
226 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
227 INTENT(IN) :: exc_ens
228 CHARACTER(LEN=10) :: info_approximation
229 INTEGER, INTENT(IN) :: unit_nr
230 TYPE(bse_env_type), INTENT(IN) :: bse_env
231
232 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_and_print_absorption_spectrum'
233
234 CHARACTER(LEN=10) :: eta_str, width_eta_format_str
235 CHARACTER(LEN=40) :: file_name_crosssection, &
236 file_name_spectrum
237 INTEGER :: handle, i, idir, j, jdir, k, num_steps, &
238 unit_nr_file, width_eta
239 REAL(kind=dp) :: eta, freq_end, freq_start, freq_step, &
240 omega
241 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: abs_cross_section, abs_spectrum
242 REAL(kind=dp), DIMENSION(:), POINTER :: eta_list
243
244 CALL timeset(routinen, handle)
245
246 freq_step = bse_env%bse_spectrum_freq_step_size
247 freq_start = bse_env%bse_spectrum_freq_start
248 freq_end = bse_env%bse_spectrum_freq_end
249 eta_list => bse_env%bse_eta_spectrum_list
250
251 ! Calculate number of steps to fit given frequency range
252 num_steps = nint((freq_end - freq_start)/freq_step) + 1
253
254 DO k = 1, SIZE(eta_list)
255 eta = eta_list(k)
256
257 ! Some magic to get a nice formatting of the eta value in filenames
258 width_eta = max(1, int(log10(eta)) + 1) + 4
259 WRITE (width_eta_format_str, "(A2,I0,A3)") '(F', width_eta, '.3)'
260 WRITE (eta_str, width_eta_format_str) eta*evolt
261 ! Filename itself
262 file_name_spectrum = 'BSE'//trim(adjustl(info_approximation))//'eta='//trim(eta_str)//'.spectrum'
263 file_name_crosssection = 'BSE'//trim(adjustl(info_approximation))//'eta='//trim(eta_str)//'.crosssection'
264
265 ! First column is frequency in eV, second column is imaginary part of the trace of the polarizability
266 ! The following 9 columns are the entries of the polarizability tensor
267 ALLOCATE (abs_spectrum(num_steps, 11))
268 abs_spectrum(:, :) = 0.0_dp
269 ! Also calculate and print the photoabsorption cross section tensor
270 ! σ_{µ µ'}(ω) = 4πω Im{α_{µ µ'}(ω)} / c
271 ALLOCATE (abs_cross_section(num_steps, 11))
272 abs_cross_section(:, :) = 0.0_dp
273
274 ! Calculate the imaginary part of the mean dipole polarizability α_{avg}(ω)
275 ! which is given by (cf. C. Ullrichs Book on TDDFT, Eq. 7.51)
276 ! We introduce an additional - due to his convention for charge vs particle density, see also:
277 ! Computer Physics Communications, 208:149–161, November 2016
278 ! https://doi.org/10.1016/j.cpc.2016.06.019
279 ! α_{avg}(ω) = - \sum_{n=1}^{N_exc} \frac{f_n}{(ω+iη)² - (Ω^n)²}
280 ! and then the imaginary part is (in the limit η -> 0)
281 ! Im[α_{avg}(ω)] = - \sum_{n=1}^{N_exc} f_n * η / ((ω - Ω^n)² + η²)
282 ! where f_n are the oscillator strengths and E_exc the excitation energies
283 ! For the full polarizability tensor, we have
284 ! α_{µ µ'}(ω) = - \sum_n [2 Ω^n d^n_µ d^n_µ'] / [(ω+iη)^2- (Ω^n)^2]
285 ! = - \sum_n "polarizability_residues" / [(ω+iη)^2- (Ω^n)^2]
286 DO i = 1, num_steps
287 omega = freq_start + (i - 1)*freq_step
288 abs_spectrum(i, 1) = omega
289 DO j = 1, SIZE(oscill_str)
290 abs_spectrum(i, 2) = abs_spectrum(i, 2) - oscill_str(j)* &
291 aimag(1/((omega + cmplx(0.0, eta, kind=dp))**2 - exc_ens(j)**2))
292 DO idir = 1, 3
293 DO jdir = 1, 3
294 ! Factor 2 from formula for tensor is already in the polarizability_residues
295 ! to follow the same convention as the oscillator strengths
296 abs_spectrum(i, 2 + (idir - 1)*3 + jdir) = abs_spectrum(i, 2 + (idir - 1)*3 + jdir) &
297 - polarizability_residues(idir, jdir, j)* &
298 aimag(1/((omega + cmplx(0.0, eta, kind=dp))**2 - exc_ens(j)**2))
299 END DO
300 END DO
301 END DO
302 END DO
303
304 ! Extract cross section σ from polarizability tensor
305 DO i = 1, num_steps
306 omega = abs_spectrum(i, 1)
307 abs_cross_section(i, 1) = omega
308 abs_cross_section(i, 2:) = 4.0_dp*pi*abs_spectrum(i, 2:)*omega/c_light_au
309 END DO
310
311 !For debug runs: Export an entry of the two tensors to allow regtests on spectra
312 IF (bse_env%bse_debug_print) THEN
313 IF (unit_nr > 0) THEN
314 WRITE (unit_nr, '(T2,A10,T13,A,T65,F16.4)') 'BSE|DEBUG|', &
315 'Averaged dynamical dipole polarizability at 8.2 eV:', &
316 abs_spectrum(83, 2)
317 WRITE (unit_nr, '(T2,A10,T13,A,T65,F16.4)') 'BSE|DEBUG|', &
318 'Averaged photoabsorption cross section at 8.2 eV:', &
319 abs_cross_section(83, 2)
320 END IF
321 END IF
322
323 ! Print it to file from the ionode of the BSE communicator, at every print level; the
324 ! default logger inside the MP2/GW driver is the subgroup's and would make every group's root write
325 IF (bse_env%para_env%is_source()) THEN
326 CALL open_file(file_name_crosssection, unit_number=unit_nr_file, &
327 file_status="UNKNOWN", file_action="WRITE")
328 WRITE (unit_nr_file, '(A,A6)') "# Photoabsorption cross section σ_{µ µ'}(ω) = -4πω/c * Im[ \sum_n "// &
329 "[2 Ω^n d_µ^n d_µ'^n] / [(ω+iη)²- (Ω^n)²] ] from Bethe Salpeter equation for method ", &
330 trim(adjustl(info_approximation))
331 WRITE (unit_nr_file, '(A20,1X,10(2X,A20,1X))') "# Frequency (eV)", "σ_{avg}(ω)", "σ_xx(ω)", &
332 "σ_xy(ω)", "σ_xz(ω)", "σ_yx(ω)", "σ_yy(ω)", "σ_yz(ω)", "σ_zx(ω)", &
333 "σ_zy(ω)", "σ_zz(ω)"
334 DO i = 1, num_steps
335 WRITE (unit_nr_file, '(11(F20.8,1X))') abs_cross_section(i, 1)*evolt, abs_cross_section(i, 2:11)
336 END DO
337 CALL close_file(unit_nr_file)
338 END IF
339 DEALLOCATE (abs_cross_section)
340
341 IF (bse_env%para_env%is_source()) THEN
342 CALL open_file(file_name_spectrum, unit_number=unit_nr_file, &
343 file_status="UNKNOWN", file_action="WRITE")
344 WRITE (unit_nr_file, '(A,A6)') "# Imaginary part of polarizability α_{µ µ'}(ω) = -\sum_n "// &
345 "[2 Ω^n d_µ^n d_µ'^n] / [(ω+iη)²- (Ω^n)²] from Bethe Salpeter equation for method ", &
346 trim(adjustl(info_approximation))
347 WRITE (unit_nr_file, '(A20,1X,10(2X,A20,1X))') "# Frequency (eV)", "Im{α_{avg}(ω)}", "Im{α_xx(ω)}", &
348 "Im{α_xy(ω)}", "Im{α_xz(ω)}", "Im{α_yx(ω)}", "Im{α_yy(ω)}", "Im{α_yz(ω)}", "Im{α_zx(ω)}", &
349 "Im{α_zy(ω)}", "Im{α_zz(ω)}"
350 DO i = 1, num_steps
351 WRITE (unit_nr_file, '(11(F20.8,1X))') abs_spectrum(i, 1)*evolt, abs_spectrum(i, 2:11)
352 END DO
353 CALL close_file(unit_nr_file)
354 END IF
355 DEALLOCATE (abs_spectrum)
356 END DO
357
358 IF (unit_nr > 0) THEN
359 WRITE (unit_nr, '(T2,A4)') 'BSE|'
360 WRITE (unit_nr, '(T2,A4,T7,A,A)') &
361 'BSE|', "Printed optical absorption spectrum to local files, e.g. "
362 WRITE (unit_nr, '(T2,A4,T7,A)') &
363 'BSE|', file_name_spectrum
364 WRITE (unit_nr, '(T2,A4,T7,A,A)') &
365 'BSE|', "as well as photoabsorption cross section to, e.g. "
366 WRITE (unit_nr, '(T2,A4,T7,A)') &
367 'BSE|', file_name_crosssection
368 WRITE (unit_nr, '(T2,A4,T7,A52)') &
369 'BSE|', "using the Eq. (7.51) from C. Ullrichs Book on TDDFT:"
370 WRITE (unit_nr, '(T2,A4)') 'BSE|'
371 WRITE (unit_nr, '(T2,A4,T10,A75)') &
372 'BSE|', "Im{α_{avg}(ω)} = -Im{\sum_{n=1}^{N_exc} \frac{f_n}{(ω+iη)² - (Ω^n)²}}"
373 WRITE (unit_nr, '(T2,A4)') 'BSE|'
374 WRITE (unit_nr, '(T2,A4,T7,A)') &
375 'BSE|', "or for the full polarizability tensor:"
376 WRITE (unit_nr, '(T2,A4,T10,A)') &
377 'BSE|', "α_{µ µ'}(ω) = -\sum_n [2 Ω^n d_µ^n d_µ'^n] / [(ω+iη)²- (Ω^n)²]"
378 WRITE (unit_nr, '(T2,A4)') 'BSE|'
379 WRITE (unit_nr, '(T2,A4,T7,A)') &
380 'BSE|', "as well as Eq. (7.48):"
381 WRITE (unit_nr, '(T2,A4,T10,A)') &
382 'BSE|', "σ_{µ µ'}(ω) = 4πω Im{α_{µ µ'}(ω)} / c"
383 WRITE (unit_nr, '(T2,A4)') 'BSE|'
384 WRITE (unit_nr, '(T2,A4,T7,A)') &
385 'BSE|', "with transition moments d_µ^n, oscillator strengths f_n,"
386 WRITE (unit_nr, '(T2,A4,T7,A)') &
387 'BSE|', "excitation energies Ω^n and the speed of light c."
388 WRITE (unit_nr, '(T2,A4)') 'BSE|'
389 WRITE (unit_nr, '(T2,A4,T7,A)') &
390 'BSE|', "Please note that we adopt an additional minus sign for both quantities,"
391 WRITE (unit_nr, '(T2,A4,T7,A)') &
392 'BSE|', "due to the convention for charge vs particle density as done in MolGW:"
393 WRITE (unit_nr, '(T2,A4,T7,A)') &
394 'BSE|', "https://doi.org/10.1016/j.cpc.2016.06.019."
395 WRITE (unit_nr, '(T2,A4)') 'BSE|'
396 END IF
397
398 CALL timestop(handle)
399
401
402! **************************************************************************************************
403!> \brief ...
404!> \param fm_X ...
405!> \param fm_Y ...
406!> \param mo_coeff ...
407!> \param homo ...
408!> \param virtual ...
409!> \param info_approximation ...
410!> \param oscill_str ...
411!> \param bse_env the BSE environment (settings, the MO sets and the section of the NTO print keys)
412!> \param unit_nr ...
413! **************************************************************************************************
414 SUBROUTINE calculate_ntos(fm_X, fm_Y, &
415 mo_coeff, homo, virtual, &
416 info_approximation, &
417 oscill_str, &
418 bse_env, unit_nr)
419
420 TYPE(cp_fm_type), INTENT(IN) :: fm_x
421 TYPE(cp_fm_type), INTENT(IN), OPTIONAL :: fm_y
422 TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: mo_coeff
423 INTEGER, INTENT(IN) :: homo, virtual
424 CHARACTER(LEN=10) :: info_approximation
425 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: oscill_str
426 TYPE(bse_env_type), INTENT(IN) :: bse_env
427 INTEGER, INTENT(IN) :: unit_nr
428
429 CHARACTER(LEN=*), PARAMETER :: routinen = 'calculate_NTOs'
430 REAL(kind=dp), PARAMETER :: coeff_err = 1.0e-5_dp
431
432 CHARACTER(LEN=20), DIMENSION(2) :: nto_name
433 INTEGER :: handle, homo_irred, i, i_nto, info_svd, &
434 j, n_exc, n_nto, nao_full, nao_trunc
435 INTEGER, DIMENSION(:), POINTER :: stride
436 LOGICAL :: append_cube, cube_file
437 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigval_svd_squ
438 REAL(kind=dp), DIMENSION(:), POINTER :: eigval_svd
439 TYPE(cp_fm_struct_type), POINTER :: fm_struct_m, fm_struct_mo_coeff, &
440 fm_struct_nto_holes, &
441 fm_struct_nto_particles, &
442 fm_struct_nto_set
443 TYPE(cp_fm_type) :: fm_eigvl, fm_eigvr_t, fm_m, fm_mo_coeff, fm_nto_coeff_holes, &
444 fm_nto_coeff_particles, fm_nto_set, fm_x_ia, fm_y_ai
445 TYPE(mo_set_type), ALLOCATABLE, DIMENSION(:) :: nto_set
446 TYPE(section_vals_type), POINTER :: bse_section, nto_section
447
448 CALL timeset(routinen, handle)
449 bse_section => bse_env%bse_section
450
451 nao_full = bse_env%mos(1)%nao
452 nao_trunc = homo + virtual
453 ! This is not influenced by the BSE cutoff
454 homo_irred = bse_env%mos(1)%homo
455 ! M will have a block structure and is quadratic in homo+virtual, i.e.
456 ! occ virt
457 ! | 0 X_i,a | occ = homo
458 ! M = | Y_a,i 0 | virt = virtual
459 !
460 ! X and Y are here not the eigenvectors X_ia,n - instead we fix n and reshape the combined ia index
461 ! Notice the index structure of the lower block, i.e. X is transposed
462 CALL cp_fm_struct_create(fm_struct_m, &
463 fm_x%matrix_struct%para_env, fm_x%matrix_struct%context, &
464 nao_trunc, nao_trunc)
465 CALL cp_fm_struct_create(fm_struct_mo_coeff, &
466 fm_x%matrix_struct%para_env, fm_x%matrix_struct%context, &
467 nao_full, nao_trunc)
468 CALL cp_fm_struct_create(fm_struct_nto_holes, &
469 fm_x%matrix_struct%para_env, fm_x%matrix_struct%context, &
470 nao_full, nao_trunc)
471 CALL cp_fm_struct_create(fm_struct_nto_particles, &
472 fm_x%matrix_struct%para_env, fm_x%matrix_struct%context, &
473 nao_full, nao_trunc)
474
475 CALL cp_fm_create(fm_mo_coeff, matrix_struct=fm_struct_mo_coeff, &
476 name="mo_coeff")
477 ! Here, we take care of possible cutoffs
478 ! Simply truncating the matrix causes problems with the print function
479 ! Therefore, we keep the dimension, but set the coefficients of truncated indices to 0
480 CALL cp_fm_to_fm_submat_general(mo_coeff(1), fm_mo_coeff, &
481 nao_full, nao_trunc, &
482 1, homo_irred - homo + 1, &
483 1, 1, &
484 mo_coeff(1)%matrix_struct%context)
485
486 ! Print some information about the NTOs
487 IF (unit_nr > 0) THEN
488 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
489 'The Natural Transition Orbital (NTO) pairs φ_I(r_e) and χ_I(r_h) for a fixed'
490 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
491 'excitation index n are obtained by singular value decomposition of T'
492 WRITE (unit_nr, '(T2,A4)') 'BSE|'
493 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
494 ' = (0 X)'
495 IF (PRESENT(fm_y)) THEN
496 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
497 'T = (Y^T 0)'
498 ELSE
499 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
500 'T = (0 0)'
501 END IF
502 WRITE (unit_nr, '(T2,A4)') 'BSE|'
503 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
504 'T = U Λ V^T'
505 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
506 'φ_I(r_e) = \sum_p V_pI ψ_p(r_e)'
507 WRITE (unit_nr, '(T2,A4,T15,A)') 'BSE|', &
508 'χ_I(r_h) = \sum_p U_pI ψ_p(r_e)'
509 WRITE (unit_nr, '(T2,A4)') 'BSE|'
510 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
511 'where we have introduced'
512 WRITE (unit_nr, '(T2,A4)') 'BSE|'
513 WRITE (unit_nr, '(T2,A4,T7,A,T20,A)') &
514 'BSE|', "ψ_p:", "occupied and virtual molecular orbitals,"
515 WRITE (unit_nr, '(T2,A4,T7,A,T20,A)') &
516 'BSE|', "φ_I(r_e):", "NTO state for the electron,"
517 WRITE (unit_nr, '(T2,A4,T7,A,T20,A)') &
518 'BSE|', "χ_I(r_h):", "NTO state for the hole,"
519 WRITE (unit_nr, '(T2,A4,T7,A,T20,A)') &
520 'BSE|', "Λ:", "diagonal matrix of NTO weights λ_I,"
521 WRITE (unit_nr, '(T2,A4)') 'BSE|'
522 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
523 "The NTOs are calculated with the following settings:"
524 WRITE (unit_nr, '(T2,A4)') 'BSE|'
525 WRITE (unit_nr, '(T2,A4,T7,A,T71,I10)') 'BSE|', 'Number of excitations, for which NTOs are computed', &
526 bse_env%num_print_exc_ntos
527 IF (bse_env%eps_nto_osc_str > 0.0_dp) THEN
528 WRITE (unit_nr, '(T2,A4,T7,A,T71,F10.3)') 'BSE|', 'Threshold for oscillator strength f^n', &
529 bse_env%eps_nto_osc_str
530 ELSE
531 WRITE (unit_nr, '(T2,A4,T7,A,T71,A10)') 'BSE|', 'Threshold for oscillator strength f^n', &
532 adjustl("---")
533 END IF
534 WRITE (unit_nr, '(T2,A4,T7,A,T72,F10.3)') 'BSE|', 'Threshold for NTO weights (λ_I)^2', &
535 bse_env%eps_nto_eigval
536 END IF
537
538 ! Write the header of NTO info table
539 IF (unit_nr > 0) THEN
540 WRITE (unit_nr, '(T2,A4)') 'BSE|'
541 IF (.NOT. PRESENT(fm_y)) THEN
542 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
543 'NTOs from solving the BSE within the TDA:'
544 ELSE
545 WRITE (unit_nr, '(T2,A4,T7,A)') 'BSE|', &
546 'NTOs from solving the BSE without the TDA:'
547 END IF
548 WRITE (unit_nr, '(T2,A4)') 'BSE|'
549 WRITE (unit_nr, '(T2,A4,T8,A12,T22,A8,T33,A14,T62,A)') 'BSE|', &
550 'Excitation n', "TDA/ABBA", "Index of NTO I", 'NTO weights (λ_I)^2'
551 END IF
552
553 DO j = 1, bse_env%num_print_exc_ntos
554 n_exc = bse_env%bse_nto_state_list_final(j)
555 ! Takes care of unallocated oscill_str array in case of Triplet
556 IF (bse_env%eps_nto_osc_str > 0.0_dp) THEN
557 ! Check actual values
558 IF (oscill_str(n_exc) < bse_env%eps_nto_osc_str) THEN
559 ! Print skipped levels to table
560 IF (unit_nr > 0) THEN
561 WRITE (unit_nr, '(T2,A4)') 'BSE|'
562 WRITE (unit_nr, '(T2,A4,T8,I12,T24,A6,T42,A39)') 'BSE|', &
563 n_exc, info_approximation, "Skipped (Oscillator strength too small)"
564 END IF
565 cycle
566 END IF
567 END IF
568
569 CALL cp_fm_create(fm_m, matrix_struct=fm_struct_m, &
570 name="single_part_trans_dm")
571 CALL cp_fm_set_all(fm_m, 0.0_dp)
572
573 CALL cp_fm_create(fm_nto_coeff_holes, matrix_struct=fm_struct_nto_holes, &
574 name="nto_coeffs_holes")
575 CALL cp_fm_set_all(fm_nto_coeff_holes, 0.0_dp)
576
577 CALL cp_fm_create(fm_nto_coeff_particles, matrix_struct=fm_struct_nto_particles, &
578 name="nto_coeffs_particles")
579 CALL cp_fm_set_all(fm_nto_coeff_particles, 0.0_dp)
580
581 ! Reshuffle from X_ia,n_exc to X_i,a
582 CALL reshuffle_eigvec(fm_x, fm_x_ia, homo, virtual, n_exc, &
583 .false., unit_nr, bse_env)
584
585 ! Copy X to upper block in M, i.e. starting from column homo+1
586 CALL cp_fm_to_fm_submat(fm_x_ia, fm_m, &
587 homo, virtual, &
588 1, 1, &
589 1, homo + 1)
590 CALL cp_fm_release(fm_x_ia)
591 ! Copy Y if present
592 IF (PRESENT(fm_y)) THEN
593 ! Reshuffle from Y_ia,n_exc to Y_a,i
594 CALL reshuffle_eigvec(fm_y, fm_y_ai, homo, virtual, n_exc, &
595 .true., unit_nr, bse_env)
596
597 ! Copy Y^T to lower block in M, i.e. starting from row homo+1
598 CALL cp_fm_to_fm_submat(fm_y_ai, fm_m, &
599 virtual, homo, &
600 1, 1, &
601 homo + 1, 1)
602
603 CALL cp_fm_release(fm_y_ai)
604
605 END IF
606
607 ! Now we compute the SVD of M_{occ+virt,occ+virt}, which yields
608 ! M = U * Lambda * V^T
609 ! Initialize matrices and arrays to store left/right eigenvectors and singular values
610 CALL cp_fm_create(matrix=fm_eigvl, &
611 matrix_struct=fm_m%matrix_struct, &
612 name="LEFT_SINGULAR_MATRIX")
613 CALL cp_fm_set_all(fm_eigvl, alpha=0.0_dp)
614 CALL cp_fm_create(matrix=fm_eigvr_t, &
615 matrix_struct=fm_m%matrix_struct, &
616 name="RIGHT_SINGULAR_MATRIX")
617 CALL cp_fm_set_all(fm_eigvr_t, alpha=0.0_dp)
618
619 ALLOCATE (eigval_svd(nao_trunc))
620 eigval_svd(:) = 0.0_dp
621 info_svd = 0
622 CALL cp_fm_svd(fm_m, fm_eigvl, fm_eigvr_t, eigval_svd, info_svd)
623 IF (info_svd /= 0) THEN
624 IF (unit_nr > 0) THEN
625 CALL cp_warn(__location__, &
626 "SVD for computation of NTOs not successful. "// &
627 "Skipping print of NTOs.")
628 IF (info_svd > 0) THEN
629 CALL cp_warn(__location__, &
630 "PDGESVD detected heterogeneity. "// &
631 "Decreasing number of MPI ranks might solve this issue.")
632 END IF
633 END IF
634 ! Release matrices to avoid memory leaks
635 CALL cp_fm_release(fm_m)
636 CALL cp_fm_release(fm_nto_coeff_holes)
637 CALL cp_fm_release(fm_nto_coeff_particles)
638 ELSE
639 ! Rescale singular values as done in Martin2003 (10.1063/1.1558471)
640 ALLOCATE (eigval_svd_squ(nao_trunc))
641 eigval_svd_squ(:) = eigval_svd(:)**2
642 ! Sanity check for TDA: In case of TDA, the sum should be \sum_ia |X_ia|^2 = 1
643 IF (.NOT. PRESENT(fm_y)) THEN
644 IF (abs(sum(eigval_svd_squ) - 1) >= coeff_err) THEN
645 cpwarn("Sum of NTO coefficients deviates from 1!")
646 END IF
647 END IF
648
649 ! Create NTO coefficients for later print to grid via TDDFT routine
650 ! Apply U = fm_eigvl to MO coeffs, which yields hole states
651 CALL parallel_gemm("N", "N", nao_full, nao_trunc, nao_trunc, 1.0_dp, fm_mo_coeff, fm_eigvl, 0.0_dp, &
652 fm_nto_coeff_holes)
653
654 ! Apply V^T = fm_eigvr_t to MO coeffs, which yields particle states
655 CALL parallel_gemm("N", "T", nao_full, nao_trunc, nao_trunc, 1.0_dp, fm_mo_coeff, fm_eigvr_t, 0.0_dp, &
656 fm_nto_coeff_particles)
657
658 !Release intermediary work matrices
659 CALL cp_fm_release(fm_m)
660 CALL cp_fm_release(fm_eigvl)
661 CALL cp_fm_release(fm_eigvr_t)
662
663 ! Transfer NTO coefficients to sets
664 nto_name(1) = 'Hole_coord'
665 nto_name(2) = 'Particle_coord'
666 ALLOCATE (nto_set(2))
667 ! Extract number of significant NTOs
668 n_nto = 0
669 DO i_nto = 1, nao_trunc
670 IF (eigval_svd_squ(i_nto) > bse_env%eps_nto_eigval) THEN
671 n_nto = n_nto + 1
672 ELSE
673 ! Since svd orders in descending order, we can exit the loop if smaller
674 EXIT
675 END IF
676 END DO
677
678 IF (unit_nr > 0) THEN
679 WRITE (unit_nr, '(T2,A4)') 'BSE|'
680 DO i_nto = 1, n_nto
681 WRITE (unit_nr, '(T2,A4,T8,I12,T24,A6,T41,I6,T71,F10.5)') 'BSE|', &
682 n_exc, info_approximation, i_nto, eigval_svd_squ(i_nto)
683 END DO
684 END IF
685
686 CALL cp_fm_struct_create(fm_struct_nto_set, template_fmstruct=fm_struct_nto_holes, &
687 ncol_global=n_nto)
688 CALL cp_fm_create(fm_nto_set, fm_struct_nto_set)
689 DO i = 1, 2
690 CALL allocate_mo_set(nto_set(i), nao_trunc, n_nto, 0, 0.0_dp, 2.0_dp, 0.0_dp)
691 CALL init_mo_set(nto_set(i), fm_ref=fm_nto_set, name=nto_name(i))
692 END DO
693 CALL cp_fm_release(fm_nto_set)
694 CALL cp_fm_struct_release(fm_struct_nto_set)
695
696 ! Fill NTO sets
697 CALL cp_fm_to_fm(fm_nto_coeff_holes, nto_set(1)%mo_coeff, ncol=n_nto)
698 CALL cp_fm_to_fm(fm_nto_coeff_particles, nto_set(2)%mo_coeff, ncol=n_nto)
699
700 ! Cube files
701 nto_section => section_vals_get_subs_vals(bse_section, "NTO_ANALYSIS")
702 CALL section_vals_val_get(nto_section, "CUBE_FILES", l_val=cube_file)
703 CALL section_vals_val_get(nto_section, "STRIDE", i_vals=stride)
704 CALL section_vals_val_get(nto_section, "APPEND", l_val=append_cube)
705 IF (cube_file) THEN
706 CALL print_bse_nto_cubes(bse_env, nto_set, n_exc, info_approximation, &
707 stride, append_cube, nto_section)
708 END IF
709
710 CALL cp_fm_release(fm_nto_coeff_holes)
711 CALL cp_fm_release(fm_nto_coeff_particles)
712 DEALLOCATE (eigval_svd)
713 DEALLOCATE (eigval_svd_squ)
714 DO i = 1, 2
715 CALL deallocate_mo_set(nto_set(i))
716 END DO
717 DEALLOCATE (nto_set)
718 END IF
719 END DO
720
721 CALL cp_fm_release(fm_mo_coeff)
722 CALL cp_fm_struct_release(fm_struct_m)
723 CALL cp_fm_struct_release(fm_struct_nto_holes)
724 CALL cp_fm_struct_release(fm_struct_nto_particles)
725 CALL cp_fm_struct_release(fm_struct_mo_coeff)
726
727 CALL timestop(handle)
728
729 END SUBROUTINE calculate_ntos
730
731! **************************************************************************************************
732!> \brief ...
733!> \param exc_descr Allocated and initialized on exit
734!> \param fm_X_ia ...
735!> \param fm_multipole_ij_trunc ...
736!> \param fm_multipole_ab_trunc ...
737!> \param fm_multipole_ai_trunc ...
738!> \param i_exc ...
739!> \param homo ...
740!> \param virtual ...
741!> \param fm_Y_ia ...
742! **************************************************************************************************
743 SUBROUTINE get_exciton_descriptors(exc_descr, fm_X_ia, &
744 fm_multipole_ij_trunc, fm_multipole_ab_trunc, &
745 fm_multipole_ai_trunc, &
746 i_exc, homo, virtual, &
747 fm_Y_ia)
748
749 TYPE(exciton_descr_type), ALLOCATABLE, &
750 DIMENSION(:) :: exc_descr
751 TYPE(cp_fm_type), INTENT(IN) :: fm_x_ia
752 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:), &
753 INTENT(IN) :: fm_multipole_ij_trunc, &
754 fm_multipole_ab_trunc, &
755 fm_multipole_ai_trunc
756 INTEGER, INTENT(IN) :: i_exc, homo, virtual
757 TYPE(cp_fm_type), INTENT(IN), OPTIONAL :: fm_y_ia
758
759 CHARACTER(LEN=*), PARAMETER :: routinen = 'get_exciton_descriptors'
760
761 INTEGER :: handle, i_dir, j_dir
762 INTEGER, DIMENSION(3) :: mask_quadrupole
763 LOGICAL :: flag_tda
764 REAL(kind=dp) :: norm_x, norm_xpy, norm_y
765 REAL(kind=dp), DIMENSION(3) :: r_e_sq_x, r_e_sq_y, r_e_x, r_e_y, &
766 r_h_sq_x, r_h_sq_y, r_h_x, r_h_y
767 REAL(kind=dp), DIMENSION(3, 3) :: r_e_h_xx, r_e_h_xy, r_e_h_yy
768 TYPE(cp_fm_struct_type), POINTER :: fm_struct_ab, fm_struct_ia
769 TYPE(cp_fm_type) :: fm_work_ba, fm_work_ia, fm_work_ia_2
770
771 CALL timeset(routinen, handle)
772 IF (PRESENT(fm_y_ia)) THEN
773 flag_tda = .false.
774 ELSE
775 flag_tda = .true.
776 END IF
777
778 ! translates 1,2,3 to diagonal entries of quadrupoles xx, yy, zz
779 ! Ordering in quadrupole moments is x, y, z, xx, xy, xz, yy, yz, zz
780 mask_quadrupole = [4, 7, 9]
781
782 CALL cp_fm_struct_create(fm_struct_ia, &
783 context=fm_x_ia%matrix_struct%context, nrow_global=homo, ncol_global=virtual)
784 CALL cp_fm_struct_create(fm_struct_ab, &
785 context=fm_x_ia%matrix_struct%context, nrow_global=virtual, ncol_global=virtual)
786
787 r_e_x(:) = 0.0_dp
788 r_e_y(:) = 0.0_dp
789 r_h_x(:) = 0.0_dp
790 r_h_y(:) = 0.0_dp
791 r_e_sq_x(:) = 0.0_dp
792 r_h_sq_x(:) = 0.0_dp
793 r_e_sq_y(:) = 0.0_dp
794 r_h_sq_y(:) = 0.0_dp
795 r_e_h_xx(:, :) = 0.0_dp
796 r_e_h_xy(:, :) = 0.0_dp
797 r_e_h_yy(:, :) = 0.0_dp
798
799 norm_x = 0.0_dp
800 norm_y = 0.0_dp
801 norm_xpy = 0.0_dp
802
803 ! Initialize values of exciton descriptors
804 exc_descr(i_exc)%r_e(:) = 0.0_dp
805 exc_descr(i_exc)%r_h(:) = 0.0_dp
806 exc_descr(i_exc)%r_e_sq(:) = 0.0_dp
807 exc_descr(i_exc)%r_h_sq(:) = 0.0_dp
808 exc_descr(i_exc)%r_e_h(:, :) = 0.0_dp
809
810 exc_descr(i_exc)%flag_TDA = flag_tda
811 exc_descr(i_exc)%norm_XpY = 0.0_dp
812
813 ! Norm of X
814 CALL cp_fm_trace(fm_x_ia, fm_x_ia, norm_x)
815 norm_xpy = norm_x
816 ! Norm of Y
817 IF (.NOT. flag_tda) THEN
818 CALL cp_fm_trace(fm_y_ia, fm_y_ia, norm_y)
819 norm_xpy = norm_xpy + norm_y
820 END IF
821
822 exc_descr(i_exc)%norm_XpY = norm_xpy
823
824 ! <r_h>_X = Tr[ X^T µ_ij X + Y µ_ab Y^T ] = X_ai µ_ij X_ja + Y_ia µ_ab Y_bi
825 DO i_dir = 1, 3
826 ! <r_h>_X = X_ai µ_ij X_ja + ...
827 CALL trace_exciton_descr(fm_x_ia, fm_multipole_ij_trunc(i_dir), fm_x_ia, r_h_x(i_dir))
828 r_h_x(i_dir) = r_h_x(i_dir)/norm_xpy
829 IF (.NOT. flag_tda) THEN
830 ! <r_h>_X = ... + Y_ia µ_ab Y_bi
831 CALL trace_exciton_descr(fm_y_ia, fm_y_ia, fm_multipole_ab_trunc(i_dir), r_h_y(i_dir))
832 r_h_y(i_dir) = r_h_y(i_dir)/norm_xpy
833 END IF
834 END DO
835 exc_descr(i_exc)%r_h(:) = r_h_x(:) + r_h_y(:)
836
837 ! <r_e>_X = Tr[ X µ_ab X^T + Y^T µ_ij Y ] = X_ia µ_ab X_bi + Y_ai µ_ij Y_ja
838 DO i_dir = 1, 3
839 ! <r_e>_X = work_ib X_bi + ... = X_ib^T work_ib + ...
840 CALL trace_exciton_descr(fm_x_ia, fm_x_ia, fm_multipole_ab_trunc(i_dir), r_e_x(i_dir))
841 r_e_x(i_dir) = r_e_x(i_dir)/norm_xpy
842 IF (.NOT. flag_tda) THEN
843 ! <r_e>_X = ... + Y_ai µ_ij Y_ja
844 CALL trace_exciton_descr(fm_y_ia, fm_multipole_ij_trunc(i_dir), fm_y_ia, r_e_y(i_dir))
845 r_e_y(i_dir) = r_e_y(i_dir)/norm_xpy
846 END IF
847 END DO
848 exc_descr(i_exc)%r_e(:) = r_e_x(:) + r_e_y(:)
849
850 ! <r_h^2>_X = Tr[ X^T M_ij X + Y M_ab Y^T ] = X_ai M_ij X_ja + Y_ia M_ab Y_bi
851 DO i_dir = 1, 3
852 ! <r_h^2>_X = X_ai M_ij X_ja + ...
853 CALL trace_exciton_descr(fm_x_ia, fm_multipole_ij_trunc(mask_quadrupole(i_dir)), &
854 fm_x_ia, r_h_sq_x(i_dir))
855 r_h_sq_x(i_dir) = r_h_sq_x(i_dir)/norm_xpy
856 IF (.NOT. flag_tda) THEN
857 ! <r_h^2>_X = ... + Y_ia M_ab Y_bi
858 CALL trace_exciton_descr(fm_y_ia, fm_y_ia, &
859 fm_multipole_ab_trunc(mask_quadrupole(i_dir)), r_h_sq_y(i_dir))
860 r_h_sq_y(i_dir) = r_h_sq_y(i_dir)/norm_xpy
861 END IF
862 END DO
863 exc_descr(i_exc)%r_h_sq(:) = r_h_sq_x(:) + r_h_sq_y(:)
864
865 ! <r_e^2>_X = Tr[ X M_ab X^T + Y^T M_ij Y ] = X_ia M_ab X_bi + Y_ai M_ij Y_ja
866 DO i_dir = 1, 3
867 ! <r_e^2>_X = work_ib X_bi + ... = X_ib^T work_ib + ...
868 CALL trace_exciton_descr(fm_x_ia, fm_x_ia, &
869 fm_multipole_ab_trunc(mask_quadrupole(i_dir)), r_e_sq_x(i_dir))
870 r_e_sq_x(i_dir) = r_e_sq_x(i_dir)/norm_xpy
871 IF (.NOT. flag_tda) THEN
872 ! <r_e^2>_X = ... + Y_ai M_ij Y_ja
873 CALL trace_exciton_descr(fm_y_ia, fm_multipole_ij_trunc(mask_quadrupole(i_dir)), &
874 fm_y_ia, r_e_sq_y(i_dir))
875 r_e_sq_y(i_dir) = r_e_sq_y(i_dir)/norm_xpy
876 END IF
877 END DO
878 exc_descr(i_exc)%r_e_sq(:) = r_e_sq_x(:) + r_e_sq_y(:)
879
880 ! <r_e^\mu r_h^\mu'>_X
881 ! = Tr[ X^T µ'_ij X µ_ab + Y^T µ_ij Y µ'_ab + 2 X µ_ai Y µ'_ai ]
882 ! = X_bj µ'_ji X_ia µ_ab + Y_bj µ_ji Y_ia µ'_ab + 2 X_ia µ_aj Y_jb µ'_bi
883 ! The i_dir and j_dir convert to mu and mu'. µ (electron) sits between a (from X) and j (from Y),
884 ! µ' (hole) between i (from X) and b (from Y); Tr[ Y µ'_ai X µ_ai ] = Tr[ X µ_ai Y µ'_ai ] by cyclicity, hence the 2.
885 CALL cp_fm_create(fm_work_ia, fm_struct_ia)
886 CALL cp_fm_create(fm_work_ia_2, fm_struct_ia)
887 CALL cp_fm_create(fm_work_ba, fm_struct_ab)
888 DO i_dir = 1, 3
889 DO j_dir = 1, 3
890 ! First term - X^T µ'_ij X µ_ab
891 CALL cp_fm_set_all(fm_work_ia, 0.0_dp)
892 CALL cp_fm_set_all(fm_work_ia_2, 0.0_dp)
893 ! work_ib = X_ia µ_ab
894 CALL parallel_gemm("N", "N", homo, virtual, virtual, 1.0_dp, &
895 fm_x_ia, fm_multipole_ab_trunc(i_dir), 0.0_dp, fm_work_ia)
896 ! work_ja_2 = µ'_ji work_ia
897 CALL parallel_gemm("N", "N", homo, virtual, homo, 1.0_dp, &
898 fm_multipole_ij_trunc(j_dir), fm_work_ia, 0.0_dp, fm_work_ia_2)
899 ! <r_e^\mu r_h^\mu'>_X = work_ia_2 X_bj + ... = X^T work_ia_2 + ...
900 CALL cp_fm_trace(fm_x_ia, fm_work_ia_2, r_e_h_xx(i_dir, j_dir))
901 r_e_h_xx(i_dir, j_dir) = r_e_h_xx(i_dir, j_dir)/norm_xpy
902 IF (.NOT. flag_tda) THEN
903 ! Second term - Y^T µ_ij Y µ'_ab
904 CALL cp_fm_set_all(fm_work_ia, 0.0_dp)
905 CALL cp_fm_set_all(fm_work_ia_2, 0.0_dp)
906 ! work_ib = Y_ia µ'_ab
907 CALL parallel_gemm("N", "N", homo, virtual, virtual, 1.0_dp, &
908 fm_y_ia, fm_multipole_ab_trunc(j_dir), 0.0_dp, fm_work_ia)
909 ! work_ja_2 = µ_ji work_ia
910 CALL parallel_gemm("N", "N", homo, virtual, homo, 1.0_dp, &
911 fm_multipole_ij_trunc(i_dir), fm_work_ia, 0.0_dp, fm_work_ia_2)
912 ! <r_h r_e>_X = work_ia_2 Y_bj + ... = Y^T work_ia_2 + ...
913 CALL cp_fm_trace(fm_y_ia, fm_work_ia_2, r_e_h_yy(i_dir, j_dir))
914 r_e_h_yy(i_dir, j_dir) = r_e_h_yy(i_dir, j_dir)/norm_xpy
915
916 ! Third term (counted twice) - X µ_ai Y µ'_ai = X_ia µ_aj Y_jb µ'_bi
917 ! Reshuffle for usage of trace (where first argument is transposed)
918 ! = µ_aj Y_jb µ'_bi X_ia =
919 ! \___________/
920 ! fm_work_ai
921 ! fm_work_ai = µ_aj Y_jb µ'_bi
922 ! fm_work_ia = µ'_ib Y_bj µ_ja
923 ! \_____/
924 ! fm_work_ba
925 CALL cp_fm_set_all(fm_work_ba, 0.0_dp)
926 CALL cp_fm_set_all(fm_work_ia, 0.0_dp)
927 ! fm_work_ba = Y_bj µ_ja
928 CALL parallel_gemm("T", "T", virtual, virtual, homo, 1.0_dp, &
929 fm_y_ia, fm_multipole_ai_trunc(i_dir), 0.0_dp, fm_work_ba)
930 ! fm_work_ia = µ'_ib fm_work_ba
931 CALL parallel_gemm("T", "N", homo, virtual, virtual, 1.0_dp, &
932 fm_multipole_ai_trunc(j_dir), fm_work_ba, 0.0_dp, fm_work_ia)
933 ! <r_e r_h>_X = ... + 2 X_ia µ_aj Y_jb µ'_bi
934 CALL cp_fm_trace(fm_work_ia, fm_x_ia, r_e_h_xy(i_dir, j_dir))
935 r_e_h_xy(i_dir, j_dir) = 2.0_dp*r_e_h_xy(i_dir, j_dir)/norm_xpy
936 END IF
937 END DO
938 END DO
939 exc_descr(i_exc)%r_e_h(:, :) = r_e_h_xx(:, :) + r_e_h_xy(:, :) + r_e_h_yy(:, :)
940
941 CALL cp_fm_release(fm_work_ia)
942 CALL cp_fm_release(fm_work_ia_2)
943 CALL cp_fm_release(fm_work_ba)
944
945 ! Now we compute all the descriptors and correlation coefficients
946 ! Order is: Directional ones, then covariances and correlation coefficients and
947
948 ! diff_r_abs = |<r_h>_X - <r_e>_X|
949 exc_descr(i_exc)%diff_r_abs = sqrt(sum((exc_descr(i_exc)%r_h(:) - exc_descr(i_exc)%r_e(:))**2))
950
951 ! σ_e = sqrt( <r_e^2>_X - <r_e>_X^2 )
952 exc_descr(i_exc)%sigma_e = sqrt(sum(exc_descr(i_exc)%r_e_sq(:)) - sum(exc_descr(i_exc)%r_e(:)**2))
953
954 ! σ_h = sqrt( <r_h^2>_X - <r_h>_X^2 )
955 exc_descr(i_exc)%sigma_h = sqrt(sum(exc_descr(i_exc)%r_h_sq(:)) - sum(exc_descr(i_exc)%r_h(:)**2))
956
957 ! Now directed ones
958 DO i_dir = 1, 3
959 exc_descr(i_exc)%d_eh_dir(i_dir) = abs(exc_descr(i_exc)%r_h(i_dir) - exc_descr(i_exc)%r_e(i_dir))
960 exc_descr(i_exc)%sigma_e_dir(i_dir) = sqrt(exc_descr(i_exc)%r_e_sq(i_dir) - exc_descr(i_exc)%r_e(i_dir)**2)
961 exc_descr(i_exc)%sigma_h_dir(i_dir) = sqrt(exc_descr(i_exc)%r_h_sq(i_dir) - exc_descr(i_exc)%r_h(i_dir)**2)
962 END DO
963
964 ! Covariance and correlation coefficient (as well as crosscorrelation matrices)
965 ! COV(r_e, r_h) = < r_e r_h >_X - < r_e >_X < r_h >_X
966 exc_descr(i_exc)%cov_e_h_sum = 0.0_dp
967 exc_descr(i_exc)%cov_e_h(:, :) = 0.0_dp
968 exc_descr(i_exc)%corr_e_h_matrix(:, :) = 0.0_dp
969 DO i_dir = 1, 3
970 DO j_dir = 1, 3
971 exc_descr(i_exc)%cov_e_h(i_dir, j_dir) = exc_descr(i_exc)%r_e_h(i_dir, j_dir) &
972 - exc_descr(i_exc)%r_e(i_dir)*exc_descr(i_exc)%r_h(j_dir)
973 exc_descr(i_exc)%corr_e_h_matrix(i_dir, j_dir) = &
974 exc_descr(i_exc)%cov_e_h(i_dir, j_dir)/ &
975 (exc_descr(i_exc)%sigma_e_dir(i_dir)*exc_descr(i_exc)%sigma_h_dir(j_dir))
976 END DO
977 exc_descr(i_exc)%cov_e_h_sum = exc_descr(i_exc)%cov_e_h_sum + &
978 exc_descr(i_exc)%r_e_h(i_dir, i_dir) - &
979 exc_descr(i_exc)%r_e(i_dir)*exc_descr(i_exc)%r_h(i_dir)
980 END DO
981
982 ! e-h-correlation coefficient R_eh = COV(r_e, r_h) / ( σ_e σ_h )
983 exc_descr(i_exc)%corr_e_h = exc_descr(i_exc)%cov_e_h_sum/(exc_descr(i_exc)%sigma_e*exc_descr(i_exc)%sigma_h)
984
985 ! root-mean-square e-h separation
986 exc_descr(i_exc)%diff_r_sqr = sqrt(exc_descr(i_exc)%diff_r_abs**2 + &
987 exc_descr(i_exc)%sigma_e**2 + exc_descr(i_exc)%sigma_h**2 &
988 - 2*exc_descr(i_exc)%cov_e_h_sum)
989
990 DO i_dir = 1, 3
991 exc_descr(i_exc)%d_exc_dir(i_dir) = sqrt(exc_descr(i_exc)%d_eh_dir(i_dir)**2 + &
992 exc_descr(i_exc)%sigma_e_dir(i_dir)**2 + &
993 exc_descr(i_exc)%sigma_h_dir(i_dir)**2 - &
994 2*exc_descr(i_exc)%cov_e_h(i_dir, i_dir))
995 END DO
996
997 ! Expectation values of r_e and r_h
998 exc_descr(i_exc)%r_e_shift(:) = exc_descr(i_exc)%r_e(:)
999 exc_descr(i_exc)%r_h_shift(:) = exc_descr(i_exc)%r_h(:)
1000
1001 CALL cp_fm_struct_release(fm_struct_ia)
1002 CALL cp_fm_struct_release(fm_struct_ab)
1003
1004 CALL timestop(handle)
1005
1006 END SUBROUTINE get_exciton_descriptors
1007
1008END MODULE bse_properties
Routines for computing excitonic properties, e.g. exciton diameter, from the BSE.
subroutine, public compute_and_print_absorption_spectrum(oscill_str, polarizability_residues, exc_ens, info_approximation, unit_nr, bse_env)
Computes and returns absorption spectrum for the frequency range and broadening provided by the user....
subroutine, public get_oscillator_strengths(fm_eigvec_x, exc_ens, fm_dipole_ai_trunc, trans_mom_bse, oscill_str, polarizability_residues, bse_env, homo_red, virtual_red, unit_nr, fm_eigvec_y)
Compute and return BSE dipoles d_r^n = sqrt(2) sum_ia < ψ_i | r | ψ_a > ( X_ia^n + Y_ia^n ) and oscil...
subroutine, public get_exciton_descriptors(exc_descr, fm_x_ia, fm_multipole_ij_trunc, fm_multipole_ab_trunc, fm_multipole_ai_trunc, i_exc, homo, virtual, fm_y_ia)
...
subroutine, public calculate_ntos(fm_x, fm_y, mo_coeff, homo, virtual, info_approximation, oscill_str, bse_env, unit_nr)
...
The BSE environment: the settings of the &BSE section and the state a GW path prepares for the solver...
Definition bse_types.F:14
Auxiliary routines for GW + Bethe-Salpeter for computing electronic excitations.
Definition bse_util.F:13
subroutine, public trace_exciton_descr(fm_a, fm_b, fm_c, alpha)
Computes trace of form Tr{A^T B C} for exciton descriptors.
Definition bse_util.F:1897
subroutine, public reshuffle_eigvec(fm_eigvec, fm_eigvec_reshuffled, homo, virtual, n_exc, do_transpose, unit_nr, bse_env)
...
Definition bse_util.F:1364
subroutine, public print_bse_nto_cubes(bse_env, mos, istate, info_approximation, stride, append_cube, print_section)
Borrowed from the tddfpt module with slight adaptions.
Definition bse_util.F:1438
subroutine, public fm_general_add_bse(fm_out, fm_in, beta, nrow_secidx_in, ncol_secidx_in, nrow_secidx_out, ncol_secidx_out, unit_nr, reordering, bse_env, row_offset, col_offset)
Adds and reorders full matrices with a combined index structure, e.g. adding W_ij,...
Definition bse_util.F:198
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:323
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:123
Basic linear algebra operations for full matrices.
subroutine, public cp_fm_scale_and_add(alpha, matrix_a, beta, matrix_b)
calc A <- alpha*A + beta*B optimized for alpha == 1.0 (just add beta*B) and beta == 0....
used for collecting some of the diagonalization schemes available for cp_fm_type. cp_fm_power also mo...
Definition cp_fm_diag.F:17
subroutine, public cp_fm_svd(matrix_a, matrix_eigvl, matrix_eigvr_t, eigval, info)
decomposes a quadratic matrix into its singular value decomposition
represent the structure of a full matrix
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_get_info(matrix, name, nrow_global, ncol_global, nrow_block, ncol_block, nrow_local, ncol_local, row_indices, col_indices, local_data, context, nrow_locals, ncol_locals, matrix_struct, para_env)
returns all kind of information about the full matrix
subroutine, public cp_fm_to_fm_submat_general(source, destination, nrows, ncols, s_firstrow, s_firstcol, d_firstrow, d_firstcol, global_context)
General copy of a submatrix of fm matrix to a submatrix of another fm matrix. The two matrices can ha...
subroutine, public cp_fm_to_fm_submat(msource, mtarget, nrow, ncol, s_firstrow, s_firstcol, t_firstrow, t_firstcol)
copy just a part ot the matrix
subroutine, public cp_fm_set_all(matrix, alpha, beta)
set all elements of a matrix to the same value, and optionally the diagonal to a different one
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
subroutine, public cp_fm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
gets a submatrix of a full matrix op(target_m)(1:n_rows,1:n_cols) =fm(start_row:start_row+n_rows,...
objects that represent the structure of input sections and the data contained in an input section
recursive type(section_vals_type) function, pointer, public section_vals_get_subs_vals(section_vals, subsection_name, i_rep_section, can_return_null)
returns the values of the requested subsection
subroutine, public section_vals_val_get(section_vals, keyword_name, i_rep_section, i_rep_val, n_rep_val, val, l_val, i_val, r_val, c_val, l_vals, i_vals, r_vals, c_vals, explicit)
returns the requested value
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Definition of mathematical constants and functions.
real(kind=dp), parameter, public pi
basic linear algebra operations for full matrixes
Definition of physical constants:
Definition physcon.F:68
real(kind=dp), parameter, public c_light_au
Definition physcon.F:90
real(kind=dp), parameter, public evolt
Definition physcon.F:183
Definition and initialisation of the mo data type.
Definition qs_mo_types.F:22
subroutine, public init_mo_set(mo_set, fm_pool, fm_ref, fm_struct, name, counter, complex_coeff)
initializes an allocated mo_set. eigenvalues, mo_coeff, occupation_numbers are valid only after this ...
subroutine, public allocate_mo_set(mo_set, nao, nmo, nelectron, n_el_f, maxocc, flexible_electron_count)
Allocates a mo set and partially initializes it (nao,nmo,nelectron, and flexible_electron_count are v...
subroutine, public deallocate_mo_set(mo_set)
Deallocate a wavefunction data structure.
Settings of the &BSE section (read_bse_section, re-read by prepare_bse_env, normalised in place by ad...
Definition bse_types.F:41
keeps the information about the structure of a full matrix
represent a full matrix