24 use,
intrinsic :: iso_fortran_env
44 type(space_t),
private :: space
45 real(real64),
allocatable :: rlattice_primitive(:, :)
46 real(real64),
allocatable :: rlattice (:, :)
47 real(real64),
allocatable :: klattice_primitive(:, :)
48 real(real64),
allocatable :: klattice (:, :)
49 real(real64) :: alpha, beta, gamma
50 real(real64) :: rcell_volume
51 logical :: nonorthogonal = .false.
59 generic ::
assignment(=) => copy
80 integer,
public :: n_cells = 0
81 integer,
allocatable :: icell(:, :)
82 type(lattice_vectors_t),
pointer :: latt => null()
85 generic ::
assignment(=) => copy
94 real(real64),
parameter :: eps = 100.0_real64 *
m_epsilon
99 type(lattice_vectors_t) function lattice_vectors_constructor_from_rlattice(namespace, space, rlattice) result(latt)
100 type(namespace_t),
intent(in) :: namespace
101 class(space_t),
intent(in) :: space
102 real(real64),
intent(in) :: rlattice(:, :)
104 integer :: idir, idir2
105 real(real64) :: volume_element
107 push_sub(lattice_vectors_constructor_from_rlattice)
111 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
112 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
113 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
114 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
116 latt%rlattice(1:space%dim, 1:space%dim) = rlattice(1:space%dim, 1:space%dim)
117 do idir = 1, space%dim
118 latt%rlattice_primitive(:, idir) = latt%rlattice(:, idir) / norm2(latt%rlattice(:, idir))
121 latt%nonorthogonal = .false.
122 do idir = 1, space%dim
123 do idir2 = 1, space%dim
124 if (idir /= idir2 .and. abs(latt%rlattice_primitive(idir, idir2)) >
m_epsilon)
then
125 latt%nonorthogonal = .
true.
130 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
131 latt%klattice = latt%klattice *
m_two*
m_pi
133 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
135 if (space%dim == 3)
then
144 pop_sub(lattice_vectors_constructor_from_rlattice)
149 type(namespace_t),
intent(in) :: namespace
150 class(space_t),
intent(in) :: space
151 character(len=*),
optional,
intent(in) :: variable_prefix
154 real(real64) :: norm, lparams(space%dim), volume_element
155 integer :: idim, jdim, ncols
156 logical :: has_angles
157 real(real64) :: angles(1:space%dim)
158 character(len=:),
allocatable :: prefix
162 if (
present(variable_prefix))
then
163 prefix = variable_prefix
170 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
171 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
172 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
173 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
175 latt%alpha = 90.0_real64
176 latt%beta = 90.0_real64
177 latt%gamma = 90.0_real64
182 if (space%is_periodic())
then
198 if (parse_block(namespace, prefix//
'LatticeParameters', blk) == 0)
then
199 ncols = parse_block_cols(blk, 0)
200 if (ncols < space%periodic_dim)
then
201 call messages_input_error(namespace, prefix//
'LatticeParameters', &
202 'The number of columns must be at least PeriodicDimensions')
205 call parse_block_float(blk, 0, idim - 1, lparams(idim))
209 do idim = ncols + 1, space%dim
210 lparams(idim) = m_one
214 if (parse_block_n(blk) > 1)
then
215 if (space%dim /= 3)
then
216 call messages_input_error(namespace, prefix//
'LatticeParameters',
'Angles can only be specified when Dimensions = 3')
219 ncols = parse_block_cols(blk, 1)
220 if (ncols /= space%dim)
then
221 call messages_input_error(namespace, prefix//
'LatticeParameters',
'You must specify three angles')
223 do idim = 1, space%dim
224 call parse_block_float(blk, 1, idim - 1, angles(idim))
228 call parse_block_end(blk)
231 call messages_input_error(namespace, prefix//
'LatticeParameters',
'Variable is mandatory for periodic systems')
236 latt%alpha = angles(1)
237 latt%beta = angles(2)
238 latt%gamma = angles(3)
240 if (parse_is_defined(namespace, prefix//
'LatticeVectors'))
then
241 message(1) =
'LatticeParameters with angles is incompatible with LatticeVectors'
242 call messages_print_var_info(prefix//
"LatticeParameters", namespace=namespace)
243 call messages_fatal(1, namespace=namespace)
266 latt%rlattice_primitive(:, :) = diagonal_matrix(space%dim, m_one)
267 latt%nonorthogonal = .false.
269 if (parse_block(namespace, prefix//
'LatticeVectors', blk) == 0)
then
270 do idim = 1, space%dim
271 do jdim = 1, space%dim
272 call parse_block_float(blk, idim - 1, jdim - 1, latt%rlattice_primitive(jdim, idim))
273 if (idim /= jdim .and. abs(latt%rlattice_primitive(jdim, idim)) > m_epsilon)
then
274 latt%nonorthogonal = .
true.
278 call parse_block_end(blk)
286 latt%rlattice_primitive(:, :) = diagonal_matrix(space%dim, m_one)
289 latt%rlattice = m_zero
290 do idim = 1, space%dim
291 norm = norm2(latt%rlattice_primitive(1:space%dim, idim))
292 lparams(idim) = lparams(idim)*norm
293 do jdim = 1, space%dim
294 latt%rlattice_primitive(jdim, idim) = latt%rlattice_primitive(jdim, idim) / norm
295 latt%rlattice(jdim, idim) = latt%rlattice_primitive(jdim, idim) * lparams(idim)
299 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
300 latt%klattice = latt%klattice * m_two*m_pi
302 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
304 if (.not. has_angles .and. space%dim == 3)
then
314 real(real64),
intent(in) :: rlattice_primitive(1:3, 1:3)
315 real(real64),
intent(out) :: alpha
316 real(real64),
intent(out) :: beta
317 real(real64),
intent(out) :: gamma
319 real(real64) :: rlatt(1:3, 1:3)
323 rlatt = matmul(transpose(rlattice_primitive), rlattice_primitive)
324 alpha =
acos(rlatt(2, 3)/
sqrt(rlatt(2, 2)*rlatt(3, 3)))/m_pi*180.0_real64
325 beta =
acos(rlatt(1, 3)/
sqrt(rlatt(1, 1)*rlatt(3, 3)))/m_pi*180.0_real64
326 gamma =
acos(rlatt(1, 2)/
sqrt(rlatt(1, 1)*rlatt(2, 2)))/m_pi*180.0_real64
338 this%space = source%space
339 safe_allocate_source_a(this%rlattice_primitive, source%rlattice_primitive)
340 safe_allocate_source_a(this%rlattice, source%rlattice)
341 safe_allocate_source_a(this%klattice_primitive, source%klattice_primitive)
342 safe_allocate_source_a(this%klattice, source%klattice)
343 this%alpha = source%alpha
344 this%beta = source%beta
345 this%gamma = source%gamma
346 this%rcell_volume = source%rcell_volume
347 this%nonorthogonal = source%nonorthogonal
358 safe_deallocate_a(this%rlattice_primitive)
359 safe_deallocate_a(this%rlattice)
360 safe_deallocate_a(this%klattice_primitive)
361 safe_deallocate_a(this%klattice)
362 this%nonorthogonal = .false.
370 real(real64),
intent(in) :: factor(this%space%dim)
377 do idir = 1, this%space%dim
378 this%rlattice(1:this%space%dim, idir) = this%rlattice(1:this%space%dim, idir)*factor(idir)
382 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
383 this%klattice = this%klattice * m_two*m_pi
392 real(real64),
intent(in) :: rlattice(this%space%dim, this%space%dim)
395 real(real64) :: volume_element, maxrlatt
399 this%rlattice(:, :) = rlattice(:, :)
400 maxrlatt = maxval(abs(this%rlattice))
401 where(abs(this%rlattice) < 1e-12_real64*maxrlatt) this%rlattice = m_zero
404 do idir = 1, this%space%dim
405 this%rlattice_primitive(:, idir) = this%rlattice(:, idir) / norm2(this%rlattice(:, idir))
409 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
410 this%klattice = this%klattice * m_two*m_pi
412 call reciprocal_lattice(this%rlattice_primitive, this%klattice_primitive, volume_element, this%space%dim)
415 if (this%space%dim == 3)
then
430 real(real64),
intent(in) :: xx_cart(this%space%dim)
431 real(real64) :: xx_red(this%space%dim)
433 xx_red = matmul(xx_cart, this%klattice)/(m_two*m_pi)
440 real(real64),
intent(in) :: xx_red(this%space%dim)
441 real(real64) :: xx_cart(this%space%dim)
443 xx_cart = matmul(this%rlattice, xx_red)
450 real(real64),
intent(in) :: xx(this%space%dim)
451 real(real64) :: new_xx(this%space%dim)
455 if (this%space%is_periodic())
then
457 new_xx = this%cart_to_red(xx)
459 do idir = 1, this%space%periodic_dim
461 new_xx(idir) = new_xx(idir) + m_half
464 new_xx(idir) = new_xx(idir) - anint(new_xx(idir))
465 if (new_xx(idir) < -1.0e-6_real64)
then
466 new_xx(idir) = new_xx(idir) + m_one
470 assert(new_xx(idir) >= -1.0e-6_real64)
471 assert(new_xx(idir) < m_one)
474 new_xx(idir) = new_xx(idir) - m_half
478 new_xx = this%red_to_cart(new_xx)
488 type(namespace_t),
intent(in) :: namespace
490 integer :: idir, idir2
494 assert(this%space%is_periodic())
496 call messages_print_with_emphasis(msg=
"Lattice", namespace=namespace)
498 write(message(1),
'(a,3a,a)')
' Lattice Vectors [', trim(units_abbrev(units_out%length)),
']'
499 do idir = 1, this%space%periodic_dim
500 write(message(1+idir),
'(9f12.6)') (units_from_atomic(units_out%length, this%rlattice(idir2, idir)), &
501 idir2 = 1, this%space%dim)
503 call messages_info(1+this%space%periodic_dim, namespace=namespace)
505 select case (this%space%periodic_dim)
507 write(message(1),
'(a)')
' Cell length ='
509 write(message(1),
'(a)')
' Cell area ='
511 write(message(1),
'(a)')
' Cell volume ='
513 write(message(1),
'(a,1x,f18.4,3a,i1.1,a)') &
514 trim(message(1)), units_from_atomic(units_out%length**this%space%periodic_dim, this%rcell_volume), &
515 ' [', trim(units_abbrev(units_out%length**this%space%periodic_dim)),
']'
516 call messages_info(1, namespace=namespace)
517 write(message(1),
'(a,3a,a)')
' Reciprocal-Lattice Vectors [', trim(units_abbrev(units_out%length**(-1))),
']'
518 do idir = 1, this%space%periodic_dim
519 write(message(1+idir),
'(3f12.6)') (units_from_atomic(unit_one / units_out%length, this%klattice(idir2, idir)), &
520 idir2 = 1, this%space%dim)
522 call messages_info(1+this%space%periodic_dim, namespace=namespace)
524 if (this%space%dim == 3)
then
525 write(message(1),
'(a)')
' Cell angles [degree]'
526 write(message(2),
'(a, f8.3)')
' alpha = ', this%alpha
527 write(message(3),
'(a, f8.3)')
' beta = ', this%beta
528 write(message(4),
'(a, f8.3)')
' gamma = ', this%gamma
529 call messages_info(4, namespace=namespace)
532 call messages_print_with_emphasis(namespace=namespace)
540 type(unit_t),
intent(in) :: unit_length
542 integer :: idir1, idir2
546 write(
info,
'(a,a,a)')
'LatticeVectors [', trim(units_abbrev(unit_length)),
'] = '
548 do idir1 = 1, this%space%dim
549 write(
info,
'(a,a)') trim(
info),
'['
550 do idir2 = 1, this%space%dim
551 write(
info,
'(a,1x,f11.6)') trim(
info), units_from_atomic(unit_length, this%rlattice(idir2, idir1))
553 write(
info,
'(a,a)') trim(
info),
']'
554 if (idir1 < this%space%dim)
then
555 write(
info,
'(a,a)') trim(
info),
', '
568 if (this%space%is_periodic())
then
569 length = maxval(norm2(this%rlattice(:,1:this%space%periodic_dim), dim=1))
580 real(real64),
intent(in) :: angles(3)
582 real(real64) :: cosang, a2, aa, cc
583 real(real64),
parameter :: tol_angle = 1.0e-6_real64
589 if (abs(angles(1) - angles(2)) < tol_angle .and. abs(angles(2) - angles(3)) < tol_angle .and. &
590 (abs(angles(1) - 90.0_real64) + abs(angles(2) - 90.0_real64) + abs(angles(3) - 90.0_real64)) > tol_angle)
then
592 cosang =
cos(m_pi*angles(1)/180.0_real64)
593 a2 = m_two/m_three*(m_one - cosang)
595 cc =
sqrt(m_one - a2)
596 this%rlattice_primitive(1, 1) = aa
597 this%rlattice_primitive(2, 1) = m_zero
598 this%rlattice_primitive(3, 1) = cc
599 this%rlattice_primitive(1, 2) = -m_half*aa
600 this%rlattice_primitive(2, 2) = m_half*
sqrt(m_three)*aa
601 this%rlattice_primitive(3, 2) = cc
602 this%rlattice_primitive(1, 3) = -m_half*aa
603 this%rlattice_primitive(2, 3) = -m_half*
sqrt(m_three)*aa
604 this%rlattice_primitive(3, 3) = cc
606 this%rlattice_primitive(1, 1) = m_one
607 this%rlattice_primitive(2, 1) = m_zero
608 this%rlattice_primitive(3, 1) = m_zero
609 this%rlattice_primitive(1, 2) =
cos(m_pi*angles(3)/180.0_real64)
610 this%rlattice_primitive(2, 2) =
sin(m_pi*angles(3)/180.0_real64)
611 this%rlattice_primitive(3, 2) = m_zero
612 this%rlattice_primitive(1, 3) =
cos(m_pi*angles(2)/180.0_real64)
613 this%rlattice_primitive(2, 3) = (
cos(m_pi*angles(1)/180.0_real64) - &
614 this%rlattice_primitive(1, 2)*this%rlattice_primitive(1, 3))/this%rlattice_primitive(2,2)
615 this%rlattice_primitive(3, 3) =
sqrt(m_one - this%rlattice_primitive(1, 3)**2 - this%rlattice_primitive(2, 3)**2)
618 this%nonorthogonal = any(abs(angles - 90.0_real64) > m_epsilon)
625 real(real64),
intent(in) :: rv(:, :)
626 real(real64),
intent(out) :: kv(:, :)
627 real(real64),
intent(out) :: volume
628 integer,
intent(in) :: dim
629 type(namespace_t),
optional,
intent(in) :: namespace
632 real(real64) :: cross(1:3)
638 cross(1:3) = dcross_product(rv(1:3, 2), rv(1:3, 3))
639 volume = dot_product(rv(1:3, 1), cross(1:3))
641 kv(1:3, 1) = dcross_product(rv(:, 2), rv(:, 3))/volume
642 kv(1:3, 2) = dcross_product(rv(:, 3), rv(:, 1))/volume
643 kv(1:3, 3) = dcross_product(rv(:, 1), rv(:, 2))/volume
645 volume = rv(1, 1)*rv(2, 2) - rv(2, 1)*rv(1, 2)
646 kv(1, 1) = rv(2, 2)/volume
647 kv(2, 1) = -rv(1, 2)/volume
648 kv(1, 2) = -rv(2, 1)/volume
649 kv(2, 2) = rv(1, 1)/volume
652 kv(1, 1) = m_one / rv(1, 1)
654 message(1) =
"Reciprocal lattice for dim > 3 assumes no periodicity."
655 call messages_warning(1, namespace=namespace)
659 kv(ii, ii) = m_one/rv(ii,ii)
661 volume = volume * norm2(rv(:, ii))
665 if (volume < m_zero)
then
666 message(1) =
"Your lattice vectors form a left-handed system."
667 call messages_fatal(1, namespace=namespace)
684 real(real64),
intent(in) :: range
685 real(real64),
optional,
intent(in) :: center(:)
688 integer :: ii, jj, idir, n_valid
689 integer :: n_size(latt%space%periodic_dim)
690 real(real64) :: temp(latt%space%dim)
691 real(real64),
allocatable :: delta(:)
692 integer,
allocatable :: n_count(:), icell_tmp(:, :)
693 integer :: icell(latt%space%dim), n_min, n_max
703 if (
present(center))
then
705 safe_allocate(delta(1:latt%space%dim))
706 safe_allocate(n_count(1:latt%space%dim))
710 do idir = 1, latt%space%periodic_dim
711 temp = range * matmul(transpose(latt%klattice), latt%klattice(:, idir)) &
712 / norm2(latt%klattice(:, idir)) / (m_two * m_pi)
713 delta(idir) = temp(idir)
714 n_count(idir) =
floor(m_one + m_two*delta(idir) +
eps) + 1
715 iter%n_cells = iter%n_cells*n_count(idir)
719 safe_allocate(icell_tmp(1:latt%space%dim, 1:iter%n_cells))
721 do ii = 1, iter%n_cells
725 do idir = latt%space%periodic_dim, 1, -1
726 n_min = ceiling(-delta(idir) - center(idir) -
eps)
727 n_max =
floor(m_one + delta(idir) - center(idir) +
eps)
728 icell(idir) = n_min + mod(jj, n_count(idir))
729 if (idir > 1) jj = jj/n_count(idir)
730 if (icell(idir) < n_min .or. icell(idir) > n_max)
then
735 if (.not. valid) cycle
736 n_valid = n_valid + 1
737 icell_tmp(:, n_valid) = icell(:)
740 iter%n_cells = n_valid
741 safe_allocate(iter%icell(1:latt%space%dim, 1:n_valid))
742 iter%icell(:, 1:n_valid) = icell_tmp(:, 1:n_valid)
744 safe_deallocate_a(icell_tmp)
745 safe_deallocate_a(delta)
746 safe_deallocate_a(n_count)
749 do idir = 1, latt%space%periodic_dim
750 temp = range * matmul(transpose(latt%klattice),latt%klattice(:, idir)) / norm2(latt%klattice(:, idir)) / (m_two * m_pi)
751 n_size(idir) = ceiling(temp(idir))
752 iter%n_cells = iter%n_cells*(2*n_size(idir) + 1)
756 safe_allocate(iter%icell(1:latt%space%dim, 1:iter%n_cells))
758 do ii = 1, iter%n_cells
760 do idir = latt%space%periodic_dim, 1, -1
761 iter%icell(idir, ii) = mod(jj, 2*n_size(idir) + 1) - n_size(idir)
762 if (idir > 1) jj = jj/(2*n_size(idir) + 1)
764 iter%icell(latt%space%periodic_dim + 1:latt%space%dim, ii) = 0
779 this%n_cells = source%n_cells
780 safe_allocate_source_a(this%icell, source%icell)
781 this%latt => source%latt
790 integer,
intent(in) :: ii
791 real(real64) :: coord(1:this%latt%space%dim)
795 assert(ii <= this%n_cells)
797 coord(:) = matmul(this%latt%rlattice(:, :), this%icell(:, ii))
807 safe_deallocate_a(this%icell)
double acos(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double sqrt(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
real(real64), parameter, public m_two
real(real64), parameter, public m_zero
real(real64), parameter, public m_pi
some mathematical constants
real(real64), parameter, public m_epsilon
real(real64) function, dimension(this%space%dim) lattice_vectors_fold_into_cell(this, xx)
pure real(real64) function, dimension(this%space%dim) lattice_vectors_red_to_cart(this, xx_red)
subroutine reciprocal_lattice(rv, kv, volume, dim, namespace)
subroutine lattice_vectors_update(this, rlattice)
Updates the lattice vector object from a new set of Cartesian lattice vectors.
pure real(real64) function, dimension(this%space%dim) lattice_vectors_cart_to_red(this, xx_cart)
character(len=140) function lattice_vectors_short_info(this, unit_length)
subroutine angles_from_rlattice_primitive(rlattice_primitive, alpha, beta, gamma)
subroutine lattice_vectors_scale(this, factor)
type(lattice_vectors_t) function lattice_vectors_constructor_from_input(namespace, space, variable_prefix)
real(real64) function, dimension(1:this%latt%space%dim) lattice_iterator_get(this, ii)
real(real64) function lattice_vectors_max_length(this)
subroutine lattice_vectors_copy(this, source)
subroutine lattice_iterator_copy(this, source)
real(real64), parameter eps
subroutine build_metric_from_angles(this, angles)
subroutine lattice_vectors_write_info(this, namespace)
subroutine lattice_vectors_finalize(this)
type(lattice_vectors_t) function lattice_vectors_constructor_from_rlattice(namespace, space, rlattice)
type(lattice_iterator_t) function lattice_iterator_constructor(latt, range, center)
subroutine lattice_iterator_finalize(this)
This module is intended to contain "only mathematical" functions and procedures.
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
This module defines the unit system, used for input and output.
The following class implements a lattice iterator. It allows one to loop over all cells that are with...