FV3 Bundle
CRTM_Utility.f90
Go to the documentation of this file.
1 !
2 ! CRTM_Utility
3 !
4 ! Module containing CRTM numerical utility routines.
5 !
6 ! NOTE: This utility is specifically for use with RTSolution codes.
7 ! Adapted from package of MOM 1991, ASYMTX adapted from DISORT.
8 !
9 !
10 ! CREATION HISTORY:
11 ! Written by: Quanhua Liu, Quanhua.Liu@noaa.gov
12 ! Yong Han, Yong.Han@noaa.gov
13 ! Paul van Delst, paul.vandelst@noaa.g
14 ! 08-Jun-2004
15 !
16 
17 MODULE crtm_utility
18 
19 
20  ! -----------------
21  ! Environment setup
22  ! -----------------
23  ! Module use
24  USE type_kinds , ONLY: fp
26  USE crtm_parameters, ONLY: zero, one, two, &
27  point_25, point_5, &
28  pi
29  ! Disable implicit typing
30  IMPLICIT NONE
31 
32 
33  ! ------------
34  ! Visibilities
35  ! ------------
36  PRIVATE
37  PUBLIC :: matinv
38  PUBLIC :: double_gauss_quadrature
39  PUBLIC :: legendre
40  PUBLIC :: legendre_m
41  PUBLIC :: asymtx,asymtx_tl,asymtx_ad
42  PUBLIC :: asymtx2_ad
43 
44 
45  ! -------------------
46  ! Procedure overloads
47  ! -------------------
48  INTERFACE legendre
49  MODULE PROCEDURE legendre_scalar
50  MODULE PROCEDURE legendre_rank1
51  END INTERFACE
52 
53 
54  ! -----------------
55  ! Module parameters
56  ! -----------------
57  ! Version Id for the module
58  CHARACTER(*), PARAMETER :: module_version_id = &
59  '$Id: CRTM_Utility.f90 60152 2015-08-13 19:19:13Z paul.vandelst@noaa.gov $'
60  ! Numerical small threshold value in Eigensystem
61  REAL(fp), PARAMETER :: eigen_threshold = 1.0e-20_fp
62 
63 
64 CONTAINS
65 
66 
67  SUBROUTINE double_gauss_quadrature(NUM, ABSCISSAS, WEIGHTS)
68  ! Generates the abscissas and weights for an even 2*NUM point
69  ! Gauss-Legendre quadrature. Only the NUM positive points are returned.
70  IMPLICIT NONE
71  INTEGER, INTENT(IN) :: num
72  REAL( fp), DIMENSION(:) :: abscissas, weights
73  INTEGER n, k, i, j, l
74  REAL(fp) :: x, xp, pl, pl1, pl2, dpl
75  ! REAL(fp), PARAMETER :: TINY1=3.0E-14_fp
76  REAL(fp) :: tiny1
77  parameter(tiny1=3.0d-14)
78  n = num
79  k = (n+1)/2
80  DO j = 1, k
81  x = cos(pi*(j-point_25)/(n+point_5))
82  i = 0
83  100 CONTINUE
84  pl1 = 1
85  pl = x
86  DO l = 2, n
87  pl2 = pl1
88  pl1 = pl
89  pl = ( (2*l-1)*x*pl1 - (l-1)*pl2 )/l
90  END DO
91  dpl = n*(x*pl-pl1)/(x*x-1)
92  xp = x
93  x = xp - pl/dpl
94  i = i+1
95  IF (abs(x-xp).GT.tiny1 .AND. i.LT.10) GO TO 100
96  abscissas(j) = (one-x)/two
97  abscissas(num+1-j) = (one+x)/two
98  weights(num+1-j) = one/((one-x*x)*dpl*dpl)
99  weights(j) = one/((one-x*x)*dpl*dpl)
100  END DO
101  RETURN
102  END SUBROUTINE double_gauss_quadrature
103 !
104 !
105  FUNCTION matinv(A, Error_Status)
106  ! --------------------------------------------------------------------
107  ! Compute the inversion of the matrix A
108  ! Invert matrix by Gauss method
109  ! --------------------------------------------------------------------
110  IMPLICIT NONE
111  REAL(fp), intent(in),dimension(:,:) :: a
112  INTEGER, INTENT( OUT ) :: error_status
113 
114  INTEGER:: n
115  REAL(fp), dimension(size(a,1),size(a,2)) :: b
116  REAL(fp ), dimension(size(a,1),size(a,2)) :: matinv
117  REAL(fp), dimension(size(a,1)) :: temp
118  ! Local parameters
119  CHARACTER(*), PARAMETER :: routine_name = 'matinv'
120  ! - - - Local Variables - - -
121  REAL(fp) :: c, d
122  INTEGER :: i, j, k, m, imax(1), ipvt(size(a,1))
123  ! - - - - - - - - - - - - - -
124  error_status = success
125  b = a
126  n=size(a,1)
127  matinv=a
128  ipvt = (/ (i, i = 1, n) /)
129  ! Go into loop- b, the inverse, is initialized equal to a above
130  DO k = 1,n
131  ! Find the largest value and position of that value
132  imax = maxloc(abs(b(k:n,k)))
133  m = k-1+imax(1)
134  ! sigular matrix check
135  IF ( abs(b(m,k)).LE.(1.e-40_fp) ) THEN
136  error_status = failure
137  CALL display_message( routine_name, 'Singular matrix', error_status )
138  RETURN
139  END IF
140  ! get the row beneath the current row if the current row will
141  ! not compute
142  IF ( m .ne. k ) THEN
143  ipvt( (/m,k/) ) = ipvt( (/k,m/) )
144  b((/m,k/),:) = b((/k,m/),:)
145  END IF
146  ! d is a coefficient - brings the pivot value to one and then is applied
147  ! to the rest of the row
148  d = 1/b(k,k)
149  temp = b(:,k)
150  DO j = 1, n
151  c = b(k,j)*d
152  b(:,j) = b(:,j)-temp*c
153  b(k,j) = c
154  END DO
155  b(:,k) = temp*(-d)
156  b(k,k) = d
157  END DO
158  matinv(:,ipvt) = b
159  END FUNCTION matinv
160 !
161 !
162  SUBROUTINE legendre_scalar(MOA,ANG,PL)
163  ! calculating Legendre polynomial using recurrence relationship
164  IMPLICIT NONE
165  INTEGER, INTENT(in) :: MOA
166  REAL(fp), intent(in) :: ANG
167  REAL(fp), intent(out), dimension(0:MOA):: PL
168  INTEGER :: j
169  pl(0)=one
170  pl(1)=ang
171  IF ( moa.GE.2 ) THEN
172  DO j = 1, moa -1
173  pl(j+1)=REAL(2*J+1,fp)/REAL(J+1,fp)*ANG*PL(J) &
174  -REAL(J,fp)/REAL(J+1,fp)*PL(J-1)
175  END DO
176  END IF
177  RETURN
178  END SUBROUTINE legendre_scalar
179 !
180 !
181  SUBROUTINE legendre_rank1(MOA,NU,ANG,PL)
182  ! calculating Legendre polynomial using recurrence relationship
183  IMPLICIT NONE
184  INTEGER, intent(in):: MOA,NU
185  REAL(fp), INTENT(in), dimension(NU):: ANG
186  REAL(fp), INTENT(out),dimension(0:MOA,NU):: PL
187  REAL(fp) :: TE
188  INTEGER :: i,j
189  DO i = 1,nu
190  te=ang(i)
191  pl(0,i)=one
192  pl(1,i)=te
193  IF(moa.GE.2) THEN
194  DO j = 1, moa-1
195  pl(j+1,i)=REAL(2*J+1, fp)/REAL(J+1, fp)*TE*PL(J,i) &
196  -REAL(J, fp)/REAL(J+1, fp)*PL(J-1,i)
197  END DO
198  END IF
199  END DO
200  RETURN
201  END SUBROUTINE legendre_rank1
202 !
203 !
204  SUBROUTINE legendre_m(MF,N,U,PL)
205  IMPLICIT NONE
206  REAL(fp), INTENT(IN) :: u
207  INTEGER, INTENT(IN) :: n,mf
208  REAL(fp), DIMENSION(0:N) :: pl
209  REAL(fp) :: f
210  INTEGER:: j
211  IF ( mf.GT.n ) THEN
212  pl = 0.0_fp
213  ELSE
214  IF ( mf .EQ. 0 ) THEN
215  f = 1.0_fp
216  pl(0) = 1.0_fp
217  ELSE
218  f=sqrt(gamma2(2*mf))/gamma2(mf)/(2**mf)
219  pl(mf)=f*sqrt((1.0_fp-u*u)**mf)
220  END IF
221  IF ( n.GT.mf ) pl(mf+1)=u*sqrt(2*mf+1.0_fp)*pl(mf)
222  IF( n.gt.(mf+1) ) THEN
223  DO j=mf+1,n-1
224  pl(j+1)=(float(2*j+1)*u*pl(j) &
225  -sqrt(float(j*j-mf*mf))*pl(j-1))/sqrt(float((j+1)**2-mf*mf))
226  END DO
227  END IF
228  END IF
229  END SUBROUTINE legendre_m
230 !
231 !
232  FUNCTION gamma2(N)
233  ! Compute gamma function value
234  IMPLICIT NONE
235  INTEGER, INTENT(IN) :: n
236  INTEGER :: i
237  REAL(fp) :: gamma2
238 
239  IF ( n.lt.0 ) THEN
240  CALL display_message('gamma2','Invalid input', warning)
241  gamma2 = 1.0_fp
242  ELSE
243  gamma2=1.0_fp
244  IF( n.gt.0 ) THEN
245  DO i = 1 , n
246  gamma2=gamma2*float(i)
247  END DO
248  END IF
249  END IF
250  END FUNCTION gamma2
251 !
252 !
253  SUBROUTINE asymtx( AAD, M, IA, IEVEC, &
254  EVECD, EVALD, IER)
256 ! ======= D O U B L E P R E C I S I O N V E R S I O N ======
257 
258 ! Solves eigenfunction problem for real asymmetric matrix
259 ! for which it is known a priori that the eigenvalues are real.
260 
261 ! This is an adaptation of a subroutine EIGRF in the IMSL
262 ! library to use real instead of complex arithmetic, accounting
263 ! for the known fact that the eigenvalues and eigenvectors in
264 ! the discrete ordinate solution are real. Other changes include
265 ! putting all the called subroutines in-line, deleting the
266 ! performance index calculation, updating many DO-loops
267 ! to Fortran77, and in calculating the machine precision
268 ! TOL instead of specifying it in a data statement.
269 
270 ! EIGRF is based primarily on EISPACK routines. The matrix is
271 ! first balanced using the parlett-reinsch algorithm. Then
272 ! the Martin-Wilkinson algorithm is applied.
273 
274 ! References:
275 ! Dongarra, J. and C. Moler, EISPACK -- A Package for Solving
276 ! Matrix Eigenvalue Problems, in Cowell, ed., 1984:
277 ! Sources and Development of Mathematical Software,
278 ! Prentice-Hall, Englewood Cliffs, NJ
279 ! Parlett and Reinsch, 1969: Balancing a Matrix for Calculation
280 ! of Eigenvalues and Eigenvectors, Num. Math. 13, 293-304
281 ! Wilkinson, J., 1965: The Algebraic Eigenvalue Problem,
282 ! Clarendon Press, Oxford
283 
284 ! I N P U T V A R I A B L E S:
285 
286 ! AAD : input asymmetric matrix, destroyed after solved
287 ! M : order of A
288 ! IA : first dimension of A
289 ! IEVEC : first dimension of EVECD
290 
291 ! O U T P U T V A R I A B L E S:
292 
293 ! EVECD : (unnormalized) eigenvectors of A
294 ! ( column J corresponds to EVALD(J) )
295 
296 ! EVALD : (unordered) eigenvalues of A ( dimension at least M )
297 
298 ! IER : if .NE. 0, signals that EVALD(IER) failed to converge;
299 ! in that case eigenvalues IER+1,IER+2,...,M are
300 ! correct but eigenvalues 1,...,IER are set to zero.
301  IMPLICIT NONE
302  LOGICAL bad_status
303  CHARACTER(len=200) :: message
304 
305 ! S C R A T C H V A R I A B L E S:
306 
307 ! WKD : WORK AREA ( DIMENSION AT LEAST 2*M )
308 !+---------------------------------------------------------------------+
309 
310 ! include file for dimensioning
311 
312 
313 !! INCLUDE '../includes/VLIDORT.PARS'
314 
315 ! input/output arguments
316 
317  INTEGER :: m, ia, ievec, ier
318  INTEGER, PARAMETER :: maxstrmstks = 30
319  REAL( fp), PARAMETER :: c1=0.4375_fp,c2=0.5_fp,c3=0.75_fp,c4=0.95_fp,c5=16.0_fp,c6=256.0_fp
320  REAL( fp), DIMENSION(:,:) :: aad, evecd
321  REAL( fp), DIMENSION(:) :: evald
322  REAL( fp) :: wkd(4*maxstrmstks), nor_factor
323 
324 
325 ! local variables (explicit declaration
326 
327  LOGICAL noconv, notlas
328  INTEGER :: i, j, l, k, kkk, lll,n, n1, n2, in, lb, ka, ii
329 ! DOUBLE PRECISION TOL, DISCRI, SGN, RNORM, W, F, G, H, P, Q, R
330  REAL(fp) :: tol, discri, sgn, rnorm, w, f, g, h, p, q, r
331  REAL(fp) :: repl, col, row, scale, t, x, z, s, y, uu, vv
332 !
333  ier = 0
334  bad_status = .false.
335  message = ' '
336 
337 ! Here change to bypass D1MACH:
338  tol = 1.0d-12
339 ! TOL = D1MACH(4)
340  IF ( m.LT.1 .OR. ia.LT.m .OR. ievec.LT.m ) THEN
341  message = 'ASYMTX--bad input variable(s)'
342  bad_status = .true.
343  RETURN
344  ENDIF
345 ! ** HANDLE 1X1 AND 2X2 SPECIAL CASES
346  IF ( m.EQ.1 ) THEN
347  evald(1) = aad(1,1)
348  evecd(1,1) = 1.0d0
349  RETURN
350  ELSE IF ( m.EQ.2 ) THEN
351  discri = ( aad(1,1) - aad(2,2) )**2 + 4.0d0*aad(1,2)*aad(2,1)
352  IF ( discri.LT.zero ) THEN
353  message = 'ASYMTX--COMPLEX EVALS IN 2X2 CASE'
354  bad_status = .true.
355  RETURN
356  ENDIF
357  sgn = one
358  IF ( aad(1,1).LT.aad(2,2) ) sgn = - one
359  evald(1) = 0.5d0*( aad(1,1) + aad(2,2) + sgn*sqrt(discri) )
360  evald(2) = 0.5d0*( aad(1,1) + aad(2,2) - sgn*sqrt(discri) )
361  evecd(1,1) = one
362  evecd(2,2) = one
363  IF ( aad(1,1).EQ.aad(2,2) .AND. &
364  (aad(2,1).EQ.zero.OR.aad(1,2).EQ.zero) ) THEN
365  rnorm = abs(aad(1,1))+abs(aad(1,2))+ &
366  abs(aad(2,1))+abs(aad(2,2))
367  w = tol * rnorm
368  evecd(2,1) = aad(2,1) / w
369  evecd(1,2) = - aad(1,2) / w
370  ELSE
371  evecd(2,1) = aad(2,1) / ( evald(1) - aad(2,2) )
372  evecd(1,2) = aad(1,2) / ( evald(2) - aad(1,1) )
373  ENDIF
374  RETURN
375  END IF
376 ! ** INITIALIZE OUTPUT VARIABLES
377  DO 20 i = 1, m
378  evald(i) = zero
379  DO 10 j = 1, m
380  evecd(i,j) = zero
381 10 CONTINUE
382  evecd(i,i) = one
383 20 CONTINUE
384 ! ** BALANCE THE INPUT MATRIX AND REDUCE ITS NORM BY
385 ! ** DIAGONAL SIMILARITY TRANSFORMATION STORED IN WK;
386 ! ** THEN SEARCH FOR ROWS ISOLATING AN EIGENVALUE
387 ! ** AND PUSH THEM DOWN
388  rnorm = zero
389  l = 1
390  k = m
391 
392 30 kkk = k
393  DO 70 j = kkk, 1, -1
394  row = zero
395  DO 40 i = 1, k
396  IF ( i.NE.j ) row = row + abs( aad(j,i) )
397 40 CONTINUE
398  IF ( row.EQ.zero ) THEN
399  wkd(k) = j
400  IF ( j.NE.k ) THEN
401  DO 50 i = 1, k
402  repl = aad(i,j)
403  aad(i,j) = aad(i,k)
404  aad(i,k) = repl
405 50 CONTINUE
406  DO 60 i = l, m
407  repl = aad(j,i)
408  aad(j,i) = aad(k,i)
409  aad(k,i) = repl
410 60 CONTINUE
411  END IF
412  k = k - 1
413  GO TO 30
414  END IF
415 70 CONTINUE
416 ! ** SEARCH FOR COLUMNS ISOLATING AN
417 ! ** EIGENVALUE AND PUSH THEM LEFT
418 80 lll = l
419  DO 120 j = lll, k
420  col = zero
421  DO 90 i = l, k
422  IF ( i.NE.j ) col = col + abs( aad(i,j) )
423 90 CONTINUE
424  IF ( col.EQ.zero ) THEN
425  wkd(l) = j
426  IF ( j.NE.l ) THEN
427  DO 100 i = 1, k
428  repl = aad(i,j)
429  aad(i,j) = aad(i,l)
430  aad(i,l) = repl
431 100 CONTINUE
432  DO 110 i = l, m
433  repl = aad(j,i)
434  aad(j,i) = aad(l,i)
435  aad(l,i) = repl
436 110 CONTINUE
437  END IF
438  l = l + 1
439  GO TO 80
440  END IF
441 120 CONTINUE
442 ! ** BALANCE THE SUBMATRIX IN ROWS L THROUGH K
443  DO 130 i = l, k
444  wkd(i) = one
445 130 CONTINUE
446 
447 140 noconv = .false.
448  DO 200 i = l, k
449  col = zero
450  row = zero
451  DO 150 j = l, k
452  IF ( j.NE.i ) THEN
453  col = col + abs( aad(j,i) )
454  row = row + abs( aad(i,j) )
455  END IF
456 150 CONTINUE
457  f = one
458  g = row / c5
459  h = col + row
460 160 IF ( col.LT.g ) THEN
461  f = f * c5
462  col = col * c6
463  GO TO 160
464  END IF
465  g = row * c5
466 170 IF ( col.GE.g ) THEN
467  f = f / c5
468  col = col / c6
469  GO TO 170
470  END IF
471 ! ** NOW BALANCE
472  IF ( (col+row)/f .LT. c4*h ) THEN
473  wkd(i) = wkd(i) * f
474  noconv = .true.
475  DO 180 j = l, m
476  aad(i,j) = aad(i,j) / f
477 180 CONTINUE
478  DO 190 j = 1, k
479  aad(j,i) = aad(j,i) * f
480 190 CONTINUE
481  END IF
482 200 CONTINUE
483 
484  IF ( noconv ) GO TO 140
485 ! ** IS -A- ALREADY IN HESSENBERG FORM?
486  IF ( k-1 .LT. l+1 ) GO TO 350
487 ! ** TRANSFER -A- TO A HESSENBERG FORM
488  DO 290 n = l+1, k-1
489  h = zero
490  wkd(n+m) = zero
491  scale = zero
492 ! ** SCALE COLUMN
493  DO 210 i = n, k
494  scale = scale + abs(aad(i,n-1))
495 210 CONTINUE
496  IF ( scale.NE.zero ) THEN
497  DO 220 i = k, n, -1
498  wkd(i+m) = aad(i,n-1) / scale
499  h = h + wkd(i+m)**2
500 220 CONTINUE
501  g = - sign( sqrt(h), wkd(n+m) )
502  h = h - wkd(n+m) * g
503  wkd(n+m) = wkd(n+m) - g
504 ! ** FORM (I-(U*UT)/H)*A
505  DO 250 j = n, m
506  f = zero
507  DO 230 i = k, n, -1
508  f = f + wkd(i+m) * aad(i,j)
509 230 CONTINUE
510  DO 240 i = n, k
511  aad(i,j) = aad(i,j) - wkd(i+m) * f / h
512 240 CONTINUE
513 250 CONTINUE
514 ! ** FORM (I-(U*UT)/H)*A*(I-(U*UT)/H)
515  DO 280 i = 1, k
516  f = zero
517  DO 260 j = k, n, -1
518  f = f + wkd(j+m) * aad(i,j)
519 260 CONTINUE
520  DO 270 j = n, k
521  aad(i,j) = aad(i,j) - wkd(j+m) * f / h
522 270 CONTINUE
523 280 CONTINUE
524  wkd(n+m) = scale * wkd(n+m)
525  aad(n,n-1) = scale * g
526  END IF
527 290 CONTINUE
528 
529  DO 340 n = k-2, l, -1
530  n1 = n + 1
531  n2 = n + 2
532  f = aad(n1,n)
533  IF ( f.NE.zero ) THEN
534  f = f * wkd(n1+m)
535  DO 300 i = n2, k
536  wkd(i+m) = aad(i,n)
537 300 CONTINUE
538  IF ( n1.LE.k ) THEN
539  DO 330 j = 1, m
540  g = zero
541  DO 310 i = n1, k
542  g = g + wkd(i+m) * evecd(i,j)
543 310 CONTINUE
544  g = g / f
545  DO 320 i = n1, k
546  evecd(i,j) = evecd(i,j) + g * wkd(i+m)
547 320 CONTINUE
548 330 CONTINUE
549  END IF
550  END IF
551 340 CONTINUE
552 
553 350 CONTINUE
554  n = 1
555  DO 370 i = 1, m
556  DO 360 j = n, m
557  rnorm = rnorm + abs(aad(i,j))
558 360 CONTINUE
559  n = i
560  IF ( i.LT.l .OR. i.GT.k ) evald(i) = aad(i,i)
561 370 CONTINUE
562  n = k
563  t = zero
564 ! ** SEARCH FOR NEXT EIGENVALUES
565 380 IF ( n.LT.l ) GO TO 530
566  in = 0
567  n1 = n - 1
568  n2 = n - 2
569 ! ** LOOK FOR SINGLE SMALL SUB-DIAGONAL ELEMENT
570 390 CONTINUE
571  DO 400 i = l, n
572  lb = n+l - i
573  IF ( lb.EQ.l ) GO TO 410
574  s = abs( aad(lb-1,lb-1) ) + abs( aad(lb,lb) )
575  IF ( s.EQ.zero ) s = rnorm
576  IF ( abs(aad(lb,lb-1)) .LE. tol*s ) GO TO 410
577 400 CONTINUE
578 
579 410 x = aad(n,n)
580  IF ( lb.EQ.n ) THEN
581 ! ** ONE EIGENVALUE FOUND
582  aad(n,n) = x + t
583  evald(n) = aad(n,n)
584  n = n1
585  GO TO 380
586  END IF
587 
588  y = aad(n1,n1)
589  w = aad(n,n1) * aad(n1,n)
590  IF ( lb.EQ.n1 ) THEN
591 ! ** TWO EIGENVALUES FOUND
592  p = (y-x) * c2
593  q = p**2 + w
594  z = sqrt( abs(q) )
595  aad(n,n) = x + t
596  x = aad(n,n)
597  aad(n1,n1) = y + t
598 ! ** REAL PAIR
599  z = p + sign(z,p)
600  evald(n1) = x + z
601  evald(n) = evald(n1)
602  IF ( z.NE.zero ) evald(n) = x - w / z
603  x = aad(n,n1)
604 ! ** EMPLOY SCALE FACTOR IN CASE
605 ! ** X AND Z ARE VERY SMALL
606  r = sqrt( x*x + z*z )
607  p = x / r
608  q = z / r
609 ! ** ROW MODIFICATION
610  DO 420 j = n1, m
611  z = aad(n1,j)
612  aad(n1,j) = q * z + p * aad(n,j)
613  aad(n,j) = q * aad(n,j) - p * z
614 420 CONTINUE
615 ! ** COLUMN MODIFICATION
616  DO 430 i = 1, n
617  z = aad(i,n1)
618  aad(i,n1) = q * z + p * aad(i,n)
619  aad(i,n) = q * aad(i,n) - p * z
620 430 CONTINUE
621 ! ** ACCUMULATE TRANSFORMATIONS
622  DO 440 i = l, k
623  z = evecd(i,n1)
624  evecd(i,n1) = q * z + p * evecd(i,n)
625  evecd(i,n) = q * evecd(i,n) - p * z
626 440 CONTINUE
627 
628  n = n2
629  GO TO 380
630  END IF
631 
632  IF ( in.EQ.30 ) THEN
633 ! ** NO CONVERGENCE AFTER 30 ITERATIONS; SET ERROR
634 ! ** INDICATOR TO THE INDEX OF THE CURRENT EIGENVALUE
635  ier = n
636  GO TO 670
637  END IF
638 ! ** FORM SHIFT
639  IF ( in.EQ.10 .OR. in.EQ.20 ) THEN
640  t = t + x
641  DO 450 i = l, n
642  aad(i,i) = aad(i,i) - x
643 450 CONTINUE
644  s = abs(aad(n,n1)) + abs(aad(n1,n2))
645  x = c3 * s
646  y = x
647  w = - c1 * s**2
648  END IF
649 
650  in = in + 1
651 ! ** LOOK FOR TWO CONSECUTIVE SMALL SUB-DIAGONAL ELEMENTS
652 
653  DO 460 j = lb, n2
654  i = n2+lb - j
655  z = aad(i,i)
656  r = x - z
657  s = y - z
658  p = ( r * s - w ) / aad(i+1,i) + aad(i,i+1)
659  q = aad(i+1,i+1) - z - r - s
660  r = aad(i+2,i+1)
661  s = abs(p) + abs(q) + abs(r)
662  p = p / s
663  q = q / s
664  r = r / s
665  IF ( i.EQ.lb ) GO TO 470
666  uu = abs( aad(i,i-1) ) * ( abs(q) + abs(r) )
667  vv = abs(p)*(abs(aad(i-1,i-1))+abs(z)+abs(aad(i+1,i+1)))
668  IF ( uu .LE. tol*vv ) GO TO 470
669 460 CONTINUE
670 
671 470 CONTINUE
672  aad(i+2,i) = zero
673  DO 480 j = i+3, n
674  aad(j,j-2) = zero
675  aad(j,j-3) = zero
676 480 CONTINUE
677 
678 ! ** DOUBLE QR STEP INVOLVING ROWS K TO N AND COLUMNS M TO N
679 
680  DO 520 ka = i, n1
681  notlas = ka.NE.n1
682  IF ( ka.EQ.i ) THEN
683  s = sign( sqrt( p*p + q*q + r*r ), p )
684  IF ( lb.NE.i ) aad(ka,ka-1) = - aad(ka,ka-1)
685  ELSE
686  p = aad(ka,ka-1)
687  q = aad(ka+1,ka-1)
688  r = zero
689  IF ( notlas ) r = aad(ka+2,ka-1)
690  x = abs(p) + abs(q) + abs(r)
691  IF ( x.EQ.zero ) GO TO 520
692  p = p / x
693  q = q / x
694  r = r / x
695  s = sign( sqrt( p*p + q*q + r*r ), p )
696  aad(ka,ka-1) = - s * x
697  END IF
698  p = p + s
699  x = p / s
700  y = q / s
701  z = r / s
702  q = q / p
703  r = r / p
704 ! ** ROW MODIFICATION
705  DO 490 j = ka, m
706  p = aad(ka,j) + q * aad(ka+1,j)
707  IF ( notlas ) THEN
708  p = p + r * aad(ka+2,j)
709  aad(ka+2,j) = aad(ka+2,j) - p * z
710  END IF
711  aad(ka+1,j) = aad(ka+1,j) - p * y
712  aad(ka,j) = aad(ka,j) - p * x
713 490 CONTINUE
714 ! ** COLUMN MODIFICATION
715  DO 500 ii = 1, min0(n,ka+3)
716  p = x * aad(ii,ka) + y * aad(ii,ka+1)
717  IF ( notlas ) THEN
718  p = p + z * aad(ii,ka+2)
719  aad(ii,ka+2) = aad(ii,ka+2) - p * r
720  END IF
721  aad(ii,ka+1) = aad(ii,ka+1) - p * q
722  aad(ii,ka) = aad(ii,ka) - p
723 500 CONTINUE
724 ! ** ACCUMULATE TRANSFORMATIONS
725  DO 510 ii = l, k
726  p = x * evecd(ii,ka) + y * evecd(ii,ka+1)
727  IF ( notlas ) THEN
728  p = p + z * evecd(ii,ka+2)
729  evecd(ii,ka+2) = evecd(ii,ka+2) - p * r
730  END IF
731  evecd(ii,ka+1) = evecd(ii,ka+1) - p * q
732  evecd(ii,ka) = evecd(ii,ka) - p
733 510 CONTINUE
734 
735 520 CONTINUE
736  GO TO 390
737 ! ** ALL EVALS FOUND, NOW BACKSUBSTITUTE REAL VECTOR
738 530 CONTINUE
739  IF ( rnorm.NE.zero ) THEN
740  DO 560 n = m, 1, -1
741  n2 = n
742  aad(n,n) = one
743  DO 550 i = n-1, 1, -1
744  w = aad(i,i) - evald(n)
745  IF ( w.EQ.zero ) w = tol * rnorm
746  r = aad(i,n)
747  DO 540 j = n2, n-1
748  r = r + aad(i,j) * aad(j,n)
749 540 CONTINUE
750  aad(i,n) = - r / w
751  n2 = i
752 550 CONTINUE
753 560 CONTINUE
754 ! ** END BACKSUBSTITUTION VECTORS OF ISOLATED EVALS
755 
756  DO 580 i = 1, m
757  IF ( i.LT.l .OR. i.GT.k ) THEN
758  DO 570 j = i, m
759  evecd(i,j) = aad(i,j)
760 570 CONTINUE
761  END IF
762 580 CONTINUE
763 ! ** MULTIPLY BY TRANSFORMATION MATRIX
764  IF ( k.NE.0 ) THEN
765  DO j = m, l, -1
766  DO i = l, k
767  z = zero
768  DO 590 n = l, min0(j,k)
769  z = z + evecd(i,n) * aad(n,j)
770 590 CONTINUE
771  evecd(i,j) = z
772  END DO
773  END DO
774  END IF
775 
776  END IF
777 
778  DO i = l, k
779  DO j = 1, m
780  evecd(i,j) = evecd(i,j) * wkd(i)
781  END DO
782  END DO
783 ! ** INTERCHANGE ROWS IF PERMUTATIONS OCCURRED
784  DO 640 i = l-1, 1, -1
785  j = int(wkd(i))
786  IF ( i.NE.j ) THEN
787  DO 630 n = 1, m
788  repl = evecd(i,n)
789  evecd(i,n) = evecd(j,n)
790  evecd(j,n) = repl
791 630 CONTINUE
792  END IF
793 640 CONTINUE
794 
795  DO 660 i = k+1, m
796  j = int(wkd(i))
797  IF ( i.NE.j ) THEN
798  DO 650 n = 1, m
799  repl = evecd(i,n)
800  evecd(i,n) = evecd(j,n)
801  evecd(j,n) = repl
802 650 CONTINUE
803  END IF
804 660 CONTINUE
805 !
806  670 CONTINUE
807 !
808 ! normalizing eigenvector
809  DO j = 1, m
810  nor_factor = zero
811  DO i = 1, m
812  nor_factor = nor_factor + evecd(i,j)**2
813  END DO
814  nor_factor = sqrt(nor_factor)
815  evecd(:,j) = evecd(:,j)/nor_factor
816  END DO
817  RETURN
818  END SUBROUTINE asymtx
819 !
820 !
821  SUBROUTINE asymtx_tl( nZ, V, VAL, A_TL, V_TL, VAL_TL, Error_Status)
822  IMPLICIT NONE
823  INTEGER :: nz,error_status
824  REAL(fp), DIMENSION(:,:) :: v, a_tl, v_tl
825  REAL(fp), DIMENSION(:) :: val, val_tl
826  REAL(fp), DIMENSION(nZ,nZ) :: v_int, a1_tl
827  REAL(fp) :: b_tl
828  INTEGER :: i,j
829  CHARACTER(*), PARAMETER :: routine_name = 'ASYMTX_TL'
830  CHARACTER(256) :: message
831  !
832  v_int = matinv( v(1:nz,1:nz), error_status )
833  IF( error_status /= success ) THEN
834  WRITE( message,'("Error in matrix inversion matinv( V(1:nZ,1:nZ), Error_Status ) ")' )
835  CALL display_message( routine_name, &
836  trim(message), &
837  error_status )
838  RETURN
839  END IF
840  ! part 1
841  !! A1_TL = ZERO
842  a1_tl = matmul( v_int, matmul(a_tl, v) )
843 
844  ! part 2
845  !! A1_TL = ZERO
846  !! V_TL = ZERO
847  DO i = 1, nz
848  val_tl(i) = a1_tl(i,i)
849  DO j = 1, nz
850  IF( i /= j ) THEN
851  if( abs(val(j)-val(i)) > eigen_threshold ) then
852  v_tl(i,j) = a1_tl(i,j)/(val(j)-val(i))
853  else
854  v_tl(i,j) = zero
855  end if
856  ELSE
857  v_tl(i,j) = zero
858  END IF
859  END DO
860  END DO
861 
862  ! part 3
863  !! A1_TL = ZERO
864  a1_tl = matmul( v, v_tl)
865  !! V_TL = ZERO
866  ! part 4
867  DO i = 1, nz
868  b_tl = zero
869  DO j = 1, nz
870  b_tl = b_tl - v(j,i)*a1_tl(j,i)
871  END DO
872 !
873  DO j = 1, nz
874  v_tl(j,i) = a1_tl(j,i) + v(j,i)*b_tl
875  END DO
876  END DO
877 
878  RETURN
879  END SUBROUTINE asymtx_tl
880 !
881 !
882  SUBROUTINE asymtx_ad( nZ, V, VAL, V_AD, VAL_AD, A_AD, Error_Status)
883  IMPLICIT NONE
884  INTEGER :: nz,error_status
885  REAL(fp), DIMENSION(:,:) :: v, v_ad, a_ad
886  REAL(fp), DIMENSION(:) :: val, val_ad
887  INTEGER :: i,j,k
888  REAL(fp), DIMENSION(nZ,nZ) :: v_int, a1_ad
889  CHARACTER(*), PARAMETER :: routine_name = 'ASYMTX_AD'
890  CHARACTER(256) :: message
891  !
892  a1_ad = zero
893  DO i = 1, nz
894  DO j = 1, nz
895  DO k = 1, nz
896  IF( k /= j ) THEN
897  if( abs(val(j)-val(k)) > eigen_threshold ) &
898  a1_ad(k,j) = a1_ad(k,j) + v(i,k)*v_ad(i,j)/(val(j)-val(k))
899  END IF
900  END DO
901  END DO
902  a1_ad(i,i) = a1_ad(i,i) + val_ad(i)
903  END DO
904  v_int = matinv( v(1:nz,1:nz), error_status )
905  IF( error_status /= success ) THEN
906  WRITE( message,'("Error in matrix inversion matinv( V(1:nZ,1:nZ), Error_Status ) ")' )
907  CALL display_message( routine_name, &
908  trim(message), &
909  error_status )
910  RETURN
911  END IF
912 
913  a_ad = matmul( transpose(v_int), matmul(a1_ad, transpose(v) ) )
914  RETURN
915  END SUBROUTINE asymtx_ad
916 !
917 !
918  SUBROUTINE asymtx2_ad( COS_Angle,n_streams, nZ, V, VAL, V_AD, VAL_AD, A_AD, Error_Status)
919  IMPLICIT NONE
920  INTEGER :: nz,error_status
921  REAL(fp), DIMENSION(:,:) :: v, v_ad, a_ad
922  REAL(fp), DIMENSION(:) :: val, val_ad
923  INTEGER :: i,j,n_streams,n
924  REAL(fp) :: cos_angle,d0,d1
925  REAL(fp), DIMENSION(nZ,nZ) :: v_int, a1_ad
926  REAL(fp) :: b_ad
927  !
928  n = 0
929  IF( nz /= n_streams ) THEN
930  ! additional stream for satellite viewing angle is used.
931  d0 = one/cos_angle
932  d0 = d0 * d0
933  d1 = 100000.0_fp
934  DO i = 1, nz
935  if( abs( d0 - val(i) ) < d1 .and. abs(v(2,i)) < eigen_threshold ) then
936  n = i
937  d1 = abs( d0 - val(i) )
938  end if
939  END DO
940  END IF
941  IF( n /= 0 ) val_ad(n) = zero
942 
943  ! using normoalization condition
944  b_ad = zero
945  !! A1_AD = ZERO
946  ! part 4
947  DO i = nz, 1, -1
948  DO j = nz, 1, -1
949  b_ad = b_ad + v(j,i)*v_ad(j,i)
950  a1_ad(j,i) = v_ad(j,i)
951  !! A1_AD(j,i) = A1_AD(j,i) + V_AD(j,i)
952  END DO
953 
954  DO j = nz, 1, -1
955  a1_ad(j,i) = a1_ad(j,i) - v(j,i)*b_ad
956  END DO
957  b_ad = zero
958  END DO
959 
960  ! part 3
961  !! V_AD = ZERO
962  !! V_AD = V_AD + matmul( transpose(V), A1_AD)
963  v_ad = matmul( transpose(v), a1_ad)
964 
965  ! part 2
966  a1_ad = zero
967  DO i = nz, 1, -1
968  DO j = nz, 1, -1
969  IF( i /= j .and. j /= n ) THEN
970  if( abs(val(j)-val(i)) > eigen_threshold ) then
971  a1_ad(i,j) = v_ad(i,j)/(val(j)-val(i))
972  else
973  v_ad(i,j) = zero
974  end if
975  ELSE
976  v_ad(i,j) = zero
977  END IF
978  END DO
979  a1_ad(i,i) = a1_ad(i,i) + val_ad(i)
980  END DO
981 
982  v_int = matinv( v(1:nz,1:nz), error_status )
983  ! part 1
984  !! A_AD = ZERO
985  a_ad = matmul( transpose(v_int), matmul(a1_ad, transpose(v) ) )
986 
987  RETURN
988  END SUBROUTINE asymtx2_ad
989 !
990 !
991  END MODULE crtm_utility
992 !
real(fp), parameter, public point_25
integer, parameter, public failure
real(fp), parameter, public zero
integer, parameter, public warning
subroutine, public legendre_m(MF, N, U, PL)
subroutine legendre_rank1(MOA, NU, ANG, PL)
integer, parameter, public fp
Definition: Type_Kinds.f90:124
real(fp), parameter eigen_threshold
subroutine, public asymtx_tl(nZ, V, VAL, A_TL, V_TL, VAL_TL, Error_Status)
character(*), parameter module_version_id
subroutine, public asymtx2_ad(COS_Angle, n_streams, nZ, V, VAL, V_AD, VAL_AD, A_AD, Error_Status)
real(fp), parameter, public one
recursive subroutine, public display_message(Routine_Name, Message, Error_State, Message_Log)
subroutine, public double_gauss_quadrature(NUM, ABSCISSAS, WEIGHTS)
real(fp) function gamma2(N)
real(fp), parameter, public two
real(fp), parameter, public point_5
************************************************************************GNU Lesser General Public License **This file is part of the GFDL Flexible Modeling System(FMS). ! *! *FMS is free software 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 Lesser General Public *License along with FMS If see< http:! ***********************************************************************subroutine READ_RECORD_CORE_(unit, field, nwords, data, start, axsiz) integer, intent(in) ::unit type(fieldtype), intent(in) ::field integer, intent(in) ::nwords MPP_TYPE_, intent(inout) ::data(nwords) integer, intent(in) ::start(:), axsiz(:) integer(SHORT_KIND) ::i2vals(nwords)!rab used in conjunction with transfer intrinsic to determine size of a variable integer(KIND=1) ::one_byte(8) integer ::word_sz!#ifdef __sgi integer(INT_KIND) ::ivals(nwords) real(FLOAT_KIND) ::rvals(nwords)!#else! integer ::ivals(nwords)! real ::rvals(nwords)!#endif real(DOUBLE_KIND) ::r8vals(nwords) pointer(ptr1, i2vals) pointer(ptr2, ivals) pointer(ptr3, rvals) pointer(ptr4, r8vals) if(mpp_io_stack_size< nwords) call mpp_io_set_stack_size(nwords) call mpp_error(FATAL, 'MPP_READ currently requires use_netCDF option') end subroutine READ_RECORD_CORE_ subroutine READ_RECORD_(unit, field, nwords, data, time_level, domain, position, tile_count, start_in, axsiz_in)!routine that is finally called by all mpp_read routines to perform the read!a non-netCDF record contains:! field ID! a set of 4 coordinates(is:ie, js:je) giving the data subdomain! a timelevel and a timestamp(=NULLTIME if field is static)! 3D real data(stored as 1D)!if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above!in a global direct access file, record position on PE is given by %record.!Treatment of timestamp:! We assume that static fields have been passed without a timestamp.! Here that is converted into a timestamp of NULLTIME.! For non-netCDF fields, field is treated no differently, but is written! with a timestamp of NULLTIME. There is no check in the code to prevent! the user from repeatedly writing a static field. integer, intent(in) ::unit, nwords type(fieldtype), intent(in) ::field MPP_TYPE_, intent(inout) ::data(nwords) integer, intent(in), optional ::time_level type(domain2D), intent(in), optional ::domain integer, intent(in), optional ::position, tile_count integer, intent(in), optional ::start_in(:), axsiz_in(:) integer, dimension(size(field%axes(:))) ::start, axsiz integer ::tlevel !, subdomain(4) integer ::i, error, is, ie, js, je, isg, ieg, jsg, jeg type(domain2d), pointer ::io_domain=> tlevel if(PRESENT(start_in) .AND. PRESENT(axsiz_in)) then if(size(start(! the data domain and compute domain must refer to the subdomain being passed ! In this ! since that attempts to gather all data on PE size(field%axes(:)) axsiz(i)
subroutine legendre_scalar(MOA, ANG, PL)
subroutine, public asymtx(AAD, M, IA, IEVEC, EVECD, EVALD, IER)
integer, parameter, public success
real(fp) function, dimension(size(a, 1), size(a, 2)), public matinv(A, Error_Status)
real(fp), parameter, public pi
subroutine, public asymtx_ad(nZ, V, VAL, V_AD, VAL_AD, A_AD, Error_Status)