1!
2! Copyright (C) 1996-2016	The SIESTA group
3!  This file is distributed under the terms of the
4!  GNU General Public License: see COPYING in the top directory
5!  or http://www.gnu.org/copyleft/gpl.txt.
6! See Docs/Contributors.txt for a list of contributors.
7!
8! This code segment has been fully created by:
9! Nick Papior Andersen, 2013, nickpapior@gmail.com
10! Please conctact the author, prior to re-using this code.
11!
12
13! This module contains the geometrical object:
14!  PLANE
15
16module m_geom_plane
17
18  use m_geom_aux
19
20  implicit none
21
22  private
23
24  ! Types
25  public :: geo_plane_delta
26  public :: geo_plane_gauss
27  public :: geo_plane_exp
28  ! Interfaces
29  public :: fgeo_read_plane
30  public :: correct_plane
31  public :: voxel_in_plane
32  public :: voxel_val_plane
33
34  ! Explicit routines
35  public :: fgeo_read_plane_delta
36  public :: fgeo_read_plane_gauss
37  public :: fgeo_read_plane_exp
38  public :: correct_plane_delta
39  public :: correct_plane_gauss
40  public :: correct_plane_exp
41  public :: voxel_in_plane_delta
42  public :: voxel_in_plane_gauss
43  public :: voxel_in_plane_exp
44  public :: voxel_val_plane_delta
45  public :: voxel_val_plane_gauss
46  public :: voxel_val_plane_exp
47
48  type geo_plane_delta
49     ! Describing an infinite plane which will satisfy the following:
50     !    n \dot ( x1 - c ) = 0
51     ! where x1 is any coordinate (x,y,z)
52     sequence
53     ! The coordinate from which the normal vector
54     ! is draw...
55     real(dp) :: c(3)
56     ! The NORMALIZED normal vector to the plane in point 'c'.
57     real(dp) :: n(3)
58     ! Auxillary distance length which is merely '- n dot c'
59     ! This will ease the computations as it is used extensively
60     real(dp) :: d = 0._dp
61  end type geo_plane_delta
62
63  type geo_plane_gauss
64     sequence
65     ! The gauss tail of the plane
66     ! This is: 1/(2*\sigma^2)
67     real(dp) :: inv_2var
68     ! The cutoff-radii of the Gaussian function
69     real(dp) :: cut_off
70
71     real(dp) :: c(3)
72     real(dp) :: n(3)
73     real(dp) :: d_A = 0._dp ! d_Above
74     real(dp) :: d_B = 0._dp ! d_Below
75  end type geo_plane_gauss
76
77  type geo_plane_exp
78     sequence
79     ! The gauss tail of the plane
80     ! This is: h^{-1} =  + r_{1/2} / log(2.)
81     real(dp) :: h
82     ! The cutoff-radii of the exponential function
83     real(dp) :: cut_off
84
85     real(dp) :: c(3)
86     real(dp) :: n(3)
87     real(dp) :: d_A = 0._dp ! d_Above
88     real(dp) :: d_B = 0._dp ! d_Below
89  end type geo_plane_exp
90
91  interface fgeo_read_plane
92     module procedure fgeo_read_plane_delta
93     module procedure fgeo_read_plane_gauss
94     module procedure fgeo_read_plane_exp
95  end interface
96
97  interface correct_plane
98     module procedure correct_plane_delta
99     module procedure correct_plane_gauss
100     module procedure correct_plane_exp
101  end interface
102
103  interface voxel_in_plane
104     module procedure voxel_in_plane_delta
105     module procedure voxel_in_plane_gauss
106     module procedure voxel_in_plane_exp
107  end interface
108
109  interface voxel_val_plane
110     module procedure voxel_val_plane_delta
111     module procedure voxel_val_plane_gauss
112     module procedure voxel_val_plane_exp
113  end interface
114
115contains
116
117  ! This subroutine returns .true. in has if the box spanned in
118  !    lr,lr+dx,lr+dx+dy,..., has any points inside the plane.
119  !  - lr is the "lower-left" of the box.
120  !  - d is the extension of the box
121
122  ! TODO, for now this can only handle Cartesian boxes, it should be
123  ! extended to be used in skewed axes....
124  ! TODO this should be easy if we project the normal vector into
125  ! the cell coordinates (i.e. do nb = (n dot bx, n dot by, n dot bz)
126
127
128  function voxel_in_plane_delta(plane,ll,d) result(has)
129    type(geo_plane_delta), intent(in) :: plane
130    real(dp), intent(in) :: ll(3), d(3)
131    logical :: has
132    ! The positive and negative vertices
133    real(dp) :: p(3), n(3)
134
135    ! We will use the (n,p)-vertex check
136    ! We select 2 vertices which are those farthest (n=negative)
137    ! and furthest (p=positiv) in the direction of the normal
138    ! vector describing the plane.
139
140    call voxel_pos_neg(ll,d,plane%n,p,n)
141
142    has = .false.
143    ! We can then consider whether the vertices lie both outside
144    ! if not, we have an intersection.
145
146    ! The positive vertex lies on the left side of the plane,
147    ! hence we know that it will not be crossing the plane
148    if ( plane%n(1)*p(1)+plane%n(2)*p(2)+plane%n(3)*p(3) <  plane%d ) return
149
150    if ( plane%n(1)*n(1)+plane%n(2)*n(2)+plane%n(3)*n(3) <= plane%d ) then
151       ! The positive vertex lies on the right side of the plane,
152       ! This check ensures that the negative vertex lies on the left side
153       ! of the plane. Hence we have an intersection.
154       has = .true.
155    end if
156
157  end function voxel_in_plane_delta
158
159  function voxel_in_plane_gauss(plane,ll,d) result(has)
160    type(geo_plane_gauss), intent(in) :: plane
161    real(dp), intent(in) :: ll(3), d(3)
162    logical :: has
163    real(dp) :: p(3), n(3)
164
165    call voxel_pos_neg(ll,d,plane%n,p,n)
166
167    has = .false.
168    if ( plane%n(1)*p(1)+plane%n(2)*p(2)+plane%n(3)*p(3) <  plane%d_B ) return
169    if ( plane%n(1)*n(1)+plane%n(2)*n(2)+plane%n(3)*n(3) <= plane%d_A ) then
170       has = .true.
171    end if
172
173  end function voxel_in_plane_gauss
174
175  function voxel_in_plane_exp(plane,ll,d) result(has)
176    type(geo_plane_exp), intent(in) :: plane
177    real(dp), intent(in) :: ll(3), d(3)
178    logical :: has
179    real(dp) :: p(3), n(3)
180
181    call voxel_pos_neg(ll,d,plane%n,p,n)
182
183    has = .false.
184    if ( plane%n(1)*p(1)+plane%n(2)*p(2)+plane%n(3)*p(3) <  plane%d_B ) return
185    if ( plane%n(1)*n(1)+plane%n(2)*n(2)+plane%n(3)*n(3) <= plane%d_A ) then
186       has = .true.
187    end if
188
189  end function voxel_in_plane_exp
190
191
192  ! This subroutine returns the value of the charge
193  ! that is attributed from the exponential function
194  !  - ll is the "lower-left" of the box.
195  !  - d is the extension of the box
196  function voxel_val_plane_gauss(plane,ll,d) result(vol)
197    type(geo_plane_gauss), intent(in) :: plane
198    real(dp), intent(in) :: ll(3), d(3)
199    real(dp) :: c(3)
200    real(dp) :: vol
201    real(dp) :: radii
202
203    ! find the center of box
204    c = ll + d * 0.5_dp
205
206    radii = plane%n(1)*c(1)+plane%n(2)*c(2)+plane%n(3)*c(3) - &
207         (plane%d_B + plane%cut_off)
208    if ( abs(radii) <= plane%cut_off ) then
209       vol = dexp(-radii**2*plane%inv_2var)
210    else
211       vol = 0._dp
212    end if
213
214  end function voxel_val_plane_gauss
215
216  function voxel_val_plane_exp(plane,ll,d) result(vol)
217    type(geo_plane_exp), intent(in) :: plane
218    real(dp), intent(in) :: ll(3), d(3)
219    real(dp) :: c(3)
220    real(dp) :: vol
221    real(dp) :: radii
222
223    ! find the center of box
224    c = ll + d * 0.5_dp
225
226    radii = abs(plane%n(1)*c(1)+plane%n(2)*c(2)+plane%n(3)*c(3) - &
227         (plane%d_B + plane%cut_off))
228    if ( radii <= plane%cut_off ) then
229       vol = dexp(-radii*plane%h)
230    else
231       vol = 0._dp
232    end if
233
234  end function voxel_val_plane_exp
235
236  function voxel_val_plane_delta(plane,ll,d) result(vol)
237    type(geo_plane_delta), intent(in) :: plane
238    real(dp), intent(in) :: ll(3), d(3)
239    real(dp) :: vol
240    vol = 1._dp
241  end function voxel_val_plane_delta
242
243  ! Returns the positive and negative corner of the voxel
244  pure subroutine voxel_pos_neg(ll,d,e,p,n)
245    real(dp), intent(in) :: ll(3), d(3), e(3)
246    real(dp), intent(out) :: p(3), n(3)
247    integer :: i
248
249    do i = 1 , 3
250       if ( e(i) >= 0._dp ) then
251          ! The normal-vector points in the i'th-direction
252          p(i) = ll(i) + d(i)
253
254          ! hence the negative vertex is the minimum coordinate
255          n(i) = ll(i)
256       else
257          ! The normal-vector points in the negative i'th-direction
258          p(i) = ll(i)
259
260          ! hence the negative index lies in the positive i'th-direction
261          n(i) = ll(i) + d(i)
262       end if
263    end do
264
265  end subroutine voxel_pos_neg
266
267  ! Subroutine for initializing the planes by transforming them
268  ! to the corresponding cell
269  subroutine correct_plane_delta(cell,plane)
270    use intrinsic_missing, only : VNORM
271    real(dp), intent(in) :: cell(3,3)
272    type(geo_plane_delta), intent(inout) :: plane
273    real(dp) :: tmp(3)
274    real(dp) :: ncell(3)
275    integer :: i
276
277    ! Normalize the vector
278    plane%n = plane%n / VNORM(plane%n)
279
280    ! Correct the direction of the normal-vector to
281    ! be that of the cell-direction
282    do i = 1 , 3
283       ncell = cell(:,i) / VNORM(cell(:,i))
284       tmp(i) = &
285            plane%n(1)*ncell(1) + &
286            plane%n(2)*ncell(2) + &
287            plane%n(3)*ncell(3)
288    end do
289    ! Copy the new normal vector in units of the cell-direction
290    plane%n = tmp / VNORM(tmp)
291
292    ! Calculate the auxillary distance
293    plane%d = plane%n(1)*plane%c(1) &
294         + plane%n(2)*plane%c(2) &
295         + plane%n(3)*plane%c(3)
296
297  end subroutine correct_plane_delta
298
299  subroutine correct_plane_gauss(cell,plane)
300    real(dp), intent(in) :: cell(3,3)
301    type(geo_plane_gauss), intent(inout) :: plane
302    type(geo_plane_delta) :: t_plane
303
304    t_plane%n = plane%n
305    t_plane%c = plane%c
306    call correct_plane_delta(cell,t_plane)
307    plane%n   = t_plane%n
308    plane%d_A = t_plane%d + plane%cut_off
309    plane%d_B = t_plane%d - plane%cut_off
310
311  end subroutine correct_plane_gauss
312
313  subroutine correct_plane_exp(cell,plane)
314    real(dp), intent(in) :: cell(3,3)
315    type(geo_plane_exp), intent(inout) :: plane
316    type(geo_plane_delta) :: t_plane
317
318    t_plane%n = plane%n
319    t_plane%c = plane%c
320    call correct_plane_delta(cell,t_plane)
321    plane%n   = t_plane%n
322    plane%d_A = t_plane%d + plane%cut_off
323    plane%d_B = t_plane%d - plane%cut_off
324
325  end subroutine correct_plane_exp
326
327
328  subroutine fgeo_read_plane_delta(bName, ngeom,geom,params,par_unit)
329
330    use intrinsic_missing, only : VNORM
331    use fdf
332
333! ********************
334! * INPUT variables  *
335! ********************
336    character(len=*), intent(in) :: bName
337    integer, intent(in) :: ngeom
338    character(len=*), intent(in), optional :: par_unit
339
340! ********************
341! * OUTPUT variables *
342! ********************
343    type(geo_plane_delta), intent(out) :: geom(ngeom)
344    real(dp), intent(out) :: params(ngeom)
345
346! ********************
347! * LOCAL variables  *
348! ********************
349    type(block_fdf)            :: bfdf
350    type(parsed_line), pointer :: pline
351    integer :: t, ip
352    real(dp) :: v
353
354    ! if none found, simply return
355    if ( ngeom <= 0 ) return
356
357    ! Count the number of planes
358    call fgeo_count(bName,GEOM_PLANE_DELTA,t)
359
360    if ( t /= ngeom ) &
361         call die('Could not find any delta planes')
362
363    ! Do the processing
364    if ( .not. fdf_block(bName,bfdf) ) &
365         call die('Could not find the block again...?')
366
367    ip = 0
368    count_geom: do while ( ip < ngeom )
369
370       ! First loop until we have the geometry
371       call fgeo_next(bfdf,t,v,unit=par_unit)
372
373       ! If it is not the correct geometry, cycle...
374       if ( t /= GEOM_PLANE_DELTA ) cycle
375
376       ! Counter for geometry
377       ip = ip + 1
378
379       ! The parameter for the geometry
380       params(ip) = v
381
382       ! Step delta
383       if ( .not. fdf_bline(bfdf,pline) ) &
384            call die('Could not step the delta mark')
385
386       ! An infinite plane is defined by any point in the plane
387       if ( .not. fdf_bline(bfdf,pline) ) &
388            call die('Could not read the point for the &
389            &infinite plane of a plane geometry object')
390       call fgeo_read_vals(pline,geom(ip)%c,units=.true.)
391
392       ! A plane is defined by the normal vector
393       if ( .not. fdf_bline(bfdf,pline) ) &
394            call die('Could not read the first vector &
395            &of a plane geometry object')
396       call fgeo_read_vals(pline,geom(ip)%n,units=.false.)
397
398    end do count_geom
399
400  end subroutine fgeo_read_plane_delta
401
402
403  subroutine fgeo_read_plane_gauss(bName, ngeom,geom,params,par_unit)
404
405    use intrinsic_missing, only : VNORM
406    use fdf
407
408! ********************
409! * INPUT variables  *
410! ********************
411    character(len=*), intent(in) :: bName
412    character(len=*), intent(in), optional :: par_unit
413
414! ********************
415! * OUTPUT variables *
416! ********************
417    integer, intent(in) :: ngeom
418    type(geo_plane_gauss), intent(out) :: geom(ngeom)
419    real(dp), intent(out) :: params(ngeom)
420
421! ********************
422! * LOCAL variables  *
423! ********************
424    type(block_fdf)            :: bfdf
425    type(parsed_line), pointer :: pline
426    integer :: t, ip
427    real(dp) :: v
428
429    ! if none found, simply return
430    if ( ngeom <= 0 ) return
431
432    ! Count the number of planes
433    call fgeo_count(bName,GEOM_PLANE_GAUSS,t)
434
435    if ( t /= ngeom ) &
436         call die('Could not find any Gaussian planes')
437
438    ! Do the processing
439    if ( .not. fdf_block(bName,bfdf) ) &
440         call die('Could not find the block again...?')
441
442    ip = 0
443    count_geom: do while ( ip < ngeom )
444
445       ! First loop until we have the geometry
446       call fgeo_next(bfdf,t,v,unit=par_unit)
447
448       ! If it is not the correct geometry, cycle...
449       if ( t /= GEOM_PLANE_GAUSS ) cycle
450
451       ! Counter for geometry
452       ip = ip + 1
453
454       ! The parameter for the geometry
455       params(ip) = v
456
457       ! Step to gauss
458       if ( .not. fdf_bline(bfdf,pline) ) &
459            call die('Could not step the gauss mark')
460
461       ! We need to read in the \sigma and cut_off
462       call fgeo_read_vals(pline,geom(ip)%c(1:2),units=.true.,unit_offset=1)
463       v = geom(ip)%c(1)
464       geom(ip)%inv_2var = 1._dp/(2._dp*v**2)
465       geom(ip)%cut_off  = geom(ip)%c(2)
466
467       ! An infinite plane is defined by any point in the plane
468       if ( .not. fdf_bline(bfdf,pline) ) &
469            call die('Could not read the point for the &
470            &infinite plane of a plane geometry object')
471       call fgeo_read_vals(pline,geom(ip)%c,units=.true.)
472
473       ! A plane is defined by the normal vector
474       if ( .not. fdf_bline(bfdf,pline) ) &
475            call die('Could not read the first vector &
476            &of a plane geometry object')
477       call fgeo_read_vals(pline,geom(ip)%n,units=.false.)
478
479    end do count_geom
480
481  end subroutine fgeo_read_plane_gauss
482
483  subroutine fgeo_read_plane_exp(bName, ngeom,geom,params,par_unit)
484
485    use intrinsic_missing, only : VNORM
486    use fdf
487
488! ********************
489! * INPUT variables  *
490! ********************
491    character(len=*), intent(in) :: bName
492    character(len=*), intent(in), optional :: par_unit
493
494! ********************
495! * OUTPUT variables *
496! ********************
497    integer, intent(in) :: ngeom
498    type(geo_plane_exp), intent(out) :: geom(ngeom)
499    real(dp), intent(out) :: params(ngeom)
500
501! ********************
502! * LOCAL variables  *
503! ********************
504    type(block_fdf)            :: bfdf
505    type(parsed_line), pointer :: pline
506    integer :: t, ip
507    real(dp) :: v
508
509    ! if none found, simply return
510    if ( ngeom <= 0 ) return
511
512    ! Count the number of planes
513    call fgeo_count(bName,GEOM_PLANE_EXP,t)
514
515    if ( t /= ngeom ) &
516         call die('Could not find any Exponential planes')
517
518    ! Do the processing
519    if ( .not. fdf_block(bName,bfdf) ) &
520         call die('Could not find the block again...?')
521
522    ip = 0
523    count_geom: do while ( ip < ngeom )
524
525       ! First loop until we have the geometry
526       call fgeo_next(bfdf,t,v,unit=par_unit)
527
528       ! If it is not the correct geometry, cycle...
529       if ( t /= GEOM_PLANE_EXP ) cycle
530
531       ! Counter for geometry
532       ip = ip + 1
533
534       ! The parameter for the geometry
535       params(ip) = v
536
537       ! Step to gauss
538       if ( .not. fdf_bline(bfdf,pline) ) &
539            call die('Could not step the gauss mark')
540
541       ! Calculate the exponential decay length
542       ! this ensures that exp(-r/h) == .5 for r == half-length
543       ! Note, we save the inverse as that is faster for computation
544       call fgeo_read_vals(pline,geom(ip)%c(1:2),units=.true.,unit_offset=1)
545       v = geom(ip)%c(1)
546       geom(ip)%h       = log(2._dp) / v
547       v = geom(ip)%c(2)
548       geom(ip)%cut_off = v
549
550       ! An infinite plane is defined by any point in the plane
551       if ( .not. fdf_bline(bfdf,pline) ) &
552            call die('Could not read the point for the &
553            &infinite plane of a plane geometry object')
554       call fgeo_read_vals(pline,geom(ip)%c,units=.true.)
555
556       ! A plane is defined by the normal vector
557       if ( .not. fdf_bline(bfdf,pline) ) &
558            call die('Could not read the first vector &
559            &of a plane geometry object')
560       call fgeo_read_vals(pline,geom(ip)%n,units=.false.)
561
562    end do count_geom
563
564  end subroutine fgeo_read_plane_exp
565
566end module m_geom_plane
567