MOM6
regrid_solvers Module Reference

Detailed Description

Solvers of linear systems.

Date of creation: 2008.06.12 L. White.

This module contains solvers of linear systems. These routines have now been updated for greater efficiency, especially in special cases.

Functions/Subroutines

subroutine, public solve_linear_system (A, R, X, N, answers_2018)
 Solve the linear system AX = R by Gaussian elimination. More...
 
subroutine, public linear_solver (N, A, R, X)
 Solve the linear system AX = R by Gaussian elimination. More...
 
subroutine, public solve_tridiagonal_system (Al, Ad, Au, R, X, N, answers_2018)
 Solve the tridiagonal system AX = R. More...
 
subroutine, public solve_diag_dominant_tridiag (Al, Ac, Au, R, X, N)
 Solve the tridiagonal system AX = R. More...
 

Function/Subroutine Documentation

◆ linear_solver()

subroutine, public regrid_solvers::linear_solver ( integer, intent(in)  N,
real, dimension(n,n), intent(inout)  A,
real, dimension(n), intent(inout)  R,
real, dimension(n), intent(inout)  X 
)

Solve the linear system AX = R by Gaussian elimination.

This routine uses Gauss's algorithm to transform the system's original matrix into an upper triangular matrix. Back substitution then yields the answer. The matrix A must be square, with the first index varing along the row.

Parameters
[in]nThe size of the system
[in,out]aThe matrix being inverted [nondim]
[in,out]rsystem right-hand side [A]
[in,out]xsolution vector [A]

Definition at line 112 of file regrid_solvers.F90.

112  integer, intent(in) :: N !< The size of the system
113  real, dimension(N,N), intent(inout) :: A !< The matrix being inverted [nondim]
114  real, dimension(N), intent(inout) :: R !< system right-hand side [A]
115  real, dimension(N), intent(inout) :: X !< solution vector [A]
116 
117  ! Local variables
118  real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed
119  real :: factor ! The factor that eliminates the leading nonzero element in a row.
120  real :: I_pivot ! The reciprocal of the pivot value [inverse of the input units of a row of A]
121  real :: swap
122  logical :: found_pivot ! If true, a pivot has been found
123  integer :: i, j, k
124 
125  ! Loop on rows to transform the problem into multiplication by an upper-right matrix.
126  do i=1,n-1
127  ! Seek a pivot for column i starting in row i, and continuing into the remaining rows. If the
128  ! pivot is in a row other than i, swap them. If no valid pivot is found, i = N+1 after this loop.
129  do k=i,n ; if ( abs( a(i,k) ) > eps ) exit ; enddo ! end loop to find pivot
130  if ( k > n ) then ! No pivot could be found and the system is singular.
131  write(0,*) ' A=',a
132  call mom_error( fatal, 'The linear system is singular !' )
133  endif
134 
135  ! If the pivot is in a row that is different than row i, swap those two rows, noting that both
136  ! rows start with i-1 zero values.
137  if ( k /= i ) then
138  do j=i,n ; swap = a(j,i) ; a(j,i) = a(j,k) ; a(j,k) = swap ; enddo
139  swap = r(i) ; r(i) = r(k) ; r(k) = swap
140  endif
141 
142  ! Transform the pivot to 1 by dividing the entire row (right-hand side included) by the pivot
143  i_pivot = 1.0 / a(i,i)
144  a(i,i) = 1.0
145  do j=i+1,n ; a(j,i) = a(j,i) * i_pivot ; enddo
146  r(i) = r(i) * i_pivot
147 
148  ! Put zeros in column for all rows below that contain the pivot (which is row i)
149  do k=i+1,n ! k is the row index
150  factor = a(i,k)
151  ! A(i,k) = 0.0 ! These elements are not used again, so this line can be skipped for speed.
152  do j=i+1,n ; a(j,k) = a(j,k) - factor * a(j,i) ; enddo
153  r(k) = r(k) - factor * r(i)
154  enddo
155 
156  enddo ! end loop on i
157 
158  ! Solve the system by back substituting into what is now an upper-right matrix.
159  x(n) = r(n) / a(n,n) ! The last row is now trivially solved.
160  do i=n-1,1,-1 ! loop on rows, starting from second to last row
161  x(i) = r(i)
162  do j=i+1,n ; x(i) = x(i) - a(j,i) * x(j) ; enddo
163  enddo
164 

◆ solve_diag_dominant_tridiag()

subroutine, public regrid_solvers::solve_diag_dominant_tridiag ( real, dimension(n), intent(in)  Al,
real, dimension(n), intent(in)  Ac,
real, dimension(n), intent(in)  Au,
real, dimension(n), intent(in)  R,
real, dimension(n), intent(out)  X,
integer, intent(in)  N 
)

Solve the tridiagonal system AX = R.

This routine uses a variant of Thomas's algorithm to solve the tridiagonal system AX = R, in a form that is guaranteed to avoid dividing by a zero pivot. The matrix A is made up of lower (Al) and upper diagonals (Au) and a central diagonal Ad = Ac+Al+Au, where Al, Au, and Ac are all positive (or negative) definite. However when Ac is smaller than roundoff compared with (Al+Au), the answers are prone to inaccuracy.

