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