Octopus
photon_mode.F90
Go to the documentation of this file.
1!! Copyright (C) 2017 Johannes Flick
2!! Copyright (C) 2019 F. Buchholz, M. Oliveira
3!! Copyright (C) 2020 S. Ohlmann
4!!
5!! This program is free software; you can redistribute it and/or modify
6!! it under the terms of the GNU General Public License as published by
7!! the Free Software Foundation; either version 2, or (at your option)
8!! any later version.
9!!
10!! This program is distributed in the hope that it will be useful,
11!! but WITHOUT ANY WARRANTY; without even the implied warranty of
12!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13!! GNU General Public License for more details.
14!!
15!! You should have received a copy of the GNU General Public License
16!! along with this program; if not, write to the Free Software
17!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18!! 02110-1301, USA.
19!!
20
21#include "global.h"
22
24 use comm_oct_m
25 use debug_oct_m
26 use global_oct_m
27 use io_oct_m
29 use, intrinsic :: iso_fortran_env
30 use mesh_oct_m
33 use mpi_oct_m
35 use parser_oct_m
37 use unit_oct_m
39
40 implicit none
41
42 private
43 public :: &
52
53 type photon_mode_t
54 ! All components are public by default
55 integer :: nmodes
56 integer :: dim
57 real(real64), allocatable :: omega(:)
58 real(real64), allocatable :: lambda(:)
59 real(real64), allocatable :: pol(:,:)
60 real(real64), allocatable :: pol_dipole(:,:)
61 real(real64) :: ex
62 real(real64), allocatable :: number(:)
63 real(real64), allocatable :: correlator(:,:)
64 real(real64) :: n_electrons
65 real(real64), pointer :: pt_coord_q0(:) => null()
66 real(real64), pointer :: pt_momen_p0(:) => null()
67 real(real64) :: mu
68 ! dressed photons for photon-free approach
69 logical :: use_photon_exchange
70 real(real64), allocatable :: dressed_omega(:)
71 real(real64), allocatable :: dressed_lambda(:)
72 real(real64), allocatable :: dressed_pol(:,:)
73 real(real64), allocatable :: rotated_omega(:)
74
75 logical :: has_q0_p0 = .false.
76 end type photon_mode_t
77
78contains
79
80 ! ---------------------------------------------------------
81 subroutine photon_mode_init(this, namespace, dim, photon_free)
82 type(photon_mode_t), intent(out) :: this
83 type(namespace_t), intent(in) :: namespace
84 integer, intent(in) :: dim
85 logical, optional, intent(in) :: photon_free
86
87 type(block_t) :: blk
88 integer :: ii, idir, iunit, ncols
89 logical :: file_exists
90 character(256) :: filename
91
92 push_sub(photon_mode_init)
93
94 this%nmodes = 0
95
96 this%dim = dim
97 this%has_q0_p0 = .false.
98
99 !%Variable PhotonmodesFilename
100 !%Type string
101 !%Default "photonmodes"
102 !%Section Linear Response::Casida
103 !%Description
104 !% Filename for photon modes in text format
105 !% - first line contains 2 integers: number of photon modes and number of
106 !% columns
107 !% - each further line contains the given number of floats for one photon
108 !% mode
109 !%End
110 call parse_variable(namespace, 'PhotonmodesFilename', 'photonmodes', filename)
111 inquire(file=trim(filename), exist=file_exists)
112 if (file_exists) then
113 if (mpi_grp_is_root(mpi_world)) then
114 message(1) = 'Opening '//trim(filename)
115 call messages_info(1, namespace=namespace)
116 ! open file on root
117 iunit = io_open(trim(filename), namespace, action='read', form='formatted')
118
119 ! get dimensions from first line
120 read(iunit, *) this%nmodes, ncols
121
122 write(message(1), '(3a,i7,a,i3,a)') 'Reading file ', trim(filename), ' with ', &
123 this%nmodes, ' photon modes and ', ncols, ' columns.'
124 call messages_info(1, namespace=namespace)
125
126 safe_allocate(this%omega(1:this%nmodes))
127 safe_allocate(this%lambda(1:this%nmodes))
128 safe_allocate(this%pol(1:this%nmodes,1:3))
129
130 ! now read in all modes
131 do ii = 1, this%nmodes
132 if (ncols == 5) then
133 read(iunit, *) this%omega(ii), this%lambda(ii), &
134 this%pol(ii,1), this%pol(ii,2), this%pol(ii,3)
135 else if (ncols == 7) then
136 this%has_q0_p0 = .true.
137 if (.not. associated(this%pt_coord_q0)) then
138 safe_allocate(this%pt_coord_q0(1:this%nmodes))
139 end if
140 if (.not. associated(this%pt_momen_p0)) then
141 safe_allocate(this%pt_momen_p0(1:this%nmodes))
142 end if
143 read(iunit, *) this%omega(ii), this%lambda(ii), &
144 this%pol(ii,1), this%pol(ii,2), this%pol(ii,3), &
145 this%pt_coord_q0(ii), this%pt_momen_p0(ii)
146 else
147 ! error if not 5 columns
148 message(1) = 'Error: unexpected number of columns in file:'
149 message(2) = filename
150 call messages_fatal(2, namespace=namespace)
151 end if
153 ! Normalize polarization vector
154 this%pol(ii,:) = this%pol(ii,:)/norm2(this%pol(ii,:))
155 end do
156 call io_close(iunit)
157 end if
158 ! broadcast first array dimensions, then allocate and broadcast arrays
159 call mpi_world%bcast(this%nmodes, 1, mpi_integer, 0)
160 call mpi_world%bcast(ncols, 1, mpi_integer, 0)
161 if (.not. mpi_grp_is_root(mpi_world)) then
162 safe_allocate(this%omega(1:this%nmodes))
163 safe_allocate(this%lambda(1:this%nmodes))
164 safe_allocate(this%pol(1:this%nmodes,1:3))
165 if (ncols == 7) then
166 safe_allocate(this%pt_coord_q0(1:this%nmodes))
167 safe_allocate(this%pt_momen_p0(1:this%nmodes))
168 end if
169 end if
170 call mpi_world%bcast(this%omega(1), this%nmodes, mpi_double_precision, 0)
171 call mpi_world%bcast(this%lambda(1), this%nmodes, mpi_double_precision, 0)
172 call mpi_world%bcast(this%pol(1,1), this%nmodes*3, mpi_double_precision, 0)
173 if (ncols == 7) then
174 call mpi_world%bcast(this%pt_coord_q0(1), this%nmodes, mpi_double_precision, 0)
175 call mpi_world%bcast(this%pt_momen_p0(1), this%nmodes, mpi_double_precision, 0)
176 end if
177 else
178 if (.not. parse_is_defined(namespace, 'PhotonModes')) then
179 call messages_write('You need to specify the correct external photon modes file')
180 call messages_write('or define the PhotonModes variable!')
181 call messages_fatal(namespace=namespace)
182 end if
183 end if
184
185 !%Variable PhotonModes
186 !%Type block
187 !%Section Hamiltonian::XC
188 !%Description
189 !% Each line of the block should specify one photon mode. The syntax is the following:
190 !%
191 !% %PhotonModes
192 !% omega1 | lambda1| PolX1 | PolY1 | PolZ1
193 !% ...
194 !% %
195 !%
196 !% The first column is the mode frequency, in units of energy.
197 !% The second column is the coupling strength, in units of energy.
198 !% The remaining columns specify the polarization direction of the mode.
199 !% If the polarization vector should be normalized to one. If that is not the case
200 !% the code will normalize it.
201 !%End
202
203 if (.not. file_exists) then
204 if (parse_block(namespace, 'PhotonModes', blk) == 0) then
205
206 this%nmodes = parse_block_n(blk)
207 safe_allocate(this%omega(1:this%nmodes))
208 safe_allocate(this%lambda(1:this%nmodes))
209 safe_allocate(this%pol(1:this%nmodes, 1:this%dim))
210
211 this%pol = m_zero
212
213 do ii = 1, this%nmodes
214 ncols = parse_block_cols(blk, ii-1)
215
216 ! Read line
217 call parse_block_float(blk, ii-1, 0, this%omega(ii), units_inp%energy) ! frequency
218 call parse_block_float(blk, ii-1, 1, this%lambda(ii), units_inp%energy) ! coupling strength
219 do idir = 1, this%dim
220 call parse_block_float(blk, ii-1, idir + 1, this%pol(ii, idir)) ! polarization vector components
221 end do
222
223 if (ncols > this%dim + 2) then
224 this%has_q0_p0 = .true.
225 if (.not. associated(this%pt_coord_q0)) then
226 safe_allocate(this%pt_coord_q0(1:this%nmodes))
227 end if
228 if (.not. associated(this%pt_momen_p0)) then
229 safe_allocate(this%pt_momen_p0(1:this%nmodes))
230 end if
231 call parse_block_float(blk, ii-1, this%dim+2, this%pt_coord_q0(ii)) !row, column
232 call parse_block_float(blk, ii-1, this%dim+3, this%pt_momen_p0(ii)) !row, column
233 end if
234 ! Sanity check
235 if ((ncols /= this%dim + 2) .and. (ncols /= this%dim + 4)) then
236 call messages_input_error(namespace, 'PhotonModes', 'Incorrect number of columns')
237 end if
238
239 ! Normalize polarization vector
240 this%pol(ii,:) = this%pol(ii,:)/norm2(this%pol(ii,:))
241
242 end do
243 call parse_block_end(blk)
244 else
245 call messages_write('You need to specify the photon modes!')
246 call messages_fatal(namespace=namespace)
247 end if
248 end if
249
250 !%Variable TDPhotonicTimeScale
251 !%Type float
252 !%Default 1.0
253 !%Section Time-Dependent::Propagation
254 !%Description
255 !% This variable defines the factor between the timescale of photonic
256 !% and electronic movement.
257 !% for more details see the documentation of TDIonicTimeScale
258 !% If you also use TDIonicTimeScale, we advise to set
259 !% TDPhotonicTimeScale = TDIonicTimeScale, in the case the
260 !% photon frequency is in a vibrational energy range.
261 !% Important: The electronic time step will be the value of
262 !% <tt>TDTimeStep</tt> divided by this variable, so if you have determined an
263 !% optimal electronic time step (that we can call <i>dte</i>), it is
264 !% recommended that you define your time step as:
265 !%
266 !% <tt>TDTimeStep</tt> = <i>dte</i> * <tt>TDPhotonicTimeScale</tt>
267 !%
268 !% so you will always use the optimal electronic time step
269 !% (<a href=http://arxiv.org/abs/0710.3321>more details</a>).
270 !%End
271
272 call parse_variable(namespace, 'TDPhotonicTimeScale', 1.0_real64, this%mu)
273
274 this%ex = m_zero
275 safe_allocate(this%number(1:this%nmodes))
276 this%number = m_zero
277
278 ! photon-free approach, which requires the dressed photon modes
279 this%use_photon_exchange = .false.
280
281 this%use_photon_exchange = optional_default(photon_free, .false.)
282 if (this%use_photon_exchange) then
283 safe_allocate(this%dressed_omega(1:this%nmodes))
284 safe_allocate(this%dressed_lambda(1:this%nmodes))
285 safe_allocate(this%dressed_pol(1:this%dim, 1:this%nmodes))
286 safe_allocate(this%rotated_omega(1:this%nmodes))
287 end if
288
289 pop_sub(photon_mode_init)
290 end subroutine photon_mode_init
291
292 ! ---------------------------------------------------------
293 subroutine photon_mode_end(this)
294 type(photon_mode_t), intent(inout) :: this
295
296 push_sub(photon_mode_end)
297
298 safe_deallocate_a(this%correlator)
299
300 safe_deallocate_a(this%omega)
301 safe_deallocate_a(this%lambda)
302 safe_deallocate_a(this%number)
303
304 safe_deallocate_a(this%pol)
305 safe_deallocate_a(this%pol_dipole)
306
307 if (this%has_q0_p0) then
308 safe_deallocate_p(this%pt_coord_q0)
309 safe_deallocate_p(this%pt_momen_p0)
310 end if
311
312 if (this%use_photon_exchange) then
313 safe_deallocate_a(this%dressed_omega)
314 safe_deallocate_a(this%dressed_lambda)
315 safe_deallocate_a(this%dressed_pol)
316 safe_deallocate_a(this%rotated_omega)
317 end if
318
319 pop_sub(photon_mode_end)
320 end subroutine photon_mode_end
321
322 !-----------------------------------------------------------------
323 subroutine photon_mode_write_info(this, iunit, namespace)
324 type(photon_mode_t), intent(in) :: this
325 integer, optional, intent(in) :: iunit
326 type(namespace_t), optional, intent(in) :: namespace
327
328 integer :: im, idir
329
330 push_sub(photon_mode_write_info)
331
332 call messages_print_with_emphasis(msg="Photon Modes", iunit=iunit, namespace=namespace)
333 write(iunit, '(6x,a,10x,a,3x)', advance='no') 'Omega', 'Lambda'
334 write(iunit, '(1x,a,i1,a)') ('Pol.(', idir, ')', idir = 1, this%dim)
335 do im = 1, this%nmodes
336 write(iunit, '(1x,f14.12)', advance='no') this%omega(im)
337 write(iunit, '(1x,f14.12)', advance='no') this%lambda(im)
338 write(iunit, '(2X,f5.3,1X)') (this%pol(im, idir), idir = 1, this%dim)
339 end do
340 call messages_print_with_emphasis(iunit=iunit, namespace=namespace)
341
343 end subroutine photon_mode_write_info
344
345 !-----------------------------------------------------------------
346 subroutine photon_mode_set_n_electrons(this, qtot)
347 type(photon_mode_t), intent(inout) :: this
348 real(real64), intent(in) :: qtot
349
351
352 this%n_electrons = qtot
353
355 end subroutine photon_mode_set_n_electrons
356
357 !-----------------------------------------------------------------
359 subroutine photon_mode_compute_dipoles(this, mesh)
360 type(photon_mode_t), intent(inout) :: this
361 class(mesh_t), intent(in) :: mesh
362
363 integer :: i, ip
364
366
367 safe_allocate(this%pol_dipole(1:mesh%np, 1:this%nmodes))
368
369 do i = 1, this%nmodes
370 ! Calculate polarization dipole
371 do ip = 1, mesh%np
372 this%pol_dipole(ip, i) = dot_product(this%pol(i, 1:this%dim), mesh%x(ip, 1:this%dim))
373 end do
374 end do
375
377 end subroutine photon_mode_compute_dipoles
378
379 !-----------------------------------------------------------------
380 subroutine photon_mode_add_poisson_terms(this, mesh, rho, pot)
381 type(photon_mode_t), intent(in) :: this
382 type(mesh_t), intent(in) :: mesh
383 real(real64), intent(in) :: rho(:)
384 real(real64), intent(inout) :: pot(:)
385
386 integer :: ip
387 real(real64) :: lx, ld, dipole(mesh%box%dim)
388
390
391 ! Currently this only works with one photon mode
392 assert(this%nmodes == 1)
393 assert(this%n_electrons > m_epsilon)
394
395 call dmf_dipole(mesh, rho, dipole)
396 ld = dot_product(this%pol(1, 1:this%dim), dipole(1:this%dim))*this%lambda(1)
397
398 do ip = 1, mesh%np
399 lx = this%pol_dipole(ip, 1)*this%lambda(1)
400 pot(ip) = pot(ip) - this%omega(1)/sqrt(this%n_electrons)*(mesh%x(ip, this%dim + 1)*ld + lx*dipole(this%dim + 1)) + lx*ld
401 end do
402
404 end subroutine photon_mode_add_poisson_terms
405
406 !-----------------------------------------------------------------
407 subroutine photon_mode_dressed(this)
408 type(photon_mode_t), intent(inout) :: this
409
410 integer :: ia, iap
411 real(real64) :: eps_epsp, wmat_off
412 real(real64), allocatable :: wmat(:,:)
413 real(real64), allocatable :: dressed_omega_sq(:)
414 real(real64), allocatable :: lambda_pol(:,:)
415 real(real64), allocatable :: dressed_lambda_pol(:,:)
417 push_sub(photon_mode_dressed)
418
419 safe_allocate(wmat(1:this%nmodes,1:this%nmodes))
420 safe_allocate(dressed_omega_sq(1:this%nmodes))
421 safe_allocate(lambda_pol(1:this%nmodes,1:this%dim))
422 safe_allocate(dressed_lambda_pol(1:this%dim, 1:this%nmodes))
423
424 ! compute \lambda**2 * polarization array
425 do ia = 1, this%nmodes
426 lambda_pol(ia, 1:this%dim) = this%lambda(ia) * this%pol(ia, 1:this%dim)
427 end do
428
429 ! compute the photon mode matrix, the W matrix
430 do ia = 1, this%nmodes
431 do iap = ia, this%nmodes
432 eps_epsp = dot_product(this%pol(ia, 1:this%dim), this%pol(iap, 1:this%dim))
433 ! TODO: n_electrons should be the total number of electrons including the core levels
434 wmat_off = this%n_electrons * this%lambda(ia) * this%lambda(iap) * eps_epsp
435 wmat(ia,iap) = wmat_off
436 wmat(iap,ia) = wmat_off
437 end do
438 wmat(ia,ia) = this%omega(ia)**2 + wmat(ia,ia)
439 end do
440
441 ! diagonalize the W matrix
442 call lalg_eigensolve(this%nmodes, wmat, dressed_omega_sq)
443
444 do ia = 1, this%nmodes
445 this%dressed_omega(ia) = sqrt(dressed_omega_sq(ia))
446 end do
447
448 ! rotate the ( coupling times polarization )
449 call dgemm('T','N', this%dim, this%nmodes, this%nmodes,&
450 m_one, lambda_pol, this%nmodes, &
451 wmat,this%nmodes, &
452 m_zero,dressed_lambda_pol,this%dim)
453
454 ! compute the rotated coupling and polarization direction
455 do ia = 1, this%nmodes
456 this%dressed_lambda(ia) = norm2(dressed_lambda_pol(1:this%dim, ia))
457 !
458 if (this%dressed_lambda(ia) .lt. m_epsilon) THEN
459 ! to avoid dividing by zero
460 this%dressed_pol(1:this%dim, ia) = m_zero
461 else
462 this%dressed_pol(1:this%dim, ia) = dressed_lambda_pol(1:this%dim, ia)/this%dressed_lambda(ia)
463 end if
464 end do
465
466 ! rotate the bare frequency
467 this%rotated_omega = m_zero
468 ! here we define the rotated bare frequency from the dressed frequency and coupling
469 do ia = 1, this%nmodes
470 this%rotated_omega(ia) = sqrt(this%dressed_omega(ia)**2 - this%n_electrons * this%dressed_lambda(ia)**2)
471 end do
472
473 safe_deallocate_a(wmat)
474 safe_deallocate_a(dressed_omega_sq)
475 safe_deallocate_a(lambda_pol)
476 safe_deallocate_a(dressed_lambda_pol)
477
478 pop_sub(photon_mode_dressed)
479
480 end subroutine photon_mode_dressed
481
482end module photon_mode_oct_m
483
484!! Local Variables:
485!! mode: f90
486!! coding: utf-8
487!! End:
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_epsilon
Definition: global.F90:203
real(real64), parameter, public m_one
Definition: global.F90:188
Definition: io.F90:114
subroutine, public io_close(iunit, grp)
Definition: io.F90:468
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:395
This module defines various routines, operating on mesh functions.
subroutine, public dmf_dipole(mesh, ff, dipole, mask)
This routine calculates the dipole of a function ff, for arbitrary dimensions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public messages_print_with_emphasis(msg, iunit, namespace)
Definition: messages.F90:930
character(len=512), private msg
Definition: messages.F90:165
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
Definition: messages.F90:624
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:420
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:723
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
Definition: mpi.F90:430
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:266
logical function, public parse_is_defined(namespace, name)
Definition: parser.F90:502
integer function, public parse_block(namespace, name, blk, check_varinfo_)
Definition: parser.F90:618
subroutine, public photon_mode_compute_dipoles(this, mesh)
Computes the polarization dipole.
subroutine, public photon_mode_add_poisson_terms(this, mesh, rho, pot)
subroutine, public photon_mode_write_info(this, iunit, namespace)
subroutine, public photon_mode_end(this)
subroutine, public photon_mode_dressed(this)
subroutine, public photon_mode_set_n_electrons(this, qtot)
subroutine, public photon_mode_init(this, namespace, dim, photon_free)
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
This module defines the unit system, used for input and output.
type(unit_system_t), public units_inp
the units systems for reading and writing
Describes mesh distribution to nodes.
Definition: mesh.F90:186
int true(void)