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
97 type(lattice_vectors_t) function lattice_vectors_constructor_from_rlattice(namespace, space, rlattice) result(latt)
98 type(namespace_t),
intent(in) :: namespace
99 class(space_t),
intent(in) :: space
100 real(real64),
intent(in) :: rlattice(:, :)
102 integer :: idir, idir2
103 real(real64) :: volume_element
105 push_sub(lattice_vectors_constructor_from_rlattice)
109 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
110 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
111 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
112 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
114 latt%rlattice(1:space%dim, 1:space%dim) = rlattice(1:space%dim, 1:space%dim)
115 do idir = 1, space%dim
116 latt%rlattice_primitive(:, idir) = latt%rlattice(:, idir) / norm2(latt%rlattice(:, idir))
119 latt%nonorthogonal = .false.
120 do idir = 1, space%dim
121 do idir2 = 1, space%dim
122 if (idir /= idir2 .and. abs(latt%rlattice_primitive(idir, idir2)) >
m_epsilon)
then
123 latt%nonorthogonal = .
true.
128 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
129 latt%klattice = latt%klattice *
m_two*
m_pi
131 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
133 if (space%dim == 3)
then
142 pop_sub(lattice_vectors_constructor_from_rlattice)
147 type(namespace_t),
intent(in) :: namespace
148 class(space_t),
intent(in) :: space
149 character(len=*),
optional,
intent(in) :: variable_prefix
152 real(real64) :: norm, lparams(space%dim), volume_element
153 integer :: idim, jdim, ncols
154 logical :: has_angles
155 real(real64) :: angles(1:space%dim)
156 character(len=:),
allocatable :: prefix
160 if (
present(variable_prefix))
then
161 prefix = variable_prefix
168 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
169 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
170 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
171 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
173 latt%alpha = 90.0_real64
174 latt%beta = 90.0_real64
175 latt%gamma = 90.0_real64
180 if (space%is_periodic())
then
196 if (parse_block(namespace, prefix//
'LatticeParameters', blk) == 0)
then
197 ncols = parse_block_cols(blk, 0)
198 if (ncols < space%periodic_dim)
then
199 call messages_input_error(namespace, prefix//
'LatticeParameters', &
200 'The number of columns must be at least PeriodicDimensions')
203 call parse_block_float(blk, 0, idim - 1, lparams(idim))
207 do idim = ncols + 1, space%dim
208 lparams(idim) = m_one
212 if (parse_block_n(blk) > 1)
then
213 if (space%dim /= 3)
then
214 call messages_input_error(namespace, prefix//
'LatticeParameters',
'Angles can only be specified when Dimensions = 3')
217 ncols = parse_block_cols(blk, 1)
218 if (ncols /= space%dim)
then
219 call messages_input_error(namespace, prefix//
'LatticeParameters',
'You must specify three angles')
221 do idim = 1, space%dim
222 call parse_block_float(blk, 1, idim - 1, angles(idim))
226 call parse_block_end(blk)
229 call messages_input_error(namespace, prefix//
'LatticeParameters',
'Variable is mandatory for periodic systems')
234 latt%alpha = angles(1)
235 latt%beta = angles(2)
236 latt%gamma = angles(3)
238 if (parse_is_defined(namespace, prefix//
'LatticeVectors'))
then
239 message(1) =
'LatticeParameters with angles is incompatible with LatticeVectors'
240 call messages_print_var_info(prefix//
"LatticeParameters", namespace=namespace)
241 call messages_fatal(1, namespace=namespace)
264 latt%rlattice_primitive = diagonal_matrix(space%dim, m_one)
265 latt%nonorthogonal = .false.
267 if (parse_block(namespace, prefix//
'LatticeVectors', blk) == 0)
then
268 do idim = 1, space%dim
269 do jdim = 1, space%dim
270 call parse_block_float(blk, idim - 1, jdim - 1, latt%rlattice_primitive(jdim, idim))
271 if (idim /= jdim .and. abs(latt%rlattice_primitive(jdim, idim)) > m_epsilon)
then
272 latt%nonorthogonal = .
true.
276 call parse_block_end(blk)
284 latt%rlattice_primitive = diagonal_matrix(space%dim, m_one)
287 latt%rlattice = m_zero
288 do idim = 1, space%dim
289 norm = norm2(latt%rlattice_primitive(1:space%dim, idim))
290 lparams(idim) = lparams(idim)*norm
291 do jdim = 1, space%dim
292 latt%rlattice_primitive(jdim, idim) = latt%rlattice_primitive(jdim, idim) / norm
293 latt%rlattice(jdim, idim) = latt%rlattice_primitive(jdim, idim) * lparams(idim)
297 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
298 latt%klattice = latt%klattice * m_two*m_pi
300 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
302 if (.not. has_angles .and. space%dim == 3)
then
312 real(real64),
intent(in) :: rlattice_primitive(1:3, 1:3)
313 real(real64),
intent(out) :: alpha
314 real(real64),
intent(out) :: beta
315 real(real64),
intent(out) :: gamma
317 real(real64) :: rlatt(1:3, 1:3)
321 rlatt = matmul(transpose(rlattice_primitive), rlattice_primitive)
322 alpha =
acos(rlatt(2, 3)/
sqrt(rlatt(2, 2)*rlatt(3, 3)))/m_pi*180.0_real64
323 beta =
acos(rlatt(1, 3)/
sqrt(rlatt(1, 1)*rlatt(3, 3)))/m_pi*180.0_real64
324 gamma =
acos(rlatt(1, 2)/
sqrt(rlatt(1, 1)*rlatt(2, 2)))/m_pi*180.0_real64
336 this%space = source%space
337 safe_allocate_source_a(this%rlattice_primitive, source%rlattice_primitive)
338 safe_allocate_source_a(this%rlattice, source%rlattice)
339 safe_allocate_source_a(this%klattice_primitive, source%klattice_primitive)
340 safe_allocate_source_a(this%klattice, source%klattice)
341 this%alpha = source%alpha
342 this%beta = source%beta
343 this%gamma = source%gamma
344 this%rcell_volume = source%rcell_volume
345 this%nonorthogonal = source%nonorthogonal
356 safe_deallocate_a(this%rlattice_primitive)
357 safe_deallocate_a(this%rlattice)
358 safe_deallocate_a(this%klattice_primitive)
359 safe_deallocate_a(this%klattice)
360 this%nonorthogonal = .false.
368 real(real64),
intent(in) :: factor(this%space%dim)
375 do idir = 1, this%space%dim
376 this%rlattice(1:this%space%dim, idir) = this%rlattice(1:this%space%dim, idir)*factor(idir)
380 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
381 this%klattice = this%klattice * m_two*m_pi
390 real(real64),
intent(in) :: rlattice(this%space%dim, this%space%dim)
393 real(real64) :: volume_element, maxrlatt
397 this%rlattice = rlattice
398 maxrlatt = maxval(abs(this%rlattice))
399 where(abs(this%rlattice) < 1e-12_real64*maxrlatt) this%rlattice = m_zero
402 do idir = 1, this%space%dim
403 this%rlattice_primitive(:, idir) = this%rlattice(:, idir) / norm2(this%rlattice(:, idir))
407 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
408 this%klattice = this%klattice * m_two*m_pi
410 call reciprocal_lattice(this%rlattice_primitive, this%klattice_primitive, volume_element, this%space%dim)
413 if (this%space%dim == 3)
then
428 real(real64),
intent(in) :: xx_cart(this%space%dim)
429 real(real64) :: xx_red(this%space%dim)
431 xx_red = matmul(xx_cart, this%klattice)/(m_two*m_pi)
438 real(real64),
intent(in) :: xx_red(this%space%dim)
439 real(real64) :: xx_cart(this%space%dim)
441 xx_cart = matmul(this%rlattice, xx_red)
448 real(real64),
intent(in) :: xx(this%space%dim)
449 real(real64) :: new_xx(this%space%dim)
453 if (this%space%is_periodic())
then
455 new_xx = this%cart_to_red(xx)
457 do idir = 1, this%space%periodic_dim
459 new_xx(idir) = new_xx(idir) + m_half
462 new_xx(idir) = new_xx(idir) - anint(new_xx(idir))
463 if (new_xx(idir) < -1.0e-6_real64)
then
464 new_xx(idir) = new_xx(idir) + m_one
468 assert(new_xx(idir) >= -1.0e-6_real64)
469 assert(new_xx(idir) < m_one)
472 new_xx(idir) = new_xx(idir) - m_half
476 new_xx = this%red_to_cart(new_xx)
486 type(namespace_t),
intent(in) :: namespace
488 integer :: idir, idir2
492 assert(this%space%is_periodic())
494 call messages_print_with_emphasis(msg=
"Lattice", namespace=namespace)
496 write(message(1),
'(a,3a,a)')
' Lattice Vectors [', trim(units_abbrev(units_out%length)),
']'
497 do idir = 1, this%space%periodic_dim
498 write(message(1+idir),
'(9f12.6)') (units_from_atomic(units_out%length, this%rlattice(idir2, idir)), &
499 idir2 = 1, this%space%dim)
501 call messages_info(1+this%space%periodic_dim, namespace=namespace)
503 select case (this%space%periodic_dim)
505 write(message(1),
'(a)')
' Cell length ='
507 write(message(1),
'(a)')
' Cell area ='
509 write(message(1),
'(a)')
' Cell volume ='
511 write(message(1),
'(a,1x,f18.4,3a,i1.1,a)') &
512 trim(message(1)), units_from_atomic(units_out%length**this%space%periodic_dim, this%rcell_volume), &
513 ' [', trim(units_abbrev(units_out%length**this%space%periodic_dim)),
']'
514 call messages_info(1, namespace=namespace)
515 write(message(1),
'(a,3a,a)')
' Reciprocal-Lattice Vectors [', trim(units_abbrev(units_out%length**(-1))),
']'
516 do idir = 1, this%space%periodic_dim
517 write(message(1+idir),
'(3f12.6)') (units_from_atomic(unit_one / units_out%length, this%klattice(idir2, idir)), &
518 idir2 = 1, this%space%dim)
520 call messages_info(1+this%space%periodic_dim, namespace=namespace)
522 if (this%space%dim == 3)
then
523 write(message(1),
'(a)')
' Cell angles [degree]'
524 write(message(2),
'(a, f8.3)')
' alpha = ', this%alpha
525 write(message(3),
'(a, f8.3)')
' beta = ', this%beta
526 write(message(4),
'(a, f8.3)')
' gamma = ', this%gamma
527 call messages_info(4, namespace=namespace)
530 call messages_print_with_emphasis(namespace=namespace)
538 type(unit_t),
intent(in) :: unit_length
540 integer :: idir1, idir2
544 write(
info,
'(a,a,a)')
'LatticeVectors [', trim(units_abbrev(unit_length)),
'] = '
546 do idir1 = 1, this%space%dim
547 write(
info,
'(a,a)') trim(
info),
'['
548 do idir2 = 1, this%space%dim
549 write(
info,
'(a,x,f11.6)') trim(
info), units_from_atomic(unit_length, this%rlattice(idir2, idir1))
551 write(
info,
'(a,a)') trim(
info),
']'
552 if (idir1 < this%space%dim)
then
553 write(
info,
'(a,a)') trim(
info),
', '
566 if (this%space%is_periodic())
then
567 length = maxval(norm2(this%rlattice(:,1:this%space%periodic_dim), dim=1))
578 real(real64),
intent(in) :: angles(3)
580 real(real64) :: cosang, a2, aa, cc
581 real(real64),
parameter :: tol_angle = 1.0e-6_real64
587 if (abs(angles(1) - angles(2)) < tol_angle .and. abs(angles(2) - angles(3)) < tol_angle .and. &
588 (abs(angles(1) - 90.0_real64) + abs(angles(2) - 90.0_real64) + abs(angles(3) - 90.0_real64)) > tol_angle)
then
590 cosang =
cos(m_pi*angles(1)/180.0_real64)
591 a2 = m_two/m_three*(m_one - cosang)
593 cc =
sqrt(m_one - a2)
594 this%rlattice_primitive(1, 1) = aa
595 this%rlattice_primitive(2, 1) = m_zero
596 this%rlattice_primitive(3, 1) = cc
597 this%rlattice_primitive(1, 2) = -m_half*aa
598 this%rlattice_primitive(2, 2) = m_half*
sqrt(m_three)*aa
599 this%rlattice_primitive(3, 2) = cc
600 this%rlattice_primitive(1, 3) = -m_half*aa
601 this%rlattice_primitive(2, 3) = -m_half*
sqrt(m_three)*aa
602 this%rlattice_primitive(3, 3) = cc
604 this%rlattice_primitive(1, 1) = m_one
605 this%rlattice_primitive(2, 1) = m_zero
606 this%rlattice_primitive(3, 1) = m_zero
607 this%rlattice_primitive(1, 2) =
cos(m_pi*angles(3)/180.0_real64)
608 this%rlattice_primitive(2, 2) =
sin(m_pi*angles(3)/180.0_real64)
609 this%rlattice_primitive(3, 2) = m_zero
610 this%rlattice_primitive(1, 3) =
cos(m_pi*angles(2)/180.0_real64)
611 this%rlattice_primitive(2, 3) = (
cos(m_pi*angles(1)/180.0_real64) - &
612 this%rlattice_primitive(1, 2)*this%rlattice_primitive(1, 3))/this%rlattice_primitive(2,2)
613 this%rlattice_primitive(3, 3) =
sqrt(m_one - this%rlattice_primitive(1, 3)**2 - this%rlattice_primitive(2, 3)**2)
616 this%nonorthogonal = any(abs(angles - 90.0_real64) > m_epsilon)
623 real(real64),
intent(in) :: rv(:,:)
624 real(real64),
intent(out) :: kv(:,:)
625 real(real64),
intent(out) :: volume
626 integer,
intent(in) :: dim
627 type(namespace_t),
optional,
intent(in) :: namespace
630 real(real64) :: cross(1:3)
636 cross(1:3) = dcross_product(rv(1:3, 2), rv(1:3, 3))
637 volume = dot_product(rv(1:3, 1), cross(1:3))
639 kv(1:3, 1) = dcross_product(rv(:, 2), rv(:, 3))/volume
640 kv(1:3, 2) = dcross_product(rv(:, 3), rv(:, 1))/volume
641 kv(1:3, 3) = dcross_product(rv(:, 1), rv(:, 2))/volume
643 volume = rv(1, 1)*rv(2, 2) - rv(2, 1)*rv(1, 2)
644 kv(1, 1) = rv(2, 2)/volume
645 kv(2, 1) = -rv(1, 2)/volume
646 kv(1, 2) = -rv(2, 1)/volume
647 kv(2, 2) = rv(1, 1)/volume
650 kv(1, 1) = m_one / rv(1, 1)
652 message(1) =
"Reciprocal lattice for dim > 3 assumes no periodicity."
653 call messages_warning(1, namespace=namespace)
657 kv(ii, ii) = m_one/rv(ii,ii)
659 volume = volume * norm2(rv(:, ii))
663 if (volume < m_zero)
then
664 message(1) =
"Your lattice vectors form a left-handed system."
665 call messages_fatal(1, namespace=namespace)
677 real(real64),
intent(in) :: range
680 integer :: ii, jj, idir
681 integer :: n_size(latt%space%periodic_dim)
682 real(real64) :: temp(latt%space%dim)
690 do idir = 1, latt%space%periodic_dim
691 temp = range * matmul(transpose(latt%klattice),latt%klattice(:, idir)) / norm2(latt%klattice(:, idir)) / (m_two * m_pi)
692 n_size(idir) = ceiling(temp(idir))
693 iter%n_cells = iter%n_cells*(2*n_size(idir) + 1)
697 safe_allocate(iter%icell(1:latt%space%dim, 1:iter%n_cells))
699 do ii = 1, iter%n_cells
701 do idir = latt%space%periodic_dim, 1, -1
702 iter%icell(idir, ii) = mod(jj, 2*n_size(idir) + 1) - n_size(idir)
703 if (idir > 1) jj = jj/(2*n_size(idir) + 1)
705 iter%icell(latt%space%periodic_dim + 1:latt%space%dim, ii) = 0
718 this%n_cells = source%n_cells
719 safe_allocate_source_a(this%icell, source%icell)
720 this%latt => source%latt
729 integer,
intent(in) :: ii
730 real(real64) :: coord(1:this%latt%space%dim)
734 assert(ii <= this%n_cells)
736 coord = matmul(this%latt%rlattice, this%icell(:, ii))
746 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__
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)
type(lattice_iterator_t) function lattice_iterator_constructor(latt, range)
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)
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)
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...