Parameters
[in]nThe size of the system
[in]acMatrix center diagonal offset from Al + Au
[in]alMatrix lower diagonal
[in]auMatrix upper diagonal
[in]rsystem right-hand side
[out]xsolution vector

Definition at line 235 of file regrid_solvers.F90.

235  integer, intent(in) :: N !< The size of the system
236  real, dimension(N), intent(in) :: Ac !< Matrix center diagonal offset from Al + Au
237  real, dimension(N), intent(in) :: Al !< Matrix lower diagonal
238  real, dimension(N), intent(in) :: Au !< Matrix upper diagonal
239  real, dimension(N), intent(in) :: R !< system right-hand side
240  real, dimension(N), intent(out) :: X !< solution vector
241  ! Local variables
242  real, dimension(N) :: c1 ! Au / pivot for the backward sweep
243  real :: d1 ! The next value of 1.0 - c1
244  real :: I_pivot ! The inverse of the most recent pivot
245  real :: denom_t1 ! The first term in the denominator of the inverse of the pivot.
246  integer :: k ! Loop index
247 
248  ! Factorization and forward sweep, in a form that will never give a division by a
249  ! zero pivot for positive definite Ac, Al, and Au.
250  i_pivot = 1.0 / (ac(1) + au(1))
251  d1 = ac(1) * i_pivot
252  c1(1) = au(1) * i_pivot
253  x(1) = r(1) * i_pivot
254  do k=2,n-1
255  denom_t1 = ac(k) + d1 * al(k)
256  i_pivot = 1.0 / (denom_t1 + au(k))
257  d1 = denom_t1 * i_pivot
258  c1(k) = au(k) * i_pivot
259  x(k) = (r(k) - al(k) * x(k-1)) * i_pivot
260  enddo
261  i_pivot = 1.0 / (ac(n) + d1 * al(n))
262  x(n) = (r(n) - al(n) * x(n-1)) * i_pivot
263  ! Backward sweep
264  do k=n-1,1,-1
265  x(k) = x(k) - c1(k) * x(k+1)
266  enddo
267 

◆ solve_linear_system()

subroutine, public regrid_solvers::solve_linear_system ( real, dimension(n,n), intent(inout)  A,
real, dimension(n), intent(inout)  R,
real, dimension(n), intent(inout)  X,
integer, intent(in)  N,
logical, intent(in), optional  answers_2018 
)

Solve the linear system AX = R by Gaussian elimination.

This routine uses Gauss's algorithm to transform the system's original matrix into an upper triangular matrix. Back substitution yields the answer. The matrix A must be square, with the first index varing down the column.

Parameters
[in]nThe size of the system
[in,out]aThe matrix being inverted [nondim]
[in,out]rsystem right-hand side [A]
[in,out]xsolution vector [A]
[in]answers_2018If true or absent use older, less efficient expressions.

Definition at line 20 of file regrid_solvers.F90.

20  integer, intent(in) :: N !< The size of the system
21  real, dimension(N,N), intent(inout) :: A !< The matrix being inverted [nondim]
22  real, dimension(N), intent(inout) :: R !< system right-hand side [A]
23  real, dimension(N), intent(inout) :: X !< solution vector [A]
24  logical, optional, intent(in) :: answers_2018 !< If true or absent use older, less efficient expressions.
25  ! Local variables
26  real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed
27  real :: factor ! The factor that eliminates the leading nonzero element in a row.
28  real :: pivot, I_pivot ! The pivot value and its reciprocal [nondim]
29  real :: swap_a, swap_b
30  logical :: found_pivot ! If true, a pivot has been found
31  logical :: old_answers ! If true, use expressions that give the original (2008 through 2018) MOM6 answers
32  integer :: i, j, k
33 
34  old_answers = .true. ; if (present(answers_2018)) old_answers = answers_2018
35 
36  ! Loop on rows to transform the problem into multiplication by an upper-right matrix.
37  do i = 1,n-1
38 
39 
40  ! Start to look for a pivot in the current row, i. If the pivot in row i is not valid,
41  ! keep looking for a valid pivot by searching the entries of column i in rows below row i.
42  ! Once a valid pivot is found (say in row k), rows i and k are swaped.
43  found_pivot = .false.
44  k = i
45  do while ( ( .NOT. found_pivot ) .AND. ( k <= n ) )
46  if ( abs( a(k,i) ) > eps ) then ! A valid pivot has been found
47  found_pivot = .true.
48  else ! Seek a valid pivot in the next row
49  k = k + 1
50  endif
51  enddo ! end loop to find pivot
52 
53  ! If no pivot could be found, the system is singular.
54  if ( .NOT. found_pivot ) then
55  write(0,*) ' A=',a
56  call mom_error( fatal, 'The linear system is singular !' )
57  endif
58 
59  ! If the pivot is in a row that is different than row i, that is if
60  ! k is different than i, we need to swap those two rows
61  if ( k /= i ) then
62  do j = 1,n
63  swap_a = a(i,j) ; a(i,j) = a(k,j) ; a(k,j) = swap_a
64  enddo
65  swap_b = r(i) ; r(i) = r(k) ; r(k) = swap_b
66  endif
67 
68  ! Transform pivot to 1 by dividing the entire row (right-hand side included) by the pivot
69  if (old_answers) then
70  pivot = a(i,i)
71  do j = i,n ; a(i,j) = a(i,j) / pivot ; enddo
72  r(i) = r(i) / pivot
73  else
74  i_pivot = 1.0 / a(i,i)
75  a(i,i) = 1.0
76  do j = i+1,n ; a(i,j) = a(i,j) * i_pivot ; enddo
77  r(i) = r(i) * i_pivot
78  endif
79 
80  ! #INV: At this point, A(i,i) is a suitable pivot and it is equal to 1
81 
82  ! Put zeros in column for all rows below that contain the pivot (which is row i)
83  do k = i+1,n ! k is the row index
84  factor = a(k,i)
85  ! A(k,i) = 0.0 ! These elements are not used again, so this line can be skipped for speed.
86  do j = i+1,n ! j is the column index
87  a(k,j) = a(k,j) - factor * a(i,j)
88  enddo
89  r(k) = r(k) - factor * r(i)
90  enddo
91 
92  enddo ! end loop on i
93 
94  ! Solve system by back substituting in what is now an upper-right matrix.
95  x(n) = r(n) / a(n,n) ! The last row is now trivially solved.
96  do i = n-1,1,-1 ! loop on rows, starting from second to last row
97  x(i) = r(i)
98  do j = i+1,n
99  x(i) = x(i) - a(i,j) * x(j)
100  enddo
101  if (old_answers) x(i) = x(i) / a(i,i)
102  enddo
103 

