xy_coordinates.f90 Source File


This file depends on

sourcefile~~xy_coordinates.f90~~EfferentGraph sourcefile~xy_coordinates.f90 xy_coordinates.f90 sourcefile~hex_coordinates.f90 hex_coordinates.f90 sourcefile~xy_coordinates.f90->sourcefile~hex_coordinates.f90 sourcefile~hex_layout.f90 hex_layout.f90 sourcefile~xy_coordinates.f90->sourcefile~hex_layout.f90

Files dependent on this one

sourcefile~~xy_coordinates.f90~~AfferentGraph sourcefile~xy_coordinates.f90 xy_coordinates.f90 sourcefile~honeyplots.f90 honeyplots.f90 sourcefile~honeyplots.f90->sourcefile~xy_coordinates.f90 sourcefile~xy_neighbors.f90 xy_neighbors.f90 sourcefile~honeyplots.f90->sourcefile~xy_neighbors.f90 sourcefile~honeytools.f90 honeytools.f90 sourcefile~honeytools.f90->sourcefile~xy_coordinates.f90 sourcefile~honeytools.f90->sourcefile~xy_neighbors.f90 sourcefile~xy_neighbors.f90->sourcefile~xy_coordinates.f90

Source Code

module xy_coordinates
   !! Defining real space 2D coordinates for honeycomb lattice sites

   use hex_coordinates
   use hex_layout
   use assert_m, only : assert ![pure assertion]

   implicit none
   private

   public :: xy, xy_site, xy_lattice                        ! xy class hyerarchy
   public :: xy_distance, xy_print, xy_ordered_union        ! xy-bound procedures
   public :: hex2center, hex2corner, hex2site, hex2lattice  ! hex-2-xy procedures
   public :: get_sublattice                                 ! xy_lattice utility

   integer, parameter :: N = 6 ! Number of vertices in a hexagon
   real(8), parameter :: PI = 4d0*atan(1d0) ! π to selected kind

   type xy
      !! Base type for 2D points
      real(8) :: x
      real(8) :: y
   contains
      generic :: operator(==) => eq_xy
      generic :: operator(/=) => neq_xy
      procedure, private :: eq_xy
      procedure, private :: neq_xy
   end type

   type, extends(xy) :: xy_site
      !! A 2D point extension for inequivalent sites
      character(1) :: label !! A or B (sublattice)
      integer      :: key=0 !! for lattice lookup
   end type

   type xy_lattice
      !! Wrapper type for storing dynamically
      !! sized collections of lattice sites
      type(xy_site),allocatable  :: site(:)
      integer                    :: size
   contains
      generic :: operator(==) => eq_lattice
      generic :: operator(/=) => neq_lattice
      procedure, private :: eq_lattice
      procedure, private :: neq_lattice
      procedure :: push_back
   end type

   interface xy_print
      procedure :: xy_class_print
      procedure :: xy_lattice_print
   end interface

