FV3 Bundle
tools_asa007.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: tools_asa007
3 ! Purpose: 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 ! Purpose: compute cholesky decomposition
31 !----------------------------------------------------------------------
32 subroutine asa007_cholesky(mpl,n,nn,a,u)
33 
34 implicit none
35 
36 ! Passed variables
37 type(mpl_type),intent(in) :: 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 
43 ! Local variables
44 integer :: i,icol,ii,irow,j,k,kk,l,m
45 real(kind_real) :: w,x
46 
47 ! Initialization
48 ii = 0
49 j = 1
50 k = 0
51 if (nn/=(n*(n+1))/2) then
52  call mpl%abort('wrong size in Cholesky decomposition')
53 end if
54 w = 0.0
55 
56 ! Factorize column by column, ICOL = column number
57 do icol=1,n
58  ii = ii+icol
59  x = eta**2*a(ii)
60  l = 0
61  kk = 0
62 
63  ! IROW = row number within column ICOL
64  do irow=1,icol
65  kk = kk+irow
66  k = k+1
67  w = a(k)
68  m = j
69  do i=1,irow-1
70  l = l+1
71  w = w-u(l)*u(m)
72  m = m+1
73  end do
74  l = l+1
75  if (irow==icol) exit
76  if (abs(u(l))>0.0) then
77  u(k) = w/u(l)
78  else
79  u(k) = 0.0
80  if (inf(abs(x*a(k)),w**2)) call mpl%abort('A is not positive semi-definite')
81  end if
82  end do
83 
84  ! End of row, estimate relative accuracy of diagonal element
85  if (infeq(abs(w),abs(eta*a(k)))) then
86  u(k) = 0.0
87  else
88  if (w<0.0) call mpl%abort('A is not positive semi-definite')
89  u(k) = sqrt(w)
90  end if
91  j = j+icol
92 end do
93 
94 end subroutine asa007_cholesky
95 
96 !----------------------------------------------------------------------
97 ! Subroutine: asa007_syminv
98 ! Purpose: compute inverse of a symmetric matrix
99 !----------------------------------------------------------------------
100 subroutine asa007_syminv(mpl,n,nn,a,c)
102 implicit none
103 
104 ! Passed variables
105 type(mpl_type),intent(in) :: mpl ! MPI data
106 integer,intent(in) :: n ! Matrix rank
107 integer,intent(in) :: nn ! Half-matrix size (n*(n-1)/2)
108 real(kind_real),intent(in) :: a(nn) ! Matrix
109 real(kind_real),intent(out) :: c(nn) ! Matrix inverse
110 
111 ! Local variables
112 integer :: i,icol,irow,j,jcol,k,l,mdiag,ndiag,nrow
113 real(kind_real) :: w(n),x
114 
115 ! Initialization
116 nrow = n
117 if (nn/=(n*(n+1))/2) then
118  call mpl%abort('wrong size in Cholesky decomposition')
119 end if
120 w = 0.0
121 
122 ! Compute the Cholesky factorization of A
123 call asa007_cholesky(mpl,n,nn,a,c)
124 
125 ! Invert C and form the product (Cinv)' * Cinv, where Cinv is the inverse of C, row by row starting with the last row
126 irow = nrow
127 ndiag = nn
128 
129 do
130  if (abs(c(ndiag))>0.0) then
131  ! General case
132  l = ndiag
133  do i=irow,nrow
134  w(i) = c(l)
135  l = l+i
136  end do
137  icol = nrow
138  jcol = nn
139  mdiag = nn
140  do
141  l = jcol
142  if (icol==irow) then
143  x = 1.0/w(irow)
144  else
145  x = 0.0
146  end if
147  k = nrow
148  do while (irow<k)
149  x = x-w(k)*c(l)
150  k = k-1
151  l = l-1
152  if (mdiag<l) l = l-k+1
153  end do
154  c(l) = x/w(irow)
155  if (icol<=irow) exit
156  mdiag = mdiag-icol
157  icol = icol-1
158  jcol = jcol-1
159  end do
160  else
161  ! Special case, zero diagonal element
162  l = ndiag
163  do j=irow,nrow
164  c(l) = 0.0
165  l = l+j
166  end do
167  end if
168  ndiag = ndiag-irow
169  irow = irow-1
170  if (irow<=0) exit
171 end do
172 
173 end subroutine asa007_syminv
174 
175 end module tools_asa007
subroutine, public asa007_syminv(mpl, n, nn, a, c)
logical function, public infeq(x, y)
Definition: tools_repro.F90:66
real(kind_real), parameter eta
subroutine, public asa007_cholesky(mpl, n, nn, a, u)
integer, parameter, public kind_real
logical function, public inf(x, y)
Definition: tools_repro.F90:47