◆ solve_tridiagonal_system()

subroutine, public regrid_solvers::solve_tridiagonal_system ( real, dimension(n), intent(in)  Al,
real, dimension(n), intent(in)  Ad,
real, dimension(n), intent(in)  Au,
real, dimension(n), intent(in)  R,
real, dimension(n), intent(out)  X,
integer, intent(in)  N,
logical, intent(in), optional  answers_2018 
)

Solve the tridiagonal system AX = R.

This routine uses Thomas's algorithm to solve the tridiagonal system AX = R. (A is made up of lower, middle and upper diagonals)

Parameters
[in]nThe size of the system
[in]adMatrix center diagonal
[in]alMatrix lower diagonal
[in]auMatrix upper diagonal
[in]rsystem right-hand side
[out]xsolution vector
[in]answers_2018If true use older, less acccurate expressions.

Definition at line 173 of file regrid_solvers.F90.

173  integer, intent(in) :: N !< The size of the system
174  real, dimension(N), intent(in) :: Ad !< Matrix center diagonal
175  real, dimension(N), intent(in) :: Al !< Matrix lower diagonal
176  real, dimension(N), intent(in) :: Au !< Matrix upper diagonal
177  real, dimension(N), intent(in) :: R !< system right-hand side
178  real, dimension(N), intent(out) :: X !< solution vector
179  logical, optional, intent(in) :: answers_2018 !< If true use older, less acccurate expressions.
180  ! Local variables
181  real, dimension(N) :: pivot, Al_piv
182  real, dimension(N) :: c1 ! Au / pivot for the backward sweep
183  real :: I_pivot ! The inverse of the most recent pivot
184  integer :: k ! Loop index
185  logical :: old_answers ! If true, use expressions that give the original (2008 through 2018) MOM6 answers
186 
187  old_answers = .true. ; if (present(answers_2018)) old_answers = answers_2018
188 
189  if (old_answers) then
190  ! This version gives the same answers as the original (2008 through 2018) MOM6 code
191  ! Factorization and forward sweep
192  pivot(1) = ad(1)
193  x(1) = r(1)
194  do k = 2,n
195  al_piv(k) = al(k) / pivot(k-1)
196  pivot(k) = ad(k) - al_piv(k) * au(k-1)
197  x(k) = r(k) - al_piv(k) * x(k-1)
198  enddo
199 
200  ! Backward sweep
201  x(n) = r(n) / pivot(n) ! This should be X(N) / pivot(N), but is OK if Al(N) = 0.
202  do k = n-1,1,-1
203  x(k) = ( x(k) - au(k)*x(k+1) ) / pivot(k)
204  enddo
205  else
206  ! This is a more typical implementation of a tridiagonal solver than the one above.
207  ! It is mathematically equivalent but differs at roundoff, which can cascade up to larger values.
208 
209  ! Factorization and forward sweep
210  i_pivot = 1.0 / ad(1)
211  x(1) = r(1) * i_pivot
212  do k = 2,n
213  c1(k-1) = au(k-1) * i_pivot
214  i_pivot = 1.0 / (ad(k) - al(k) * c1(k-1))
215  x(k) = (r(k) - al(k) * x(k-1)) * i_pivot
216  enddo
217  ! Backward sweep
218  do k = n-1,1,-1
219  x(k) = x(k) - c1(k) * x(k+1)
220  enddo
221 
222  endif
223