contains

   ! PUBLIC API [private at bottom]

   pure elemental function xy_distance(A,B) result(d)
      !! Polymorphic euclidean distance for xy class
      class(xy),intent(in)    :: A,B
      type(xy)                :: C
      real(8)                 :: d
      C % x = A % x - B % x
      C % y = A % y - B % y
      d = xy_norm(C)
   end function

   pure elemental function hex2site(layout,H,label) result(site)
      !! Convert hex coordinates to real 2D lattice sites
      !! [returning the xy coordinates for the unique unit-cell,
      !!  sites "A" and "B": this of course does not account for
      !!  border effects, so would be suitable only for simple,
      !!  periodized, systems, which for now are out of scope.]
      !! Actual real-space layout has to be specified by passing
      !! a (scalar) unit_cell object.
      type(unit_cell),intent(in) :: layout
      type(hex),intent(in)       :: H
      character(1),intent(in)    :: label  !! A or B
      type(xy)                   :: center
      type(xy)                   :: offset
      type(xy_site)              :: site
      center = hex2center(layout,H)
      select case(label)
       case("A")
         offset = ith_corner_offset(layout,i=1)
       case("B")
         offset = ith_corner_offset(layout,i=2)
       case default
         error stop "Honeycomb sites must have 'A' or 'B' label"
      end select
      site%x = center%x + offset%x
      site%y = center%y + offset%y
   end function

   pure elemental function hex2corner(layout,H) result(corner)
      !! Convert hex coordinates to real 2D space
      !! [returning the xy coordinates for the hexagon corners,
      !!  as appropriatiely wrapped in the "xy_lattice" type]
      !! Actual real-space layout has to be specified by passing
      !! a (scalar) unit_cell object.
      type(unit_cell),intent(in) :: layout
      type(hex),intent(in)       :: H
      type(xy)                   :: center
      type(xy)                   :: offset
      type(xy_lattice)           :: corner
      integer                    :: i
      center = hex2center(layout,H)
      allocate(corner%site(N))
      corner%size = N
      do i = 1,N
         offset = ith_corner_offset(layout,i)
         corner%site(i)%x = center%x + offset%x
         corner%site(i)%y = center%y + offset%y
         if(modulo(i,2)==1)then
            corner%site(i)%label = "A"
         else
            corner%site(i)%label = "B"
         endif
         corner%site(i)%key = i
      enddo
   end function

   pure elemental function hex2center(layout,H) result(center)
      !! Convert hex coordinates to real 2D space
      !! [returning the xy coordinates of the hexagon center]
      !! Actual real-space layout has to be specified by passing
      !! a (scalar) unit_cell object.
      type(unit_cell),intent(in) :: layout
      type(hex),intent(in)       :: H
      type(xy)                   :: center
      type(hex_orientation)      :: basis
      ! Project [q,r] along unit-cell basis to get [x,y]
      basis = layout%orientation
      center%x = basis%uq(1) * H%q + basis%ur(1) * H%r ! mixed math ok
      center%y = basis%uq(2) * H%q + basis%ur(2) * H%r ! mixed math ok
      ! Rescale and recenter according to unit-cell data
      center%x = center%x * layout%size + layout%origin(1)
      center%y = center%y * layout%size + layout%origin(2)
      ! Equivalent to:
      !        ⎡x⎤   ⎡uq(1)  ur(1)⎤   ⎡q⎤
      !        ⎥ ⎥ = ⎥            ⎥ × ⎥ ⎥ × size + origin
      !        ⎣y⎦   ⎣uq(2)  ur(2)⎦   ⎣r⎦
   end function

   pure function hex2lattice(layout,hexagons) result(lattice)
      !! Generate a type(xy_lattice) object from any given hex
      !! array, provided a suitable layout (unit-cell)
      type(unit_cell),intent(in) :: layout
      type(hex),intent(in)       :: hexagons(:)
      type(xy_lattice)           :: corners(size(hexagons))
      type(xy_lattice)           :: lattice
      integer                    :: i
      corners = hex2corner(layout,hexagons)
      if(size(corners)==1)then
         lattice = corners(1)
         return
      endif
      lattice = xy_ordered_union(corners(1),corners(2))
      do i = 3,size(hexagons)
         lattice = xy_ordered_union(lattice,corners(i))
      enddo
   end function

   pure elemental function get_sublattice(lattice,label) result(sublattice)
      !! Extract sublattice, given a lattice and a label ("A" or "B")
      type(xy_lattice),intent(in)   :: lattice
      character(1),intent(in)       :: label  !! A or B
      type(xy_lattice)              :: sublattice
      associate (sites => lattice%site)
         sublattice%site = pack(sites,sites%label==label)
      end associate
   end function

   pure function xy_ordered_union(A,B) result(C)
      !! An ordered-keys set union for xy_lattices.
      !! It compares the lattice sites, looking at
      !! their x and y coordinates only: equal x,y
      !! entries are inserted in C just once, so to
      !! make it a set. A and B must be sets too.
      !! The sublattice labels are preserved in the
      !! process, assuming that two sites with the
      !! same x,y pertain to the same sublattice.
      !! For A the keys are assumed to be 1:size(A),
      !! and preserved as such (with an assertion).
      !! The keys of B are instead discarded, so to
      !! be replaced by size(A):size(C), which thus
      !! amounts to have result C uniquely indexed
      !! as 1, 2, ... , size(A) + size(B).
      !! This allows building consistently indexed
      !! matrices to act on the lattice array, such
      !! as real-space tight-binding hamiltonians.
      !! The keys would then be used to index other
      !! real space quantities, such as LDOS, Chern
      !! marker, local magnetization, and so on.
      type(xy_lattice),intent(in)   :: A
      type(xy_lattice),intent(in)   :: B
      type(xy_lattice)              :: C
      integer                       :: i,j
      C = A !copy all data of A into C
      do j = 1,size(A%site)
         if(C%site(j)%key /= j)then
            C%site(j)%key  = j
         endif
      enddo
      do i = 1,size(B%site)
         if(all(B%site(i) /= A%site))then
            call C%push_back(B%site(i))
            j = size(C%site)
            C%site(j)%key=j
         endif
      enddo
      ! What if we allocate to (size(A)+size(B))
      ! memory to C, fill in the unique elements
      ! and then just truncate to the correct size
      ! by just calling pack(C,C/=0)? Assuming of
      ! course that C has been initialized to 0d0.
      ! Would it perform better than dynamically
      ! growing the array at each insertion? 0ˆ0-,
   end function

   ! THESE ARE PRIVATE NAMES

   pure function ith_corner_offset(layout,i) result(offset)
      !! Compute the i-th offset vector connecting the hexagon center
      !! to its corners, returning a (scalar) xy coordinate
      type(unit_cell),intent(in) :: layout
      integer,intent(in)         :: i
      type(xy)                   :: offset
      real(8)                    :: angle
      angle = 2d0*PI/N * (layout%orientation%angle + i) ! mixed math ok
      offset = xy(x=layout%size*cos(angle), y=layout%size*sin(angle))
   end function

   pure elemental function eq_xy(A,B) result(isequal)
      !! tolerance equality for xy class
      !! [absolute tol hard-coded to 1d-12]
      !! -> probably to improve...
      class(xy),intent(in) :: A,B
      logical              :: isequal
      isequal = abs(A%x - B%x) < 1d-12
      isequal = abs(A%y - B%y) < 1d-12 .and. isequal
   end function

   pure elemental function neq_xy(A,B) result(notequal)
      !! tolerance inequality for xy class
      !! [absolute tol hard-coded to 1d-12]
      !! -> probably to improve...
      class(xy),intent(in) :: A,B
      logical              :: notequal
      notequal = .not.(eq_xy(A,B))
   end function

   pure elemental function eq_lattice(A,B) result(isequal)
      !! polymorphic equality overload for xy_lattice type
      class(xy_lattice),intent(in)  :: A,B
      logical                       :: isequal
      isequal = all(eq_xy(A%site,B%site))
   end function

   pure elemental function neq_lattice(A,B) result(notequal)
      !! polymorphic inequality overload for xy_lattice type
      class(xy_lattice),intent(in)  :: A,B
      logical                       :: notequal
      notequal = .not.(eq_lattice(A,B))
   end function

   pure elemental function xy_norm(A) result(r)
      !! polymorphic euclidean norm for xy class
      class(xy),intent(in) :: A
      real(8)              :: r
      r = sqrt(A%x**2 + A%y**2)
   end function

   pure subroutine push_back(vec,val)
      !! Poor man implementation of a dynamic
      !! array, a là std::vector (but without
      !! preallocation and smart doubling...)
      class(xy_lattice),intent(inout)  :: vec
      type(xy_site),intent(in)         :: val
      type(xy_site),allocatable        :: tmp(:)
      integer                          :: len
      if (allocated(vec%site)) then
         len = size(vec%site)
         allocate(tmp(len+1))
         tmp(:len) = vec%site
         call move_alloc(tmp,vec%site)
         len = len + 1
      else
         len = 1
         allocate(vec%site(len))
      end if
      !PUSH val at the BACK
      vec%site(len) = val
      !Increade formal size
      vec%size = len
   end subroutine push_back

   impure elemental subroutine xy_class_print(S,unit,quiet)
      !! Pretty print of xy coordinates in static arrays
      class(xy),intent(in)         :: S
      integer,intent(in),optional  :: unit  !! default = $stdout
      logical,intent(in),optional  :: quiet !! default = .false.
      integer                      :: stdunit
      logical                      :: verbose
      if(present(quiet))then
         verbose = .not.quiet
      else
         verbose = .true.
      endif
      if(present(unit))then
         stdunit = unit
      else
         stdunit = 6 ! stdout
      endif
      if(verbose)then
         select type(S)
          type is(xy_site)
            write(stdunit,*) "site #", S%key, "[x,y]: ", S%x, S%y
          class default
            write(stdunit,*) "real-space coordinates [x,y]: ", S%x, S%y
         end select
      else
         write(stdunit,*) S%x, S%y
      endif
   end subroutine

   impure elemental subroutine xy_lattice_print(S,unit,quiet)
      !! Pretty print of xy coordinates in dynamic arrays
      type(xy_lattice),intent(in)  :: S
      integer,intent(in),optional  :: unit  !! default = $stdout
      logical,intent(in),optional  :: quiet !! default = .false.
      type(xy_site)                :: vector(size(S%site))
      integer                      :: stdunit
      logical                      :: verbose
      integer                      :: i
      if(present(quiet))then
         verbose = .not.quiet
      else
         verbose = .true.
      endif
      if(present(unit))then
         stdunit = unit
      else
         stdunit = 6 ! stdout
      endif
      vector = S%site
      do i = 1,size(vector)
         call xy_print(vector(i),unit=stdunit,quiet=.not.verbose)
      enddo
   end subroutine

end module xy_coordinates