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 call messages_write("Debug: calculating pes_flux cub surface integral (accelerated direct expression)")
2012 call messages_info(debug_only=.true.)
2013
2015 tdstep_on_node = 1
2016 if (bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
2017 if (mesh%parallel_in_domains) tdstep_on_node = mesh%mpi_grp%rank + 1
2018 end if
2019
2020 stst = st%st_start
2021 stend = st%st_end
2022 kptst = st%d%kpt%start
2023 kptend = st%d%kpt%end
2024 sdim = st%d%dim
2025 mdim = mesh%box%dim
2026
2027 ikp_start = this%nkpnts_start
2028 ikp_end = this%nkpnts_end
2029
2030
2031
2032 safe_allocate(jk_cub(stst:stend, 1:sdim, kptst:kptend, ikp_start:ikp_end))
2033 safe_allocate(spctramp_cub(stst:stend, 1:sdim, kptst:kptend, ikp_start:ikp_end))
2034 spctramp_cub = m_z0
2035
2036
2037 safe_allocate(vphase(ikp_start:ikp_end, kptst:kptend))
2038 safe_allocate(phase(ikp_start:ikp_end, kptst:kptend))
2039 vphase(:,:) = m_z0
2040 phase(:,:) = m_z0
2041
2042
2043 nfaces = mdim * 2
2044 if (this%surf_shape == pes_plane) nfaces = 1
2045
2046 safe_allocate( wfpw(ikp_start:ikp_end))
2047 safe_allocate(gwfpw(ikp_start:ikp_end))
2048
2049
2050 do ifc = 1, nfaces
2051
2052 if (this%face_idx_range(ifc, 1)<0) cycle ! this face have no local surface point
2053
2054
2055 isp_start = this%face_idx_range(ifc, 1)
2056 isp_end = this%face_idx_range(ifc, 2)
2057
2058
2059 nfp = isp_end - isp_start + 1 ! faces can have a different number of points
2060
2061 wfpw = m_z0
2062 gwfpw = m_z0
2063
2064 ! get the directions normal to the surface and parallel to it
2065 imdim = 1
2066 do idir = 1, mdim
2067 if (abs(this%srfcnrml(idir, isp_start)) >= m_epsilon) then
2068 n_dir = idir
2069 else
2070 dir_on_face(imdim)=idir
2071 imdim = imdim + 1
2072 end if
2073 end do
2074
2075
2076 ! get the previous Volkov phase
2077 vphase(ikp_start:ikp_end,:) = this%conjgphase_prev(ikp_start:ikp_end,:)
2078
2079 jk_cub(:, :, :, :) = m_z0
2080
2081 do itstep = 1, this%itstep
2082
2083 do ik = kptst, kptend
2084
2085 if (kpoints%have_zero_weight_path()) then
2086 ik_map = ik
2087 else
2088 ik_map = 1
2089 end if
2090
2091 kpoint(1:mdim) = kpoints%get_point(ik)
2092 do ikp = ikp_start, ikp_end
2093 vec = sum((this%kcoords_cub(1:mdim, ikp, ik_map) - this%veca(1:mdim, itstep) / p_c)**2)
2094 vphase(ikp, ik) = vphase(ikp, ik) * exp(m_zi * vec * dt / m_two)
2095
2096
2097 if (space%is_periodic()) then
2098 phase(ikp, ik) = vphase(ikp, ik) * this%bvk_phase(ikp,ik)
2099 else
2100 phase(ikp, ik) = vphase(ikp, ik)
2101 end if
2102
2103 end do
2104
2105 if (itstep /= tdstep_on_node) cycle
2106
2107 do ist = stst, stend
2108 do isdim = 1, sdim
2109
2110
2111 ! calculate the surface integrals
2112 if (.not. this%use_symmetry .or. kpoints%have_zero_weight_path()) then
2113
2114 do ikp = ikp_start , ikp_end
2115
2116 gwfpw(ikp) = &
2117 sum(this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, n_dir) &
2118 * this%expkr(isp_start:isp_end, ikp, ik_map, 1) &
2119 * this%srfcnrml(n_dir, isp_start:isp_end))
2120
2121
2122 wfpw(ikp) = &
2123 sum(this%wf(ist, isdim, ik, isp_start:isp_end, itstep) &
2124 * this%expkr(isp_start:isp_end, ikp,ik_map, 1) &
2125 * this%srfcnrml(n_dir, isp_start:isp_end))
2126 end do
2127
2128 else
2129 !$omp parallel do private (ikpv,ikpz,face_int_gwf,face_int_wf) shared(gwfpw, wfpw)
2130 do ikpu = 1, this%ll(dir_on_face(1))
2131 do ikpv = 1, this%ll(dir_on_face(2))
2132
2133
2134 face_int_gwf = sum(this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, n_dir) &
2135 * this%expkr(isp_start:isp_end, ikpu, ik_map, dir_on_face(1)) &
2136 * this%expkr(isp_start:isp_end, ikpv, ik_map, dir_on_face(2)) &
2137 * this%srfcnrml(n_dir, isp_start:isp_end))
2138
2139 face_int_wf = sum(this%wf(ist, isdim, ik, isp_start:isp_end, itstep) &
2140 * this%expkr(isp_start:isp_end, ikpu, ik_map, dir_on_face(1)) &
2141 * this%expkr(isp_start:isp_end, ikpv, ik_map, dir_on_face(2)) &
2142 * this%srfcnrml(n_dir, isp_start:isp_end))
2143
2144 do ikpz = 1, this%ll(n_dir)
2145
2146 gwfpw(get_ikp(this, ikpu, ikpv, ikpz, n_dir)) = face_int_gwf &
2147 * this%expkr_perp(ikpz, ifc)
2148 wfpw( get_ikp(this, ikpu, ikpv, ikpz, n_dir)) = face_int_wf &
2149 * this%expkr_perp(ikpz, ifc)
2150
2151 end do
2152
2153 end do
2154 end do
2155
2156
2157 end if
2158
2159
2160 ! Sum it up
2161 jk_cub(ist, isdim, ik, ikp_start:ikp_end) = jk_cub(ist, isdim, ik, ikp_start:ikp_end) + &
2162 phase(ikp_start:ikp_end, ik) * (wfpw(ikp_start:ikp_end) * &
2163 (m_two * this%veca(n_dir, itstep) / p_c - this%kcoords_cub(n_dir, ikp_start:ikp_end, ik_map)) + &
2164 m_zi * gwfpw(ikp_start:ikp_end))
2165
2166
2167 end do ! isdim
2168 end do ! ist
2169 end do ! is
2170
2171 end do !istep
2172
2173
2174 spctramp_cub(:,:,:,:) = spctramp_cub(:,:,:,:) + jk_cub(:,:,:,:) * m_half
2175
2176
2177 end do ! face loop
2178
2179
2180 this%conjgphase_prev(ikp_start:ikp_end, :) = vphase(ikp_start:ikp_end, :)
2181
2182
2183 if (mesh%parallel_in_domains .and.(bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0 &
2184 .or. bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0)) then
2185 call mesh%allreduce(spctramp_cub)
2186 end if
2187
2188
2189
2190 this%spctramp_cub = this%spctramp_cub + spctramp_cub * dt
2191
2192 safe_deallocate_a(gwfpw)
2193 safe_deallocate_a(wfpw)
2194
2195 safe_deallocate_a(jk_cub)
2196 safe_deallocate_a(spctramp_cub)
2197 safe_deallocate_a(phase)
2198 safe_deallocate_a(vphase)
2199
2200 call profiling_out('pes_flux_integrate_cub')
2201
2202 pop_sub(pes_flux_integrate_cub)
2203 end subroutine pes_flux_integrate_cub
2204
2205
2206
2207 ! ---------------------------------------------------------
2208 subroutine pes_flux_integrate_sph(this, mesh, st, dt)
2209 type(pes_flux_t), intent(inout) :: this
2210 type(mesh_t), intent(in) :: mesh
2211 type(states_elec_t), intent(inout) :: st
2212 real(real64), intent(in) :: dt
2213
2214 integer :: stst, stend, kptst, kptend, sdim
2215 integer :: ist, ik, isdim, imdim
2216 integer :: itstep, lmax
2217 integer :: ll, mm
2218 complex(real64), allocatable :: s1_node(:,:,:,:,:,:), s1_act(:,:,:)
2219 complex(real64), allocatable :: s2_node(:,:,:,:,:), s2_act(:,:)
2220 complex(real64), allocatable :: integ11_t(:,:), integ12_t(:,:,:)
2221 complex(real64), allocatable :: integ21_t(:), integ22_t(:,:)
2222 complex(real64), allocatable :: spctramp_sph(:,:,:,:,:)
2223 integer :: ikk, ikk_start, ikk_end
2224 integer :: isp_start, isp_end
2225 integer :: iomk, iomk_start, iomk_end
2226 complex(real64), allocatable :: phase_act(:,:)
2227 real(real64) :: vec
2228 integer :: tdstep_on_node
2229
2230 push_sub(pes_flux_integrate_sph)
2231
2232 ! this routine is parallelized over time steps and surface points
2233
2234 call messages_write("Debug: calculating pes_flux sph surface integral")
2235 call messages_info(debug_only=.true.)
2236
2237 tdstep_on_node = 1
2238 if (mesh%parallel_in_domains) tdstep_on_node = mesh%mpi_grp%rank + 1
2239
2240 stst = st%st_start
2241 stend = st%st_end
2242 kptst = st%d%kpt%start
2243 kptend = st%d%kpt%end
2244 sdim = st%d%dim
2245
2246 lmax = this%lmax
2247 ikk_start = this%nk_start
2248 ikk_end = this%nk_end
2249 isp_start = this%nsrfcpnts_start
2250 isp_end = this%nsrfcpnts_end
2251 iomk_start = this%nstepsomegak_start
2252 iomk_end = this%nstepsomegak_end
2253
2254 ! surface integral S_lm (for time step on node)
2255 safe_allocate(s1_node(stst:stend, 1:sdim, kptst:kptend, 0:lmax, -lmax:lmax, 1:3))
2256 safe_allocate(s2_node(stst:stend, 1:sdim, kptst:kptend, 0:lmax, -lmax:lmax))
2257
2258 safe_allocate(s1_act(0:lmax, -lmax:lmax, 1:3))
2259 safe_allocate(s2_act(0:lmax, -lmax:lmax))
2260
2261 do itstep = 1, this%itstep
2262
2263 do ik = kptst, kptend
2264 do ist = stst, stend
2265 do isdim = 1, sdim
2266
2267 s2_act = m_z0
2268
2269 do ll = 0, lmax
2270 do mm = -ll, ll
2271 do imdim = 1, 3
2272 ! surface integrals
2273 s1_act(ll, mm, imdim) = &
2274 sum(this%ylm_r(ll, mm, isp_start:isp_end) * this%srfcnrml(imdim, isp_start:isp_end) * &
2275 this%wf(ist, isdim, ik, isp_start:isp_end, itstep))
2276
2277 s2_act(ll, mm) = s2_act(ll, mm) + &
2278 sum(this%ylm_r(ll, mm, isp_start:isp_end) * this%srfcnrml(imdim, isp_start:isp_end) * &
2279 this%gwf(ist, isdim, ik, isp_start:isp_end, itstep, imdim))
2280 end do
2281 end do
2282 end do
2283
2284 if (mesh%parallel_in_domains .and. bitand(this%par_strategy, option__pes_flux_parallelization__pf_surface) /= 0) then
2285 call mesh%allreduce(s1_act)
2286 call mesh%allreduce(s2_act)
2287 end if
2288
2289 if (itstep == tdstep_on_node) then
2290 s1_node(ist, isdim, ik, :, :, :) = s1_act(:, :, :)
2291 s2_node(ist, isdim, ik, :, :) = s2_act(:, :)
2292 end if
2293
2294 end do
2295 end do
2296 end do
2297 end do
2298
2299 ! spectral amplitude for k-points on node
2300 safe_allocate(integ11_t(0:this%nstepsomegak, 1:3))
2301 safe_allocate(integ21_t(0:this%nstepsomegak))
2302 safe_allocate(integ12_t(ikk_start:ikk_end, 1:this%nstepsomegak, 1:3))
2303 safe_allocate(integ22_t(ikk_start:ikk_end, 1:this%nstepsomegak))
2304
2305 safe_allocate(spctramp_sph(stst:stend, 1:sdim, kptst:kptend, 0:this%nk, 1:this%nstepsomegak))
2306 spctramp_sph = m_z0
2307
2308 ! get the previous Volkov phase
2309 safe_allocate(phase_act(ikk_start:ikk_end, 1:this%nstepsomegak))
2310 if (ikk_start > 0) then
2311 phase_act(ikk_start:ikk_end, :) = this%conjgphase_prev(ikk_start:ikk_end, :)
2312 else
2313 phase_act(ikk_start:ikk_end, :) = m_z0
2314 end if
2315
2316 do itstep = 1, this%itstep
2317 ! calculate the current Volkov phase
2318 do ikk = ikk_start, ikk_end
2319 do iomk = 1, this%nstepsomegak
2320 vec = sum((this%kcoords_sph(1:3, ikk, iomk) - this%veca(1:3, itstep) / p_c)**m_two)
2321 phase_act(ikk, iomk) = phase_act(ikk, iomk) * exp(m_zi * vec * dt / m_two)
2322 end do
2323 end do
2324
2325 do ik = kptst, kptend
2326 do ist = stst, stend
2327 do isdim = 1, sdim
2328
2329 ! communicate the current surface integrals
2330 if (itstep == tdstep_on_node) then
2331 s1_act(:,:,:) = s1_node(ist, isdim, ik, :, :, :)
2332 s2_act(:,:) = s2_node(ist, isdim, ik, :, :)
2333 end if
2334 if (mesh%parallel_in_domains) then
2335 call mesh%mpi_grp%bcast(s1_act, (lmax + 1) * (2 * lmax + 1) * 3, mpi_double_complex, itstep - 1)
2336 call mesh%mpi_grp%bcast(s2_act, (lmax + 1) * (2 * lmax + 1), mpi_double_complex, itstep - 1)
2337 end if
2338
2339 integ12_t = m_z0
2340 integ22_t = m_z0
2341
2342 do ll = 0, lmax
2343
2344 integ11_t = m_z0
2345 integ21_t = m_z0
2346
2347 do mm = -ll, ll
2348 ! multiply with spherical harmonics & sum over all mm
2349 do imdim = 1, 3
2350 integ11_t(iomk_start:iomk_end, imdim) = integ11_t(iomk_start:iomk_end, imdim) + &
2351 s1_act(ll, mm, imdim) * this%ylm_k(ll, mm, iomk_start:iomk_end)
2352 end do
2353 integ21_t(iomk_start:iomk_end) = integ21_t(iomk_start:iomk_end) + &
2354 s2_act(ll, mm) * this%ylm_k(ll, mm, iomk_start:iomk_end)
2355 end do
2356
2357 if (mesh%parallel_in_domains) then
2358 call mesh%allreduce(integ11_t)
2359 call mesh%allreduce(integ21_t)
2360 end if
2361
2362 ! multiply with Bessel function & sum over all ll
2363 do ikk = ikk_start, ikk_end
2364 integ12_t(ikk, 1:this%nstepsomegak, 1:3) = integ12_t(ikk, 1:this%nstepsomegak, 1:3) + &
2365 integ11_t(1:this%nstepsomegak, 1:3) * this%j_l(ll, ikk) * ( - m_zi)**ll
2366 integ22_t(ikk, 1:this%nstepsomegak) = integ22_t(ikk, 1:this%nstepsomegak) + &
2367 integ21_t(1:this%nstepsomegak) * this%j_l(ll, ikk) * ( - m_zi)**ll
2368 end do
2369 end do
2370 ! sum over dimensions
2371 do imdim = 1, 3
2372 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) = &
2373 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) + &
2374 phase_act(ikk_start:ikk_end,:) * (integ12_t(ikk_start:ikk_end,:, imdim) * &
2375 (m_two * this%veca(imdim, itstep) / p_c - this%kcoords_sph(imdim, ikk_start:ikk_end,:)))
2376 end do
2377 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end, :) = &
2378 spctramp_sph(ist, isdim, ik, ikk_start:ikk_end,:) + &
2379 phase_act(ikk_start:ikk_end,:) * integ22_t(ikk_start:ikk_end,:) * m_zi
2380 end do
2381 end do
2382 end do
2383 end do
2384
2385 safe_deallocate_a(s1_node)
2386 safe_deallocate_a(s2_node)
2387 safe_deallocate_a(s1_act)
2388 safe_deallocate_a(s2_act)
2389
2390 safe_deallocate_a(integ11_t)
2391 safe_deallocate_a(integ12_t)
2392 safe_deallocate_a(integ21_t)
2393 safe_deallocate_a(integ22_t)
2394
2395 ! save the Volkov phase and the spectral amplitude
2396 this%conjgphase_prev = m_z0
2397
2398 if (ikk_start > 0) then
2399 this%conjgphase_prev(ikk_start:ikk_end, :) = phase_act(ikk_start:ikk_end, :)
2400 end if
2401 safe_deallocate_a(phase_act)
2402
2403 if (mesh%parallel_in_domains .and. bitand(this%par_strategy, option__pes_flux_parallelization__pf_time) /= 0) then
2404 call mesh%allreduce(this%conjgphase_prev)
2405 call mesh%allreduce(spctramp_sph)
2406 end if
2407
2408 this%spctramp_sph(:, :, :, 1:this%nk, :) = this%spctramp_sph(:, :, :, 1:this%nk, :) &
2409 + spctramp_sph(:, :, :, 1:this%nk, :) * dt
2410 safe_deallocate_a(spctramp_sph)
2411
2412 pop_sub(pes_flux_integrate_sph)
2413 end subroutine pes_flux_integrate_sph
2414
2415 ! ---------------------------------------------------------
2416 subroutine pes_flux_getcube(this, mesh, abb, border, offset, fc_ptdens)
2417 type(mesh_t), intent(in) :: mesh
2418 type(pes_flux_t), intent(inout) :: this
2419 type(absorbing_boundaries_t), intent(in) :: abb
2420 real(real64), intent(in) :: border(:)
2421 real(real64), intent(in) :: offset(:)
2422 real(real64), intent(in) :: fc_ptdens
2423
2424 integer, allocatable :: which_surface(:)
2425 real(real64) :: xx(mesh%box%dim), dd, area, dS(mesh%box%dim, 2), factor
2426 integer :: mdim, imdim, idir, isp, pm,nface, idim, ndir, iu,iv, iuv(1:2)
2427 integer(int64) :: ip_global
2428 integer :: npface
2429 integer :: rankmin, nsurfaces
2430 logical :: in_ab
2431 integer :: ip_local, NN(mesh%box%dim, 2), idx(mesh%box%dim, 2)
2432 integer :: isp_end, isp_start, ifc, n_dir, nfaces, mindim
2433 real(real64) :: RSmax(2), RSmin(2), RS(2), dRS(2), weight
2434
2435
2436 push_sub(pes_flux_getcube)
2437
2438 ! this routine works on absolute coordinates
2439
2440
2441 mdim = mesh%box%dim
2442
2443
2444
2445 safe_allocate(this%face_idx_range(1:mdim * 2, 1:2))
2446 safe_allocate(this%LLr(mdim, 1:2))
2447 safe_allocate(this%NN(mdim, 1:2))
2448
2449 this%face_idx_range(:, :) = 0
2450 this%LLr(:, :) = m_zero
2451 this%NN(:, :) = 1
2452 nn(:, :) = 1
2453
2454 if (this%surf_interp) then
2455 ! Create a surface with points not on the mesh
2456
2457 idx(:,:) = 0
2458
2459 mindim = 1
2460 factor = m_two
2461 if (this%surf_shape == pes_plane) then
2462 mindim = mdim ! We only have two planes along the non periodic dimension
2463 ! this is due to the fact that for semiperiodic systems we multiply border by two to prenvet the creation of surfaces at the edges
2464 factor = m_one
2465 end if
2466
2467
2468 this%nsrfcpnts = 0
2469
2470 do ndir = mdim, mindim, -1
2471 area = m_one
2472 do idir=1, mdim
2473 if (idir == ndir) cycle
2474 area = area * border(idir) * factor
2475 end do
2476
2477 npface = int(fc_ptdens * area) ! number of points on the face
2478
2479 idim = 1
2480 do idir=1, mdim
2481 if (idir == ndir) cycle
2482 nn(ndir, idim) = int(sqrt(npface * (border(idir) * factor)**2 / area))
2483 ds(ndir, idim) = border(idir) * factor / nn(ndir, idim)
2484 this%LLr(ndir, idim) = nn(ndir, idim) * ds(ndir, idim)
2485 idx(ndir, idim) = idir
2486 idim = idim + 1
2487 end do
2488 this%nsrfcpnts = this%nsrfcpnts + 2 * product(nn(ndir, 1:mdim - 1))
2489 end do
2490
2491 this%NN(1:mdim, :) = nn(1:mdim, :)
2492
2493
2494 assert(this%nsrfcpnts > 0) !at this point should be satisfied otherwise the point density is way too small
2495
2496
2497 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2498 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2499
2500 this%srfcnrml(:, :) = m_zero
2501 this%rcoords(:, :) = m_zero
2502
2503
2504 isp = 1
2505 ifc = 1
2506 do ndir = mdim, mindim, -1
2507
2508 !Up face
2509 this%face_idx_range(ifc,1) = isp
2510 do iu = 1, nn(ndir,1)
2511 do iv = 1, nn(ndir,2)
2512 this%rcoords(ndir, isp) = border(ndir)
2513 this%srfcnrml(ndir, isp) = product(ds(ndir,1:mdim-1))
2514 iuv =(/iu, iv/)
2515 do idim = 1, mdim-1
2516 this%rcoords(idx(ndir, idim), isp) = (-nn(ndir, idim) * m_half - m_half + iuv(idim)) * ds(ndir, idim)
2517 end do
2518 isp = isp + 1
2519 end do
2520 end do
2521 this%face_idx_range(ifc, 2) = isp-1
2522
2523 ifc = ifc + 1
2524
2525 !Down face
2526 this%face_idx_range(ifc, 1) = isp
2527 do iu = 1, nn(ndir, 1)
2528 do iv = 1, nn(ndir, 2)
2529 this%rcoords(ndir, isp) = -border(ndir)
2530 this%srfcnrml(ndir, isp) = -product(ds(ndir, 1:mdim - 1))
2531 iuv =(/iu, iv/)
2532 do idim = 1, mdim-1
2533 this%rcoords(idx(ndir, idim), isp) = (-nn(ndir, idim) * m_half -m_half + iuv(idim)) * ds(ndir, idim)
2534 end do
2535 isp = isp + 1
2536 end do
2537 end do
2538 this%face_idx_range(ifc, 2) = isp-1
2539
2540 ifc = ifc + 1
2541 end do
2542
2543 do isp = 1, this%nsrfcpnts
2544 this%rcoords(1:mdim, isp) = mesh%coord_system%to_cartesian(this%rcoords(1:mdim, isp))
2545 end do
2546
2547 else
2548 ! Surface points are on the mesh
2549
2550 nfaces = mdim * 2
2551 if (this%surf_shape == pes_plane) nfaces = 2
2552
2553 in_ab = .false.
2554
2555 safe_allocate(which_surface(1:mesh%np_global))
2556 which_surface = 0
2557
2558 ! get the surface points
2559 this%nsrfcpnts = 0
2560 do ip_local = 1, mesh%np
2561 ip_global = mesh_local2global(mesh, ip_local)
2562
2563 nsurfaces = 0
2564
2565 xx(1:mdim) = mesh%x(ip_local, 1:mdim) - offset(1:mdim)
2566
2567 ! eventually check whether we are in absorbing zone
2568 select case (abb%abtype)
2569 case (mask_absorbing)
2570 in_ab = .not. is_close(abb%mf(ip_local), m_one)
2571 case (imaginary_absorbing)
2572 in_ab = abs(abb%mf(ip_local)) > m_epsilon
2573 case default
2574 assert(.false.)
2575 end select
2576
2577 ! check whether the point is inside the cube
2578 if (all(abs(xx(1:mdim)) <= border(1:mdim)) .and. .not. in_ab) then
2579 ! check whether the point is close to any border
2580 do imdim = 1, mdim
2581 if (this%surf_shape == pes_plane) then
2582 dd = border(imdim) - xx(imdim)
2583 else
2584 dd = border(imdim) - abs(xx(imdim))
2585 end if
2586 if (dd < mesh%spacing(imdim) / m_two) then
2587 nsurfaces = nsurfaces + 1
2588 which_surface(ip_global) = int(sign(m_one, xx(imdim))) * imdim ! +-x=+-1, +-y=+-2, +-z=+-3
2589 end if
2590 end do
2591
2592 ! check whether the point is close to one border only (not an edge)
2593 if (nsurfaces == 1) then
2594 this%nsrfcpnts = this%nsrfcpnts + 1
2595 else
2596 which_surface(ip_global) = 0
2597 end if
2598 end if
2599
2600 end do
2601
2602 if (mesh%parallel_in_domains) then
2603 assert(mesh%np_global < huge(0_int32))
2604 call mesh%allreduce(this%nsrfcpnts)
2605 call mesh%allreduce(which_surface)
2606 end if
2607
2608 safe_allocate(this%srfcpnt(1:this%nsrfcpnts))
2609 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2610 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2611 safe_allocate(this%rankmin(1:this%nsrfcpnts))
2612
2613 this%srfcnrml = m_zero
2614 this%rcoords = m_zero
2615
2616 this%face_idx_range(:,:) = 0
2617
2618 isp = 0
2619 nface = 0
2620 do idir = mdim, 1, -1 ! Start counting from z to simplify implementation for semiperiodic systems (pln)
2621 do pm = 1, -1, -2
2622 nface = nface + 1
2623 this%face_idx_range(nface, 1) = isp + 1
2624
2625 do ip_global = 1, mesh%np_global
2626 if (abs(which_surface(ip_global)) == idir .and. sign(1, which_surface(ip_global)) == pm) then
2627 isp = isp + 1
2628 ! coordinate of surface point
2629 xx(1:mdim) = mesh_x_global(mesh, ip_global)
2630 this%rcoords(1:mdim, isp) = xx(1:mdim)
2631 ! local ip & node which has the surface point
2632 this%srfcpnt(isp) = mesh_nearest_point(mesh, this%rcoords(1:mdim, isp), dd, rankmin)
2633 this%rankmin(isp) = rankmin
2634 ! surface normal
2635 this%srfcnrml(idir, isp) = sign(1, which_surface(ip_global))
2636 ! add the surface element (of directions orthogonal to the normal vector)
2637 do imdim = 1, mdim
2638 if (imdim == idir) cycle
2639 this%srfcnrml(idir, isp) = this%srfcnrml(idir, isp) * mesh%spacing(imdim)
2640 end do
2641 end if
2642 end do
2643
2644 this%face_idx_range(nface,2) = isp
2645
2646 end do
2647 end do
2648
2649
2650 !Get dimensions and spacing on each (pair of) face
2651 do ifc = 1, nint((nfaces + 0.5) / 2)
2652 isp_start = this%face_idx_range(ifc, 1)
2653 isp_end = this%face_idx_range(ifc, 2)
2654
2655 !retrieve face normal direction
2656 n_dir = 0
2657 do idir = 1, mdim
2658 if (abs(this%srfcnrml(idir, isp_start)) >= m_epsilon) n_dir = idir
2659 end do
2660
2661 !retrieve the spacing on the face
2662 drs(:) = m_zero
2663 idim = 1
2664 do idir = 1, mdim
2665 if (idir == n_dir) cycle
2666 drs(idim)= mesh%spacing(idir)
2667 idim = idim + 1
2668 end do
2669
2670
2671 !retrieve the dimensions of the face
2672 rsmin = m_zero
2673 rsmax = m_zero
2674 do isp = isp_start, isp_end
2675 !measure in reduced coordinates
2676 xx = mesh%coord_system%from_cartesian(this%rcoords(1:mdim, isp))
2677 idim = 1
2678 do idir = 1, mdim
2679 if (idir == n_dir) cycle
2680 rs(idim)=xx(idir)
2681 if (rs(idim) < rsmin(idim)) rsmin(idim) = rs(idim)
2682 if (rs(idim) > rsmax(idim)) rsmax(idim) = rs(idim)
2683 idim = idim + 1
2684 end do
2685 end do
2686
2687
2688 do idir = 1, mdim - 1
2689 this%LLr(n_dir, idir) = rsmax(idir) - rsmin(idir) + drs(idir)
2690 if (drs(idir) > m_zero) this%NN(n_dir, idir) = int(this%LLr(n_dir, idir) / drs(idir))
2691 end do
2692
2693 end do
2694
2695
2696 end if
2697
2698 if (this%anisotrpy_correction) then
2699 !Compensate the fact that the angular distribution of points is not uniform
2700 !and have peaks in correspondence to the edges and corners of the parallelepiped
2701 do isp = 1, this%nsrfcpnts
2702 weight = sum(this%rcoords(1:mdim, isp) * this%srfcnrml(1:mdim, isp))
2703 weight = weight/sum(this%rcoords(1:mdim, isp)**2) / sum(this%srfcnrml(1:mdim, isp)**2)
2704 this%srfcnrml(1:mdim, isp) = this%srfcnrml(1:mdim, isp) * abs(weight)
2705 end do
2706
2707 end if
2708
2709
2710 safe_deallocate_a(which_surface)
2711
2712 pop_sub(pes_flux_getcube)
2713 end subroutine pes_flux_getcube
2714
2715 ! ---------------------------------------------------------
2716 subroutine pes_flux_getsphere(this, mesh, nstepsthetar, nstepsphir)
2717 type(pes_flux_t), intent(inout) :: this
2718 type(mesh_t), intent(in) :: mesh
2719 integer, intent(inout) :: nstepsthetar, nstepsphir
2720
2721 integer :: mdim
2722 integer :: isp, ith, iph
2723 real(real64) :: thetar, phir
2724 real(real64) :: weight, dthetar
2725
2726 push_sub(pes_flux_getsphere)
2727
2728 mdim = mesh%box%dim
2729
2730 if (nstepsphir == 0) nstepsphir = 1
2731
2732 if (nstepsthetar <= 1) then
2733 nstepsphir = 1
2734 nstepsthetar = 1
2735 end if
2736 dthetar = m_pi / nstepsthetar
2737 this%nsrfcpnts = nstepsphir * (nstepsthetar - 1) + 2
2738
2739 safe_allocate(this%srfcnrml(1:mdim, 0:this%nsrfcpnts))
2740 safe_allocate(this%rcoords(1:mdim, 0:this%nsrfcpnts))
2741
2742 this%srfcnrml = m_zero
2743 this%rcoords = m_zero
2744
2745 ! initializing spherical grid
2746 isp = 0
2747 do ith = 0, nstepsthetar
2748 thetar = ith * dthetar
2749
2750 if (ith == 0 .or. ith == nstepsthetar) then
2751 weight = (m_one - cos(dthetar / m_two)) * m_two * m_pi
2752 else
2753 weight = abs(cos(thetar - dthetar / m_two) - cos(thetar + dthetar / m_two)) &
2754 * m_two * m_pi / nstepsphir
2755 end if
2756
2757 do iph = 0, nstepsphir - 1 ! 2*pi is the same than zero
2758 isp = isp + 1
2759 phir = iph * m_two * m_pi / nstepsphir
2760 this%srfcnrml(1, isp) = cos(phir) * sin(thetar)
2761 if (mdim >= 2) this%srfcnrml(2, isp) = sin(phir) * sin(thetar)
2762 if (mdim == 3) this%srfcnrml(3, isp) = cos(thetar)
2763 this%rcoords(1:mdim, isp) = this%radius * this%srfcnrml(1:mdim, isp)
2764 ! here we also include the surface elements
2765 this%srfcnrml(1:mdim, isp) = this%radius**m_two * weight * this%srfcnrml(1:mdim, isp)
2766 if (ith == 0 .or. ith == nstepsthetar) exit
2767 end do
2768 end do
2769
2770 pop_sub(pes_flux_getsphere)
2771 end subroutine pes_flux_getsphere
2772
2773
2774
2775
2776 ! ---------------------------------------------------------
2777 subroutine pes_flux_distribute(istart_global, iend_global, istart, iend, comm)
2778 integer, intent(in) :: istart_global
2779 integer, intent(in) :: iend_global
2780 integer, intent(inout) :: istart
2781 integer, intent(inout) :: iend
2782 type(mpi_comm), intent(in) :: comm
2783
2784#if defined(HAVE_MPI)
2785 integer, allocatable :: dimrank(:)
2786 integer :: mpisize, mpirank, irest, irank
2787 integer :: inumber
2788#endif
2789
2790 push_sub(pes_flux_distribute)
2791
2792#if defined(HAVE_MPI)
2793 call mpi_comm_size(comm, mpisize, mpi_err)
2794 call mpi_comm_rank(comm, mpirank, mpi_err)
2795
2796 safe_allocate(dimrank(0:mpisize-1))
2797
2798 inumber = iend_global - istart_global + 1
2799 irest = mod(inumber, mpisize)
2800 do irank = 0, mpisize - 1
2801 if (irest > 0) then
2802 dimrank(irank) = int(inumber / mpisize) + 1
2803 irest = irest - 1
2804 else
2805 dimrank(irank) = int(inumber / mpisize)
2806 end if
2807 end do
2808
2809 iend = istart_global + sum(dimrank(:mpirank)) - 1
2810 istart = iend - dimrank(mpirank) + 1
2811
2812 if (istart > iend) then
2813 iend = 0
2814 istart = 0
2815 end if
2816
2817 safe_deallocate_a(dimrank)
2818
2819#else
2820 istart = istart_global
2821 iend = iend_global
2822#endif
2823
2824 pop_sub(pes_flux_distribute)
2825 end subroutine pes_flux_distribute
2826
2827
2828#include "pes_flux_out_inc.F90"
2829
2830end module pes_flux_oct_m
2831
2832!! Local Variables:
2833!! mode: f90
2834!! coding: utf-8
2835!! End:
Prints out to iunit a message in the form: ["InputVariable" = value] where "InputVariable" is given b...
Definition: messages.F90:180
double exp(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
integer, parameter, public imaginary_absorbing
integer, parameter, public mask_absorbing
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:122
This module handles the calculation mode.
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public zderivatives_grad(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the gradient to a mesh function
type(lasers_t) function, pointer, public list_get_lasers(partners)
real(real64), parameter, public m_two
Definition: global.F90: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:223
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:301
subroutine, public mesh_interpolation_init(this, mesh)
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
integer function, public mesh_nearest_point(mesh, pos, dmin, rankmin)
Returns the index of the point which is nearest to a given vector position pos.
Definition: mesh.F90:380
integer(int64) function, public mesh_local2global(mesh, ip)
This function returns the global mesh index for a given local index.
Definition: mesh.F90:959
real(real64) function, dimension(1:mesh%box%dim), public mesh_x_global(mesh, ipg)
Definition: mesh.F90:804
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1125
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
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:624
integer, public mpi_err
used to store return values of mpi calls
Definition: mpi.F90:269
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:2983
subroutine pes_flux_getcube(this, mesh, abb, border, offset, fc_ptdens)
Definition: pes_flux.F90:2510
subroutine, public pes_flux_calc(this, space, mesh, st, der, ext_partners, kpoints, iter, dt, stopping)
Definition: pes_flux.F90:1683
subroutine, public pes_flux_out_energy(this, pesK, file, namespace, ll, Ekin, dim)
Definition: pes_flux.F90:3639
subroutine pes_flux_getsphere(this, mesh, nstepsthetar, nstepsphir)
Definition: pes_flux.F90:2810
subroutine, public pes_flux_output(this, box, st, namespace)
Definition: pes_flux.F90:3845
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:4467
subroutine, public pes_flux_pmesh(this, namespace, dim, kpoints, ll, pmesh, idxZero, krng, Lp, Ekin)
Definition: pes_flux.F90:2943
subroutine, public pes_flux_dump(restart, this, mesh, st, ierr)
Definition: pes_flux.F90:4375
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:2302
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:2871
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:3729
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
This module handles spin dimensions of the states and the k-point distribution.
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:223
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
type(unit_system_t), public units_inp
the units systems for reading and writing
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:118
character pure function, public index2axis(idir)
Definition: utils.F90:202
subroutine fill_non_periodic_dimension(this)
Definition: pes_flux.F90: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:186
The states_elec_t class contains all electronic wave functions.
int true(void)