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