SABER
tools_asa007.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: tools_asa007
3 !> Inverse of symmetric positive definite matrix routines
4 ! Source: https://people.sc.fsu.edu/~jburkardt/f_src/asa007/asa007.html
5 ! Author: Michael Healy
6 ! Original licensing: none
7 ! Modified by Alan Miller
8 ! Fortran 90 version by John Burkardt
9 ! Modified by Benjamin Menetrier for BUMP
10 ! Licensing: this code is distributed under the CeCILL-C license
11 ! Copyright © 2015-... UCAR, CERFACS, METEO-FRANCE and IRIT
12 !----------------------------------------------------------------------
14 
15 use tools_kinds, only: kind_real
16 use tools_repro, only: inf,infeq
17 use type_mpl, only: mpl_type
18 
19 implicit none
20 
21 real(kind_real),parameter :: eta = 1.0e-9_kind_real ! Small parameter for the Cholesky decomposition
22 
23 private
25 
26 contains
27 
28 !----------------------------------------------------------------------
29 ! Subroutine: asa007_cholesky
30 !> Compute cholesky decomposition
31 !----------------------------------------------------------------------
32 subroutine asa007_cholesky(mpl,n,nn,a,u,ierr)
33 
34 implicit none
35 
36 ! Passed variables
37 type(mpl_type),intent(inout) :: mpl !< MPI data
38 integer,intent(in) :: n !< Matrix rank
39 integer,intent(in) :: nn !< Half-matrix size (n*(n-1)/2)
40 real(kind_real),intent(in) :: a(nn) !< Matrix
41 real(kind_real),intent(out) :: u(nn) !< Matrix square-root
42 integer,intent(out) :: ierr !< Error status
43 
44 ! Local variables
45 integer :: i,icol,ii,irow,j,k,kk,l,m
46 real(kind_real) :: w,x
47 character(len=1024),parameter :: subr = 'asa007_cholesky'
48 
49 ! Initialization
50 ii = 0
51 j = 1
52 k = 0
53 if (nn/=(n*(n+1))/2) then
54  call mpl%abort(subr,'wrong size in Cholesky decomposition')
55 end if
56 w = 0.0
57 
58 ! Factorize column by column, ICOL = column number
59 do icol=1,n
60  ii = ii+icol
61  x = eta**2*a(ii)
62  l = 0
63  kk = 0
64 
65  ! IROW = row number within column ICOL
66  do irow=1,icol
67  kk = kk+irow
68  k = k+1
69  w = a(k)
70  m = j
71  do i=1,irow-1
72  l = l+1
73  w = w-u(l)*u(m)
74  m = m+1
75  end do
76  l = l+1
77  if (irow==icol) exit
78  if (abs(u(l))>0.0) then
79  u(k) = w/u(l)
80  else
81  u(k) = 0.0
82  if (inf(abs(x*a(k)),w**2)) then
83  ierr = 1
84  return
85  end if
86  end if
87  end do
88 
89  ! End of row, estimate relative accuracy of diagonal element
90  if (infeq(abs(w),abs(eta*a(k)))) then
91  u(k) = 0.0
92  else
93  if (w<0.0) then
94  ierr = 2
95  return
96  end if
97  u(k) = sqrt(w)
98  end if
99  j = j+icol
100 end do
101 
102 ! No error
103 ierr = 0
104 
105 end subroutine asa007_cholesky
106 
107 !----------------------------------------------------------------------
108 ! Subroutine: asa007_syminv
109 !> Compute inverse of a symmetric matrix
110 !----------------------------------------------------------------------
111 subroutine asa007_syminv(mpl,n,nn,a,c,ierr)
112 
113 implicit none
114 
115 ! Passed variables
116 type(mpl_type),intent(inout) :: mpl !< MPI data
117 integer,intent(in) :: n !< Matrix rank
118 integer,intent(in) :: nn !< Half-matrix size (n*(n-1)/2)
119 real(kind_real),intent(in) :: a(nn) !< Matrix
120 real(kind_real),intent(out) :: c(nn) !< Matrix inverse
121 integer,intent(out) :: ierr !< Error status
122 
123 ! Local variables
124 integer :: i,icol,irow,j,jcol,k,l,mdiag,ndiag,nrow
125 real(kind_real) :: w(n),x
126 character(len=1024),parameter :: subr = 'asa007_syminv'
127 
128 ! Initialization
129 nrow = n
130 if (nn/=(n*(n+1))/2) then
131  call mpl%abort(subr,'wrong size in matrix inversion')
132 end if
133 w = 0.0
134 
135 ! Compute the Cholesky factorization of A
136 call asa007_cholesky(mpl,n,nn,a,c,ierr)
137 if (ierr/=0) return
138 
139 ! Invert C and form the product (Cinv)' * Cinv, where Cinv is the inverse of C, row by row starting with the last row
140 irow = nrow
141 ndiag = nn
142 
143 do
144  if (abs(c(ndiag))>0.0) then
145  ! General case
146  l = ndiag
147  do i=irow,nrow
148  w(i) = c(l)
149  l = l+i
150  end do
151  icol = nrow
152  jcol = nn
153  mdiag = nn
154  do
155  l = jcol
156  if (icol==irow) then
157  x = 1.0/w(irow)
158  else
159  x = 0.0
160  end if
161  k = nrow
162  do while (irow<k)
163  x = x-w(k)*c(l)
164  k = k-1
165  l = l-1
166  if (mdiag<l) l = l-k+1
167  end do
168  c(l) = x/w(irow)
169  if (icol<=irow) exit
170  mdiag = mdiag-icol
171  icol = icol-1
172  jcol = jcol-1
173  end do
174  else
175  ! Special case, zero diagonal element
176  l = ndiag
177  do j=irow,nrow
178  c(l) = 0.0
179  l = l+j
180  end do
181  end if
182  ndiag = ndiag-irow
183  irow = irow-1
184  if (irow<=0) exit
185 end do
186 
187 end subroutine asa007_syminv
188 
189 end module tools_asa007
tools_repro::inf
logical function, public inf(x, y)
Inferior test for reals.
Definition: tools_repro.F90:53
tools_asa007
Inverse of symmetric positive definite matrix routines.
Definition: tools_asa007.F90:13
tools_repro
Reproducibility functions.
Definition: tools_repro.F90:8
tools_asa007::asa007_cholesky
subroutine, public asa007_cholesky(mpl, n, nn, a, u, ierr)
Compute cholesky decomposition.
Definition: tools_asa007.F90:33
tools_repro::infeq
logical function, public infeq(x, y)
Inferior or equal test for reals.
Definition: tools_repro.F90:73
tools_asa007::asa007_syminv
subroutine, public asa007_syminv(mpl, n, nn, a, c, ierr)
Compute inverse of a symmetric matrix.
Definition: tools_asa007.F90:112
tools_asa007::eta
real(kind_real), parameter eta
Definition: tools_asa007.F90:21
tools_kinds
Kinds definition.
Definition: tools_kinds.F90:8
type_mpl
MPI parameters derived type.
Definition: type_mpl.F90:8
type_mpl::mpl_type
Definition: type_mpl.F90:24
tools_kinds::kind_real
integer, parameter, public kind_real
Definition: tools_kinds.F90:18