Octopus
pes_flux.F90
Go to the documentation of this file.
1!! Copyright (C) 2015 P. Wopperer and U. De Giovannini
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
21module pes_flux_oct_m
24 use box_oct_m
28 use comm_oct_m
29 use debug_oct_m
33 use global_oct_m
36 use io_oct_m
37 use, intrinsic :: iso_fortran_env
38 use lasers_oct_m
39 use loct_oct_m
41 use math_oct_m
43 use mesh_oct_m
46 use mpi_oct_m
48 use parser_oct_m
51 use space_oct_m
54 use string_oct_m
55 use unit_oct_m
57 use utils_oct_m
59
60 implicit none
61
62 private
63
64 public :: &
65 pes_flux_t, &
77
78 type pes_flux_t
79 private
80
83
84 integer :: nkpnts
85 integer :: nkpnts_start, nkpnts_end
86 integer :: nk
87 integer :: nk_start, nk_end
88
89 ! spherical momentum grid
90 integer :: nstepsthetak, nstepsphik
91 real(real64) :: thetak_rng(1:2)
92 real(real64) :: phik_rng(1:2)
93 integer :: nstepsomegak
94 integer :: nstepsomegak_start, nstepsomegak_end
95 real(real64) :: ktransf(1:3,1:3)
96
97 real(real64), allocatable :: klinear(:,:)
98 ! !< polar: klinear(nk,1) defines the radial part of the k-mesh
99 ! !< cartesian: klinear(nk,1:3) defines the the k-mesh along each axes
100
101 real(real64) :: dk
102 real(real64), allocatable :: kcoords_cub(:,:,:)
103 real(real64), allocatable :: kcoords_sph(:,:,:)
104 integer :: kgrid
105
106 ! Surface relates quantities
107 integer, public :: surf_shape
108 integer :: nsrfcpnts
109 integer :: nsrfcpnts_start, nsrfcpnts_end
110 real(real64), allocatable :: srfcnrml(:,:)
111 real(real64), allocatable :: rcoords(:,:)
112 integer, allocatable :: srfcpnt(:)
113 integer, allocatable :: rankmin(:)
114 integer :: lmax
115 complex(real64), allocatable :: ylm_r(:,:,:)
116 complex(real64), allocatable :: ylm_k(:,:,:)
117 real(real64), allocatable :: j_l(:,:)
118 real(real64) :: radius
119 integer, allocatable :: face_idx_range(:,:)
120 ! !! 1 (2) start (end) idx of face nface in rcoords(:,:)
121
122
123 complex(real64), allocatable :: bvk_phase(:,:)
124 complex(real64), allocatable :: expkr(:,:,:,:)
125 complex(real64), allocatable :: expkr_perp(:,:)
126 ! !! direction perpendicular to the face
127 real(real64), allocatable :: LLr(:,:)
128 integer, allocatable :: NN(:,:)
129 integer, allocatable :: Lkpuvz_inv(:,:,:)
130 ! !! (u,v parametric on face and z perpendicular)
131
132 ! Surface and time integration
133 integer :: tdsteps
134 ! !< = mesh%mpi_grp%size (PES_SPHERICAL)
135 integer :: max_iter
136 integer :: save_iter
137 integer :: itstep
138
139 complex(real64), allocatable :: wf(:,:,:,:,:)
140 complex(real64), allocatable :: gwf(:,:,:,:,:,:)
141 real(real64), allocatable :: veca(:,:)
142 complex(real64), allocatable :: conjgphase_prev(:,:)
143 complex(real64), allocatable :: spctramp_cub(:,:,:,:)
144 complex(real64), allocatable :: spctramp_sph(:,:,:,:,:)
145
146 integer, public :: ll(3)
147 ! !< mesh. Used when working with semi-periodic systems
148
149 type(mesh_interpolation_t) :: interp
150
151 logical :: parallel_in_momentum
152 logical :: arpes_grid
153 logical :: surf_interp
154 logical :: use_symmetry
155 logical :: runtime_output
156 logical :: anisotrpy_correction
157
158 integer :: par_strategy
159 integer :: dim
160 integer :: pdim
161
162 end type pes_flux_t
163
164 integer, parameter :: &
165 PES_CUBIC = 1, &
166 pes_spherical = 2, &
167 pes_plane = 3
168
169 integer, parameter :: &
170 PES_POLAR = 1, &
172
173
174contains
175
176
177 ! ---------------------------------------------------------
178 subroutine pes_flux_init(this, namespace, space, mesh, st, ext_partners, kpoints, abb, save_iter, max_iter)
179 type(pes_flux_t), intent(inout) :: this
180 type(namespace_t), intent(in) :: namespace
181 class(space_t), intent(in) :: space
182 type(mesh_t), intent(in) :: mesh
183 type(states_elec_t), intent(in) :: st
184 type(partner_list_t), intent(in) :: ext_partners
185 type(kpoints_t), intent(in) :: kpoints
186 type(absorbing_boundaries_t), intent(in) :: abb
187 integer, intent(in) :: save_iter
188 integer, intent(in) :: max_iter
189
190 type(block_t) :: blk
191 real(real64) :: border(space%dim) ! distance of surface from border
192 real(real64) :: offset(space%dim) ! offset for border
193 integer :: stst, stend, kptst, kptend, sdim, mdim, pdim
194 integer :: imdim
195 integer :: isp
196 integer :: il
198 integer :: nstepsphir, nstepsthetar
199 integer :: ll, mm
200 integer :: default_shape
201 real(real64) :: fc_ptdens
203 integer :: par_strategy
204 logical :: use_symmetry
206 type(lasers_t), pointer :: lasers
210 stst = st%st_start
211 stend = st%st_end
212 kptst = st%d%kpt%start
213 kptend = st%d%kpt%end
214 sdim = st%d%dim
215 mdim = space%dim
216 pdim = space%periodic_dim
218 this%surf_interp = .false.
219
221 lasers => list_get_lasers(ext_partners)
222 if(associated(lasers)) then
223 do il = 1, lasers%no_lasers
224 if (laser_kind(lasers%lasers(il)) /= e_field_vector_potential) then
225 message(1) = 't-surff only works in velocity gauge.'
226 call messages_fatal(1, namespace=namespace)
227 end if
228 end do
229 end if
231 message(1) = 'Info: Calculating PES using t-surff technique.'
232 call messages_info(1, namespace=namespace)
234 ! -----------------------------------------------------------------
235 ! Setting up r-mesh (the surface)
236 ! -----------------------------------------------------------------
237 !%Variable PES_Flux_Shape
238 !%Type integer
239 !%Section Time-Dependent::PhotoElectronSpectrum
240 !%Description
241 !% The shape of the surface.
242 !%Option cub 1
243 !% Uses a parallelepiped as surface. All surface points are grid points.
244 !% Choose the location of the surface with variable <tt>PES_Flux_Lsize</tt>
245 !% (default for 1D and 2D).
246 !%Option sph 2
247 !% Constructs a sphere with radius <tt>PES_Flux_Radius</tt>. Points on the sphere
248 !% are interpolated by trilinear interpolation (default for 3D).
249 !%Option pln 3
250 !% This option is for periodic systems.
251 !% Constructs a plane perpendicular to the non-periodic dimension
252 !% at <tt>PES_Flux_Lsize</tt>.
253 !%End
254
255 default_shape = pes_spherical
256 if (space%is_periodic()) then
257 default_shape = pes_plane
258 else if (mdim <= 2) then
259 default_shape = pes_cubic
260 else
261 select type (box => mesh%box)
263 default_shape = pes_cubic
264 end select
265 end if
266
267 call parse_variable(namespace, 'PES_Flux_Shape', default_shape, this%surf_shape)
268 if (.not. varinfo_valid_option('PES_Flux_Shape', this%surf_shape, is_flag = .true.)) then
269 call messages_input_error(namespace,'PES_Flux_Shape')
270 end if
271 if (this%surf_shape == pes_spherical .and. mdim /= 3) then
272 message(1) = 'Spherical grid works only in 3d.'
273 call messages_fatal(1, namespace=namespace)
274 end if
275 call messages_print_var_option('PES_Flux_Shape', this%surf_shape, namespace=namespace)
276
277
278 !%Variable PES_Flux_AnisotropyCorrection
279 !%Type logical
280 !%Section Time-Dependent::PhotoElectronSpectrum
281 !%Description
282 !% Apply anisotropy correction.
283 !%
284 !%End
285 if (this%surf_shape == pes_cubic) then
286 call parse_variable(namespace, 'PES_Flux_AnisotropyCorrection', .false., this%anisotrpy_correction)
287 call messages_print_var_value('PES_Flux_AnisotropyCorrection', this%anisotrpy_correction, namespace=namespace)
288 end if
289
290 !%Variable PES_Flux_Offset
291 !%Type block
292 !%Section Time-Dependent::PhotoElectronSpectrum
293 !%Description
294 !% Shifts the surface for <tt>PES_Flux_Shape = cub</tt>. The syntax is:
295 !%
296 !% <tt>%PES_Flux_Offset
297 !% <br>&nbsp;&nbsp;xshift | yshift | zshift
298 !% <br>%
299 !% </tt>
300 !%End
301 offset = m_zero
302 if (parse_block(namespace, 'PES_Flux_Offset', blk) == 0) then
303 do imdim = 1, mdim
304 call parse_block_float(blk, 0, imdim - 1, offset(imdim))
305 end do
306 call parse_block_end(blk)
307 end if
308
309 !%Variable PES_Flux_Lmax
310 !%Type integer
311 !%Default 80
312 !%Section Time-Dependent::PhotoElectronSpectrum
313 !%Description
314 !% Maximum order of the spherical harmonic to be integrated on an equidistant spherical
315 !% grid (to be changed to Gauss-Legendre quadrature).
316 !%End
317 if (this%surf_shape == pes_spherical) then
318 call parse_variable(namespace, 'PES_Flux_Lmax', 80, this%lmax)
319 if (this%lmax < 1) call messages_input_error(namespace,'PES_Flux_Lmax', 'must be > 0')
320 call messages_print_var_value('PES_Flux_Lmax', this%lmax, namespace=namespace)
321 end if
322
323
324 if (this%surf_shape == pes_cubic .or. this%surf_shape == pes_plane) then
325
326
327 !%Variable PES_Flux_Lsize
328 !%Type block
329 !%Section Time-Dependent::PhotoElectronSpectrum
330 !%Description
331 !% For <tt>PES_Flux_Shape = cub</tt> sets the dimensions along each direction. The syntax is:
332 !%
333 !% <tt>%PES_Flux_Lsize
334 !% <br>&nbsp;&nbsp;xsize | ysize | zsize
335 !% <br>%
336 !% </tt>
337 !%
338 !% where <tt>xsize</tt>, <tt>ysize</tt>, and <tt>zsize</tt> are with respect to the origin. The surface can
339 !% be shifted with <tt>PES_Flux_Offset</tt>.
340 !% If <tt>PES_Flux_Shape = pln</tt>, specifies the position of two planes perpendicular to
341 !% the non-periodic dimension symmetrically placed at <tt>PES_Flux_Lsize</tt> distance from
342 !% the origin.
343 !%End
344 if (parse_block(namespace, 'PES_Flux_Lsize', blk) == 0) then
345 do imdim = 1, mdim
346 call parse_block_float(blk, 0, imdim - 1, border(imdim))
347 end do
348 ! Snap the face to the closest grid point
349 border(1:mdim) = int(border(1:mdim) / mesh%spacing(1:mdim)) * mesh%spacing(1:mdim)
350 call parse_block_end(blk)
351
352 else if (parse_is_defined(namespace, 'PES_Flux_Lsize')) then
353 border(mdim) = mesh%box%bounding_box_l(mdim) * m_half
354 if (space%is_periodic()) then
355 ! the cube sides along the periodic directions are out of the simulation box
356 border(1:pdim)= mesh%box%bounding_box_l(1:pdim) * m_two
357 call parse_variable(namespace, 'PES_Flux_Lsize', border(mdim), border(mdim))
358 ! Snap the plane to the closest grid point
359 border(mdim) = floor(border(mdim) / mesh%spacing(mdim)) * mesh%spacing(mdim)
360 else
361 call parse_variable(namespace, 'PES_Flux_Lsize', border(mdim), border(mdim))
362 border(1:mdim - 1) = border(mdim)
363 border(1:mdim) = floor(border(1:mdim) / mesh%spacing(1:mdim)) * mesh%spacing(1:mdim)
364 end if
365 else
366 select type (box => mesh%box)
367 type is (box_sphere_t)
368 border(1:mdim) = box%radius / sqrt(m_two) * m_half
369 type is (box_parallelepiped_t)
370 border(1:mdim) = box%half_length(1:mdim) * m_half
371 class default
372 call messages_write('PES_Flux_Lsize not specified. No default values available for this box shape.')
373 call messages_new_line()
374 call messages_write('Specify the location of the parallelepiped with block PES_Flux_Lsize.')
375 call messages_fatal(namespace=namespace)
376 end select
377 call messages_write('PES_Flux_Lsize not specified. Using default values.')
378 call messages_info(namespace=namespace)
379 end if
380
381 call messages_print_var_value('PES_Flux_Lsize', border(1:mdim), namespace=namespace)
382
383 !%Variable PES_Flux_Face_Dens
384 !%Type block
385 !%Section Time-Dependent::PhotoElectronSpectrum
386 !%Description
387 !% Define the number of points density per unit of area (in au) on the
388 !% face of the 'cub' surface.
389 !%End
390 if (parse_is_defined(namespace, 'PES_Flux_Face_Dens')) then
391 this%surf_interp = .true.
392 call parse_variable(namespace, 'PES_Flux_Face_Dens', m_one, fc_ptdens)
393 call messages_print_var_value('PES_Flux_Face_Dens', fc_ptdens, namespace=namespace)
394 end if
395
396 else
397
398 this%surf_interp = .true.
399
400 !%Variable PES_Flux_Radius
401 !%Type float
402 !%Section Time-Dependent::PhotoElectronSpectrum
403 !%Description
404 !% The radius of the sphere, if <tt>PES_Flux_Shape == sph</tt>.
405 !%End
406 if (parse_is_defined(namespace, 'PES_Flux_Radius')) then
407 call parse_variable(namespace, 'PES_Flux_Radius', m_zero, this%radius)
408 if (this%radius <= m_zero) call messages_input_error(namespace, 'PES_Flux_Radius')
409 call messages_print_var_value('PES_Flux_Radius', this%radius, namespace=namespace)
410 else
411 select type (box => mesh%box)
412 type is (box_sphere_t)
413 this%radius = box%radius
414 type is (box_parallelepiped_t)
415 this%radius = minval(box%half_length(1:mdim))
416 class default
417 message(1) = 'PES_Flux_Radius not specified. No default values available for this box shape.'
418 message(2) = 'Specify the radius of the sphere with variable PES_Flux_Radius.'
419 call messages_fatal(2, namespace=namespace)
420 end select
421 message(1) = 'PES_Flux_Radius not specified. Using default values.'
422 call messages_info(1, namespace=namespace)
423 call messages_print_var_value('PES_Flux_Radius', this%radius, namespace=namespace)
424 end if
425
426 !%Variable PES_Flux_StepsThetaR
427 !%Type integer
428 !%Default: 2 <tt>PES_Flux_Lmax</tt> + 1
429 !%Section Time-Dependent::PhotoElectronSpectrum
430 !%Description
431 !% Number of steps in <math>\theta</math> (<math>0 \le \theta \le \pi</math>) for the spherical surface.
432 !%End
433 call parse_variable(namespace, 'PES_Flux_StepsThetaR', 2 * this%lmax + 1, nstepsthetar)
434 if (nstepsthetar < 0) call messages_input_error(namespace, 'PES_Flux_StepsThetaR')
435 call messages_print_var_value("PES_Flux_StepsThetaR", nstepsthetar, namespace=namespace)
436
437 !%Variable PES_Flux_StepsPhiR
438 !%Type integer
439 !%Default: 2 <tt>PES_Flux_Lmax</tt> + 1
440 !%Section Time-Dependent::PhotoElectronSpectrum
441 !%Description
442 !% Number of steps in <math>\phi</math> (<math>0 \le \phi \le 2 \pi</math>) for the spherical surface.
443 !%End
444 call parse_variable(namespace, 'PES_Flux_StepsPhiR', 2 * this%lmax + 1, nstepsphir)
445 if (nstepsphir < 0) call messages_input_error(namespace, 'PES_Flux_StepsPhiR')
446 call messages_print_var_value("PES_Flux_StepsPhiR", nstepsphir, namespace=namespace)
447
448 end if
449
450
451 if (this%surf_interp) call mesh_interpolation_init(this%interp, mesh)
452
453 ! -----------------------------------------------------------------
454 ! Get the surface points
455 ! -----------------------------------------------------------------
456 if (this%surf_shape == pes_cubic .or. this%surf_shape == pes_plane) then
457 call pes_flux_getcube(this, mesh, abb, border, offset, fc_ptdens)
458 else
459 ! equispaced grid in theta & phi (Gauss-Legendre would optimize to nstepsthetar = this%lmax & nstepsphir = 2*this%lmax + 1):
460 ! nstepsthetar = M_TWO * this%lmax + 1
461 ! nstepsphir = M_TWO * this%lmax + 1
462 call pes_flux_getsphere(this, mesh, nstepsthetar, nstepsphir)
463 end if
464
465
466 !%Variable PES_Flux_Parallelization
467 !%Type flag
468 !%Section Time-Dependent::PhotoElectronSpectrum
469 !%Description
470 !% The parallelization strategy to be used to calculate the PES spectrum
471 !% using the resources available in the domain parallelization pool.
472 !% This option is not available without domain parallelization.
473 !% Parallelization over k-points and states is always enabled when available.
474 !%Option pf_none bit(1)
475 !% No parallelization.
476 !%Option pf_time bit(2)
477 !% Parallelize time integration. This requires to store some quantities over a
478 !% number of time steps equal to the number of cores available.
479 !%Option pf_momentum bit(3)
480 !% Parallelize over the final momentum grid. This strategy has a much lower
481 !% memory footprint than the one above (time) but seems to provide a smaller
482 !% speedup.
483 !%Option pf_surface bit(4)
484 !% Parallelize over surface points.
485 !%
486 !%
487 !% Option pf_time and pf_surface can be combined: pf_time + pf_surface.
488 !%
489 !%End
490
491 this%par_strategy = option__pes_flux_parallelization__pf_none
492 if (mesh%parallel_in_domains) then
493
494 if (this%surf_shape == pes_spherical) then
495 this%par_strategy = option__pes_flux_parallelization__pf_time &
496 + option__pes_flux_parallelization__pf_surface
497 else
498 this%par_strategy = option__pes_flux_parallelization__pf_surface
499 if (space%dim == 1) this%par_strategy = option__pes_flux_parallelization__pf_time
500 end if
501 par_strategy = this%par_strategy
502 call parse_variable(namespace, 'PES_Flux_Parallelization', par_strategy, this%par_strategy)
503 if (.not. varinfo_valid_option('PES_Flux_Parallelization', this%par_strategy, is_flag = .true.)) then
504 call messages_input_error(namespace,'PES_Flux_Parallelization')
505 end if
506
507 end if
508
509 !Sanity check
510 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0 .and. &
511 bitand(this%par_strategy, option__pes_flux_parallelization__pf_momentum) /= 0) then
512 call messages_input_error(namespace,'PES_Flux_Parallelization', "Cannot combine pf_surface and pf_momentum")
513 end if
514
515 write(message(1),'(a)') 'Input: [PES_Flux_Parallelization = '
516 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_none) /= 0) then
517 write(message(1),'(a,1x,a)') trim(message(1)), 'pf_none '
518 end if
519 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
520 write(message(1),'(a,1x,a)') trim(message(1)), 'pf_time '
521 end if
522 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_momentum) /= 0) then
523 write(message(1),'(a,1x,a)') trim(message(1)), 'pf_momentum'
524 end if
525 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0) then
526 write(message(1),'(a,1x,a)') trim(message(1)), 'pf_surface'
527 end if
528 write(message(1),'(2a)') trim(message(1)), ']'
529 call messages_info(1, namespace=namespace)
530
531
532
533 ! distribute the surface points on nodes,
534 ! since mesh domains may have different numbers of surface points.
535 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0) then
536
537 call pes_flux_distribute(1, this%nsrfcpnts, this%nsrfcpnts_start, this%nsrfcpnts_end, mesh%mpi_grp%comm)
538
539 if (this%surf_shape /= pes_spherical) call pes_flux_distribute_facepnts_cub(this, mesh)
540
541! Keep this because is useful for debug but not enough to bother having it polished out
542! if (debug%info) then
543! call mpi_world%barrier()
544! write(*,*) &
545! 'Debug: surface points on node ', mpi_world%rank, ' : ', this%nsrfcpnts_start, this%nsrfcpnts_end
546! call mpi_world%barrier()
547! end if
548
549 else
550 this%nsrfcpnts_start = 1
551 this%nsrfcpnts_end = this%nsrfcpnts
552
553 end if
554
555! Keep this because is useful for debug but not enough to bother having it polished out
556! if (debug%info .and. mpi_grp_is_root(mpi_world)) then
557! do isp = 1, this%nsrfcpnts
558! write(223,*) isp, this%rcoords(:, isp), this%srfcnrml(:,isp)
559! end do
560! if (this%nsrfcpnts > 0) flush(223)
561! end if
562
563 ! Generate the momentum space mesh grid
564 call pes_flux_reciprocal_mesh_gen(this, namespace, space, st, kpoints, mesh%mpi_grp%comm)
565
566
567 !%Variable PES_Flux_UseSymmetries
568 !%Type logical
569 !%Section Time-Dependent::PhotoElectronSpectrum
570 !%Description
571 !% Use surface and momentum grid symmetries to speed up calculation and
572 !% lower memory footprint.
573 !% By default available only when the surface shape matches the grid symmetry i.e.:
574 !% PES_Flux_Shape = m_cub or m_pln and PES_Flux_Momenutum_Grid = m_cartesian
575 !% or
576 !% PES_Flux_Shape = m_sph and PES_Flux_Momenutum_Grid = m_polar
577 !%End
578 use_symmetry = .false.
579 if ((this%surf_shape == pes_cubic .or. this%surf_shape == pes_plane) &
580 .and. this%kgrid == pes_cartesian .and. mdim == 3) use_symmetry = .true.
581 if (this%surf_shape == pes_spherical .and. this%kgrid == pes_polar) use_symmetry = .true.
582 call parse_variable(namespace, 'PES_Flux_UseSymmetries', use_symmetry, this%use_symmetry)
583 call messages_print_var_value('PES_Flux_UseSymmetries', this%use_symmetry, namespace=namespace)
584
585
586
587 ! get the values of the spherical harmonics for the surface points for PES_SPHERICAL
588 if (this%surf_shape == pes_spherical) then
589 safe_allocate(this%ylm_r(0:this%lmax, -this%lmax:this%lmax, this%nsrfcpnts_start:this%nsrfcpnts_end))
590 this%ylm_r = m_z0
591
592 if (this%nsrfcpnts_start > 0) then
593 do isp = this%nsrfcpnts_start, this%nsrfcpnts_end
594 do ll = 0, this%lmax
595 do mm = -ll, ll
596 call ylmr_cmplx(this%rcoords(1:3, isp), ll, mm, this%ylm_r(ll, mm, isp))
597 this%ylm_r(ll, mm, isp) = conjg(this%ylm_r(ll, mm, isp))
598 end do
599 end do
600 end do
601 end if
602
603 else
604
605 call pes_flux_integrate_cub_tabulate(this, space, mesh, st, kpoints)
606
607 end if
608
609
610 !%Variable PES_Flux_RuntimeOutput
611 !%Type logical
612 !%Section Time-Dependent::PhotoElectronSpectrum
613 !%Description
614 !% Write output in ascii format at runtime.
615 !%
616 !%End
617 call parse_variable(namespace, 'PES_Flux_RuntimeOutput', .false., this%runtime_output)
618 call messages_print_var_value('PES_Flux_RuntimeOutput', this%runtime_output, namespace=namespace)
619
620
621
622 ! -----------------------------------------------------------------
623 ! Options for time integration
624 ! -----------------------------------------------------------------
625 this%max_iter = max_iter
626 this%save_iter = save_iter
627 this%itstep = 0
628 this%tdsteps = 1
629
630 if (mesh%parallel_in_domains .and. bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
631 this%tdsteps = mesh%mpi_grp%size
632 end if
633
634 ! -----------------------------------------------------------------
635 ! Allocations
636 ! -----------------------------------------------------------------
637
638 safe_allocate(this%wf(stst:stend, 1:sdim, kptst:kptend, 0:this%nsrfcpnts, 1:this%tdsteps))
639 this%wf = m_z0
640
641 safe_allocate(this%gwf(stst:stend, 1:sdim, kptst:kptend, 0:this%nsrfcpnts, 1:this%tdsteps, 1:mdim))
642 this%gwf = m_z0
643
644 safe_allocate(this%veca(1:mdim, 1:this%tdsteps))
645 this%veca = m_zero
646
647 if (this%surf_shape == pes_spherical) then
648 safe_allocate(this%spctramp_sph(stst:stend, 1:sdim, kptst:kptend, 1:this%nk, 1:this%nstepsomegak))
649 this%spctramp_sph = m_z0
650
651 safe_allocate(this%conjgphase_prev(1:this%nk, 1:this%nstepsomegak))
652
653 else
654 safe_allocate(this%spctramp_cub(stst:stend, 1:sdim, kptst:kptend, 1:this%nkpnts))
655 this%spctramp_cub = m_z0
656
657 safe_allocate(this%conjgphase_prev(1:this%nkpnts, kptst:kptend))
658
659 end if
660
661 this%conjgphase_prev = m_z1
662
663 call messages_write('Info: Total number of surface points = ')
664 call messages_write(this%nsrfcpnts)
665 call messages_info(namespace=namespace)
666
667 call messages_write('Info: Total number of momentum points = ')
668 call messages_write(this%nkpnts)
669 call messages_info(namespace=namespace)
670
671 pop_sub(pes_flux_init)
672 end subroutine pes_flux_init
673
674 ! ---------------------------------------------------------
675 subroutine pes_flux_end(this)
676 type(pes_flux_t), intent(inout) :: this
677
678 push_sub(pes_flux_end)
679
680 if (this%surf_shape == pes_spherical) then
681 safe_deallocate_a(this%kcoords_sph)
682 safe_deallocate_a(this%ylm_k)
683 safe_deallocate_a(this%j_l)
684 safe_deallocate_a(this%ylm_r)
685 safe_deallocate_a(this%conjgphase_prev)
686 safe_deallocate_a(this%spctramp_sph)
687 else
688 safe_deallocate_a(this%kcoords_cub)
689 safe_deallocate_a(this%conjgphase_prev)
690 safe_deallocate_a(this%spctramp_cub)
691
692 if (.not. this%surf_interp) then
693 safe_deallocate_a(this%srfcpnt)
694 end if
695 safe_deallocate_a(this%rankmin)
696
697 safe_deallocate_a(this%face_idx_range)
698 safe_deallocate_a(this%LLr)
699 safe_deallocate_a(this%NN)
700
701 safe_deallocate_a(this%expkr)
702 safe_deallocate_a(this%expkr_perp)
703 safe_deallocate_a(this%bvk_phase)
704
705 safe_deallocate_a(this%Lkpuvz_inv)
706
707 end if
708
709 safe_deallocate_a(this%klinear)
710
711 safe_deallocate_a(this%srfcnrml)
712 safe_deallocate_a(this%rcoords)
713
714 safe_deallocate_a(this%wf)
715 safe_deallocate_a(this%gwf)
716 safe_deallocate_a(this%veca)
717
718 pop_sub(pes_flux_end)
719 end subroutine pes_flux_end
720
721 ! ---------------------------------------------------------
722 subroutine pes_flux_reciprocal_mesh_gen(this, namespace, space, st, kpoints, comm, post)
723 type(pes_flux_t), intent(inout) :: this
724 type(namespace_t), intent(in) :: namespace
725 class(space_t), intent(in) :: space
726 type(states_elec_t), intent(in) :: st
727 type(kpoints_t), intent(in) :: kpoints
728 type(mpi_comm), intent(in) :: comm
729 logical, optional, intent(in) :: post
730
731 integer :: mdim, pdim
732 integer :: kptst, kptend
733 integer :: ikp, ikpt
734 integer :: ll, mm, idim, idir
735 integer :: ikk, ith, iph, iomk,ie, ik1, ik2, ik3, kgrid_block_dim
736 real(real64) :: kmax(space%dim), kmin(space%dim), kact, thetak, phik
737 type(block_t) :: blk
738
739 real(real64) :: emin, emax, de , kvec(1:3), xx(3)
740 integer :: nkp_out, nkmin, nkmax
741
742 integer :: kgrid
743 real(real64), allocatable :: gpoints(:,:), gpoints_reduced(:,:)
744 real(real64) :: dk(1:3), kpoint(1:3), dthetak, dphik
745 logical :: use_enegy_grid, arpes_grid
746
748
749 kptst = st%d%kpt%start
750 kptend = st%d%kpt%end
751 mdim = space%dim
752 pdim = space%periodic_dim
753
754 this%dim = mdim
755 this%pdim = pdim
756
757 !%Variable PES_Flux_Momenutum_Grid
758 !%Type integer
759 !%Section Time-Dependent::PhotoElectronSpectrum
760 !%Description
761 !% Decides how the grid in momentum space is generated.
762 !%Option polar 1
763 !% The grid is in polar coordinates with the zenith axis is along z.
764 !% The grid parameters are defined by PES_Flux_Kmax, PES_Flux_DeltaK,
765 !% PES_Flux_StepsThetaK, PES_Flux_StepsPhiK.
766 !% This is the default choice for PES_Flux_Shape = sph or cub.
767 !%Option cartesian 2
768 !% The grid is in cartesian coordinates with parameters defined by
769 !% PES_Flux_ARPES_grid, PES_Flux_EnergyGrid.
770 !% This is the default choice for PES_Flux_Shape = sph or cub.
771 !%End
772
773 ! default values
774 select case (this%surf_shape)
775 case (pes_spherical)
776 kgrid = pes_polar
777 case (pes_plane)
778 kgrid = pes_cartesian
779 case (pes_cubic)
780 kgrid = pes_cartesian
781 if (mdim == 1) kgrid = pes_polar
782
783 case default
784 assert(.false.)
785
786 end select
787
788 call parse_variable(namespace, 'PES_Flux_Momenutum_Grid', kgrid, this%kgrid)
789 if (.not. varinfo_valid_option('PES_Flux_Momenutum_Grid', this%kgrid, is_flag = .true.)) then
790 call messages_input_error(namespace,'PES_Flux_Momenutum_Grid')
791 end if
792 call messages_print_var_option('PES_Flux_Momenutum_Grid', this%kgrid, namespace=namespace)
793
794
795
796 !Check availability of the calculation requested
797 if (this%surf_shape == pes_spherical) then
798 if (this%kgrid == pes_cartesian) then
799 call messages_not_implemented('Cartesian momentum grid with a spherical surface', namespace=namespace)
800 end if
801 if (space%is_periodic()) then
802 call messages_not_implemented('Spherical surface flux for periodic systems', namespace=namespace)
803 end if
804 if (mdim == 1) then
805 call messages_not_implemented('Spherical surface flux for one-dimensional systems', namespace=namespace)
806 end if
807 end if
808
809 if (this%surf_shape == pes_cubic) then
810 if (space%is_periodic()) then
811 call messages_not_implemented('Use of cubic surface for periodic systems (use pln)', namespace=namespace)
812 end if
813 end if
814
816
817 !%Variable PES_Flux_EnergyGrid
818 !%Type block
819 !%Section Time-Dependent::PhotoElectronSpectrum
820 !%Description
821 !% The block <tt>PES_Flux_EnergyGrid</tt> specifies the energy grid
822 !% in momentum space.
823 !% <tt><br>%PES_Flux_EnergyGrid
824 !% <br>&nbsp;&nbsp; Emin | Emax | DeltaE
825 !% <br>%</tt>
826 !%End
827
828 emin = 0
829 emax = 10
830 de = 0.1_real64
831 use_enegy_grid = .false.
832
833 if (parse_block(namespace, 'PES_Flux_EnergyGrid', blk) == 0) then
834
835 call parse_block_float(blk, 0, 0, emin)
836 call parse_block_float(blk, 0, 1, emax)
837 call parse_block_float(blk, 0, 2, de)
838
839 emin = units_to_atomic(units_inp%energy, emin)
840 emax = units_to_atomic(units_inp%energy, emax)
841 de = units_to_atomic(units_inp%energy, de)
842
843 call parse_block_end(blk)
844 use_enegy_grid = .true.
845
846
847 call messages_write("Energy grid (Emin, Emax, DE) [")
848 call messages_write(trim(units_abbrev(units_out%energy)))
849 call messages_write("]: (")
850 call messages_write(units_from_atomic(units_out%energy, emin),fmt = '(f8.3)')
851 call messages_write(", ")
852 call messages_write(units_from_atomic(units_out%energy, emax), fmt = '(f8.3)')
853 call messages_write(", ")
854 call messages_write(units_from_atomic(units_out%energy, de), fmt = '(e9.2)')
855 call messages_write(")")
856 call messages_info(namespace=namespace)
857
858 kmax(1:mdim) = sqrt(m_two*emax)
859 kmin(1:mdim) = sqrt(m_two*emin)
860 this%dk = sqrt(m_two*de)
861
862 end if
863
864 kgrid_block_dim = 1
865 !ugly hack (since this input variable is properly handled below) but effective
866 call parse_variable(namespace, 'PES_Flux_ARPES_grid', .false., this%arpes_grid)
867 if (.not. use_enegy_grid .or. this%arpes_grid) then
868
869 !%Variable PES_Flux_Kmax
870 !%Type float
871 !%Default 1.0
872 !%Section Time-Dependent::PhotoElectronSpectrum
873 !%Description
874 !% The maximum value of the photoelectron momentum.
875 !% For cartesian momentum grids one can specify a value different
876 !% for cartesian direction using a block input.
877 !%End
878 if (parse_block(namespace, 'PES_Flux_Kmax', blk) == 0) then
879 if (this%kgrid == pes_cartesian) then
880 do idim = 1, mdim
881 call parse_block_float(blk, 0, idim - 1, kmax(idim))
882 end do
883 kgrid_block_dim = mdim
884 else
885 message(1) = 'Wrong block format for PES_Flux_Kmax and non-cartesian grid'
886 call messages_fatal(1, namespace=namespace)
887 end if
888 else
889 call parse_variable(namespace, 'PES_Flux_Kmax', m_one, kmax(1))
890 kmax(:)=kmax(1)
891 end if
892
893 !%Variable PES_Flux_Kmin
894 !%Type float
895 !%Default 0.0
896 !%Section Time-Dependent::PhotoElectronSpectrum
897 !%Description
898 !% The minimum value of the photoelectron momentum.
899 !% For cartesian momentum grids one can specify a value different
900 !% for cartesian direction using a block input.
901 !%End
902 if (parse_block(namespace, 'PES_Flux_Kmin', blk) == 0) then
903 if (this%kgrid == pes_cartesian) then
904 do idim = 1, mdim
905 call parse_block_float(blk, 0, idim - 1, kmin(idim))
906 end do
907 kgrid_block_dim = mdim
908 else
909 message(1) = 'Wrong block format for PES_Flux_Kmin and non-cartesian grid'
910 call messages_fatal(1, namespace=namespace)
911 end if
912 else
913 call parse_variable(namespace, 'PES_Flux_Kmin', m_zero, kmin(1))
914 kmin(:)=kmin(1)
915 end if
916
917
918 !%Variable PES_Flux_DeltaK
919 !%Type float
920 !%Default 0.02
921 !%Section Time-Dependent::PhotoElectronSpectrum
922 !%Description
923 !% Spacing of the the photoelectron momentum grid.
924 !%End
925 call parse_variable(namespace, 'PES_Flux_DeltaK', 0.02_real64, this%dk)
926 if (this%dk <= m_zero) call messages_input_error(namespace,'PES_Flux_DeltaK')
927
928 end if
929
930 do idim = 1, kgrid_block_dim
931 if (kgrid_block_dim == 1) then
932 call messages_write("Momentum linear grid (Pmin, Pmax, DP) [")
933 else
934 call messages_write("Momentum linear grid (Pmin, Pmax, DP) "//index2axis(idim)//"-axis [")
935 end if
936 call messages_write(trim(units_abbrev(units_out%mass*units_out%velocity)))
937 call messages_write("]: (")
938 call messages_write(kmin(idim),fmt = '(f8.3)')
939 call messages_write(", ")
940 call messages_write(kmax(idim), fmt = '(f8.3)')
941 call messages_write(", ")
942 call messages_write(this%dk, fmt = '(e9.2)')
943 call messages_write(")")
944 call messages_info(namespace=namespace)
945 end do
946
947
948
949! if (this%surf_shape == PES_SPHERICAL .or. this%surf_shape == PES_CUBIC) then
950 if (this%kgrid == pes_polar) then
951 ! -----------------------------------------------------------------
952 ! Setting up k-mesh
953 ! 1D = 2 points, 2D = polar coordinates, 3D = spherical coordinates
954 ! -----------------------------------------------------------------
955
956
957 !%Variable PES_Flux_ThetaK
958 !%Type block
959 !%Section Time-Dependent::PhotoElectronSpectrum
960 !%Description
961 !% Define the grid points on theta (<math>0 \le \theta \le \pi</math>) when
962 !% using a spherical grid in momentum.
963 !% The block defines the maximum and minimum values of theta and the number of
964 !% of points for the discretization.
965 !%
966 !% <tt>%PES_Flux_ThetaK
967 !% <br>&nbsp;&nbsp; theta_min | theta_max | npoints
968 !% <br>%
969 !% </tt>
970 !%
971 !% By default theta_min=0, theta_max = pi, npoints = 45.
972 !%End
973 this%nstepsthetak = 45
974 this%thetak_rng(1:2) = (/m_zero, m_pi/)
975 if (parse_block(namespace, 'PES_Flux_ThetaK', blk) == 0) then
976 call parse_block_float(blk, 0, 0, this%thetak_rng(1))
977 call parse_block_float(blk, 0, 1, this%thetak_rng(2))
978 call parse_block_integer(blk, 0, 2, this%nstepsthetak)
979 call parse_block_end(blk)
980 do idim = 1,2
981 if (this%thetak_rng(idim) < m_zero .or. this%thetak_rng(idim) > m_pi) then
982 call messages_input_error(namespace,'PES_Flux_ThetaK')
983 end if
984 end do
985 if (this%nstepsthetak < 0) call messages_input_error(namespace,'PES_Flux_ThetaK')
986 else
987
988 !%Variable PES_Flux_StepsThetaK
989 !%Type integer
990 !%Default 45
991 !%Section Time-Dependent::PhotoElectronSpectrum
992 !%Description
993 !% Number of steps in <math>\theta</math> (<math>0 \le \theta \le \pi</math>) for the spherical grid in k.
994 !%End
995 call parse_variable(namespace, 'PES_Flux_StepsThetaK', 45, this%nstepsthetak)
996 if (this%nstepsthetak < 0) call messages_input_error(namespace,'PES_Flux_StepsThetaK')
997
998 ! should do this at some point
999 ! call messages_obsolete_variable(namespace, 'PES_Flux_StepsThetaK')
1000 end if
1001
1002
1003
1004 !%Variable PES_Flux_PhiK
1005 !%Type block
1006 !%Section Time-Dependent::PhotoElectronSpectrum
1007 !%Description
1008 !% Define the grid points on theta (<math>0 \le \theta \le 2\pi</math>) when
1009 !% using a spherical grid in momentum.
1010 !% The block defines the maximum and minimum values of theta and the number of
1011 !% of points for the discretization.
1012 !%
1013 !% <tt>%PES_Flux_PhiK
1014 !% <br>&nbsp;&nbsp; theta_min | theta_max | npoints
1015 !% <br>%
1016 !% </tt>
1017 !%
1018 !% By default theta_min=0, theta_max = pi, npoints = 90.
1019 !%End
1020 this%nstepsphik = 90
1021 this%phik_rng(1:2) = (/m_zero, 2 * m_pi/)
1022 if (mdim == 1) this%phik_rng(1:2) = (/m_pi, m_zero/)
1023 if (parse_block(namespace, 'PES_Flux_PhiK', blk) == 0) then
1024 call parse_block_float(blk, 0, 0, this%phik_rng(1))
1025 call parse_block_float(blk, 0, 1, this%phik_rng(2))
1026 call parse_block_integer(blk, 0, 2, this%nstepsphik)
1027 call parse_block_end(blk)
1028 do idim = 1,2
1029 if (this%phik_rng(idim) < m_zero .or. this%phik_rng(idim) > m_two * m_pi) then
1030 call messages_input_error(namespace,'PES_Flux_PhiK')
1031 end if
1032 end do
1033 if (this%nstepsphik < 0) call messages_input_error(namespace,'PES_Flux_PhiK')
1034
1035 else
1036
1037 !%Variable PES_Flux_StepsPhiK
1038 !%Type integer
1039 !%Default 90
1040 !%Section Time-Dependent::PhotoElectronSpectrum
1041 !%Description
1042 !% Number of steps in <math>\phi</math> (<math>0 \le \phi \le 2 \pi</math>) for the spherical grid in k.
1043 !%End
1044 call parse_variable(namespace, 'PES_Flux_StepsPhiK', 90, this%nstepsphik)
1045 if (this%nstepsphik < 0) call messages_input_error(namespace,'PES_Flux_StepsPhiK')
1046 if (this%nstepsphik == 0) this%nstepsphik = 1
1047 end if
1048
1049
1050 dthetak = m_zero
1051 if (mdim == 3) dthetak = abs(this%thetak_rng(2) - this%thetak_rng(1)) / (this%nstepsthetak)
1052 dphik = abs(this%phik_rng(2) - this%phik_rng(1)) / (this%nstepsphik)
1053
1054
1055 select case (mdim)
1056 case (1)
1057 this%nstepsthetak = 0
1058 this%nstepsphik = 2
1059 this%nstepsomegak = this%nstepsphik
1060
1061 case (2)
1062 this%nstepsthetak = 0
1063 this%nstepsomegak = this%nstepsphik
1064
1065 case (3)
1066 if (this%nstepsthetak <= 1) then
1067 this%nstepsphik = 1
1068 this%nstepsthetak = 1
1069 end if
1070 ! count the omegak samples
1071 iomk = 0
1072 do ith = 0, this%nstepsthetak
1073 thetak = ith * dthetak + this%thetak_rng(1)
1074 do iph = 0, this%nstepsphik - 1
1075 phik = iph * dphik + this%phik_rng(1)
1076 iomk = iomk + 1
1077 if (thetak < m_epsilon .or. abs(thetak-m_pi) < m_epsilon) exit
1078 end do
1079 end do
1080 this%nstepsomegak = iomk
1081
1082 case default
1083 assert(.false.)
1084
1085 end select
1086
1087 write(message(1),'(a)') "Polar momentum grid:"
1088 call messages_info(1, namespace=namespace)
1089 if (mdim == 3) then
1090 write(message(1),'(a,f12.6,a,f12.6,a, i6)') &
1091 " Theta = (", this%thetak_rng(1), ",",this%thetak_rng(2), &
1092 "); n = ", this%nstepsthetak
1093 call messages_info(1, namespace=namespace)
1094 end if
1095 write(message(1),'(a,f12.6,a,f12.6,a, i6)') &
1096 " Phi = (", this%phik_rng(1), ",",this%phik_rng(2), &
1097 "); n = ", this%nstepsphik
1098 call messages_info(1, namespace=namespace)
1099
1100
1101
1102 if (use_enegy_grid) then
1103 this%nk = nint(abs(emax - emin) / de)
1104 else
1105 this%nk = nint(abs(kmax(1) - kmin(1)) / this%dk)
1106 end if
1107 this%nkpnts = this%nstepsomegak * this%nk
1108
1109 this%ll(1) = this%nk
1110 this%ll(2) = this%nstepsphik
1111 this%ll(3) = this%nstepsthetak !- 1
1112 this%ll(mdim+1:3) = 1
1113
1114 safe_allocate(this%klinear(1:this%nk, 1))
1115
1116
1117 else
1118 ! Cartesian
1119
1120 dk(1:mdim) = m_one/kpoints%nik_axis(1:mdim)
1121
1122 this%arpes_grid = .false.
1123 if (space%is_periodic()) then
1124 !%Variable PES_Flux_ARPES_grid
1125 !%Type logical
1126 !%Section Time-Dependent::PhotoElectronSpectrum
1127 !%Description
1128 !% Use a curvilinear momentum space grid that compensates the transformation
1129 !% used to obtain ARPES. With this choice ARPES data is laid out on a Cartesian
1130 !% regular grid.
1131 !% By default true when <tt>PES_Flux_Shape = pln</tt> and a <tt>KPointsPath</tt>
1132 !% is specified.
1133 !%End
1134 arpes_grid = kpoints%have_zero_weight_path()
1135 call parse_variable(namespace, 'PES_Flux_ARPES_grid', arpes_grid, this%arpes_grid)
1136 call messages_print_var_value("PES_Flux_ARPES_grid", this%arpes_grid, namespace=namespace)
1137 end if
1138
1139
1140
1141
1142 this%ll(:) = 1
1143
1144 if (kpoints%have_zero_weight_path()) then
1145
1146 if (this%arpes_grid) then
1147 nkmax = floor(emax / de)
1148 nkmin = floor(emin / de)
1149
1150 else
1151 nkmax = floor(kmax(mdim) / this%dk)
1152 nkmin = floor(kmin(mdim) / this%dk)
1153
1154 end if
1155
1156 this%ll(mdim) = abs(nkmax - nkmin) + 1
1157
1158 this%nk = this%ll(mdim)
1159
1160
1161 else
1162
1163 if (.not. this%arpes_grid) then
1164 this%ll(1:mdim) = floor(abs(kmax(1:mdim) - kmin(1:mdim))/this%dk) + 1
1165 this%nk = maxval(this%ll(1:mdim))
1166
1167 else
1168
1169 nkmax = floor(emax / de)
1170 nkmin = floor(emin / de)
1171 this%nk = abs(nkmax - nkmin) + 1
1172
1173 this%ll(1:pdim) = floor(abs(kmax(1:pdim) - kmin(1:pdim))/this%dk) + 1
1174 this%ll(mdim) = this%nk
1175
1176 end if
1177
1178 safe_allocate(this%klinear(1:maxval(this%ll(1:mdim)), 1:mdim))
1179 this%klinear = m_zero
1180
1181 end if
1182
1183 ! Total number of points
1184 this%nkpnts = product(this%ll(1:mdim))
1185
1186
1187 end if
1188
1189
1190
1191
1192
1193 this%parallel_in_momentum = .false.
1194
1195 ! Create the grid
1196 select case (this%kgrid)
1197
1198 case (pes_polar)
1199
1200 if (use_enegy_grid) then
1201 do ie = 1, this%nk
1202 this%klinear(ie, 1) = sqrt(m_two * (ie * de + emin))
1203 end do
1204 else
1205 do ikk = 1, this%nk
1206 this%klinear(ikk, 1) = ikk * this%dk + kmin(1)
1207 end do
1208 end if
1209
1210
1211 if (this%surf_shape == pes_spherical) then
1212
1213 if (optional_default(post, .false.)) then
1215 return
1216 end if
1217
1218 ! we split the k-mesh in radial & angular part
1219 call pes_flux_distribute(1, this%nk, this%nk_start, this%nk_end, comm)
1220 if ((this%nk_end - this%nk_start + 1) < this%nk) this%parallel_in_momentum = .true.
1221 call pes_flux_distribute(1, this%nstepsomegak, this%nstepsomegak_start, this%nstepsomegak_end, comm)
1222
1223! Keep this because is useful for debug but not enough to bother having it polished out
1224! if (debug%info) then
1225! call mpi_world%barrier()
1226! write(*,*) &
1227! 'Debug: momentum points on node ', mpi_world%rank, ' : ', this%nk_start, this%nk_end
1228! call mpi_world%barrier()
1229! write(*,*) &
1230! 'Debug: momentum directions on node ', mpi_world%rank, ' : ', this%nstepsomegak_start, this%nstepsomegak_end
1231! call mpi_world%barrier()
1232! end if
1233 safe_allocate(this%j_l(0:this%lmax, this%nk_start:this%nk_end))
1234 this%j_l = m_zero
1235
1236 safe_allocate(this%kcoords_sph(1:3, this%nk_start:this%nk_end, 1:this%nstepsomegak))
1237 this%kcoords_sph = m_zero
1238
1239 safe_allocate(this%ylm_k(0:this%lmax, - this%lmax:this%lmax, this%nstepsomegak_start:this%nstepsomegak_end))
1240 this%ylm_k = m_z0
1241
1242 ! spherical harmonics & kcoords_sph
1243 iomk = 0
1244 do ith = 0, this%nstepsthetak
1245 thetak = ith * dthetak + this%thetak_rng(1)
1246 do iph = 0, this%nstepsphik - 1
1247 phik = iph * dphik + this%phik_rng(1)
1248 iomk = iomk + 1
1249 if (iomk >= this%nstepsomegak_start .and. iomk <= this%nstepsomegak_end) then
1250 do ll = 0, this%lmax
1251 do mm = -ll, ll
1252 xx(1) = cos(phik) * sin(thetak)
1253 xx(2) = sin(phik) * sin(thetak)
1254 xx(3) = cos(thetak)
1255 call ylmr_cmplx(xx, ll, mm, this%ylm_k(ll, mm, iomk))
1256 end do
1257 end do
1258 end if
1259 if (this%nk_start > 0) then
1260 this%kcoords_sph(1, this%nk_start:this%nk_end, iomk) = cos(phik) * sin(thetak)
1261 this%kcoords_sph(2, this%nk_start:this%nk_end, iomk) = sin(phik) * sin(thetak)
1262 this%kcoords_sph(3, this%nk_start:this%nk_end, iomk) = cos(thetak)
1263 end if
1264 if (thetak < m_epsilon .or. abs(thetak-m_pi) < m_epsilon) exit
1265 end do
1266 end do
1267
1268 if (this%nk_start > 0) then
1269 ! Bessel functions & kcoords_sph
1270 do ikk = this%nk_start, this%nk_end
1271 kact = this%klinear(ikk,1)
1272 do ll = 0, this%lmax
1273 this%j_l(ll, ikk) = loct_sph_bessel(ll, kact * this%radius) * &
1274 m_two * m_pi / (m_two * m_pi)**m_three / m_two
1275 end do
1276 this%kcoords_sph(:, ikk, :) = kact * this%kcoords_sph(:, ikk, :)
1277 end do
1278 end if
1279
1280 else
1281 !planar or cubic surface
1282
1283
1284 ! no distribution
1285 this%nkpnts_start = 1
1286 this%nkpnts_end = this%nkpnts
1287
1288
1289 safe_allocate(this%kcoords_cub(1:mdim, this%nkpnts_start:this%nkpnts_end, 1))
1290
1291 this%kcoords_cub = m_zero
1292
1293 if (mdim == 1) then
1294
1295 ikp = 0
1296 do ikk = -this%nk, this%nk
1297 if (ikk == 0) cycle
1298 ikp = ikp + 1
1299 kact = sign(1,ikk) * this%klinear(abs(ikk), 1)
1300 this%kcoords_cub(1, ikp, 1) = kact
1301 end do
1302
1303 else !mdim = 2,3
1304 thetak = m_pi / m_two
1305
1306 ikp = 0
1307 do ikk = 1, this%nk
1308 do ith = 0, this%nstepsthetak
1309 if (mdim == 3) thetak = ith * dthetak + this%thetak_rng(1)
1310 do iph = 0, this%nstepsphik - 1
1311 ikp = ikp + 1
1312 phik = iph * dphik + this%phik_rng(1)
1313 kact = this%klinear(ikk,1)
1314 this%kcoords_cub(1, ikp,1) = kact * cos(phik) * sin(thetak)
1315 this%kcoords_cub(2, ikp,1) = kact * sin(phik) * sin(thetak)
1316 if (mdim == 3) this%kcoords_cub(3, ikp,1) = kact * cos(thetak)
1317 if (mdim == 3 .and. (thetak < m_epsilon .or. abs(thetak-m_pi) < m_epsilon)) exit
1318 end do
1319 end do
1320 end do
1321
1322 end if
1323 end if
1324
1325 case (pes_cartesian)
1326
1327 this%nkpnts_start = 1
1328 this%nkpnts_end = this%nkpnts
1329
1330
1331
1332
1333
1334 if (kpoints%have_zero_weight_path()) then
1335 ! its a special case because we are generating a different (1D) grid for
1336 ! each kpoint and then we combine it in postprocessing
1337
1338 safe_allocate(this%kcoords_cub(1:mdim, this%nkpnts_start:this%nkpnts_end, kptst:kptend))
1339
1340 nkp_out = 0
1341 do ikpt = kptst, kptend
1342 ikp = 0
1343 do ikk = nkmin, nkmax
1344
1345 kvec(1:mdim) = - kpoints%get_point(ikpt)
1347
1348 end do
1349 end do
1350
1351 else
1352
1353 safe_allocate(this%kcoords_cub(1:mdim, this%nkpnts_start:this%nkpnts_end, 1))
1354
1355 safe_allocate(this%Lkpuvz_inv(1:this%ll(1), 1:this%ll(2), 1:this%ll(3)))
1356
1357 do idir = 1, mdim
1358 do ikk = 1, this%ll(idir)
1359 this%klinear(ikk, idir) = ikk * this%dk + kmin(idir)
1360 end do
1361 end do
1362
1363
1364 if (.not. this%arpes_grid) then
1365 ! Normal velocity map
1366
1367
1368 ikpt = 1
1369 ikp = 0
1370 kvec(:) = m_zero
1371 do ik3 = 1, this%ll(3)
1372 if (mdim>2) kvec(3) = this%klinear(ik3, 3)
1373 do ik2 = 1, this%ll(2)
1374 if (mdim>1) kvec(2) = this%klinear(ik2, 2)
1375 do ik1 = 1, this%ll(1)
1376 ikp = ikp + 1
1377 kvec(1) = this%klinear(ik1, 1)
1378 this%kcoords_cub(1:mdim, ikp, ikpt) = kvec(1:mdim)
1379 this%Lkpuvz_inv(ik1, ik2, ik3) = ikp
1380
1381 end do
1382 end do
1383 end do
1384
1385 else ! we want an ARPES-friendly grid layout
1386
1387 nkp_out = 0
1388 ikpt = 1
1389 ikp = 0
1390 kvec(:) = m_zero
1391 do ikk = nkmin, nkmax ! this is going to be turned into energy
1392
1393 ! loop over periodic directions
1394 select case (pdim)
1395 case (1)
1396
1397 do ik1 = 1, this%ll(1)
1398 kvec(1) = this%klinear(ik1,1)
1399 kvec(1:pdim) = matmul(kpoints%latt%klattice_primitive(1:pdim, 1:pdim), kvec(1:pdim))
1401 end do
1402
1403 case (2)
1404
1405 do ik2 = 1, this%ll(2)
1406 do ik1 = 1, this%ll(1)
1407 kvec(1:2) = (/this%klinear(ik1,1), this%klinear(ik2,2)/)
1408 kvec(1:pdim) = matmul(kpoints%latt%klattice_primitive(1:pdim, 1:pdim), kvec(1:pdim))
1410
1411 this%Lkpuvz_inv(ik1, ik2, ikk - nkmin + 1) = ikp
1412
1413
1414 end do
1415 end do
1416
1417 case default
1418 assert(.false.)
1419
1420 end select
1421
1422 end do
1423
1424
1425 end if
1426
1427 end if
1428
1429! Keep this because is useful for debug but not enough to bother having it polished out
1430! if (debug%info .and. mpi_grp_is_root(mpi_world)) then
1431! ! this does not work for parallel in kpoint
1432! ! you need to gather kcoords_pln
1433! if (kpoints_have_zero_weight_path(sb%kpoints)) then
1434! write(229,*) "# ikpt (kpoint index), ikp (momentum index), this%kcoords_cub(1:mdim, ikp, ikpt)"
1435! do ikpt = kptst, kptend
1436! do ikp = 1, this%nkpnts
1437! write(229,*) ikpt, ikp, this%kcoords_cub(1:mdim, ikp, ikpt)
1438! end do
1439! end do
1440! else
1441! write(229,*) "# ikp (momentum index), this%kcoords_cub(1:mdim, ikp, ikpt)"
1442! do ikp = 1, this%nkpnts
1443! write(229,*) ikp, this%kcoords_cub(1:mdim, ikp, 1)
1444! end do
1445! end if
1446! flush(229)
1447!
1448!
1449! if (mdim == 3) then
1450! write(230,*) "# ik1, ik2, ik3, this%Lkpuvz_inv(ik1,ik2,ik3) "
1451! do ik1 = 1, this%ll(1)
1452! do ik2 = 1, this%ll(2)
1453! do ik3 = 1, this%ll(3)
1454! write(230,*) ik1, ik2, ik3, this%Lkpuvz_inv(ik1,ik2,ik3)
1455! end do
1456! end do
1457! end do
1458!
1459! flush(230)
1460! end if
1461!
1462! end if
1463
1464 if (this%arpes_grid) then
1465 call messages_write("Number of points with E<p//^2/2 = ")
1466 call messages_write(nkp_out)
1467 call messages_write(" [of ")
1468 call messages_write(this%nkpnts*kpoints_number(kpoints))
1469 call messages_write("]")
1470 call messages_info(namespace=namespace)
1471 end if
1472
1473 safe_deallocate_a(gpoints)
1474 safe_deallocate_a(gpoints_reduced)
1475
1476 case default
1477 assert(.false.)
1478
1479 end select
1480
1481
1482 !%Variable PES_Flux_GridTransformMatrix
1483 !%Type block
1484 !%Section Time-Dependent::PhotoElectronSpectrum
1485 !%Description
1486 !% Define an optional transformation matrix for the momentum grid.
1487 !%
1488 !% <tt>%PES_Flux_GridTransformMatrix
1489 !% <br>&nbsp;&nbsp; M_11 | M_12 | M_13
1490 !% <br>&nbsp;&nbsp; M_21 | M_22 | M_23
1491 !% <br>&nbsp;&nbsp; M_31 | M_32 | M_33
1492 !% <br>%
1493 !% </tt>
1494 !%End
1495 this%ktransf(:,:) = m_zero
1496 do idim = 1,mdim
1497 this%ktransf(idim,idim) = m_one
1498 end do
1499
1500 if (parse_block(namespace, 'PES_Flux_GridTransformMatrix', blk) == 0) then
1501 do idim = 1,mdim
1502 do idir = 1, mdim
1503 call parse_block_float(blk, idir - 1, idim - 1, this%ktransf(idim, idir))
1504 end do
1505 end do
1506 call parse_block_end(blk)
1507
1508 write(message(1),'(a)') 'Momentum grid transformation matrix :'
1509 do idir = 1, space%dim
1510 write(message(1 + idir),'(9f12.6)') ( this%ktransf(idim, idir), idim = 1, mdim)
1511 end do
1512 call messages_info(1 + mdim, namespace=namespace)
1513
1514
1515 !Apply the transformation
1516 if (this%surf_shape == pes_spherical) then
1517
1518 iomk = 0
1519 do ith = 0, this%nstepsthetak
1520 do iph = 0, this%nstepsphik - 1
1521 iomk = iomk + 1
1522 do ikk = this%nk_start, this%nk_end
1523 kvec(1:mdim) = this%kcoords_sph(1:mdim, ikk, iomk)
1524 this%kcoords_sph(1:mdim, ikk, iomk) = matmul(this%ktransf(1:mdim, 1:mdim), kvec(1:mdim))
1525 end do
1526 if (ith == 0 .or. ith == this%nstepsthetak) exit
1527 end do
1528 end do
1529
1530 else !planar or cubic surface
1531
1532 do ikpt = kptst, kptend + 1
1533 if (ikpt == kptend + 1) then
1534 kpoint(1:space%dim) = m_zero
1535 else
1536 kpoint(1:space%dim) = kpoints%get_point(ikpt)
1537 end if
1538
1539 do ikp = 1, this%nkpnts
1540
1541 kvec(1:mdim) = this%kcoords_cub(1:mdim, ikp, ikpt) - kpoint(1:mdim)
1542 this%kcoords_cub(1:mdim, ikp, ikpt) = matmul(this%ktransf(1:mdim, 1:mdim), kvec(1:mdim)) &
1543 + kpoint(1:mdim)
1544 end do
1545 end do
1546
1547 end if
1548
1549
1550 end if
1551
1552
1553
1554
1556
1557 contains
1558
1559 ! Fill the non-periodic direction
1560 subroutine fill_non_periodic_dimension(this)
1561 type(pes_flux_t), intent(inout) :: this
1562
1563 integer :: sign
1564 real(real64) :: kpar(1:pdim), val
1565
1566 ikp = ikp + 1
1567
1568 sign = 1
1569 if (ikk /= 0) sign= ikk / abs(ikk)
1570
1571 kpar(1:pdim) = kvec(1:pdim)
1572 val = abs(ikk) * de * m_two - sum(kpar(1:pdim)**2)
1573 if (val >= 0) then
1574 kvec(mdim) = sign * sqrt(val)
1575 else ! if E < p//^2/2
1576 !FIXME: Should handle the exception doing something smarter than this
1577 kvec(mdim) = sqrt(val) ! set to NaN
1578 nkp_out = nkp_out + 1
1579 end if
1580
1581 this%kcoords_cub(1:mdim, ikp, ikpt) = kvec(1:mdim)
1582
1583
1584 end subroutine fill_non_periodic_dimension
1585
1586
1587
1588 end subroutine pes_flux_reciprocal_mesh_gen
1589
1590 ! ---------------------------------------------------------
1591 subroutine pes_flux_calc(this, space, mesh, st, der, ext_partners, kpoints, iter, dt, stopping)
1592 type(pes_flux_t), intent(inout) :: this
1593 class(space_t), intent(in) :: space
1594 type(mesh_t), intent(in) :: mesh
1595 type(states_elec_t), intent(inout) :: st
1596 type(derivatives_t), intent(in) :: der
1597 type(partner_list_t), intent(in) :: ext_partners
1598 type(kpoints_t), intent(in) :: kpoints
1599 integer, intent(in) :: iter
1600 real(real64), intent(in) :: dt
1601 logical, intent(in) :: stopping
1602
1603 integer :: stst, stend, kptst, kptend, sdim, mdim
1604 integer :: ist, ik, isdim, imdim
1605 integer :: isp,ip
1606 integer :: il, ip_local
1607 complex(real64), allocatable :: gpsi(:,:), psi(:)
1608 complex(real64), allocatable :: interp_values(:)
1609 logical :: contains_isp
1610 real(real64) :: kpoint(1:3)
1611 complex(real64), allocatable :: tmp_array(:)
1612
1613 type(lasers_t), pointer :: lasers
1614
1615 push_sub(pes_flux_calc)
1616
1617 if (iter > 0) then
1618
1619 stst = st%st_start
1620 stend = st%st_end
1621 kptst = st%d%kpt%start
1622 kptend = st%d%kpt%end
1623 sdim = st%d%dim
1624 mdim = space%dim
1625
1626 safe_allocate(psi(1:mesh%np_part))
1627 safe_allocate(gpsi(1:mesh%np_part, 1:mdim))
1628
1629 if (this%surf_interp) then
1630 safe_allocate(interp_values(1:this%nsrfcpnts))
1631 end if
1632
1633 this%itstep = this%itstep + 1
1634
1635 ! get and save current laser field
1636 lasers => list_get_lasers(ext_partners)
1637 if(associated(lasers)) then
1638 do il = 1, lasers%no_lasers
1639 call laser_field(lasers%lasers(il), this%veca(1:mdim, this%itstep), iter*dt)
1640 end do
1641 end if
1642 this%veca(:, this%itstep) = - this%veca(:, this%itstep)
1643
1644! Ideally one could directly access uniform_vector_potential
1645! this%veca(:, this%itstep) = hm%hm_base%uniform_vector_potential(:)
1646
1647 if(mesh%parallel_in_domains) then
1648 safe_allocate(tmp_array(1:this%nsrfcpnts))
1649 end if
1650
1651 ! save wavefunctions & gradients
1652 do ik = kptst, kptend
1653 do ist = stst, stend
1654 do isdim = 1, sdim
1655 call states_elec_get_state(st, mesh, isdim, ist, ik, psi)
1656
1657 ! We apply the PBC first before applying the phase
1658 call boundaries_set(der%boundaries, mesh, psi)
1659
1660 if (this%surf_shape == pes_plane) then
1661 ! Apply the phase containing kpoint only
1662 kpoint(1:mdim) = kpoints%get_point(st%d%get_kpoint_index(ik))
1663
1664 !$omp parallel do schedule(static)
1665 do ip = 1, mesh%np_part
1666 psi(ip) = exp(- m_zi * sum(mesh%x(ip, 1:mdim) * kpoint(1:mdim))) * psi(ip)
1667 end do
1668 !$omp end parallel do
1669 end if
1670
1671 call zderivatives_grad(der, psi, gpsi, set_bc = .false.)
1672
1673 if (this%surf_interp) then
1674 call mesh_interpolation_evaluate(this%interp, this%nsrfcpnts, psi(1:mesh%np_part), &
1675 this%rcoords(1:mdim, 1:this%nsrfcpnts), interp_values(1:this%nsrfcpnts))
1676 this%wf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep) = st%occ(ist, ik) * interp_values(1:this%nsrfcpnts)
1677 do imdim = 1, mdim
1678 call mesh_interpolation_evaluate(this%interp, this%nsrfcpnts, gpsi(1:mesh%np_part, imdim), &
1679 this%rcoords(1:mdim, 1:this%nsrfcpnts), interp_values(1:this%nsrfcpnts))
1680 this%gwf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep, imdim) = st%occ(ist, ik) * interp_values(1:this%nsrfcpnts)
1681 end do
1682
1683 else
1685 contains_isp = .true.
1686 do isp = 1, this%nsrfcpnts
1687 if (mesh%parallel_in_domains) then
1688 if (mesh%mpi_grp%rank == this%rankmin(isp)) then
1689 contains_isp = .true.
1690 else
1691 contains_isp = .false.
1692 end if
1693 end if
1694
1695 if (contains_isp) then
1696 ip_local = this%srfcpnt(isp)
1697 this%wf(ist, isdim, ik, isp, this%itstep) = st%occ(ist, ik) * psi(ip_local)
1698 this%gwf(ist, isdim, ik, isp, this%itstep, 1:mdim) = &
1699 st%occ(ist, ik) * gpsi(ip_local, 1:mdim)
1700 end if
1701 end do
1702 if (mesh%parallel_in_domains) then
1703
1704 tmp_array = this%wf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep)
1705 call mesh%allreduce(tmp_array)
1706 this%wf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep) = tmp_array
1707
1708 do imdim = 1, mdim
1709 tmp_array = this%gwf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep, imdim)
1710 call mesh%allreduce(tmp_array)
1711 this%gwf(ist, isdim, ik, 1:this%nsrfcpnts, this%itstep, imdim) = tmp_array
1712 end do
1713 end if
1714 end if
1715 end do
1716 end do
1717 end do
1718
1719 if(mesh%parallel_in_domains) then
1720 safe_deallocate_a(tmp_array)
1721 end if
1722
1723 safe_deallocate_a(psi)
1724 safe_deallocate_a(gpsi)
1725
1726 if (this%itstep == this%tdsteps .or. mod(iter, this%save_iter) == 0 .or. iter == this%max_iter .or. stopping) then
1727 if (this%surf_shape == pes_cubic .or. this%surf_shape == pes_plane) then
1728 call pes_flux_integrate_cub(this, space, mesh, st, kpoints, dt)
1729 else
1730 call pes_flux_integrate_sph(this, mesh, st, dt)
1731 end if
1732
1733 ! clean up fields
1734 this%wf = m_z0
1735 this%gwf = m_z0
1736 this%veca = m_zero
1737 this%itstep = 0
1738 end if
1739
1740 end if
1741
1742 pop_sub(pes_flux_calc)
1743 end subroutine pes_flux_calc
1744
1745
1746
1747 ! ---------------------------------------------------------
1748 subroutine pes_flux_integrate_cub_tabulate(this, space, mesh, st, kpoints)
1749 type(pes_flux_t), intent(inout) :: this
1750 class(space_t), intent(in) :: space
1751 type(mesh_t), intent(in) :: mesh
1752 type(states_elec_t), intent(in) :: st
1753 type(kpoints_t), intent(in) :: kpoints
1754
1755 integer :: kptst, kptend, mdim
1756 integer :: ik, j1, j2, jvec(1:2)
1757 integer :: isp, ikp, ikp_start, ikp_end
1758 integer :: ik_map
1759
1760 integer :: idir, pdim, nfaces, ifc, n_dir
1761 complex(real64) :: tmp
1762 real(real64) :: kpoint(1:3), vec(1:3), lvec(1:3)
1763
1765
1766 if (kpoints%have_zero_weight_path()) then
1767 kptst = st%d%kpt%start
1768 kptend = st%d%kpt%end
1769 else
1770 kptst = 1
1771 kptend = 1
1772 end if
1773
1774 mdim = space%dim
1775 pdim = space%periodic_dim
1776
1777 ikp_start = this%nkpnts_start
1778 ikp_end = this%nkpnts_end
1779
1780
1781 if (this%surf_shape == pes_plane) then
1782 ! This is not general but should work in the specific case where it is relevant
1783 !i.e. when the system is semiperiodic in <=2 dimensions
1784 this%srfcnrml(:, 1:this%nsrfcpnts) = this%srfcnrml(:, 1:this%nsrfcpnts) * mesh%coord_system%surface_element(3)
1785 end if
1786
1787 if (.not. this%use_symmetry .or. kpoints%have_zero_weight_path()) then
1788
1789 safe_allocate(this%expkr(1:this%nsrfcpnts,ikp_start:ikp_end,kptst:kptend,1))
1790
1791 do ik = kptst, kptend
1792 do ikp = ikp_start, ikp_end
1793 do isp = 1, this%nsrfcpnts
1794 this%expkr(isp,ikp,ik,1) = exp(-m_zi * sum(this%rcoords(1:mdim,isp) &
1795 * this%kcoords_cub(1:mdim, ikp, ik))) &
1796 * (m_two * m_pi)**(-mdim / m_two)
1797
1798 end do
1799 end do
1800 end do
1801
1802
1803
1804 else !do something smart to exploit symmetries
1805
1806 nfaces = mdim*2
1807 if (this%surf_shape == pes_plane) nfaces = 1
1808
1809
1810 safe_allocate(this%expkr(1:this%nsrfcpnts,maxval(this%ll(1:mdim)),kptst:kptend,1:mdim))
1811 this%expkr(:,:,:,:) = m_z1
1812
1813 do ik = kptst, kptend !should only be ik=1
1814 do idir = 1, mdim
1815 do ikp = 1, this%ll(idir)
1816 do isp = 1, this%nsrfcpnts
1817 this%expkr(isp,ikp,ik,idir) = exp(-m_zi * this%rcoords(idir,isp) &
1818 * this%klinear(ikp, idir)) &
1819 * (m_two * m_pi)**(-m_one / m_two)
1820
1821 end do
1822 end do
1823 end do
1824 end do
1825
1826 safe_allocate(this%expkr_perp(1:maxval(this%ll(1:mdim)), 1:nfaces))
1827 this%expkr_perp(:,:) = m_z1
1828
1829 do ifc = 1, nfaces
1830 if (this%face_idx_range(ifc, 1) < 0) cycle ! this face have no local surface point
1831 isp = this%face_idx_range(ifc, 1)
1832 do idir = 1, mdim
1833 if (abs(this%srfcnrml(idir, isp)) >= m_epsilon) n_dir = idir
1834 end do
1835
1836 do ikp = 1, this%ll(n_dir)
1837 this%expkr_perp(ikp, ifc) = exp(- m_zi * this%rcoords(n_dir,isp) &
1838 * this%klinear(ikp, n_dir)) &
1839 * (m_two * m_pi)**(-m_one / m_two)
1840
1842 end do
1843 end do
1844
1845 end if
1846
1847
1848 if (space%is_periodic()) then
1849 !Tabulate the Born-von Karman phase
1850 safe_allocate(this%bvk_phase(ikp_start:ikp_end,st%d%kpt%start:st%d%kpt%end))
1851
1852 this%bvk_phase(:,:) = m_z0
1853 vec(:) = m_zero
1854
1855 do ik = st%d%kpt%start, st%d%kpt%end
1856 kpoint(1:mdim) = kpoints%get_point(ik)
1857 if (kpoints%have_zero_weight_path()) then
1858 ik_map = ik
1859 else
1860 ik_map = 1
1861 end if
1862 do ikp = ikp_start, ikp_end
1863 vec(1:pdim) = this%kcoords_cub(1:pdim, ikp, ik_map) + kpoint(1:pdim)
1864 do j1 = 0, kpoints%nik_axis(1) - 1
1865 do j2 = 0, kpoints%nik_axis(2) - 1
1866 jvec(1:2)=(/j1, j2/)
1867 lvec(1:pdim) = matmul(kpoints%latt%rlattice(1:pdim, 1:2), jvec(1:2))
1868 tmp = sum(lvec(1:pdim) * vec(1:pdim))
1869 this%bvk_phase(ikp, ik) = this%bvk_phase(ikp,ik) &
1870 + exp(m_zi * tmp)
1871
1872 end do
1873 end do
1874
1875 end do
1876 end do
1877 this%bvk_phase(:,:) = this%bvk_phase(:,:) * m_one / product(kpoints%nik_axis(1:pdim))
1878
1879
1880! Keep this because is useful for debug but not enough to bother having it polished out
1881! if (debug%info .and. mpi_grp_is_root(mpi_world)) then
1882! write(225,*) "ik, ikp, this%bvk_phase(ikp,ik)"
1883! do ik = st%d%kpt%start, st%d%kpt%end
1884! do ikp = ikp_start, ikp_end
1885! write(225,*) ik, ikp, this%bvk_phase(ikp,ik)
1886! end do
1887! end do
1888! flush(225)
1889! end if
1890
1891 end if
1892
1894
1895 end subroutine pes_flux_integrate_cub_tabulate
1896
1897
1898 ! ---------------------------------------------------------
1899 pure function get_ikp(this, ikpu, ikpv, ikpz, n_dir) result(ikp)
1900 type(pes_flux_t), intent(in) :: this
1901 integer, intent(in) :: ikpu
1902 integer, intent(in) :: ikpv
1903 integer, intent(in) :: ikpz
1904 integer, intent(in) :: n_dir
1905 integer :: ikp
1906
1907 select case (n_dir)
1908 case (1)
1909 ikp = this%Lkpuvz_inv(ikpz, ikpu, ikpv)
1910 case (2)
1911 ikp = this%Lkpuvz_inv(ikpu, ikpz, ikpv)
1912 case (3)
1913 ikp = this%Lkpuvz_inv(ikpu, ikpv, ikpz)
1914
1915 case default
1916 ! should die here but cannot use assert in a pure function
1917
1918 end select
1919
1920 end function get_ikp
1921
1922 ! ---------------------------------------------------------
1923 subroutine pes_flux_distribute_facepnts_cub(this, mesh)
1924 type(pes_flux_t), intent(inout) :: this
1925 type(mesh_t), intent(in) :: mesh
1926
1927
1928 integer :: mdim, nfaces, ifc, ifp_start, ifp_end
1929
1931
1932 mdim = mesh%box%dim
1933
1934 nfaces = mdim*2
1935 if (this%surf_shape == pes_plane) nfaces = 1
1936
1937 do ifc = 1, nfaces
1938
1939 ifp_start = this%face_idx_range(ifc, 1)
1940 ifp_end = this%face_idx_range(ifc, 2)
1941
1942
1943 if (this%nsrfcpnts_start <= ifp_end) then ! the local domain could be in this face
1944
1945 if (this%nsrfcpnts_start >= ifp_start) then
1946 if (this%nsrfcpnts_start <= ifp_end) then
1947 this%face_idx_range(ifc, 1) = this%nsrfcpnts_start
1948 else
1949 this%face_idx_range(ifc, 1:2) = -1 ! the local domain is not in this face
1950 end if
1951 end if
1952
1953 if (this%nsrfcpnts_end <= ifp_end) then
1954 if (this%nsrfcpnts_end >= ifp_start) then
1955 this%face_idx_range(ifc, 2) = this%nsrfcpnts_end
1956 else
1957 this%face_idx_range(ifc, 1:2) = -1 ! the local domain is not in this face
1958 end if
1959 end if
1960
1961 else
1962
1963 this%face_idx_range(ifc, 1:2) = -1 ! the local domain is not in this face
1964
1965 end if
1966
1967! Keep this because is useful for debug but not enough to bother having it polished out
1968! if (debug%info) then
1969! print *, mpi_world%rank, ifc, ifp_start, ifp_end, this%face_idx_range(ifc, 1:2), this%nsrfcpnts_start, this%nsrfcpnts_end
1970! end if
1971
1972 end do
1973
1974
1977
1978
1979 ! ---------------------------------------------------------
1980 subroutine pes_flux_integrate_cub(this, space, mesh, st, kpoints, dt)
1981 type(pes_flux_t), intent(inout) :: this
1982 class(space_t), intent(in) :: space
1983 type(mesh_t), intent(in) :: mesh
1984 type(states_elec_t), intent(inout) :: st
1985 type(kpoints_t), intent(in) :: kpoints
1986 real(real64), intent(in) :: dt
1987
1988 integer :: stst, stend, kptst, kptend, sdim, mdim
1989 integer :: ist, ik, isdim, imdim, ik_map
1990 integer :: ikp, itstep
1991 integer :: idir, n_dir, nfaces
1992 complex(real64), allocatable :: jk_cub(:,:,:,:), spctramp_cub(:,:,:,:)
1993 integer :: ikp_start, ikp_end, isp_start, isp_end
1994 real(real64) :: vec, kpoint(1:3)
1995 integer :: ifc
1996 complex(real64), allocatable :: wfpw(:), gwfpw(:)
1997 complex(real64), allocatable :: phase(:,:),vphase(:,:)
1998
1999 integer :: tdstep_on_node
2000 integer :: nfp
2001
2002 !Symmetry helpers
2003 integer :: ikpu, ikpv, ikpz, dir_on_face(1:2)
2004 complex(real64) :: face_int_gwf, face_int_wf
2005
2006
2007 push_sub(pes_flux_integrate_cub)
2008
2009 ! this routine is parallelized over time steps and surface points
2010
2011 call profiling_in('pes_flux_integrate_cub')
2012
2013 call messages_write("Debug: calculating pes_flux cub surface integral (accelerated direct expression)")
2014 call messages_info(debug_only=.true.)
2015
2017 tdstep_on_node = 1
2018 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
2019 if (mesh%parallel_in_domains) tdstep_on_node = mesh%mpi_grp%rank + 1
2020 end if
2021
2022 stst = st%st_start
2023 stend = st%st_end
2024 kptst = st%d%kpt%start
2025 kptend = st%d%kpt%end
2026 sdim = st%d%dim
2027 mdim = mesh%box%dim
2028
2029 ikp_start = this%nkpnts_start
2030 ikp_end = this%nkpnts_end
2031
2032
2033
2034 safe_allocate(jk_cub(stst:stend, 1:sdim, kptst:kptend, ikp_start:ikp_end))
2035 safe_allocate(spctramp_cub(stst:stend, 1:sdim, kptst:kptend, ikp_start:ikp_end))
2036 spctramp_cub = m_z0
2037
2038
2039 safe_allocate(vphase(ikp_start:ikp_end, kptst:kptend))
2040 safe_allocate(phase(ikp_start:ikp_end, kptst:kptend))
2041 vphase(:,:) = m_z0
2042 phase(:,:) = m_z0
2043
2044
2045 nfaces = mdim * 2
2046 if (this%surf_shape == pes_plane) nfaces = 1
2047
2048 safe_allocate( wfpw(ikp_start:ikp_end))
2049 safe_allocate(gwfpw(ikp_start:ikp_end))
2050
2051
2052 do ifc = 1, nfaces
2053
2054 if (this%face_idx_range(ifc, 1)<0) cycle ! this face have no local surface point
2055
2056
2057 isp_start = this%face_idx_range(ifc, 1)
2058 isp_end = this%face_idx_range(ifc, 2)
2059
2060
2061 nfp = isp_end - isp_start + 1 ! faces can have a different number of points
2062
2063 wfpw = m_z0
2064 gwfpw = m_z0
2065
2066 ! get the directions normal to the surface and parallel to it
2067 imdim = 1
2068 do idir = 1, mdim
2069 if (abs(this%srfcnrml(idir, isp_start)) >= m_epsilon) then
2070 n_dir = idir
2071 else
2072 dir_on_face(imdim)=idir
2073 imdim = imdim + 1
2074 end if
2075 end do
2076
2077
2078 ! get the previous Volkov phase
2079 vphase(ikp_start:ikp_end,:) = this%conjgphase_prev(ikp_start:ikp_end,:)
2080
2081 jk_cub(:, :, :, :) = m_z0
2082
2083 do itstep = 1, this%itstep
2084
2085 do ik = kptst, kptend
2086
2087 if (kpoints%have_zero_weight_path()) then
2088 ik_map = ik
2089 else
2090 ik_map = 1
2091 end if
2092
2093 kpoint(1:mdim) = kpoints%get_point(ik)
2094 do ikp = ikp_start, ikp_end
2095 vec = sum((this%kcoords_cub(1:mdim, ikp, ik_map) - this%veca(1:mdim, itstep) / p_c)**2)
2096 vphase(ikp, ik) = vphase(ikp, ik) * exp(m_zi * vec * dt / m_two)
2097
2098
2099 if (space%is_periodic()) then
2100 phase(ikp, ik) = vphase(ikp, ik) * this%bvk_phase(ikp,ik)
2101 else
2102 phase(ikp, ik) = vphase(ikp, ik)
2103 end if
2104
2105 end do
2106
2107 if (itstep /= tdstep_on_node) cycle
2108
2109 do ist = stst, stend
2110 do isdim = 1, sdim
2111
2112
2113 ! calculate the surface integrals
2114 if (.not. this%use_symmetry .or. kpoints%have_zero_weight_path()) then
2115
2116 do ikp = ikp_start , ikp_end
2117
2118 gwfpw(ikp) = &
2119 sum(this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, n_dir) &
2120 * this%expkr(isp_start:isp_end, ikp, ik_map, 1) &
2121 * this%srfcnrml(n_dir, isp_start:isp_end))
2122
2123
2124 wfpw(ikp) = &
2125 sum(this%wf(ist, isdim, ik, isp_start:isp_end, itstep) &
2126 * this%expkr(isp_start:isp_end, ikp,ik_map, 1) &
2127 * this%srfcnrml(n_dir, isp_start:isp_end))
2128 end do
2129
2130 else
2131 !$omp parallel do private (ikpv,ikpz,face_int_gwf,face_int_wf) shared(gwfpw, wfpw)
2132 do ikpu = 1, this%ll(dir_on_face(1))
2133 do ikpv = 1, this%ll(dir_on_face(2))
2134
2135
2136 face_int_gwf = sum(this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, n_dir) &
2137 * this%expkr(isp_start:isp_end, ikpu, ik_map, dir_on_face(1)) &
2138 * this%expkr(isp_start:isp_end, ikpv, ik_map, dir_on_face(2)) &
2139 * this%srfcnrml(n_dir, isp_start:isp_end))
2140
2141 face_int_wf = sum(this%wf(ist, isdim, ik, isp_start:isp_end, itstep) &
2142 * this%expkr(isp_start:isp_end, ikpu, ik_map, dir_on_face(1)) &
2143 * this%expkr(isp_start:isp_end, ikpv, ik_map, dir_on_face(2)) &
2144 * this%srfcnrml(n_dir, isp_start:isp_end))
2145
2146 do ikpz = 1, this%ll(n_dir)
2147
2148 gwfpw(get_ikp(this, ikpu, ikpv, ikpz, n_dir)) = face_int_gwf &
2149 * this%expkr_perp(ikpz, ifc)
2150 wfpw( get_ikp(this, ikpu, ikpv, ikpz, n_dir)) = face_int_wf &
2151 * this%expkr_perp(ikpz, ifc)
2152
2153 end do
2154
2155 end do
2156 end do
2157
2158
2159 end if
2160
2161
2162 ! Sum it up
2163 jk_cub(ist, isdim, ik, ikp_start:ikp_end) = jk_cub(ist, isdim, ik, ikp_start:ikp_end) + &
2164 phase(ikp_start:ikp_end, ik) * (wfpw(ikp_start:ikp_end) * &
2165 (m_two * this%veca(n_dir, itstep) / p_c - this%kcoords_cub(n_dir, ikp_start:ikp_end, ik_map)) + &
2166 m_zi * gwfpw(ikp_start:ikp_end))
2167
2168
2169 end do ! isdim
2170 end do ! ist
2171 end do ! is
2172
2173 end do !istep
2174
2175
2176 spctramp_cub(:,:,:,:) = spctramp_cub(:,:,:,:) + jk_cub(:,:,:,:) * m_half
2177
2178
2179 end do ! face loop
2180
2181
2182 this%conjgphase_prev(ikp_start:ikp_end, :) = vphase(ikp_start:ikp_end, :)
2183
2184
2185 if (mesh%parallel_in_domains .and.(bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0 &
2186 .or. bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0)) then
2187 call mesh%allreduce(spctramp_cub)
2188 end if
2189
2190
2191
2192 this%spctramp_cub = this%spctramp_cub + spctramp_cub * dt
2193
2194 safe_deallocate_a(gwfpw)
2195 safe_deallocate_a(wfpw)
2196
2197 safe_deallocate_a(jk_cub)
2198 safe_deallocate_a(spctramp_cub)
2199 safe_deallocate_a(phase)
2200 safe_deallocate_a(vphase)
2201
2202 call profiling_out('pes_flux_integrate_cub')
2203
2204 pop_sub(pes_flux_integrate_cub)
2205 end subroutine pes_flux_integrate_cub
2206
2207
2208
2209 ! ---------------------------------------------------------
2210 subroutine pes_flux_integrate_sph(this, mesh, st, dt)
2211 type(pes_flux_t), intent(inout) :: this
2212 type(mesh_t), intent(in) :: mesh
2213 type(states_elec_t), intent(inout) :: st
2214 real(real64), intent(in) :: dt
2215
2216 integer :: stst, stend, kptst, kptend, sdim
2217 integer :: ist, ik, isdim, imdim
2218 integer :: itstep, lmax
2219 integer :: ll, mm
2220 complex(real64), allocatable :: s1_node(:,:,:,:,:,:), s1_act(:,:,:)
2221 complex(real64), allocatable :: s2_node(:,:,:,:,:), s2_act(:,:)
2222 complex(real64), allocatable :: integ11_t(:,:), integ12_t(:,:,:)
2223 complex(real64), allocatable :: integ21_t(:), integ22_t(:,:)
2224 complex(real64), allocatable :: spctramp_sph(:,:,:,:,:)
2225 integer :: ikk, ikk_start, ikk_end
2226 integer :: isp_start, isp_end
2227 integer :: iomk, iomk_start, iomk_end
2228 complex(real64), allocatable :: phase_act(:,:)
2229 real(real64) :: vec
2230 integer :: tdstep_on_node
2231
2232 push_sub(pes_flux_integrate_sph)
2233
2234 ! this routine is parallelized over time steps and surface points
2235
2236 call messages_write("Debug: calculating pes_flux sph surface integral")
2237 call messages_info(debug_only=.true.)
2238
2239 tdstep_on_node = 1
2240 if (mesh%parallel_in_domains) tdstep_on_node = mesh%mpi_grp%rank + 1
2241
2242 stst = st%st_start
2243 stend = st%st_end
2244 kptst = st%d%kpt%start
2245 kptend = st%d%kpt%end
2246 sdim = st%d%dim
2247
2248 lmax = this%lmax
2249 ikk_start = this%nk_start
2250 ikk_end = this%nk_end
2251 isp_start = this%nsrfcpnts_start
2252 isp_end = this%nsrfcpnts_end
2253 iomk_start = this%nstepsomegak_start
2254 iomk_end = this%nstepsomegak_end
2255
2256 ! surface integral S_lm (for time step on node)
2257 safe_allocate(s1_node(stst:stend, 1:sdim, kptst:kptend, 0:lmax, -lmax:lmax, 1:3))
2258 safe_allocate(s2_node(stst:stend, 1:sdim, kptst:kptend, 0:lmax, -lmax:lmax))
2259
2260 safe_allocate(s1_act(0:lmax, -lmax:lmax, 1:3))
2261 safe_allocate(s2_act(0:lmax, -lmax:lmax))
2262
2263 do itstep = 1, this%itstep
2264
2265 do ik = kptst, kptend
2266 do ist = stst, stend
2267 do isdim = 1, sdim
2268
2269 s2_act = m_z0
2270
2271 do ll = 0, lmax
2272 do mm = -ll, ll
2273 do imdim = 1, 3
2274 ! surface integrals
2275 s1_act(ll, mm, imdim) = &
2276 sum(this%ylm_r(ll, mm, isp_start:isp_end) * this%srfcnrml(imdim, isp_start:isp_end) * &
2277 this%wf(ist, isdim, ik, isp_start:isp_end, itstep))
2278
2279 s2_act(ll, mm) = s2_act(ll, mm) + &
2280 sum(this%ylm_r(ll, mm, isp_start:isp_end) * this%srfcnrml(imdim, isp_start:isp_end) * &
2281 this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, imdim))
2282 end do
2283 end do
2284 end do
2285
2286 if (mesh%parallel_in_domains .and. bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0) then
2287 call mesh%allreduce(s1_act)
2288 call mesh%allreduce(s2_act)
2289 end if
2290
2291 if (itstep == tdstep_on_node) then
2292 s1_node(ist, isdim, ik, :, :, :) = s1_act(:, :, :)
2293 s2_node(ist, isdim, ik, :, :) = s2_act(:, :)
2294 end if
2295
2296 end do
2297 end do
2298 end do
2299 end do
2300
2301 ! spectral amplitude for k-points on node
2302 safe_allocate(integ11_t(0:this%nstepsomegak, 1:3))
2303 safe_allocate(integ21_t(0:this%nstepsomegak))
2304 safe_allocate(integ12_t(ikk_start:ikk_end, 1:this%nstepsomegak, 1:3))
2305 safe_allocate(integ22_t(ikk_start:ikk_end, 1:this%nstepsomegak))
2306
2307 safe_allocate(spctramp_sph(stst:stend, 1:sdim, kptst:kptend, 0:this%nk, 1:this%nstepsomegak))
2308 spctramp_sph = m_z0
2309
2310 ! get the previous Volkov phase
2311 safe_allocate(phase_act(ikk_start:ikk_end, 1:this%nstepsomegak))
2312 if (ikk_start > 0) then
2313 phase_act(ikk_start:ikk_end, :) = this%conjgphase_prev(ikk_start:ikk_end, :)
2314 else
2315 phase_act(ikk_start:ikk_end, :) = m_z0
2316 end if
2317
2318 do itstep = 1, this%itstep
2319 ! calculate the current Volkov phase
2320 do ikk = ikk_start, ikk_end
2321 do iomk = 1, this%nstepsomegak
2322 vec = sum((this%kcoords_sph(1:3, ikk, iomk) - this%veca(1:3, itstep) / p_c)**m_two)
2323 phase_act(ikk, iomk) = phase_act(ikk, iomk) * exp(m_zi * vec * dt / m_two)
2324 end do
2325 end do
2326
2327 do ik = kptst, kptend
2328 do ist = stst, stend
2329 do isdim = 1, sdim
2330
2331 ! communicate the current surface integrals
2332 if (itstep == tdstep_on_node) then
2333 s1_act(:,:,:) = s1_node(ist, isdim, ik, :, :, :)
2334 s2_act(:,:) = s2_node(ist, isdim, ik, :, :)
2335 end if
2336 if (mesh%parallel_in_domains) then
2337 call mesh%mpi_grp%bcast(s1_act, (lmax + 1) * (2 * lmax + 1) * 3, mpi_double_complex, itstep - 1)
2338 call mesh%mpi_grp%bcast(s2_act, (lmax + 1) * (2 * lmax + 1), mpi_double_complex, itstep - 1)
2339 end if
2340
2341 integ12_t = m_z0
2342 integ22_t = m_z0
2343
2344 do ll = 0, lmax
2345
2346 integ11_t = m_z0
2347 integ21_t = m_z0
2348
2349 do mm = -ll, ll
2350 ! multiply with spherical harmonics & sum over all mm
2351 do imdim = 1, 3
2352 integ11_t(iomk_start:iomk_end, imdim) = integ11_t(iomk_start:iomk_end, imdim) + &
2353 s1_act(ll, mm, imdim) * this%ylm_k(ll, mm, iomk_start:iomk_end)
2354 end do
2355 integ21_t(iomk_start:iomk_end) = integ21_t(iomk_start:iomk_end) + &
2356 s2_act(ll, mm) * this%ylm_k(ll, mm, iomk_start:iomk_end)
2357 end do
2358
2359 call mesh%allreduce(integ11_t)
2360 call mesh%allreduce(integ21_t)
2361
2362 ! multiply with Bessel function & sum over all ll
2363 do ikk = ikk_start, ikk_end
2364 integ12_t(ikk, 1:this%nstepsomegak, 1:3) = integ12_t(ikk, 1:this%nstepsomegak, 1:3) + &
2365 integ11_t(1:this%nstepsomegak, 1:3) * this%j_l(ll, ikk) * ( - m_zi)**ll
2366 integ22_t(ikk, 1:this%nstepsomegak) = integ22_t(ikk, 1:this%nstepsomegak) + &
2367 integ21_t(1:this%nstepsomegak) * this%j_l(ll, ikk) * ( - m_zi)**ll
2368 end do
2369 end do
2370 ! sum over dimensions
2371 do imdim = 1, 3
2372 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) = &
2373 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) + &
2374 phase_act(ikk_start:ikk_end,:) * (integ12_t(ikk_start:ikk_end,:, imdim) * &
2375 (m_two * this%veca(imdim, itstep) / p_c - this%kcoords_sph(imdim, ikk_start:ikk_end,:)))
2376 end do
2377 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) = &
2378 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end,:) + &
2379 phase_act(ikk_start:ikk_end,:) * integ22_t(ikk_start:ikk_end,:) * m_zi
2380 end do
2381 end do
2382 end do
2383 end do
2384
2385 safe_deallocate_a(s1_node)
2386 safe_deallocate_a(s2_node)
2387 safe_deallocate_a(s1_act)
2388 safe_deallocate_a(s2_act)
2389
2390 safe_deallocate_a(integ11_t)
2391 safe_deallocate_a(integ12_t)
2392 safe_deallocate_a(integ21_t)
2393 safe_deallocate_a(integ22_t)
2394
2395 ! save the Volkov phase and the spectral amplitude
2396 this%conjgphase_prev = m_z0
2397
2398 if (ikk_start > 0) then
2399 this%conjgphase_prev(ikk_start:ikk_end, :) = phase_act(ikk_start:ikk_end, :)
2400 end if
2401 safe_deallocate_a(phase_act)
2402
2403 if (mesh%parallel_in_domains .and. bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
2404 call mesh%allreduce(this%conjgphase_prev)
2405 call mesh%allreduce(spctramp_sph)
2406 end if
2407
2408 this%spctramp_sph(:, :, :, 1:this%nk, :) = this%spctramp_sph(:, :, :, 1:this%nk, :) &
2409 + spctramp_sph(:, :, :, 1:this%nk, :) * dt
2410 safe_deallocate_a(spctramp_sph)
2411
2412 pop_sub(pes_flux_integrate_sph)
2413 end subroutine pes_flux_integrate_sph
2414
2415 ! ---------------------------------------------------------
2416 subroutine pes_flux_getcube(this, mesh, abb, border, offset, fc_ptdens)
2417 type(mesh_t), intent(in) :: mesh
2418 type(pes_flux_t), intent(inout) :: this
2419 type(absorbing_boundaries_t), intent(in) :: abb
2420 real(real64), intent(in) :: border(:)
2421 real(real64), intent(in) :: offset(:)
2422 real(real64), intent(in) :: fc_ptdens
2423
2424 integer, allocatable :: which_surface(:)
2425 real(real64) :: xx(mesh%box%dim), dd, area, dS(mesh%box%dim, 2), factor
2426 integer :: mdim, imdim, idir, isp, pm,nface, idim, ndir, iu,iv, iuv(1:2)
2427 integer(int64) :: ip_global
2428 integer :: npface
2429 integer :: rankmin, nsurfaces
2430 logical :: in_ab
2431 integer :: ip_local, NN(mesh%box%dim, 2), idx(mesh%box%dim, 2)
2432 integer :: isp_end, isp_start, ifc, n_dir, nfaces, mindim
2433 real(real64) :: RSmax(2), RSmin(2), RS(2), dRS(2), weight
2434
2435
2436 push_sub(pes_flux_getcube)
2437
2438 ! this routine works on absolute coordinates
2439
2440
2441 mdim = mesh%box%dim
2442
2443
2444
2445 safe_allocate(this%face_idx_range(1:mdim * 2, 1:2))
2446 safe_allocate(this%LLr(mdim, 1:2))
2447 safe_allocate(this%NN(mdim, 1:2))
2448
2449 this%face_idx_range(:, :) = 0
2450 this%LLr(:, :) = m_zero
2451 this%NN(:, :) = 1
2452 nn(:, :) = 1
2453
2454 if (this%surf_interp) then
2455 ! Create a surface with points not on the mesh
2456
2457 idx(:,:) = 0
2458
2459 mindim = 1
2460 factor = m_two
2461 if (this%surf_shape == pes_plane) then
2462 mindim = mdim ! We only have two planes along the non periodic dimension
2463 ! this is due to the fact that for semiperiodic systems we multiply border
2464 ! by two to prenvet the creation of surfaces at the edges
2465 factor = m_one
2466 end if
2467
2468
2469 this%nsrfcpnts = 0
2470
2471 do ndir = mdim, mindim, -1
2472 area = m_one
2473 do idir=1, mdim
2474 if (idir == ndir) cycle
2475 area = area * border(idir) * factor
2476 end do
2477
2478 npface = int(fc_ptdens * area) ! number of points on the face
2479
2480 idim = 1
2481 do idir=1, mdim
2482 if (idir == ndir) cycle
2483 nn(ndir, idim) = int(sqrt(npface * (border(idir) * factor)**2 / area))
2484 ds(ndir, idim) = border(idir) * factor / nn(ndir, idim)
2485 this%LLr(ndir, idim) = nn(ndir, idim) * ds(ndir, idim)
2486 idx(ndir, idim) = idir
2487 idim = idim + 1
2488 end do
2489 this%nsrfcpnts = this%nsrfcpnts + 2 * product(nn(ndir, 1:mdim - 1))
2490 end do
2491
2492 this%NN(1:mdim, :) = nn(1:mdim, :)
2493
2494
2495 assert(this%nsrfcpnts > 0) !at this point should be satisfied otherwise the point density is way too small
2496
2497
2498 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2499 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2500
2501 this%srfcnrml(:, :) = m_zero
2502 this%rcoords(:, :) = m_zero
2503
2504
2505 isp = 1
2506 ifc = 1
2507 do ndir = mdim, mindim, -1
2508
2509 !Up face
2510 this%face_idx_range(ifc,1) = isp
2511 do iu = 1, nn(ndir,1)
2512 do iv = 1, nn(ndir,2)
2513 this%rcoords(ndir, isp) = border(ndir)
2514 this%srfcnrml(ndir, isp) = product(ds(ndir,1:mdim-1))
2515 iuv =(/iu, iv/)
2516 do idim = 1, mdim-1
2517 this%rcoords(idx(ndir, idim), isp) = (-nn(ndir, idim) * m_half - m_half + iuv(idim)) * ds(ndir, idim)
2518 end do
2519 isp = isp + 1
2520 end do
2521 end do
2522 this%face_idx_range(ifc, 2) = isp-1
2523
2524 ifc = ifc + 1
2525
2526 !Down face
2527 this%face_idx_range(ifc, 1) = isp
2528 do iu = 1, nn(ndir, 1)
2529 do iv = 1, nn(ndir, 2)
2530 this%rcoords(ndir, isp) = -border(ndir)
2531 this%srfcnrml(ndir, isp) = -product(ds(ndir, 1:mdim - 1))
2532 iuv =(/iu, iv/)
2533 do idim = 1, mdim-1
2534 this%rcoords(idx(ndir, idim), isp) = (-nn(ndir, idim) * m_half -m_half + iuv(idim)) * ds(ndir, idim)
2535 end do
2536 isp = isp + 1
2537 end do
2538 end do
2539 this%face_idx_range(ifc, 2) = isp-1
2540
2541 ifc = ifc + 1
2542 end do
2543
2544 do isp = 1, this%nsrfcpnts
2545 this%rcoords(1:mdim, isp) = mesh%coord_system%to_cartesian(this%rcoords(1:mdim, isp))
2546 end do
2547
2548 else
2549 ! Surface points are on the mesh
2550
2551 nfaces = mdim * 2
2552 if (this%surf_shape == pes_plane) nfaces = 2
2553
2554 in_ab = .false.
2555
2556 safe_allocate(which_surface(1:mesh%np_global))
2557 which_surface = 0
2558
2559 ! get the surface points
2560 this%nsrfcpnts = 0
2561 do ip_local = 1, mesh%np
2562 ip_global = mesh_local2global(mesh, ip_local)
2563
2564 nsurfaces = 0
2565
2566 xx(1:mdim) = mesh%x(ip_local, 1:mdim) - offset(1:mdim)
2567
2568 ! eventually check whether we are in absorbing zone
2569 select case (abb%abtype)
2570 case (mask_absorbing)
2571 in_ab = .not. is_close(abb%mf(ip_local), m_one)
2572 case (imaginary_absorbing)
2573 in_ab = abs(abb%mf(ip_local)) > m_epsilon
2574 case default
2575 assert(.false.)
2576 end select
2577
2578 ! check whether the point is inside the cube
2579 if (all(abs(xx(1:mdim)) <= border(1:mdim)) .and. .not. in_ab) then
2580 ! check whether the point is close to any border
2581 do imdim = 1, mdim
2582 if (this%surf_shape == pes_plane) then
2583 dd = border(imdim) - xx(imdim)
2584 else
2585 dd = border(imdim) - abs(xx(imdim))
2586 end if
2587 if (dd < mesh%spacing(imdim) / m_two) then
2588 nsurfaces = nsurfaces + 1
2589 which_surface(ip_global) = int(sign(m_one, xx(imdim))) * imdim ! +-x=+-1, +-y=+-2, +-z=+-3
2590 end if
2591 end do
2592
2593 ! check whether the point is close to one border only (not an edge)
2594 if (nsurfaces == 1) then
2595 this%nsrfcpnts = this%nsrfcpnts + 1
2596 else
2597 which_surface(ip_global) = 0
2598 end if
2599 end if
2600
2601 end do
2602
2603 if (mesh%parallel_in_domains) then
2604 assert(mesh%np_global < huge(0_int32))
2605 call mesh%allreduce(this%nsrfcpnts)
2606 call mesh%allreduce(which_surface)
2607 end if
2608
2609 safe_allocate(this%srfcpnt(1:this%nsrfcpnts))
2610 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2611 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2612 safe_allocate(this%rankmin(1:this%nsrfcpnts))
2613
2614 this%srfcnrml = m_zero
2615 this%rcoords = m_zero
2616
2617 this%face_idx_range(:,:) = 0
2618
2619 isp = 0
2620 nface = 0
2621 do idir = mdim, 1, -1 ! Start counting from z to simplify implementation for semiperiodic systems (pln)
2622 do pm = 1, -1, -2
2623 nface = nface + 1
2624 this%face_idx_range(nface, 1) = isp + 1
2625
2626 do ip_global = 1, mesh%np_global
2627 if (abs(which_surface(ip_global)) == idir .and. sign(1, which_surface(ip_global)) == pm) then
2628 isp = isp + 1
2629 ! coordinate of surface point
2630 xx(1:mdim) = mesh_x_global(mesh, ip_global)
2631 this%rcoords(1:mdim, isp) = xx(1:mdim)
2632 ! local ip & node which has the surface point
2633 this%srfcpnt(isp) = mesh_nearest_point(mesh, this%rcoords(1:mdim, isp), dd, rankmin)
2634 this%rankmin(isp) = rankmin
2635 ! surface normal
2636 this%srfcnrml(idir, isp) = sign(1, which_surface(ip_global))
2637 ! add the surface element (of directions orthogonal to the normal vector)
2638 do imdim = 1, mdim
2639 if (imdim == idir) cycle
2640 this%srfcnrml(idir, isp) = this%srfcnrml(idir, isp) * mesh%spacing(imdim)
2641 end do
2642 end if
2643 end do
2644
2645 this%face_idx_range(nface,2) = isp
2646
2647 end do
2648 end do
2649
2650
2651 !Get dimensions and spacing on each (pair of) face
2652 do ifc = 1, nint((nfaces + 0.5) / 2)
2653 isp_start = this%face_idx_range(ifc, 1)
2654 isp_end = this%face_idx_range(ifc, 2)
2655
2656 !retrieve face normal direction
2657 n_dir = 0
2658 do idir = 1, mdim
2659 if (abs(this%srfcnrml(idir, isp_start)) >= m_epsilon) n_dir = idir
2660 end do
2661
2662 !retrieve the spacing on the face
2663 drs(:) = m_zero
2664 idim = 1
2665 do idir = 1, mdim
2666 if (idir == n_dir) cycle
2667 drs(idim)= mesh%spacing(idir)
2668 idim = idim + 1
2669 end do
2670
2671
2672 !retrieve the dimensions of the face
2673 rsmin = m_zero
2674 rsmax = m_zero
2675 do isp = isp_start, isp_end
2676 !measure in reduced coordinates
2677 xx = mesh%coord_system%from_cartesian(this%rcoords(1:mdim, isp))
2678 idim = 1
2679 do idir = 1, mdim
2680 if (idir == n_dir) cycle
2681 rs(idim)=xx(idir)
2682 if (rs(idim) < rsmin(idim)) rsmin(idim) = rs(idim)
2683 if (rs(idim) > rsmax(idim)) rsmax(idim) = rs(idim)
2684 idim = idim + 1
2685 end do
2686 end do
2687
2688
2689 do idir = 1, mdim - 1
2690 this%LLr(n_dir, idir) = rsmax(idir) - rsmin(idir) + drs(idir)
2691 if (drs(idir) > m_zero) this%NN(n_dir, idir) = int(this%LLr(n_dir, idir) / drs(idir))
2692 end do
2693
2694 end do
2695
2696
2697 end if
2698
2699 if (this%anisotrpy_correction) then
2700 !Compensate the fact that the angular distribution of points is not uniform
2701 !and have peaks in correspondence to the edges and corners of the parallelepiped
2702 do isp = 1, this%nsrfcpnts
2703 weight = sum(this%rcoords(1:mdim, isp) * this%srfcnrml(1:mdim, isp))
2704 weight = weight/sum(this%rcoords(1:mdim, isp)**2) / sum(this%srfcnrml(1:mdim, isp)**2)
2705 this%srfcnrml(1:mdim, isp) = this%srfcnrml(1:mdim, isp) * abs(weight)
2706 end do
2707
2708 end if
2709
2710
2711 safe_deallocate_a(which_surface)
2712
2713 pop_sub(pes_flux_getcube)
2714 end subroutine pes_flux_getcube
2715
2716 ! ---------------------------------------------------------
2717 subroutine pes_flux_getsphere(this, mesh, nstepsthetar, nstepsphir)
2718 type(pes_flux_t), intent(inout) :: this
2719 type(mesh_t), intent(in) :: mesh
2720 integer, intent(inout) :: nstepsthetar, nstepsphir
2721
2722 integer :: mdim
2723 integer :: isp, ith, iph
2724 real(real64) :: thetar, phir
2725 real(real64) :: weight, dthetar
2726
2727 push_sub(pes_flux_getsphere)
2728
2729 mdim = mesh%box%dim
2730
2731 if (nstepsphir == 0) nstepsphir = 1
2732
2733 if (nstepsthetar <= 1) then
2734 nstepsphir = 1
2735 nstepsthetar = 1
2736 end if
2737 dthetar = m_pi / nstepsthetar
2738 this%nsrfcpnts = nstepsphir * (nstepsthetar - 1) + 2
2739
2740 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2741 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2742
2743 this%srfcnrml = m_zero
2744 this%rcoords = m_zero
2745
2746 ! initializing spherical grid
2747 isp = 0
2748 do ith = 0, nstepsthetar
2749 thetar = ith * dthetar
2750
2751 if (ith == 0 .or. ith == nstepsthetar) then
2752 weight = (m_one - cos(dthetar / m_two)) * m_two * m_pi
2753 else
2754 weight = abs(cos(thetar - dthetar / m_two) - cos(thetar + dthetar / m_two)) &
2755 * m_two * m_pi / nstepsphir
2756 end if
2757
2758 do iph = 0, nstepsphir - 1 ! 2*pi is the same than zero
2759 isp = isp + 1
2760 phir = iph * m_two * m_pi / nstepsphir
2761 this%srfcnrml(1, isp) = cos(phir) * sin(thetar)
2762 if (mdim >= 2) this%srfcnrml(2, isp) = sin(phir) * sin(thetar)
2763 if (mdim == 3) this%srfcnrml(3, isp) = cos(thetar)
2764 this%rcoords(1:mdim, isp) = this%radius * this%srfcnrml(1:mdim, isp)
2765 ! here we also include the surface elements
2766 this%srfcnrml(1:mdim, isp) = this%radius**m_two * weight * this%srfcnrml(1:mdim, isp)
2767 if (ith == 0 .or. ith == nstepsthetar) exit
2768 end do
2769 end do
2770
2771 pop_sub(pes_flux_getsphere)
2772 end subroutine pes_flux_getsphere
2773
2774
2775
2776
2777 ! ---------------------------------------------------------
2778 subroutine pes_flux_distribute(istart_global, iend_global, istart, iend, comm)
2779 integer, intent(in) :: istart_global
2780 integer, intent(in) :: iend_global
2781 integer, intent(inout) :: istart
2782 integer, intent(inout) :: iend
2783 type(mpi_comm), intent(in) :: comm
2784
2785#if defined(HAVE_MPI)
2786 integer, allocatable :: dimrank(:)
2787 integer :: mpisize, mpirank, irest, irank
2788 integer :: inumber
2789#endif
2790
2791 push_sub(pes_flux_distribute)
2792
2793#if defined(HAVE_MPI)
2794 call mpi_comm_size(comm, mpisize, mpi_err)
2795 call mpi_comm_rank(comm, mpirank, mpi_err)
2796
2797 safe_allocate(dimrank(0:mpisize-1))
2798
2799 inumber = iend_global - istart_global + 1
2800 irest = mod(inumber, mpisize)
2801 do irank = 0, mpisize - 1
2802 if (irest > 0) then
2803 dimrank(irank) = int(inumber / mpisize) + 1
2804 irest = irest - 1
2805 else
2806 dimrank(irank) = int(inumber / mpisize)
2807 end if
2808 end do
2809
2810 iend = istart_global + sum(dimrank(:mpirank)) - 1
2811 istart = iend - dimrank(mpirank) + 1
2812
2813 if (istart > iend) then
2814 iend = 0
2815 istart = 0
2816 end if
2817
2818 safe_deallocate_a(dimrank)
2819
2820#else
2821 istart = istart_global
2822 iend = iend_global
2823#endif
2824
2825 pop_sub(pes_flux_distribute)
2826 end subroutine pes_flux_distribute
2827
2828
2829#include "pes_flux_out_inc.F90"
2830
2831end module pes_flux_oct_m
2832
2833!! Local Variables:
2834!! mode: f90
2835!! coding: utf-8
2836!! End:
Prints out to iunit a message in the form: ["InputVariable" = value] where "InputVariable" is given b...
Definition: messages.F90:180
double exp(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
integer, parameter, public imaginary_absorbing
integer, parameter, public mask_absorbing
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:122
This module handles the calculation mode.
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public zderivatives_grad(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the gradient to a mesh function
type(lasers_t) function, pointer, public list_get_lasers(partners)
real(real64), parameter, public m_two
Definition: global.F90:190
real(real64), parameter, public m_zero
Definition: global.F90:188
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:186
complex(real64), parameter, public m_z0
Definition: global.F90:198
complex(real64), parameter, public m_zi
Definition: global.F90:202
real(real64), parameter, public m_epsilon
Definition: global.F90:204
complex(real64), parameter, public m_z1
Definition: global.F90:199
real(real64), parameter, public m_half
Definition: global.F90:194
real(real64), parameter, public p_c
Electron gyromagnetic ratio, see Phys. Rev. Lett. 130, 071801 (2023)
Definition: global.F90:224
real(real64), parameter, public m_one
Definition: global.F90:189
real(real64), parameter, public m_three
Definition: global.F90:191
This module defines classes and functions for interaction partners.
Definition: io.F90:114
integer pure function, public kpoints_number(this)
Definition: kpoints.F90:1099
integer, parameter, public e_field_vector_potential
Definition: lasers.F90:177
integer pure elemental function, public laser_kind(laser)
Definition: lasers.F90:716
subroutine, public laser_field(laser, field, time)
Retrieves the value of either the electric or the magnetic field. If the laser is given by a scalar p...
Definition: lasers.F90:1116
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:115
subroutine, public ylmr_cmplx(xx, li, mi, ylm)
Computes spherical harmonics ylm at position (x, y, z)
Definition: math.F90:304
subroutine, public mesh_interpolation_init(this, mesh)
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
integer function, public mesh_nearest_point(mesh, pos, dmin, rankmin)
Returns the index of the point which is nearest to a given vector position pos.
Definition: mesh.F90:380
integer(int64) function, public mesh_local2global(mesh, ip)
This function returns the global mesh index for a given local index.
Definition: mesh.F90:959
real(real64) function, dimension(1:mesh%box%dim), public mesh_x_global(mesh, ipg)
Definition: mesh.F90:804
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1113
subroutine, public messages_new_line()
Definition: messages.F90:1134
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:414
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:713
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:616
integer, public mpi_err
used to store return values of mpi calls
Definition: mpi.F90:273
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:145
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 pes_flux_map_from_states(this, restart, st, ll, pesP, krng, Lp, istin)
Definition: pes_flux.F90:2984
subroutine pes_flux_getcube(this, mesh, abb, border, offset, fc_ptdens)
Definition: pes_flux.F90:2510
subroutine, public pes_flux_calc(this, space, mesh, st, der, ext_partners, kpoints, iter, dt, stopping)
Definition: pes_flux.F90:1685
subroutine, public pes_flux_out_energy(this, pesK, file, namespace, ll, Ekin, dim)
Definition: pes_flux.F90:3640
subroutine pes_flux_getsphere(this, mesh, nstepsthetar, nstepsphir)
Definition: pes_flux.F90:2811
subroutine, public pes_flux_output(this, box, st, namespace)
Definition: pes_flux.F90:3846
subroutine, public pes_flux_reciprocal_mesh_gen(this, namespace, space, st, kpoints, comm, post)
Definition: pes_flux.F90:816
subroutine, public pes_flux_load(restart, this, st, ierr)
Definition: pes_flux.F90:4468
subroutine, public pes_flux_pmesh(this, namespace, dim, kpoints, ll, pmesh, idxZero, krng, Lp, Ekin)
Definition: pes_flux.F90:2944
subroutine, public pes_flux_dump(restart, this, mesh, st, ierr)
Definition: pes_flux.F90:4376
subroutine pes_flux_distribute_facepnts_cub(this, mesh)
Definition: pes_flux.F90:2017
subroutine pes_flux_integrate_sph(this, mesh, st, dt)
Definition: pes_flux.F90:2304
integer, parameter pes_cartesian
Definition: pes_flux.F90:262
integer, parameter pes_plane
Definition: pes_flux.F90:257
subroutine pes_flux_distribute(istart_global, iend_global, istart, iend, comm)
Definition: pes_flux.F90:2872
subroutine pes_flux_integrate_cub(this, space, mesh, st, kpoints, dt)
Definition: pes_flux.F90:2074
subroutine, public pes_flux_end(this)
Definition: pes_flux.F90:769
pure integer function get_ikp(this, ikpu, ikpv, ikpz, n_dir)
Definition: pes_flux.F90:1993
subroutine pes_flux_integrate_cub_tabulate(this, space, mesh, st, kpoints)
Definition: pes_flux.F90:1842
subroutine, public pes_flux_init(this, namespace, space, mesh, st, ext_partners, kpoints, abb, save_iter, max_iter)
Definition: pes_flux.F90:272
integer, parameter pes_spherical
Definition: pes_flux.F90:257
subroutine, public pes_flux_out_vmap(this, pesK, file, namespace, ll, pmesh, dim)
Definition: pes_flux.F90:3730
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
This module handles spin dimensions of the states and the k-point distribution.
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:223
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
type(unit_system_t), public units_inp
the units systems for reading and writing
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:118
character pure function, public index2axis(idir)
Definition: utils.F90:202
subroutine fill_non_periodic_dimension(this)
Definition: pes_flux.F90:1654
Class implementing a parallelepiped box. Currently this is restricted to a rectangular cuboid (all th...
Class implementing a spherical box.
Definition: box_sphere.F90:132
class representing derivatives
Describes mesh distribution to nodes.
Definition: mesh.F90:186
The states_elec_t class contains all electronic wave functions.
int true(void)