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