amun-code/sources/algebra.F90
Grzegorz Kowal 81de98d9e2 Update the copyright year to 2023.
Signed-off-by: Grzegorz Kowal <grzegorz@amuncode.org>
2023-02-01 18:36:37 -03:00

1364 lines
31 KiB
Fortran

!!******************************************************************************
!!
!! This file is part of the AMUN source code, a program to perform
!! Newtonian or relativistic magnetohydrodynamical simulations on uniform or
!! adaptive mesh.
!!
!! Copyright (C) 2008-2023 Grzegorz Kowal <grzegorz@amuncode.org>
!!
!! This program is free software: you can redistribute it and/or modify
!! it under the terms of the GNU General Public License as published by
!! the Free Software Foundation, either version 3 of the License, or
!! (at your option) any later version.
!!
!! This program is distributed in the hope that it will be useful,
!! but WITHOUT ANY WARRANTY; without even the implied warranty of
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
!! GNU General Public License for more details.
!!
!! You should have received a copy of the GNU General Public License
!! along with this program. If not, see <http://www.gnu.org/licenses/>.
!!
!!******************************************************************************
!!
!! module: ALGEBRA
!!
!! This module provides all sort of algebra subroutines.
!!
!!******************************************************************************
!
module algebra
! include external procedures
!
use iso_fortran_env, only : real32, real64, real128
! module variables are not implicit by default
!
implicit none
! maximum real kind
!
integer, parameter :: max_prec = max(real32, real64, real128)
! by default everything is private
!
private
! declare public subroutines
!
public :: max_prec
public :: quadratic, quadratic_normalized
public :: cubic, cubic_normalized
public :: quartic
public :: tridiag, pentadiag, invert
!- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!
contains
!
!===============================================================================
!
! function QUADRATIC:
! ------------------
!
! Function finds real roots of a quadratic equation:
!
! f(x) = a₂ x² + a₁ x + a₀ = 0
!
! Remark:
!
! The coefficients are renormalized in order to avoid overflow and
! unnecessary underflow. The catastrophic cancellation is avoided as well.
!
! Arguments:
!
! a₂, a₁, a₀ - the quadratic equation coefficients;
! x - the root array; if there are two roots, x(1) corresponds to
! the one with '-' and x(2) to the one with '+';
!
! Return value:
!
! The number of roots found.
!
!===============================================================================
!
function quadratic(a, x) result(nr)
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
real(kind=8), dimension(3), intent(in) :: a
real(kind=8), dimension(2), intent(out) :: x
integer :: nr
! local variables
!
real(kind=8), dimension(3) :: b
real(kind=8) :: bh, dl, dr, tm
!
!-------------------------------------------------------------------------------
!
! in order to avoid overflow or needless underflow, divide all coefficients by
! the maximum among them, but only if the maximum value is really large
!
b(1:3) = a(1:3) / maxval(abs(a(1:3)))
! check if the coefficient a₂ is not zero
!
if (abs(b(3)) > 0.0d+00) then
! check if the coefficient a₀ is not zero
!
if (abs(b(1)) > 0.0d+00) then
! coefficients a₂ and a₀ are nonzero, so we solve the quadratic equation
!
! a₂ x² + a₁ x + a₀ = 0
!
! calculate half of a₁
!
bh = 0.5d+00 * b(2)
! calculate Δ = a₁² - 4 a₂ a₀
!
dl = bh * bh - b(3) * b(1)
! check if Δ is larger then zero
!
if (dl > 0.0d+00) then
! calculate quare root of Δ
!
dr = sqrt(dl)
! Δ > 0, so the quadratic has two real roots
!
if (b(2) > 0.0d+00) then
tm = - bh - dr
x(1) = tm / b(3)
x(2) = b(1) / tm
else if (b(2) < 0.0d+00) then
tm = - bh + dr
x(1) = b(1) / tm
x(2) = tm / b(3)
else
x(2) = dr / b(3)
x(1) = - x(2)
end if
! update the number of roots
!
nr = 2
else if (dl < 0.0d+00) then ! Δ < 0
! Δ < 0, so the quadratic does not have any real roots
!
x(1) = 0.0d+00
x(2) = 0.0d+00
! update the number of roots
!
nr = 0
else ! Δ = 0
! Δ = 0, so the quadratic has two identical real roots
!
x(1) = - 0.5d+00 * b(2) / b(3)
x(2) = x(1)
! update the number of roots
!
nr = 2
end if ! Δ < 0
else ! a₀ = 0
! since a₀ is zero, we have one zero root and the second root is calculated from
! the linear formula
!
! (a₂ x + a₁) x = 0
!
tm = - b(2) / b(3)
if (tm >= 0.0d+00) then
x(1) = 0.0d+00
x(2) = tm
else
x(1) = tm
x(2) = 0.0d+00
end if
! update the number of roots
!
nr = 2
end if ! a₀ = 0
else ! a₂ = 0
! since coefficient a₂ is zero, the quadratic equation is reduced to a linear
! one; one root is undetermined in this case
!
! a₁ x + a₀ = 0
!
x(2) = 0.0d+00
! find the unique root
!
if (abs(b(2)) > 0.0d+00) then
! the coefficient a₁ is not zero, so the quadratic has only one root
!
x(1) = - b(1) / b(2)
! update the number of roots
!
nr = 1
else ! a₁ = 0
! the coefficient a₁ is zero, so the quadratic has no roots
!
! a₀ = 0
!
x(1) = 0.0d+00
! update the number of roots
!
nr = 0
end if ! a₁ = 0
end if ! a₂ = 0
!-------------------------------------------------------------------------------
!
end function quadratic
!
!===============================================================================
!
! function QUADRATIC_NORMALIZED:
! -----------------------------
!
! Function finds real roots of the normalized quadratic equation:
!
! f(x) = x² + a₁ x + a₀ = 0
!
! Remark:
!
! The coefficients are renormalized in order to avoid overflow and
! unnecessary underflow. The catastrophic cancellation is avoided as well.
!
! Arguments:
!
! a₁, a₀ - the quadratic equation coefficients;
! x - the root array;
!
! Return value:
!
! The number of roots found.
!
!===============================================================================
!
function quadratic_normalized(a, x) result(nr)
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
real(kind=8), dimension(2), intent(in) :: a
real(kind=8), dimension(2), intent(out) :: x
integer :: nr
! local variables
!
real(kind=8) :: bh, dl, dr, tm
!
!-------------------------------------------------------------------------------
!
! check if the coefficient a₀ is not zero
!
if (abs(a(1)) > 0.0d+00) then
! coefficients a₀ is nonzero, so we solve the quadratic equation
!
! x² + a₁ x + a₀ = 0
!
! calculate half of a₁
!
bh = 0.5d+00 * a(2)
! calculate Δ = ¼ a₁² - a₀
!
dl = bh * bh - a(1)
! check if Δ is larger then zero
!
if (dl > 0.0d+00) then
! calculate quare root of Δ
!
dr = sqrt(dl)
! Δ > 0, so the quadratic has two real roots, x₁ = - ½ a₁ ± √Δ
!
if (a(2) > 0.0d+00) then
tm = bh + dr
x(1) = - a(1) / tm
x(2) = - tm
else if (a(2) < 0.0d+00) then
tm = bh - dr
x(1) = - tm
x(2) = - a(1) / tm
else
x(1) = dr
x(2) = - dr
end if
! update the number of roots
!
nr = 2
else if (dl < 0.0d+00) then
! Δ < 0, so the quadratic does not have any real root
!
x(1) = 0.0d+00
x(2) = 0.0d+00
! update the number of roots
!
nr = 0
else
! Δ = 0, so the quadratic has two identical real roots
!
x(:) = - bh
! update the number of roots
!
nr = 2
end if
else
! since a₀ is zero, we have one zero root and the second root is calculated from
! the linear formula
!
! (x + a₁) x = 0
!
x(1) = 0.0d+00
x(2) = - a(2)
! update the number of roots
!
nr = 2
end if ! a₀ = 0
!-------------------------------------------------------------------------------
!
end function quadratic_normalized
!
!===============================================================================
!
! function CUBIC:
! --------------
!
! Function finds real roots of a cubic equation:
!
! f(x) = a₃ x³ + a₂ x² + a₁ x + a₀ = 0
!
! Remark:
!
! The coefficients are renormalized in order to avoid overflow and
! unnecessary underflow.
!
! Arguments:
!
! a(:) - the cubic equation coefficients vector (in increasing moments);
! x(:) - the root vector;
!
! Return value:
!
! The number of roots found.
!
!===============================================================================
!
function cubic(a, x) result(nr)
! include external constants
!
use constants, only : pi2
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
real(kind=8), dimension(4), intent(in) :: a
real(kind=8), dimension(3), intent(out) :: x
integer :: nr
! local variables
!
real(kind=8) :: ac, bc, qc, cc, dc, ec, rc, th, cs, a2, b3
! local parameters
!
real(kind=8), parameter :: c0 = 1.0d+00 / 3.0d+00
!
!-------------------------------------------------------------------------------
!
! check if the coefficient a₃ is not zero
!
if (abs(a(4)) > 0.0d+00) then
! check if the coefficient a₀ is not zero
!
if (abs(a(1)) > 0.0d+00) then
! both coefficients a₃ and a₀ are not zero, so we have to solve full cubic
! equation
!
! a₃ x³ + a₂ x² + a₁ x + a₀ = 0
!
! compute coefficient A = 2 a₂³ - 9 a₃ a₂ a₁ + 27 a₃² a₀
!
ac = 2.0d+00 * a(3)**3 - 9.0d+00 * a(4) * a(3) * a(2) &
+ 27.0d+00 * a(4)**2 * a(1)
! compute coefficient B = a₂² - 3 a₃ a₁
!
bc = a(3)**2 - 3.0d+00 * a(4) * a(2)
! calculate A² and B³
!
a2 = ac**2
b3 = bc**3
! compute coefficient Q² = A² - 4 B³
!
qc = a2 - 4.0d+00 * b3
! check the condition for any root existance
!
if (qc > 0.0d+00) then
! the case Q > 0, in which only one root is real
!
! calculate C³ = ½ (Q + A)
!
cc = 0.5d+00 * (sqrt(qc) + ac)
dc = sign(1.0d+00, cc) * abs(cc)**c0
if (abs(cc) > 0.0d+00) then
ec = b3 / cc
else
ec = b3 / a2
ec = 2.0d+00 * ec * (1.0d+00 / sqrt(1.0d+00 - 4.0d+00 * ec) &
+ 1.0d+00 / ac)
end if
ec = sign(1.0d+00, ec) * abs(ec)**c0
! calculate the unique real root
!
x(1) = - c0 * (a(3) + dc + ec) / a(4)
x(2) = 0.0d+00
x(3) = 0.0d+00
! update the number of roots
!
nr = 1
else if (qc < 0.0d+00) then
! Q < 0, so there are three distinct and real roots
!
! compute Q and R
!
th = 3.0d+00 * a(4)
qc = bc / th**2
rc = 0.5d+00 * ac / th**3
! compute cosine
!
cs = sign(0.5d+00, ac * bc) * sqrt(ac**2 / abs(bc)**3)
! check condition
!
if (cs <= 1.0d+00) then
! there are three real roots; prepare coefficients to calculate them
!
th = acos(cs)
ac = - 2.0d+00 * sqrt(qc)
bc = - c0 * (a(3) / a(4))
! calculate roots
!
x(1) = ac * cos(c0 * th ) + bc
x(2) = ac * cos(c0 * (th + pi2)) + bc
x(3) = ac * cos(c0 * (th - pi2)) + bc
! update the number of roots
!
nr = 3
else
! there is only one real root; prepare coefficients to calculate it
!
ac = - sign(1.0d+00, rc) * (abs(rc) + sqrt(rc**2 - qc**3))**c0
if (abs(ac) > 0.0d+00) then
bc = qc / ac
else
bc = 0.0d+00
end if
! calculate the root
!
x(1) = (ac + bc) - c0 * (a(3) / a(4))
! reset remaining roots
!
x(2) = 0.0d+00
x(3) = 0.0d+00
! update the number of roots
!
nr = 1
end if
else
if (abs(bc) > 0.0d+00) then
! the case Q = 0 and B ≠ 0, in which all three roots are real and one root
! is double
!
x(1) = 0.5d+00 * (9.0d+00 * a(4) * a(1) - a(3) * a(2)) / bc
x(2) = x(1)
x(3) = (- 9.0d+00 * a(4) * a(1) + 4.0d+00 * a(3) * a(2) &
- a(3)**3 / a(4)) / bc
! update the number of roots
!
nr = 3
else
! the case Q = 0 and B = 0, in which all three roots are real and equal
!
x(:) = - c0 * a(3) / a(4)
! update the number of roots
!
nr = 3
end if
end if
else
! since the coefficient a₀ is zero, there is one zero root, and the remaining
! roots are found from a quadratic equation
!
! (a₃ x² + a₂ x + a₁) x = 0
!
x(3) = 0.0d+00
! call function quadratic() to find the remaining roots
!
nr = quadratic(a(2:4), x(1:2))
! increase the number of roots
!
nr = nr + 1
end if
else
! since the coefficient a₃ is zero, the equation reduces to a quadratic one,
! and one root is undetermined
!
! a₂ x² + a₁ x + a₀ = 0
!
x(3) = 0.0d+00
! call function quadratic() to find the remaining roots
!
nr = quadratic(a(1:3), x(1:2))
end if
! sort roots in the increasing order
!
call sort(nr, x(:))
!-------------------------------------------------------------------------------
!
end function cubic
!
!===============================================================================
!
! function CUBIC_NORMALIZED:
! -------------------------
!
! Function finds real roots of the normalized cubic equation:
!
! f(x) = x³ + a₂ x² + a₁ x + a₀ = 0
!
! Arguments:
!
! a(:) - the cubic equation coefficients vector (in increasing moments);
! x(:) - the root vector;
!
! Return value:
!
! The number of roots found.
!
!===============================================================================
!
function cubic_normalized(a, x) result(nr)
! include external constants
!
use constants, only : pi2
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
real(kind=8), dimension(3), intent(in) :: a
real(kind=8), dimension(3), intent(out) :: x
integer :: nr
! local variables
!
real(kind=8) :: ac, bc, qc, a3, b2, aa, bb, sq, cs, ph
! local parameters
!
real(kind=8), parameter :: c0 = 1.0d+00 / 3.0d+00, c1 = 1.0d+00 / 2.7d+01
real(kind=8), parameter :: c2 = 1.0d+00 / 4.0d+00
!
!-------------------------------------------------------------------------------
!
! check if the coefficient a₀ is not zero
!
if (abs(a(1)) > 0.0d+00) then
! both coefficients a₃ and a₀ are not zero, so we have to solve full cubic
! equation
!
! x³ + a₂ x² + a₁ x + a₀ = 0
!
! compute coefficient A = a₂² - 3 a₁
!
ac = c0 * (a(3)**2 - 3.0d+00 * a(2))
! compute coefficient B = (2 a₂³ - 9 a₂ a₁ + 27 a₀) / 27
!
bc = c1 * (2.0d+00 * a(3)**3 - 9.0d+00 * a(3) * a(2) + 2.7d+01 * a(1))
! calculate powers of A and B
!
a3 = c1 * ac**3
b2 = c2 * bc**2
! compute coefficient Q² = B² / 4 + A³ / 27
!
qc = b2 - a3
! check the condition for any root existance
!
if (qc > 0.0d+00) then
! the case Q > 0, in which only one root is real
!
! calculate A and B
!
sq = sqrt(qc)
ph = - 0.5d+00 * bc + sq
aa = sign(1.0d+00, ph) * abs(ph)**c0
ph = - 0.5d+00 * bc - sq
bb = sign(1.0d+00, ph) * abs(ph)**c0
! calculate the unique real root
!
x(1) = aa + bb - (c0 * a(3))
x(2) = 0.0d+00
x(3) = 0.0d+00
! update the number of roots
!
nr = 1
else if (qc < 0.0d+00) then
! calculate angle φ
!
if (bc > 0.0d+00) then
cs = - sqrt(b2 / a3)
else if (bc < 0.0d+00) then
cs = sqrt(b2 / a3)
else ! B = 0
cs = 0.0d+00
end if
! there are three real roots; prepare coefficients to calculate them
!
ph = acos(cs)
aa = 2.0d+00 * sqrt(c0 * ac)
! calculate all three roots
!
x(1) = aa * cos(c0 * (ph - pi2))
x(2) = aa * cos(c0 * ph )
x(3) = aa * cos(c0 * (ph + pi2))
! return to the original variables
!
x(:) = x(:) - (c0 * a(3))
! update the number of roots
!
nr = 3
else ! Q = 0
! the case Q = 0, for which all three roots are real and two roots are equal
!
if (bc > 0.0d+00) then
aa = sqrt(c0 * ac)
x(1) = - 2.0d+00 * aa
x(2) = aa
x(3) = aa
else if (bc < 0.0d+00) then
aa = sqrt(c0 * ac)
x(1) = 2.0d+00 * aa
x(2) = - aa
x(3) = - aa
else
x(:) = 0.0d+00
end if
! return to the original variables
!
x(:) = x(:) - (c0 * a(3))
! update the number of roots
!
nr = 3
end if ! Q = 0
else ! a₀ = 0
! since the coefficient a₀ is zero, there is one zero root, and the remaining
! roots are found by solving the quadratic equation
!
! (x² + a₂ x + a₁) x = 0
!
x(3) = 0.0d+00
! call function quadratic() to find the remaining roots
!
nr = quadratic_normalized(a(2:3), x(1:2))
! increase the number of roots
!
nr = nr + 1
end if ! a₀ = 0
! sort roots in the increasing order
!
call sort(nr, x(:))
!-------------------------------------------------------------------------------
!
end function cubic_normalized
!
!===============================================================================
!
! subroutine QUARTIC:
! ------------------
!
! Subroutine finds real roots of a quartic equation:
!
! f(x) = a₄ * x⁴ + a₃ * x³ + a₂ * x² + a₁ * x + a₀ = 0
!
! Arguments:
!
! a(:) - the quartic equation coefficients vector (in increasing moments);
! x(:) - the root vector;
!
! References:
!
! Hacke, Jr. J. E., "A Simple Solution of the General Quartic",
! The American Mathematical Monthly, 1941, vol. 48, no. 5, pp. 327-328
!
!===============================================================================
!
function quartic(a, x) result(nr)
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
real(kind=8), dimension(5), intent(in) :: a
real(kind=8), dimension(4), intent(out) :: x
integer :: nr
! local variables
!
integer :: n, m
real(kind=8) :: pc, qc, rc
real(kind=8) :: zm, k2, l2, kc, lc
! local arrays
!
real(kind=8), dimension(4) :: b
real(kind=8), dimension(2) :: y
real(kind=8), dimension(3) :: c, z
!
!-------------------------------------------------------------------------------
!
! check if the coefficient a₄ is not zero
!
if (abs(a(5)) > 0.0d+00) then
! check if the coefficient a₀ is not zero
!
if (abs(a(1)) > 0.0d+00) then
! both coefficients a₄ and a₀ are not zero, so we have to solve full quartic
! equation
!
! a₄ * x⁴ + a₃ * x³ + a₂ * x² + a₁ * x + a₀ = 0
!
! calculate coefficients of P, Q, and R
!
pc = (- 3.0d+00 * a(4)**2 + 8.0d+00 * a(5) * a(3)) / (8.0d+00 * a(5)**2)
qc = (a(4)**3 - 4.0d+00 * a(5) * a(4) * a(3) &
+ 8.0d+00 * a(5)**2 * a(2)) / (8.0d+00 * a(5)**3)
rc = (- 3.0d+00 * a(4)**4 + 16.0d+00 * a(5) * a(4)**2 * a(3) &
- 64.0d+00 * a(5)**2 * a(4) * a(2) + 256.0d+00 * a(5)**3 * a(1)) &
/ (256.0d+00 * a(5)**4)
! prepare coefficients for the cubic equation
!
b(4) = 8.0d+00
b(3) = - 4.0d+00 * pc
b(2) = - 8.0d+00 * rc
b(1) = 4.0d+00 * pc * rc - qc**2
! find roots of the cubic equation
!
nr = cubic(b(1:4), z(1:3))
! check if the cubic solver returned any roots
!
if (nr > 0) then
! take the maximum root
!
zm = z(1)
! calculate coefficients to find quartic roots
!
k2 = 2.0d+00 * zm - pc
l2 = zm * zm - rc
! check if both coefficients are larger then zero
!
if (k2 > 0.0d+00 .or. l2 > 0.0d+00) then
! prepare coefficients K and L
!
if (k2 > l2) then
kc = sqrt(k2)
lc = 0.5d+00 * qc / kc
else if (l2 > k2) then
lc = sqrt(l2)
kc = 0.5d+00 * qc / lc
else
kc = sqrt(k2)
lc = sqrt(l2)
end if
! prepare coefficients for the first quadratic equation
!
c(3) = 1.0d+00
c(2) = - kc
c(1) = zm + lc
! find the first pair of roots
!
m = quadratic(c(1:3), y(1:2))
! update the roots
!
do n = 1, m
x(n) = y(n) - 0.25d+00 * a(4) / a(5)
end do
! update the number of roots
!
nr = m
! prepare coefficients for the second quadratic equation
!
c(3) = 1.0d+00
c(2) = kc
c(1) = zm - lc
! find the second pair of roots
!
m = quadratic(c(1:3), y(1:2))
! update the roots
!
do n = 1, m
x(nr + n) = y(n) - 0.25d+00 * a(4) / a(5)
end do
! update the number of roots
!
nr = nr + m
! reset the remainings roots
!
do n = nr + 1, 4
x(n) = 0.0d+00
end do
else
! both K² and L² are negative, which signifies that the quartic equation has
! no real roots; reset them
!
x(:) = 0.0d+00
! update the number of roots
!
nr = 0
end if
else
! weird, no cubic roots... it might mean that this is not real quartic equation
! or something is wrong; reset the roots
!
x(:) = 0.0d+00
end if
else
! since the coefficient a₀ is zero, there is one zero root, and the remaining
! roots are found from a cubic equation
!
! (a₄ * x³ + a₃ * x² + a₂ * x + a₁) * x = 0
!
x(4) = 0.0d+00
! call function cubic() to find the remaining roots
!
nr = cubic(a(2:5), x(1:3))
! increase the number of roots
!
nr = nr + 1
end if
else
! since the coefficient a₄ is zero, the equation reduces to a cubic one, and
! one root is undetermined
!
! a₃ * x³ + a₂ * x² + a₁ * x + a₀ = 0
!
x(4) = 0.0d+00
! call function cubic() to find the remaining roots
!
nr = cubic(a(1:4), x(1:3))
end if
! sort roots in the increasing order
!
call sort(nr, x(:))
!-------------------------------------------------------------------------------
!
end function quartic
!
!===============================================================================
!
! subroutine SORT:
! ---------------
!
! Subroutine sorts the input vector in the ascending order by straight
! insertion.
!
! Arguments:
!
! n - the number of elements to sort;
! x(:) - the vector which needs to be sorted;
!
! References:
!
! [1] Press, W. H, Teukolsky, S. A., Vetterling, W. T., Flannery, B. P.,
! "Numerical Recipes in Fortran",
! Cambridge University Press, Cambridge, 1992
!
!===============================================================================
!
subroutine sort(n, x)
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
integer , intent(in) :: n
real(kind=8), dimension(:), intent(inout) :: x
! local variables
!
integer :: k, l
real(kind=8) :: y
!
!-------------------------------------------------------------------------------
!
! return if only one element
!
if (n < 2) return
! iterate over all elements
!
do k = 2, n
! pick the element
!
y = x(k)
! go back to find the right place
!
do l = k - 1, 1, -1
if (x(l) <= y) goto 10
x(l+1) = x(l)
end do
! reset the index
!
l = 0
! insert the element
!
10 x(l+1) = y
end do ! k = 2, n
!-------------------------------------------------------------------------------
!
end subroutine sort
!
!===============================================================================
!
! subroutine TRIDIAG:
! ------------------
!
! Subroutine solves the system of tridiagonal linear equations using
! Thomas algorithm.
!
! Arguments:
!
! n - the size of the matrix;
! a(:) - the vector of the lower off-diagonal values;
! b(:) - the vector of the diagonal values;
! c(:) - the vector of the upper off-diagonal values;
! d(:) - the vector of the right side values;
! x(:) - the solution vector;
!
! References:
!
! [1] https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
!
!===============================================================================
!
subroutine tridiag(n, a, b, c, d, x)
implicit none
integer , intent(in) :: n
real(kind=8), dimension(n), intent(in) :: a, b, c, d
real(kind=8), dimension(n), intent(out) :: x
integer :: i
real(kind=8) :: t
real(kind=8), dimension(n) :: cp, dp
!
!-------------------------------------------------------------------------------
!
! decomposition and forward substitution
!
cp(1) = c(1) / b(1)
dp(1) = d(1) / b(1)
do i = 2, n
t = b(i) - a(i) * cp(i-1)
cp(i) = c(i) / t
dp(i) = (d(i) - a(i) * dp(i-1)) / t
end do
! backward substitution
!
x(n) = dp(n)
do i = n - 1, 1, -1
x(i) = dp(i) - cp(i) * x(i+1)
end do
!-------------------------------------------------------------------------------
!
end subroutine tridiag
!
!===============================================================================
!
! subroutine PENTADIAG:
! --------------------
!
! Subroutine solves the system of pentadiagonal linear equations using
! PTRANS-I algorithm.
!
! Arguments:
!
! n - the size of the matrix;
! e(:) - the vector of the outer lower off-diagonal values;
! c(:) - the vector of the inner lower off-diagonal values;
! d(:) - the vector of the diagonal values;
! a(:) - the vector of the inner upper off-diagonal values;
! b(:) - the vector of the outer upper off-diagonal values;
! y(:) - the vector of the right side values;
! x(:) - the solution vector;
!
! References:
!
! [1] Askar, S. S., and Karawia, A. A.,
! "On Solving Pentadiagonal Linear Systems via Transformations",
! Mathematical Problems in Engineering,
! 2015, Volume 2015, Article ID 232456, 9 pages
! http://dx.doi.org/10.1155/2015/232456
!
!===============================================================================
!
subroutine pentadiag(n, e, c, d, a, b, y, x)
implicit none
integer , intent(in) :: n
real(kind=8), dimension(n), intent(in) :: e, c, d, a, b, y
real(kind=8), dimension(n), intent(out) :: x
integer :: i
real(kind=8), dimension(n) :: al, bt, gm, mu, z
!
!-------------------------------------------------------------------------------
!
mu(1) = d(1)
al(1) = a(1) / mu(1)
bt(1) = b(1) / mu(1)
z (1) = y(1) / mu(1)
gm(2) = c(2)
mu(2) = d(2) - al(1) * gm(2)
al(2) = (a(2) - bt(1) * gm(2)) / mu(2)
bt(2) = b(2) / mu(2)
z (2) = (y(2) - z(1) * gm(2)) / mu(2)
do i = 3, n-2
gm(i) = c(i) - al(i-2) * e(i)
mu(i) = d(i) - bt(i-2) * e(i) - al(i-1) * gm(i)
al(i) = (a(i) - bt(i-1) * gm(i)) / mu(i)
bt(i) = b(i) / mu(i)
z (i) = (y(i) - z(i-2) * e(i) - z(i-1) * gm(i)) / mu(i)
end do
gm(n-1) = c(n-1) - al(n-3) * e(n-1)
mu(n-1) = d(n-1) - bt(n-3) * e(n-1) - al(n-2) * gm(n-1)
al(n-1) = (a(n-1) - bt(n-2) * gm(n-1)) / mu(n-1)
gm(n) = c(n) - al(n-2) * e(n)
mu(n) = d(n) - bt(n-3) * e(n) - al(n-1) * gm(n)
z(n-1) = (y(n-1) - z(n-2) * e(n-1) - z(n-2) * gm(n-1)) / mu(n-1)
z(n ) = (y(n) - z(n-1) * e(n) - z(n-1) * gm(n)) / mu(n)
x(n ) = z(n)
x(n-1) = z(n-1) - al(n-1) * x(n)
do i = n - 2, 1, -1
x(i) = z(i) - al(i) * x(i+1) - bt(i) * x(i+2)
end do
!-------------------------------------------------------------------------------
!
end subroutine pentadiag
!
!===============================================================================
!
! subroutine INVERT:
! -----------------
!
! Subroutine inverts the squared matrix.
!
! Arguments:
!
! n - the size of the matrix;
! m(:,:) - the matrix elements;
! r(:,:) - the inverted matrix elements;
! f - the solution flag;
!
! References:
!
! [1] Press, W. H, Teukolsky, S. A., Vetterling, W. T., Flannery, B. P.,
! "Numerical Recipes in Fortran",
! Cambridge University Press, Cambridge, 1992
!
!===============================================================================
!
subroutine invert(n, m, r, f)
! local variables are not implicit by default
!
implicit none
! input/output arguments
!
integer , intent(in) :: n
real(kind=max_prec), dimension(n,n), intent(in) :: m
real(kind=max_prec), dimension(n,n), intent(out) :: r
logical , intent(out) :: f
! local variables
!
logical :: flag = .true.
integer :: i, j, k
real(kind=max_prec) :: t
real(kind=max_prec), dimension(n,2*n) :: g
!
!-------------------------------------------------------------------------------
!
! augment input matrix with an identity matrix
!
do i = 1, n
do j = 1, 2 * n
if (j <= n) then
g(i,j) = m(i,j)
else if ((i+n) == j) then
g(i,j) = 1.0_max_prec
else
g(i,j) = 0.0_max_prec
end if
end do
end do
! reduce augmented matrix to upper traingular form
!
do k = 1, n - 1
if (.not. (abs(g(k,k)) > 0.0_max_prec)) then
flag = .false.
do i = k + 1, n
if (abs(g(i,k)) > 0.0_max_prec) then
do j = 1, 2 * n
g(k,j) = g(k,j) + g(i,j)
end do
flag = .true.
exit
end if
if (flag .eqv. .false.) then
r(:,:) = 0.0_max_prec
f = .false.
return
end if
end do
end if
do j = k + 1, n
t = g(j,k) / g(k,k)
do i = k, 2 * n
g(j,i) = g(j,i) - t * g(k,i)
end do
end do
end do
! test for invertibility
!
do i = 1, n
if (.not. (abs(g(i,i)) > 0.0_max_prec)) then
r(:,:) = 0.0_max_prec
f = .false.
return
end if
end do
! make diagonal elements as 1
!
do i = 1, n
t = g(i,i)
do j = i , 2 * n
g(i,j) = g(i,j) / t
end do
end do
! reduced right side half of augmented matrix to identity matrix
!
do k = n - 1, 1, -1
do i = 1, k
t = g(i,k+1)
do j = k, 2 * n
g(i,j) = g(i,j) - g(k+1,j) * t
end do
end do
end do
! store answer
!
do i = 1, n
do j = 1, n
r(i,j) = g(i,j+n)
end do
end do
f = .true.
!-------------------------------------------------------------------------------
!
end subroutine invert
!===============================================================================
!
end module algebra