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!  SQUARE
15
16module m_geom_square
17
18  use m_geom_aux
19  use m_geom_plane, only : geo_plane_delta
20  use m_geom_plane, only : geo_plane_gauss
21  use m_geom_plane, only : geo_plane_exp
22  use m_geom_plane, only : voxel_in_plane
23  use m_geom_plane, only : voxel_val_plane
24  use m_geom_plane, only : correct_plane
25
26  implicit none
27
28  private
29
30  ! Types
31  public :: geo_square_delta
32  public :: geo_square_gauss
33  public :: geo_square_exp
34  ! Interfaces
35  public :: fgeo_read_square
36  public :: correct_square
37  public :: voxel_in_square
38  public :: voxel_val_square
39
40  ! Explicit routines
41  public :: fgeo_read_square_delta
42  public :: fgeo_read_square_gauss
43  public :: fgeo_read_square_exp
44  public :: correct_square_delta
45  public :: correct_square_gauss
46  public :: correct_square_exp
47  public :: voxel_in_square_delta
48  public :: voxel_in_square_gauss
49  public :: voxel_in_square_exp
50  public :: voxel_val_square_delta
51  public :: voxel_val_square_gauss
52  public :: voxel_val_square_exp
53
54  type geo_square_delta
55     ! Describing a bounded plane which can be described by the infinite plane
56     ! One can retrieve the normal vector of the plane spanned by: v1 and v2 by
57     !    n = (v1 - o) \cross (v2 - o)
58     ! 'o' describes the origo of the
59     ! plane spanned by the vectors, v1 and v2.
60     ! With n we describe the infinite plane as in the previous case
61     ! and subsequently bound it by:
62     !    ---------------------     --------
63     !    | v1_1 | v2_1 | n_1 |     | x1_1 |
64     !    | v1_2 | v2_2 | n_2 | x = | x1_2 |
65     !    | v1_3 | v2_3 | n_3 |     | x1_3 |
66     !    ---------------------     --------
67     ! and x = (a,b,c)^T.
68     ! Now to be in the bounded plane, a and b must both satisfy:
69     !    \{a,b\} \in [0;1], we check whether c is small enough via
70     ! the inifinite plane method.
71     sequence
72     ! The coordinate from which the plane is spanned
73     ! by the two vectors v1 and v2 is assigned to p%c
74     ! The vectors which span the plane
75     real(dp) :: v1(3), v2(3)
76     ! Infinite plane
77     type(geo_plane_delta) :: p
78  end type geo_square_delta
79
80  type geo_square_gauss
81     sequence
82     real(dp) :: v1(3), v2(3)
83     type(geo_plane_gauss) :: p
84  end type geo_square_gauss
85
86  type geo_square_exp
87     sequence
88     real(dp) :: v1(3), v2(3)
89     type(geo_plane_exp) :: p
90  end type geo_square_exp
91
92  interface fgeo_read_square
93     module procedure fgeo_read_square_delta
94     module procedure fgeo_read_square_gauss
95     module procedure fgeo_read_square_exp
96  end interface
97
98  interface correct_square
99     module procedure correct_square_delta
100     module procedure correct_square_gauss
101     module procedure correct_square_exp
102  end interface
103
104  interface voxel_in_square
105     module procedure voxel_in_square_delta
106     module procedure voxel_in_square_gauss
107     module procedure voxel_in_square_exp
108  end interface
109
110  interface voxel_val_square
111     module procedure voxel_val_square_delta
112     module procedure voxel_val_square_gauss
113     module procedure voxel_val_square_exp
114  end interface
115
116contains
117
118  ! This subroutine returns .true. in has if the box spanned in
119  !    lr,lr+dx,lr+dx+dy,..., has any points inside the plane.
120  !  - lr is the "lower-left" of the box.
121  !  - d is the extension of the box
122  function voxel_in_square_delta(plane,ll,d) result(has)
123    type(geo_square_delta), intent(in) :: plane
124    real(dp), intent(in) :: ll(3), d(3)
125    logical :: has
126
127    ! We retrieve whether the box passes through the
128    ! infinite plane spanned by the local plane
129    has = voxel_in_plane(plane%p,ll,d)
130
131    ! If it does not intersect, we can safely return
132    if ( .not. has ) return
133
134    has = P_voxel_square(ll,d,plane%v1,plane%v2,plane%p%n,plane%p%c)
135
136  end function voxel_in_square_delta
137
138  function voxel_in_square_gauss(plane,ll,d) result(has)
139    type(geo_square_gauss), intent(in) :: plane
140    real(dp), intent(in) :: ll(3), d(3)
141    logical :: has
142
143    has = voxel_in_plane(plane%p,ll,d)
144
145    if ( .not. has ) return
146
147    has = P_voxel_square(ll,d,plane%v1,plane%v2,plane%p%n,plane%p%c)
148
149  end function voxel_in_square_gauss
150
151  function voxel_in_square_exp(plane,ll,d) result(has)
152    type(geo_square_exp), intent(in) :: plane
153    real(dp), intent(in) :: ll(3), d(3)
154    logical :: has
155
156    has = voxel_in_plane(plane%p,ll,d)
157
158    if ( .not. has ) return
159
160    has = P_voxel_square(ll,d,plane%v1,plane%v2,plane%p%n,plane%p%c)
161
162  end function voxel_in_square_exp
163
164
165  function P_voxel_square(ll,d,v1,v2,n,o) result(has)
166    real(dp), intent(in) :: ll(3), d(3), v1(3), v2(3), n(3), o(3)
167    logical :: has
168    integer :: ipiv(4)
169    real(dp) :: sys(3,4)
170
171    ! We need to check whether the bounds of the voxel
172    ! lies within the plane...
173    ! Thus we solve the linear equation:
174    ! Ax=B
175    ! where A = [v1,v2,n] and B = ll+.5*d
176    sys(:,1) = v1
177    sys(:,2) = v2
178    ! We do need the normal vector, otherwise
179    ! we would not be able to retrieve the
180    ! solution to the system. (i.e. we do not
181    ! know if the center cuts the plane)
182    sys(:,3) = n
183    ! Move the point down to origo
184    sys(:,4) = ll + .5_dp * d - o
185    call dgesv(3,1,sys(1,1),3,ipiv,sys(1,4),3,ipiv(4))
186    if ( ipiv(4) /= 0 ) then
187       write(*,*) ' LAPACK-error: Could not solve the linear &
188            &equation for the voxel'
189       ! This should never happen as any point in space
190       ! should be reacheable by the vectors
191       ! (we however, need that v1 and v2 are linearly
192       ! independent)
193    end if
194
195    ! We already know that it lies in the plane
196    ! so now we need to ensure that it lies within
197    ! the two spanning vectors plane...
198    has= 0._dp <= sys(1,4) .and. sys(1,4) <= 1.0_dp .and. &
199         0._dp <= sys(2,4) .and. sys(2,4) <= 1.0_dp
200
201  end function P_voxel_square
202
203  function voxel_val_square_delta(plane,ll,d) result(vol)
204    type(geo_square_delta), intent(in) :: plane
205    real(dp), intent(in) :: ll(3), d(3)
206    real(dp) :: vol
207    vol = 1._dp
208  end function voxel_val_square_delta
209
210  ! This subroutine returns the value of the charge
211  ! that is attributed from the gaussian function about the
212  ! finite plane
213  !  - ll is the "lower-left" of the box.
214  !  - d is the extension of the box
215  function voxel_val_square_gauss(plane,ll,d) result(vol)
216    type(geo_square_gauss), intent(in) :: plane
217    real(dp), intent(in) :: ll(3), d(3)
218    real(dp) :: vol
219
220    ! It has already been checked that it exists
221    vol = voxel_val_plane(plane%p,ll,d)
222
223  end function voxel_val_square_gauss
224
225  function voxel_val_square_exp(plane,ll,d) result(vol)
226    type(geo_square_exp), intent(in) :: plane
227    real(dp), intent(in) :: ll(3), d(3)
228    real(dp) :: vol
229
230    vol = voxel_val_plane(plane%p,ll,d)
231
232  end function voxel_val_square_exp
233
234
235  ! Subroutine for initializing the planes by transforming them
236  ! to the corresponding cell
237  subroutine correct_square_delta(cell,plane)
238    real(dp), intent(in) :: cell(3,3)
239    type(geo_square_delta), intent(inout) :: plane
240    real(dp), parameter :: EPS = 1e-5
241    real(dp) :: fac
242    logical :: inde
243    integer :: i
244
245    ! The simple test is that if any-one coordinate
246    ! is zero, and the other is not, then
247    ! they are linearly independent
248    inde = .false.
249    do i = 1 , 3
250       if ( abs(plane%v1(i)) < EPS .and. &
251            abs(plane%v2(i)) > EPS ) then
252          inde = .true.
253       else if ( abs(plane%v2(i)) < EPS .and. &
254            abs(plane%v1(i)) > EPS ) then
255          inde = .true.
256       else if ( abs(plane%v1(i)) > EPS .and. &
257            abs(plane%v2(i)) > EPS ) then
258          ! We create the factor here (as we must not divide by 0)
259          fac = plane%v1(i) / plane%v2(i)
260       end if
261    end do
262
263    if ( .not. inde ) then
264       inde = .not. ( &
265            count( abs(fac * plane%v2 - plane%v1) <= EPS ) == 3 )
266    end if
267
268
269    ! Kill if the vectors are linearly dependent...
270    if ( .not. inde ) then
271       call die('The geometric finite plane has linearly &
272            &dependent spanning vectors. This is not allowed.')
273    end if
274
275    ! Correct the infinite plane that the thing lies in
276    call correct_plane(cell,plane%p)
277
278  end subroutine correct_square_delta
279
280  ! Subroutine for initializing the planes by transforming them
281  ! to the corresponding cell
282  subroutine correct_square_gauss(cell,plane)
283    real(dp), intent(in) :: cell(3,3)
284    type(geo_square_gauss), intent(inout) :: plane
285    real(dp), parameter :: EPS = 1e-5
286    type(geo_square_delta) :: t_plane
287    type(geo_plane_delta) :: ti_plane
288
289    t_plane%v1 = plane%v1
290    t_plane%v2 = plane%v2
291    ti_plane%n = plane%p%n
292    ti_plane%c = plane%p%c
293    t_plane%p  = ti_plane
294    call correct_square_delta(cell,t_plane)
295    plane%p%n   = t_plane%p%n
296    plane%p%d_A = t_plane%p%d + plane%p%cut_off
297    plane%p%d_B = t_plane%p%d - plane%p%cut_off
298
299  end subroutine correct_square_gauss
300
301  ! Subroutine for initializing the planes by transforming them
302  ! to the corresponding cell
303  subroutine correct_square_exp(cell,plane)
304    real(dp), intent(in) :: cell(3,3)
305    type(geo_square_exp), intent(inout) :: plane
306    real(dp), parameter :: EPS = 1e-5
307    type(geo_square_delta) :: t_plane
308    type(geo_plane_delta) :: ti_plane
309
310    t_plane%v1 = plane%v1
311    t_plane%v2 = plane%v2
312    ti_plane%n = plane%p%n
313    ti_plane%c = plane%p%c
314    t_plane%p  = ti_plane
315    call correct_square_delta(cell,t_plane)
316    plane%p%n   = t_plane%p%n
317    plane%p%d_A = t_plane%p%d + plane%p%cut_off
318    plane%p%d_B = t_plane%p%d - plane%p%cut_off
319
320  end subroutine correct_square_exp
321
322
323  ! Reading in information from block about the square-delta object
324  subroutine fgeo_read_square_delta(bName, ngeom,geom,params,par_unit)
325
326    use intrinsic_missing, only : VNORM
327    use fdf
328
329! ********************
330! * INPUT variables  *
331! ********************
332    character(len=*), intent(in) :: bName
333    integer, intent(in) :: ngeom
334    character(len=*), intent(in), optional :: par_unit
335
336! ********************
337! * OUTPUT variables *
338! ********************
339    type(geo_square_delta), intent(out) :: geom(ngeom)
340    real(dp),  intent(out) :: params(ngeom)
341
342! ********************
343! * LOCAL variables  *
344! ********************
345    type(block_fdf)            :: bfdf
346    type(parsed_line), pointer :: pline
347    integer :: t, ip
348    real(dp) :: v
349
350    ! if none found, simply return
351    if ( ngeom <= 0 ) return
352
353    ! Count the number of planes
354    call fgeo_count(bName,GEOM_SQUARE_DELTA,t)
355
356    if ( t /= ngeom ) &
357         call die('Could not find any delta squares')
358
359    ! Do the processing
360    if ( .not. fdf_block(bName,bfdf) ) &
361         call die('Could not find the block again...?')
362
363    ip = 0
364    count_geom: do while ( ip < ngeom )
365
366       ! First loop until we have the geometry
367       call fgeo_next(bfdf,t,v,unit=par_unit)
368
369       ! If it is not the correct geometry, cycle...
370       if ( t /= GEOM_SQUARE_DELTA ) cycle
371
372       ! Counter for geometry
373       ip = ip + 1
374
375       ! The parameter for the geometry
376       params(ip) = v
377
378       ! Step delta
379       if ( .not. fdf_bline(bfdf,pline) ) &
380            call die('Could not step the delta mark')
381
382      ! A plane is defined by the lower-left corner
383       if ( .not. fdf_bline(bfdf,pline) ) &
384            call die('Could not read the lower-left corner &
385            &of a plane geometry object')
386       call fgeo_read_vals(pline,geom(ip)%p%c,units=.true.)
387
388       ! A plane is defined by two spanning vectors
389       if ( .not. fdf_bline(bfdf,pline) ) &
390            call die('Could not read the first vector &
391            &of a plane geometry object')
392       call fgeo_read_vals(pline,geom(ip)%v1,units=.true.)
393
394       if ( .not. fdf_bline(bfdf,pline) ) &
395            call die('Could not read the second vector &
396            &of a plane geometry object')
397       call fgeo_read_vals(pline,geom(ip)%v2,units=.true.)
398
399       ! We need to calculate the equivalent
400       ! infinite plane for this finite plane
401       geom(ip)%p%n(1) = &
402            geom(ip)%v1(2)*geom(ip)%v2(3) - &
403            geom(ip)%v1(3)*geom(ip)%v2(2)
404       geom(ip)%p%n(2) = &
405            geom(ip)%v1(3)*geom(ip)%v2(1) - &
406            geom(ip)%v1(1)*geom(ip)%v2(3)
407       geom(ip)%p%n(3) = &
408            geom(ip)%v1(1)*geom(ip)%v2(2) - &
409            geom(ip)%v1(2)*geom(ip)%v2(1)
410       ! Normalize the normal vector
411       geom(ip)%p%n = geom(ip)%p%n / VNORM(geom(ip)%p%n)
412
413    end do count_geom
414
415  end subroutine fgeo_read_square_delta
416
417
418  subroutine fgeo_read_square_gauss(bName, ngeom,geom,params,par_unit)
419
420    use intrinsic_missing, only : VNORM
421    use fdf
422
423! ********************
424! * INPUT variables  *
425! ********************
426    character(len=*), intent(in) :: bName
427    integer, intent(in) :: ngeom
428    character(len=*), intent(in), optional :: par_unit
429
430! ********************
431! * OUTPUT variables *
432! ********************
433    type(geo_square_gauss), intent(out) :: geom(ngeom)
434    real(dp), intent(out) :: params(ngeom)
435
436! ********************
437! * LOCAL variables  *
438! ********************
439    type(block_fdf)            :: bfdf
440    type(parsed_line), pointer :: pline
441    integer  :: t, ip
442    real(dp) :: v
443
444    ! if none found, simply return
445    if ( ngeom <= 0 ) return
446
447    ! Count the number of planes
448    call fgeo_count(bName,GEOM_SQUARE_GAUSS,t)
449
450    if ( t /= ngeom ) &
451         call die('Could not find any Gaussian squares')
452
453    ! Do the processing
454    if ( .not. fdf_block(bName,bfdf) ) &
455         call die('Could not find the block again...?')
456
457    ip = 0
458    count_geom: do while ( ip < ngeom )
459
460       ! First loop until we have the geometry
461       call fgeo_next(bfdf,t,v,unit=par_unit)
462
463       ! If it is not the correct geometry, cycle...
464       if ( t /= GEOM_SQUARE_GAUSS ) cycle
465
466       ! Counter for geometry
467       ip = ip + 1
468
469       ! The parameter for the geometry
470       params(ip) = v
471
472       ! Step to gauss
473       if ( .not. fdf_bline(bfdf,pline) ) &
474            call die('Could not step the gauss mark')
475
476       ! We need to read in the \sigma and cut_off
477       call fgeo_read_vals(pline,geom(ip)%p%c(1:2),units=.true.,unit_offset=1)
478       v = geom(ip)%p%c(1)
479       geom(ip)%p%inv_2var = 1._dp/(2._dp*v**2)
480       geom(ip)%p%cut_off  = geom(ip)%p%c(2)
481
482      ! A plane is defined by the lower-left corner
483       if ( .not. fdf_bline(bfdf,pline) ) &
484            call die('Could not read the lower-left corner &
485            &of a plane geometry object')
486       call fgeo_read_vals(pline,geom(ip)%p%c,units=.true.)
487
488       ! A plane is defined by two spanning vectors
489       if ( .not. fdf_bline(bfdf,pline) ) &
490            call die('Could not read the first vector &
491            &of a plane geometry object')
492       call fgeo_read_vals(pline,geom(ip)%v1,units=.true.)
493
494       if ( .not. fdf_bline(bfdf,pline) ) &
495            call die('Could not read the second vector &
496            &of a plane geometry object')
497       call fgeo_read_vals(pline,geom(ip)%v2,units=.true.)
498
499       ! We need to calculate the equivalent
500       ! infinite plane for this finite plane
501       geom(ip)%p%n(1) = &
502            geom(ip)%v1(2)*geom(ip)%v2(3) - &
503            geom(ip)%v1(3)*geom(ip)%v2(2)
504       geom(ip)%p%n(2) = &
505            geom(ip)%v1(3)*geom(ip)%v2(1) - &
506            geom(ip)%v1(1)*geom(ip)%v2(3)
507       geom(ip)%p%n(3) = &
508            geom(ip)%v1(1)*geom(ip)%v2(2) - &
509            geom(ip)%v1(2)*geom(ip)%v2(1)
510       ! Normalize the normal vector
511       geom(ip)%p%n = geom(ip)%p%n / VNORM(geom(ip)%p%n)
512
513    end do count_geom
514
515  end subroutine fgeo_read_square_gauss
516
517  subroutine fgeo_read_square_exp(bName, ngeom,geom,params,par_unit)
518
519    use intrinsic_missing, only : VNORM
520    use fdf
521
522! ********************
523! * INPUT variables  *
524! ********************
525    character(len=*), intent(in) :: bName
526    integer, intent(in) :: ngeom
527    character(len=*), intent(in), optional :: par_unit
528
529! ********************
530! * OUTPUT variables *
531! ********************
532    type(geo_square_exp), intent(out) :: geom(ngeom)
533    real(dp), intent(out) :: params(ngeom)
534
535! ********************
536! * LOCAL variables  *
537! ********************
538    type(block_fdf)            :: bfdf
539    type(parsed_line), pointer :: pline
540    integer  :: t, ip
541    real(dp) :: v
542
543    ! if none found, simply return
544    if ( ngeom <= 0 ) return
545
546    ! Count the number of planes
547    call fgeo_count(bName,GEOM_SQUARE_EXP,t)
548
549    if ( t /= ngeom ) &
550         call die('Could not find any Gaussian squares')
551
552    ! Do the processing
553    if ( .not. fdf_block(bName,bfdf) ) &
554         call die('Could not find the block again...?')
555
556    ip = 0
557    count_geom: do while ( ip < ngeom )
558
559       ! First loop until we have the geometry
560       call fgeo_next(bfdf,t,v,unit=par_unit)
561
562       ! If it is not the correct geometry, cycle...
563       if ( t /= GEOM_SQUARE_EXP ) cycle
564
565       ! Counter for geometry
566       ip = ip + 1
567
568       ! The parameter for the geometry
569       params(ip) = v
570
571       ! Step to gauss
572       if ( .not. fdf_bline(bfdf,pline) ) &
573            call die('Could not step the gauss mark')
574
575       ! We need to read in the half-length and cut_off
576       call fgeo_read_vals(pline,geom(ip)%p%c(1:2),units=.true.,unit_offset=1)
577       v = geom(ip)%p%c(1)
578       geom(ip)%p%h        = log(2._dp) / v
579       geom(ip)%p%cut_off  = geom(ip)%p%c(2)
580
581      ! A plane is defined by the lower-left corner
582       if ( .not. fdf_bline(bfdf,pline) ) &
583            call die('Could not read the lower-left corner &
584            &of a plane geometry object')
585       call fgeo_read_vals(pline,geom(ip)%p%c,units=.true.)
586
587       ! A plane is defined by two spanning vectors
588       if ( .not. fdf_bline(bfdf,pline) ) &
589            call die('Could not read the first vector &
590            &of a plane geometry object')
591       call fgeo_read_vals(pline,geom(ip)%v1,units=.true.)
592
593       if ( .not. fdf_bline(bfdf,pline) ) &
594            call die('Could not read the second vector &
595            &of a plane geometry object')
596       call fgeo_read_vals(pline,geom(ip)%v2,units=.true.)
597
598       ! We need to calculate the equivalent
599       ! infinite plane for this finite plane
600       geom(ip)%p%n(1) = &
601            geom(ip)%v1(2)*geom(ip)%v2(3) - &
602            geom(ip)%v1(3)*geom(ip)%v2(2)
603       geom(ip)%p%n(2) = &
604            geom(ip)%v1(3)*geom(ip)%v2(1) - &
605            geom(ip)%v1(1)*geom(ip)%v2(3)
606       geom(ip)%p%n(3) = &
607            geom(ip)%v1(1)*geom(ip)%v2(2) - &
608            geom(ip)%v1(2)*geom(ip)%v2(1)
609       ! Normalize the normal vector
610       geom(ip)%p%n = geom(ip)%p%n / VNORM(geom(ip)%p%n)
611
612    end do count_geom
613
614  end subroutine fgeo_read_square_exp
615
616end module m_geom_square
617