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