You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CACmb/src/subroutines.f90

293 lines
9.4 KiB

module subroutines
use parameters
use functions
use box
implicit none
integer :: allostat, deallostat
public
contains
!This subroutine is just used to break the code and exit on an error
subroutine read_error_check(para, loc)
integer, intent(in) :: para
character(len=100), intent(in) :: loc
if (para > 0) then
print *, "Read error in ", trim(loc), " because of ", para
stop "Exit with error"
end if
end subroutine
subroutine matrix_inverse(a, n, a_inv)
integer :: i, j, k, piv_loc
integer, intent(in) :: n
real(kind = dp) :: coeff, sum_l, sum_u
real(kind = dp), dimension(n) :: b, x, y, b_piv
real(kind = dp), dimension(n, n) :: l, u, p
real(kind = dp), dimension(n, n), intent(in) :: a
real(kind = dp), dimension(n, n), intent(out) :: a_inv
real(kind = dp), allocatable :: v(:), u_temp(:), l_temp(:), p_temp(:)
l(:, :) = identity_mat(n)
u(:, :) = a(:, :)
p(:, :) = identity_mat(n)
!LU decomposition with partial pivoting
do j = 1, n-1
allocate(v(n-j+1), stat = allostat)
if(allostat /=0 ) then
print *, 'Fail to allocate v in matrix_inverse'
stop
end if
v(:) = u(j:n, j)
if(maxval(abs(v)) < lim_zero) then
print *, 'Fail to inverse matrix', a
stop
end if
piv_loc = maxloc(abs(v), 1)
deallocate(v, stat = deallostat)
if(deallostat /=0 ) then
print *, 'Fail to deallocate v in matrix_inverse'
stop
end if
!partial pivoting
if(piv_loc /= 1) then
allocate( u_temp(n-j+1), p_temp(n), stat = allostat)
if(allostat /=0 ) then
print *, 'Fail to allocate p_temp and/or u_temp in matrix_inverse'
stop
end if
u_temp(:) = u(j, j:n)
u(j, j:n) = u(piv_loc+j-1, j:n)
u(piv_loc+j-1, j:n) = u_temp(:)
p_temp(:) = p(j, :)
p(j, :) = p(piv_loc+j-1, :)
p(piv_loc+j-1, :) = p_temp(:)
deallocate( u_temp, p_temp, stat = deallostat)
if(deallostat /=0 ) then
print *, 'Fail to deallocate p_temp and/or u_temp in matrix_inverse'
stop
end if
if(j > 1) then
allocate( l_temp(j-1), stat = allostat)
if(allostat /= 0) then
print *, 'Fail to allocate l_temp in matrix_inverse'
stop
end if
l_temp(:) = l(j, 1:j-1)
l(j, 1:j-1) = l(piv_loc+j-1, 1:j-1)
l(piv_loc+j-1, 1:j-1) = l_temp(:)
deallocate( l_temp, stat = deallostat)
if(deallostat /=0 ) then
print *, 'Fail to deallocate l_temp in matrix_inverse'
stop
end if
end if
end if
!LU decomposition
do i = j+1, n
coeff = u(i, j)/u(j, j)
l(i, j) = coeff
u(i, j:n) = u(i, j:n)-coeff*u(j, j:n)
end do
end do
a_inv(:, :) = 0.0_dp
do j = 1, n
b(:) = 0.0_dp
b(j) = 1.0_dp
b_piv(:) = matmul(p, b)
!Now we have LUx = b_piv
!the first step is to solve y from Ly = b_piv
!forward substitution
do i = 1, n
if(i == 1) then
y(i) = b_piv(i)/l(i, i)
else
sum_l = 0
do k = 1, i-1
sum_l = sum_l+l(i, k)*y(k)
end do
y(i) = (b_piv(i)-sum_l)/l(i, i)
end if
end do
!then we solve x from ux = y
!backward subsitution
do i = n, 1, -1
if(i == n) then
x(i) = y(i)/u(i, i)
else
sum_u = 0
do k = i+1, n
sum_u = sum_u+u(i, k)*x(k)
end do
x(i) = (y(i)-sum_u)/u(i, i)
end if
end do
!put x into j column of a_inv
a_inv(:, j) = x(:)
end do
return
end subroutine matrix_inverse
subroutine parse_ori_vec(ori_string, ori_vec)
!This subroutine parses a string to vector in the format [ijk]
character(len=20), intent(in) :: ori_string
real(kind=dp), dimension(3), intent(out) :: ori_vec
integer :: i, ori_pos, stat
ori_pos=2
do i = 1,3
do while(ori_string(ori_pos:ori_pos)==' ')
ori_pos=ori_pos+1
end do
if (ori_string(ori_pos:ori_pos) == '-') then
ori_pos = ori_pos + 1
read(ori_string(ori_pos:ori_pos), *, iostat=stat) ori_vec(i)
if (stat>0) STOP "Error reading orientation value"
ori_vec(i) = -ori_vec(i)
ori_pos = ori_pos + 1
else
read(ori_string(ori_pos:ori_pos), *, iostat=stat) ori_vec(i)
if(stat>0) STOP "Error reading orientation value"
ori_pos=ori_pos + 1
end if
end do
return
end subroutine parse_ori_vec
subroutine apply_periodic(r)
!This function checks if an atom is outside the box and wraps it back in. This is generally used when some
!kind of displacement is applied but the simulation cell is desired to be maintained as the same size.
real(kind=dp), dimension(3), intent(inout) :: r
integer :: j
real(kind=dp) ::box_len
do j = 1, 3
if(box_bc(j:j) == 'p') then
box_len = box_bd(2*j) - box_bd(2*j-1)
if (r(j) > box_bd(2*j)) then
r(j) = r(j) - box_len
else if (r(j) < box_bd(2*j-1)) then
r(j) = r(j) + box_len
end if
end if
end do
end subroutine
subroutine check_right_ortho(ori, isortho, isrighthanded)
!This subroutine checks whether provided orientations in the form:
! | x1 x2 x3 |
! | y1 y2 y3 |
! | z1 z2 z3 |
!are right handed
real(kind=dp), dimension(3,3), intent(in) :: ori
logical, intent(out) :: isortho, isrighthanded
integer :: i, j
real(kind=dp) :: v(3), v_k(3)
!Initialize variables
isortho = .true.
isrighthanded=.true.
do i = 1, 3
do j = i+1, 3
if(abs(dot_product(ori(i,:), ori(j,:))) > lim_zero) then
isortho = .false.
end if
!Check if they are righthanded
if (j == i+1) then
v(:) = cross_product(ori(i,:), ori(j,:))
v_k(:) = v(:) - ori(mod(j, 3)+1,:)
else if ((i==1).and.(j==3)) then
v(:) = cross_product(ori(j,:),ori(i,:))
v_k(:) = v(:) - ori(i+1, :)
end if
if(norm2(v_k) > 10.0_dp**(-8.0_dp)) then
isrighthanded=.false.
end if
end do
end do
return
end subroutine check_right_ortho
subroutine init_random_seed()
implicit none
integer, allocatable :: seed(:)
integer :: i, n, un, istat, dt(8), pid, t(2), s
integer(8) :: count, tms
call random_seed(size = n)
allocate(seed(n))
! First try if the OS provides a random number generator
open(newunit=un, file="/dev/urandom", access="stream", &
form="unformatted", action="read", status="old", iostat=istat)
if (istat == 0) then
read(un) seed
close(un)
else
! Fallback to XOR:ing the current time and pid. The PID is
! useful in case one launches multiple instances of the same
! program in parallel.
call system_clock(count)
if (count /= 0) then
t = transfer(count, t)
else
call date_and_time(values=dt)
tms = (dt(1) - 1970) * 365_8 * 24 * 60 * 60 * 1000 &
+ dt(2) * 31_8 * 24 * 60 * 60 * 1000 &
+ dt(3) * 24 * 60 * 60 * 60 * 1000 &
+ dt(5) * 60 * 60 * 1000 &
+ dt(6) * 60 * 1000 + dt(7) * 1000 &
+ dt(8)
t = transfer(tms, t)
end if
s = ieor(t(1), t(2))
pid = getpid() + 1099279 ! Add a prime
s = ieor(s, pid)
if (n >= 3) then
seed(1) = t(1) + 36269
seed(2) = t(2) + 72551
seed(3) = pid
if (n > 3) then
seed(4:) = s + 37 * (/ (i, i = 0, n - 4) /)
end if
else
seed = s + 37 * (/ (i, i = 0, n - 1 ) /)
end if
end if
call random_seed(put=seed)
end subroutine init_random_seed
end module subroutines