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