FMS  2024.03
Flexible Modeling System
tridiagonal.inc
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 
20 !> @addtogroup tridiagonal_mod
21 !> @{
22 
23 !> @brief Sets up and solves the tridiagonal system of equations
24 !!
25 !> For simplicity, A and C are assumed to be dimensioned the same size
26 !! as B, D, and X, although any input values for A(N) and C(1) are ignored.
27 !! There are no checks to make sure the sizes agree.
28 !!
29 !! The value of A(N) is modified on output, and B and C are unchanged.
30 !!
31 !! For mixed precision, this routine uses the kind size macro(FMS_TRID_KIND_) to determine
32 !! which module variables are used/stored. This means a,b, and c values will only be stored for calls
33 !! of the same real kind value unless store_both_kinds is present and .true..
34 subroutine tri_invert_(x,d,a,b,c, store_both_kinds)
35 
36  real(FMS_TRID_KIND_), intent(out), dimension(:,:,:) :: x !< Solution to the tridiagonal system of equations
37  real(FMS_TRID_KIND_), intent(in), dimension(:,:,:) :: d !< The right-hand side term, see the schematic above.
38  real(FMS_TRID_KIND_), optional, dimension(:,:,:) :: a,b,c !< Left hand side terms(see schematic on module page).
39  !! If not provided, values from last call are used
40  logical, optional :: store_both_kinds !< Will save module state
41  !! variables for both kind types in order to be used in
42  !! subsequent calls with either kind.
43 
44  real(FMS_TRID_KIND_), dimension(size(x,1),size(x,2),size(x,3)) :: f
45  integer, parameter :: kindl = fms_trid_kind_
46 
47  integer :: k
48 
49  if(present(a)) then
50  !$OMP SINGLE
51  init_var = .true.
52  if(allocated(trid_real_type%e)) deallocate(trid_real_type%e)
53  if(allocated(trid_real_type%g)) deallocate(trid_real_type%g)
54  if(allocated(trid_real_type%bb)) deallocate(trid_real_type%bb)
55  if(allocated(trid_real_type%cc)) deallocate(trid_real_type%cc)
56  allocate(trid_real_type%e (size(x,1),size(x,2),size(x,3)))
57  allocate(trid_real_type%g (size(x,1),size(x,2),size(x,3)))
58  allocate(trid_real_type%bb(size(x,1),size(x,2)))
59  allocate(trid_real_type%cc(size(x,1),size(x,2),size(x,3)))
60  !$OMP END SINGLE
61 
62  trid_real_type%e(:,:,1) = - a(:,:,1) / b(:,:,1)
63  a(:,:,size(x,3)) = 0.0_kindl
64 
65  do k= 2,size(x,3)
66  trid_real_type%g(:,:,k) = 1.0_kindl/(b(:,:,k)+c(:,:,k)*trid_real_type%e(:,:,k-1))
67  trid_real_type%e(:,:,k) = - a(:,:,k)* trid_real_type%g(:,:,k)
68  end do
69  trid_real_type%cc = c
70  trid_real_type%bb = 1.0_kindl/b(:,:,1)
71 
72  end if
73 
74  if(.not.init_var) call mpp_error(fatal, 'tri_invert: a,b,and c args not provided or previously calculated.')
75 
76  f(:,:,1) = d(:,:,1)*trid_real_type%bb
77  do k= 2, size(x,3)
78  f(:,:,k) = (d(:,:,k) - trid_real_type%cc(:,:,k)*f(:,:,k-1))*trid_real_type%g(:,:,k)
79  end do
80 
81  x(:,:,size(x,3)) = f(:,:,size(x,3))
82  do k = size(x,3)-1,1,-1
83  x(:,:,k) = trid_real_type%e(:,:,k)*x(:,:,k+1)+f(:,:,k)
84  end do
85 
86  ! stores both kind values for subsequent calculations if running with option
87  if( present(store_both_kinds)) then
88  if( store_both_kinds ) then
89  if( fms_trid_kind_ .eq. r8_kind) then
90  tridiag_r4%e = real(trid_real_type%e, r4_kind)
91  tridiag_r4%g = real(trid_real_type%g, r4_kind)
92  tridiag_r4%cc = real(trid_real_type%cc, r4_kind)
93  tridiag_r4%bb = real(trid_real_type%bb, r4_kind)
94  init_tridiagonal_r4 = .true.
95  else
96  tridiag_r8%e = real(trid_real_type%e, r8_kind)
97  tridiag_r8%g = real(trid_real_type%g, r8_kind)
98  tridiag_r8%cc = real(trid_real_type%cc, r8_kind)
99  tridiag_r8%bb = real(trid_real_type%bb, r8_kind)
100  init_tridiagonal_r8 = .true.
101  endif
102  endif
103  endif
104 
105  return
106 end subroutine TRI_INVERT_
subroutine tri_invert_(x, d, a, b, c, store_both_kinds)
Sets up and solves the tridiagonal system of equations.
Definition: tridiagonal.inc:35