FV3 Bundle
tools_func.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: tools_func
3 ! Purpose: usual functions
4 ! Author: Benjamin Menetrier
5 ! Licensing: this code is distributed under the CeCILL-C license
6 ! Copyright © 2015-... UCAR, CERFACS, METEO-FRANCE and IRIT
7 !----------------------------------------------------------------------
8 module tools_func
9 
11 use tools_const, only: pi
12 use tools_kinds, only: kind_real
13 use tools_missing, only: msi,msr,isnotmsr
14 use tools_repro, only: inf
15 use type_mpl, only: mpl_type
16 
17 implicit none
18 
19 real(kind_real),parameter :: gc2gau = 0.28 ! GC99 support radius to Gaussian Daley length-scale (empirical)
20 real(kind_real),parameter :: gau2gc = 3.57 ! Gaussian Daley length-scale to GC99 support radius (empirical)
21 real(kind_real),parameter :: dmin = 1.0e-12_kind_real ! Minimum tensor diagonal value
22 real(kind_real),parameter :: condmax = 1.0e2 ! Maximum tensor conditioning number
23 integer,parameter :: m = 0 ! Number of implicit itteration for the Matern function (Gaussian function if M = 0)
24 
25 private
26 public :: gc2gau,gau2gc,dmin,m
29 
30 contains
31 
32 !----------------------------------------------------------------------
33 ! Subroutine: lonlatmod
34 ! Purpose: set latitude between -pi/2 and pi/2 and longitude between -pi and pi
35 !----------------------------------------------------------------------
36 subroutine lonlatmod(lon,lat)
37 
38 implicit none
39 
40 ! Passed variables
41 real(kind_real),intent(inout) :: lon ! Longitude
42 real(kind_real),intent(inout) :: lat ! Latitude
43 
44 ! Check latitude bounds
45 if (lat>0.5*pi) then
46  lat = pi-lat
47  lon = lon+pi
48 elseif (lat<-0.5*pi) then
49  lat = -pi-lat
50  lon = lon+pi
51 end if
52 
53 ! Check longitude bounds
54 if (lon>pi) then
55  lon = lon-2.0*pi
56 elseif (lon<-pi) then
57  lon = lon+2.0*pi
58 end if
59 
60 end subroutine lonlatmod
61 
62 !----------------------------------------------------------------------
63 ! Subroutine: sphere_dist
64 ! Purpose: compute the great-circle distance between two points
65 !----------------------------------------------------------------------
66 subroutine sphere_dist(lon_i,lat_i,lon_f,lat_f,dist)
67 
68 implicit none
69 
70 ! Passed variable
71 real(kind_real),intent(in) :: lon_i ! Initial point longitude (radian)
72 real(kind_real),intent(in) :: lat_i ! Initial point latitude (radian)
73 real(kind_real),intent(in) :: lon_f ! Final point longitude (radian)
74 real(kind_real),intent(in) :: lat_f ! Final point longilatitudetude (radian)
75 real(kind_real),intent(out) :: dist ! Great-circle distance
76 
77 ! Check that there is no missing value
78 if (isnotmsr(lon_i).and.isnotmsr(lat_i).and.isnotmsr(lon_f).and.isnotmsr(lat_f)) then
79  ! Great-circle distance using Vincenty formula on the unit sphere
80  dist = atan2(sqrt((cos(lat_f)*sin(lon_f-lon_i))**2 &
81  & +(cos(lat_i)*sin(lat_f)-sin(lat_i)*cos(lat_f)*cos(lon_f-lon_i))**2), &
82  & sin(lat_i)*sin(lat_f)+cos(lat_i)*cos(lat_f)*cos(lon_f-lon_i))
83 else
84  call msr(dist)
85 end if
86 
87 end subroutine sphere_dist
88 
89 !----------------------------------------------------------------------
90 ! Subroutine: reduce_arc
91 ! Purpose: reduce arc to a given distance
92 !----------------------------------------------------------------------
93 subroutine reduce_arc(lon_i,lat_i,lon_f,lat_f,maxdist,dist)
94 
95 implicit none
96 
97 ! Passed variables
98 real(kind_real),intent(in) :: lon_i ! Initial point longitude
99 real(kind_real),intent(in) :: lat_i ! Initial point latitude
100 real(kind_real),intent(inout) :: lon_f ! Final point longitude
101 real(kind_real),intent(inout) :: lat_f ! Final point latitude
102 real(kind_real),intent(in) :: maxdist ! Maximum distance
103 real(kind_real),intent(out) :: dist ! Effective distance
104 
105 ! Local variable
106 real(kind_real) :: theta
107 
108 ! Compute distance
109 call sphere_dist(lon_i,lat_i,lon_f,lat_f,dist)
110 
111 ! Check with the maximum distance
112 if (dist>maxdist) then
113  ! Compute bearing
114  theta = atan2(sin(lon_f-lon_i)*cos(lat_f),cos(lat_i)*sin(lat_f)-sin(lat_i)*cos(lat_f)*cos(lon_f-lon_i))
115 
116  ! Reduce distance
117  dist = maxdist
118 
119  ! Compute new point
120  lat_f = asin(sin(lat_i)*cos(dist)+cos(lat_i)*sin(dist)*cos(theta))
121  lon_f = lon_i+atan2(sin(theta)*sin(dist)*cos(lat_i),cos(dist)-sin(lat_i)*sin(lat_f))
122 end if
123 
124 end subroutine reduce_arc
125 
126 !----------------------------------------------------------------------
127 ! Subroutine: vector_product
128 ! Purpose: compute normalized vector product
129 !----------------------------------------------------------------------
130 subroutine vector_product(v1,v2,vp)
132 implicit none
133 
134 ! Passed variables
135 real(kind_real),intent(in) :: v1(3) ! First vector
136 real(kind_real),intent(in) :: v2(3) ! Second vector
137 real(kind_real),intent(out) :: vp(3) ! Vector product
138 
139 ! Local variable
140 real(kind_real) :: r
141 
142 ! Vector product
143 vp(1) = v1(2)*v2(3)-v1(3)*v2(2)
144 vp(2) = v1(3)*v2(1)-v1(1)*v2(3)
145 vp(3) = v1(1)*v2(2)-v1(2)*v2(1)
146 
147 ! Normalization
148 r = sqrt(sum(vp**2))
149 if (r>0.0) vp = vp/r
150 
151 end subroutine vector_product
152 
153 !----------------------------------------------------------------------
154 ! Subroutine: vector_triple_product
155 ! Purpose: compute vector triple product
156 !----------------------------------------------------------------------
157 subroutine vector_triple_product(v1,v2,v3,p)
159 implicit none
160 
161 ! Passed variables
162 real(kind_real),intent(in) :: v1(3) ! First vector
163 real(kind_real),intent(in) :: v2(3) ! Second vector
164 real(kind_real),intent(in) :: v3(3) ! Third vector
165 real(kind_real),intent(out) :: p ! Triple product
166 
167 ! Local variable
168 real(kind_real) :: vp(3)
169 
170 ! Vector product
171 vp(1) = v1(2)*v2(3)-v1(3)*v2(2)
172 vp(2) = v1(3)*v2(1)-v1(1)*v2(3)
173 vp(3) = v1(1)*v2(2)-v1(2)*v2(1)
174 
175 ! Scalar product
176 p = sum(vp*v3)
177 
178 end subroutine vector_triple_product
179 
180 !----------------------------------------------------------------------
181 ! Subroutine: add
182 ! Purpose: check if value missing and add if not missing
183 !----------------------------------------------------------------------
184 subroutine add(value,cumul,num,wgt)
186 implicit none
187 
188 ! Passed variables
189 real(kind_real),intent(in) :: value ! Value to add
190 real(kind_real),intent(inout) :: cumul ! Cumul
191 real(kind_real),intent(inout) :: num ! Number of values
192 real(kind_real),intent(in),optional :: wgt ! Weight
193 
194 ! Local variables
195 real(kind_real) :: lwgt
196 
197 ! Initialize weight
198 lwgt = 1.0
199 if (present(wgt)) lwgt = wgt
200 
201 ! Add value to cumul
202 if (isnotmsr(value)) then
203  cumul = cumul+lwgt*value
204  num = num+1.0
205 end if
206 
207 end subroutine add
208 
209 !----------------------------------------------------------------------
210 ! Subroutine: divide
211 ! Purpose: check if value missing and divide if not missing
212 !----------------------------------------------------------------------
213 subroutine divide(value,num)
215 implicit none
216 
217 ! Passed variables
218 real(kind_real),intent(inout) :: value ! Value to divide
219 real(kind_real),intent(in) :: num ! Divider
220 
221 ! Divide cumul by num
222 if (abs(num)>0.0) then
223  value = value/num
224 else
225  call msr(value)
226 end if
227 
228 end subroutine divide
229 
230 !----------------------------------------------------------------------
231 ! Subroutine: fit_diag
232 ! Purpose: compute diagnostic fit function
233 !----------------------------------------------------------------------
234 subroutine fit_diag(mpl,nc3,nl0r,nl0,l0rl0_to_l0,disth,distv,rh,rv,fit)
236 implicit none
237 
238 ! Passed variables
239 type(mpl_type),intent(in) :: mpl ! MPI data
240 integer,intent(in) :: nc3 ! Number of classes
241 integer,intent(in) :: nl0r ! Reduced number of levels
242 integer,intent(in) :: nl0 ! Number of levels
243 integer,intent(in) :: l0rl0_to_l0(nl0r,nl0) ! Reduced level to level
244 real(kind_real),intent(in) :: disth(nc3) ! Horizontal distance
245 real(kind_real),intent(in) :: distv(nl0,nl0) ! Vertical distance
246 real(kind_real),intent(in) :: rh(nl0) ! Horizontal support radius
247 real(kind_real),intent(in) :: rv(nl0) ! Vertical support radius
248 real(kind_real),intent(out) :: fit(nc3,nl0r,nl0) ! Fit
249 
250 ! Local variables
251 integer :: jl0r,jl0,il0,kl0r,kl0,jc3,kc3,ip,jp,np,np_new
252 integer,allocatable :: plist(:,:),plist_new(:,:)
253 real(kind_real) :: rhsq,rvsq,distnorm,disttest
254 real(kind_real),allocatable :: dist(:,:)
255 logical :: add_to_front
256 
257 ! Initialization
258 fit = 0.0
259 
260 !$omp parallel do schedule(static) private(il0,np,jl0r,np_new,ip,jc3,jl0,kc3,kl0r,kl0,rhsq,rvsq,distnorm,disttest,add_to_front), &
261 !$omp& firstprivate(plist,plist_new,dist)
262 do il0=1,nl0
263  ! Allocation
264  allocate(plist(nc3*nl0r,2))
265  allocate(plist_new(nc3*nl0r,2))
266  allocate(dist(nc3,nl0r))
267 
268  ! Initialize the front
269  np = 1
270  call msi(plist)
271  plist(1,1) = 1
272  do jl0r=1,nl0r
273  if (l0rl0_to_l0(jl0r,il0)==il0) plist(1,2) = jl0r
274  end do
275  dist = 1.0
276  dist(plist(1,1),plist(1,2)) = 0.0
277 
278  do while (np>0)
279  ! Propagate the front
280  np_new = 0
281 
282  do ip=1,np
283  ! Indices of the central point
284  jc3 = plist(ip,1)
285  jl0r = plist(ip,2)
286  jl0 = l0rl0_to_l0(jl0r,il0)
287 
288  ! Loop over neighbors
289  do kc3=max(jc3-1,1),min(jc3+1,nc3)
290  do kl0r=max(jl0r-1,1),min(jl0r+1,nl0r)
291  kl0 = l0rl0_to_l0(kl0r,il0)
292  if (isnotmsr(rh(jl0)).and.isnotmsr(rh(kl0))) then
293  rhsq = 0.5*(rh(jl0)**2+rh(kl0)**2)
294  else
295  rhsq = 0.0
296  end if
297  if (isnotmsr(rv(jl0)).and.isnotmsr(rv(kl0))) then
298  rvsq = 0.5*(rv(jl0)**2+rv(kl0)**2)
299  else
300  rvsq = 0.0
301  end if
302  distnorm = 0.0
303  if (rhsq>0.0) then
304  distnorm = distnorm+(disth(kc3)-disth(jc3))**2/rhsq
305  elseif (kc3/=jc3) then
306  distnorm = distnorm+0.5*huge(1.0)
307  end if
308  if (rvsq>0.0) then
309  distnorm = distnorm+distv(kl0,jl0)**2/rvsq
310  elseif (kl0/=jl0) then
311  distnorm = distnorm+0.5*huge(1.0)
312  end if
313  disttest = dist(jc3,jl0r)+sqrt(distnorm)
314 
315  if (disttest<1.0) then
316  ! Point is inside the support
317  if (disttest<dist(kc3,kl0r)) then
318  ! Update distance
319  dist(kc3,kl0r) = disttest
320 
321  ! Check if the point should be added to the front (avoid duplicates)
322  add_to_front = .true.
323  do jp=1,np_new
324  if ((plist_new(jp,1)==kc3).and.(plist_new(jp,2)==kl0r)) then
325  add_to_front = .false.
326  exit
327  end if
328  end do
329 
330  if (add_to_front) then
331  ! Add point to the front
332  np_new = np_new+1
333  plist_new(np_new,1) = kc3
334  plist_new(np_new,2) = kl0r
335  end if
336  end if
337  end if
338  end do
339  end do
340  end do
341 
342  ! Copy new front
343  np = np_new
344  plist(1:np,:) = plist_new(1:np,:)
345  end do
346 
347  do jl0r=1,nl0r
348  do jc3=1,nc3
349  ! Gaspari-Cohn (1999) function
350  distnorm = dist(jc3,jl0r)
351  fit(jc3,jl0r,il0) = gc99(mpl,distnorm)
352  end do
353  end do
354 
355  ! Release memory
356  deallocate(plist)
357  deallocate(plist_new)
358  deallocate(dist)
359 end do
360 !$omp end parallel do
361 
362 end subroutine fit_diag
363 
364 !----------------------------------------------------------------------
365 ! Subroutine: fit_diag_dble
366 ! Purpose: compute diagnostic fit function
367 !----------------------------------------------------------------------
368 subroutine fit_diag_dble(mpl,nc3,nl0r,nl0,l0rl0_to_l0,disth,distv,rh,rv,rv_rfac,rv_coef,fit)
370 implicit none
371 
372 ! Passed variables
373 type(mpl_type),intent(in) :: mpl ! MPI data
374 integer,intent(in) :: nc3 ! Number of classes
375 integer,intent(in) :: nl0r ! Reduced number of levels
376 integer,intent(in) :: nl0 ! Number of levels
377 integer,intent(in) :: l0rl0_to_l0(nl0r,nl0) ! Reduced level to level
378 real(kind_real),intent(in) :: disth(nc3) ! Horizontal distance
379 real(kind_real),intent(in) :: distv(nl0,nl0) ! Vertical distance
380 real(kind_real),intent(in) :: rh(nl0) ! Horizontal support radius
381 real(kind_real),intent(in) :: rv(nl0) ! Vertical support radius
382 real(kind_real),intent(in) :: rv_rfac(nl0) ! Vertical fit support radius ratio for the positive component
383 real(kind_real),intent(in) :: rv_coef(nl0) ! Vertical fit coefficient
384 real(kind_real),intent(out) :: fit(nc3,nl0r,nl0) ! Fit
385 
386 ! Local variables
387 integer :: jl0r,jl0,il0,kl0r,kl0,jc3,kc3,ip,jp,np,np_new
388 integer,allocatable :: plist(:,:),plist_new(:,:)
389 real(kind_real) :: rhsq,rvsq,distnorm,disttest,rfac,coef,distnormv,distnormh
390 real(kind_real),allocatable :: dist(:,:)
391 logical :: add_to_front
392 
393 ! Initialization
394 fit = 0.0
395 
396 !$omp parallel do schedule(static) private(il0,np,jl0r,np_new,ip,jc3,jl0,kc3,kl0r,kl0,rhsq,rvsq,distnorm,disttest,add_to_front), &
397 !$omp& private(coef,distnormv,distnormh) firstprivate(plist,plist_new,dist)
398 do il0=1,nl0
399  ! Allocation
400  allocate(plist(nc3*nl0r,2))
401  allocate(plist_new(nc3*nl0r,2))
402  allocate(dist(nc3,nl0r))
403 
404  ! Initialize the front
405  np = 1
406  call msi(plist)
407  plist(1,1) = 1
408  do jl0r=1,nl0r
409  if (l0rl0_to_l0(jl0r,il0)==il0) plist(1,2) = jl0r
410  end do
411  dist = 1.0
412  dist(plist(1,1),plist(1,2)) = 0.0
413 
414  do while (np>0)
415  ! Propagate the front
416  np_new = 0
417 
418  do ip=1,np
419  ! Indices of the central point
420  jc3 = plist(ip,1)
421  jl0r = plist(ip,2)
422  jl0 = l0rl0_to_l0(jl0r,il0)
423 
424  ! Loop over neighbors
425  do kc3=max(jc3-1,1),min(jc3+1,nc3)
426  do kl0r=max(jl0r-1,1),min(jl0r+1,nl0r)
427  kl0 = l0rl0_to_l0(kl0r,il0)
428  if (isnotmsr(rh(jl0)).and.isnotmsr(rh(kl0))) then
429  rhsq = 0.5*(rh(jl0)**2+rh(kl0)**2)
430  else
431  rhsq = 0.0
432  end if
433  if (isnotmsr(rv(jl0)).and.isnotmsr(rv(kl0))) then
434  rvsq = 0.5*(rv(jl0)**2+rv(kl0)**2)
435  else
436  rvsq = 0.0
437  end if
438  distnorm = 0.0
439  if (rhsq>0.0) then
440  distnorm = distnorm+(disth(kc3)-disth(jc3))**2/rhsq
441  elseif (kc3/=jc3) then
442  distnorm = distnorm+0.5*huge(1.0)
443  end if
444  if (rvsq>0.0) then
445  distnorm = distnorm+distv(kl0,jl0)**2/rvsq
446  elseif (kl0/=jl0) then
447  distnorm = distnorm+0.5*huge(1.0)
448  end if
449  disttest = dist(jc3,jl0r)+sqrt(distnorm)
450 
451  if (disttest<1.0) then
452  ! Point is inside the support
453  if (disttest<dist(kc3,kl0r)) then
454  ! Update distance
455  dist(kc3,kl0r) = disttest
456 
457  ! Check if the point should be added to the front (avoid duplicates)
458  add_to_front = .true.
459  do jp=1,np_new
460  if ((plist_new(jp,1)==kc3).and.(plist_new(jp,2)==kl0r)) then
461  add_to_front = .false.
462  exit
463  end if
464  end do
465 
466  if (add_to_front) then
467  ! Add point to the front
468  np_new = np_new+1
469  plist_new(np_new,1) = kc3
470  plist_new(np_new,2) = kl0r
471  end if
472  end if
473  end if
474  end do
475  end do
476  end do
477 
478  ! Copy new front
479  np = np_new
480  plist(1:np,:) = plist_new(1:np,:)
481  end do
482 
483  do jl0r=1,nl0r
484  jl0 = l0rl0_to_l0(jl0r,il0)
485  distnormv = dist(1,jl0r)
486  if ((abs(rv_rfac(il0))<0.0).or.(abs(rv_rfac(jl0))<0.0)) then
487  rfac = 0.0
488  else
489  rfac = sqrt(rv_rfac(il0)*rv_rfac(jl0))
490  end if
491  if ((abs(rv_coef(il0))<0.0).or.(abs(rv_coef(jl0))<0.0)) then
492  coef = 0.0
493  else
494  coef = sqrt(rv_coef(il0)*rv_coef(jl0))
495  end if
496  do jc3=1,nc3
497  ! Double Gaspari-Cohn (1999) function
498  distnorm = dist(jc3,jl0r)
499  distnormh = sqrt(distnorm**2-distnormv**2)
500  fit(jc3,jl0r,il0) = gc99(mpl,distnormh)*((1.0+coef)*gc99(mpl,distnormv/rfac)-coef*gc99(mpl,distnormv))
501  end do
502  end do
503 
504  ! Release memory
505  deallocate(plist)
506  deallocate(plist_new)
507  deallocate(dist)
508 end do
509 !$omp end parallel do
510 
511 end subroutine fit_diag_dble
512 
513 !----------------------------------------------------------------------
514 ! Function: gc99
515 ! Purpose: Gaspari and Cohn (1999) function, with the support radius as a parameter
516 !----------------------------------------------------------------------
517 function gc99(mpl,distnorm)
519 ! Passed variables
520 type(mpl_type),intent(in) :: mpl ! MPI data
521 real(kind_real),intent(in) :: distnorm ! Normalized distance
522 
523 ! Returned variable
524 real(kind_real) :: gc99
525 
526 ! Distance check bound
527 if (distnorm<0.0) call mpl%abort('negative normalized distance')
528 
529 ! Gaspari and Cohn (1999) function
530 if (distnorm<0.5) then
531  gc99 = -8.0*distnorm**5+8.0*distnorm**4+5.0*distnorm**3-20.0/3.0*distnorm**2+1.0
532 else if (distnorm<1.0) then
533  gc99 = 8.0/3.0*distnorm**5-8.0*distnorm**4+5.0*distnorm**3+20.0/3.0*distnorm**2-10.0*distnorm+4.0-1.0/(3.0*distnorm)
534 else
535  gc99 = 0.0
536 end if
537 
538 ! Enforce positivity
539 gc99 = max(gc99,0.0_kind_real)
540 
541 end function gc99
542 
543 !----------------------------------------------------------------------
544 ! Subroutine: fit_lct
545 ! Purpose: LCT fit
546 !----------------------------------------------------------------------
547 subroutine fit_lct(mpl,nc,nl0,dx,dy,dz,dmask,nscales,D,coef,fit)
549 implicit none
550 
551 ! Passed variables
552 type(mpl_type),intent(in) :: mpl ! MPI data
553 integer,intent(in) :: nc ! Number of classes
554 integer,intent(in) :: nl0 ! Number of levels
555 real(kind_real),intent(in) :: dx(nc,nl0) ! Zonal separation
556 real(kind_real),intent(in) :: dy(nc,nl0) ! Meridian separation
557 real(kind_real),intent(in) :: dz(nc,nl0) ! Vertical separation
558 logical,intent(in) :: dmask(nc,nl0) ! Mask
559 integer,intent(in) :: nscales ! Number of LCT scales
560 real(kind_real),intent(in) :: d(4,nscales) ! LCT components
561 real(kind_real),intent(in) :: coef(nscales) ! LCT coefficients
562 real(kind_real),intent(out) :: fit(nc,nl0) ! Fit
563 
564 ! Local variables
565 integer :: jl0,jc3,iscales
566 real(kind_real) :: dcoef(nscales),d11,d22,d33,d12,h11,h22,h33,h12,rsq
567 
568 ! Initialization
569 call msr(fit)
570 
571 ! Coefficients
572 dcoef = max(dmin,min(coef,1.0_kind_real))
573 dcoef = dcoef/sum(dcoef)
574 
575 do iscales=1,nscales
576  ! Ensure positive-definiteness of D
577  d11 = max(dmin,d(1,iscales))
578  d22 = max(dmin,d(2,iscales))
579  if (nl0>1) then
580  d33 = max(dmin,d(3,iscales))
581  else
582  d33 = 0.0
583  end if
584  d12 = sqrt(d11*d22)*max(-1.0_kind_real+dmin,min(d(4,iscales),1.0_kind_real-dmin))
585 
586  ! Inverse D to get H
587  call lct_d2h(mpl,d11,d22,d33,d12,h11,h22,h33,h12)
588 
589  ! Homogeneous anisotropic approximation
590  !$omp parallel do schedule(static) private(jl0,jc3,rsq)
591  do jl0=1,nl0
592  do jc3=1,nc
593  if (dmask(jc3,jl0)) then
594  ! Initialization
595  if (iscales==1) fit(jc3,jl0) = 0.0
596 
597  ! Squared distance
598  rsq = h11*dx(jc3,jl0)**2+h22*dy(jc3,jl0)**2+h33*dz(jc3,jl0)**2+2.0*h12*dx(jc3,jl0)*dy(jc3,jl0)
599 
600  if (m==0) then
601  ! Gaussian function
602  if (rsq<40.0) fit(jc3,jl0) = fit(jc3,jl0)+dcoef(iscales)*exp(-0.5*rsq)
603  else
604  ! Matern function
605  fit(jc3,jl0) = fit(jc3,jl0)+dcoef(iscales)*matern(mpl,m,sqrt(rsq))
606  end if
607  end if
608  end do
609  end do
610  !$omp end parallel do
611 end do
612 
613 end subroutine fit_lct
614 
615 !----------------------------------------------------------------------
616 ! Subroutine: lct_d2h
617 ! Purpose: inversion from D (Daley tensor) to H (local correlation tensor)
618 !----------------------------------------------------------------------
619 subroutine lct_d2h(mpl,D11,D22,D33,D12,H11,H22,H33,H12)
621 implicit none
622 
623 ! Passed variables
624 type(mpl_type),intent(in) :: mpl ! MPI data
625 real(kind_real),intent(in) :: d11 ! Daley tensor component 11
626 real(kind_real),intent(in) :: d22 ! Daley tensor component 22
627 real(kind_real),intent(in) :: d33 ! Daley tensor component 33
628 real(kind_real),intent(in) :: d12 ! Daley tensor component 12
629 real(kind_real),intent(out) :: h11 ! Local correlation tensor component 11
630 real(kind_real),intent(out) :: h22 ! Local correlation tensor component 22
631 real(kind_real),intent(out) :: h33 ! Local correlation tensor component 33
632 real(kind_real),intent(out) :: h12 ! Local correlation tensor component 12
633 
634 ! Local variables
635 real(kind_real) :: det
636 
637 ! Compute horizontal determinant
638 det = d11*d22-d12**2
639 
640 ! Inverse D to get H
641 if (det>0.0) then
642  h11 = d22/det
643  h22 = d11/det
644  h12 = -d12/det
645 else
646  call mpl%abort('non-invertible tensor')
647 end if
648 if (d33>0.0) then
649  h33 = 1.0/d33
650 else
651  h33 = 0.0
652 end if
653 
654 end subroutine lct_d2h
655 
656 !----------------------------------------------------------------------
657 ! Subroutine: check_cond
658 ! Purpose: check tensor conditioning
659 !----------------------------------------------------------------------
660 subroutine check_cond(d1,d2,nod,valid)
662 implicit none
663 
664 ! Passed variables
665 real(kind_real),intent(in) :: d1 ! First diagonal coefficient
666 real(kind_real),intent(in) :: d2 ! Second diagonal coefficient
667 real(kind_real),intent(in) :: nod ! Normalized off-diagonal coefficient
668 logical,intent(out) :: valid ! Conditioning validity
669 
670 ! Local variables
671 real(kind_real) :: det,tr,diff,ev1,ev2
672 
673 ! Compute trace and determinant
674 tr = d1+d2
675 det = d1*d2*(1.0-nod**2)
676 diff = 0.25*(d1-d2)**2+d1*d2*nod**2
677 
678 if ((det>0.0).and..not.(diff<0.0)) then
679  ! Compute eigenvalues
680  ev1 = 0.5*tr+sqrt(diff)
681  ev2 = 0.5*tr-sqrt(diff)
682 
683  if (ev2>0.0) then
684  ! Check conditioning
685  valid = inf(ev1,condmax*ev2)
686  else
687  ! Lowest negative eigenvalue is negative
688  valid = .false.
689  end if
690 else
691  ! Non-positive definite tensor
692  valid = .false.
693 end if
694 
695 end subroutine check_cond
696 
697 !----------------------------------------------------------------------
698 ! Function: matern
699 ! Purpose: compute the normalized diffusion function from eq. (55) of Mirouze and Weaver (2013), for the 3d case (d = 3)
700 !----------------------------------------------------------------------
701 real(kind_real) function matern(mpl,M,x)
703 implicit none
704 
705 ! Passed variables
706 type(mpl_type),intent(in) :: mpl ! MPI data
707 integer,intent(in) :: m ! Matern function order
708 real(kind_real),intent(in) :: x ! Argument
709 
710 ! Local variables
711 integer :: j
712 real(kind_real) :: xtmp,beta
713 
714 ! Check
715 if (m<2) call mpl%abort('M should be larger than 2')
716 if (mod(m,2)>0) call mpl%abort('M should be even')
717 
718 ! Initialization
719 matern = 0.0
720 beta = 1.0
721 xtmp = x*sqrt(real(2*m-5,kind_real))
722 
723 do j=0,m-3
724  ! Update sum
725  matern = matern+beta*(xtmp)**(m-2-j)
726 
727  ! Update beta
728  beta = beta*real((j+1+M-2)*(-j+M-2),kind_real)/real(2*(j+1),kind_real)
729 end do
730 
731 ! Last term and normalization
732 matern = matern/beta+1.0
733 
734 ! Exponential factor
735 matern = matern*exp(-xtmp)
736 
737 end function matern
738 
739 !----------------------------------------------------------------------
740 ! Subroutine: cholesky
741 ! Purpose: compute cholesky decomposition
742 ! Author: Original FORTRAN77 version by Michael Healy, modifications by AJ Miller, FORTRAN90 version by John Burkardt.
743 !----------------------------------------------------------------------
744 subroutine cholesky(mpl,n,a,u)
746 implicit none
747 
748 ! Passed variables
749 type(mpl_type),intent(in) :: mpl ! MPI data
750 integer,intent(in) :: n ! Matrix rank
751 real(kind_real),intent(in) :: a(n,n) ! Matrix
752 real(kind_real),intent(out) :: u(n,n) ! Matrix square-root
753 
754 ! Local variables
755 integer :: nn,i,j,ij
756 real(kind_real),allocatable :: apack(:),upack(:)
757 
758 ! Allocation
759 nn = (n*(n+1))/2
760 allocate(apack(nn))
761 allocate(upack(nn))
762 
763 ! Pack matrix
764 ij = 0
765 do i=1,n
766  do j=1,i
767  ij = ij+1
768  apack(ij) = a(i,j)
769  end do
770 end do
771 
772 ! Cholesky decomposition
773 call asa007_cholesky(mpl,n,nn,apack,upack)
774 
775 ! Unpack matrix
776 ij = 0
777 u = 0.0
778 do i=1,n
779  do j=1,i
780  ij = ij+1
781  u(i,j) = upack(ij)
782  end do
783 end do
784 
785 end subroutine cholesky
786 
787 !----------------------------------------------------------------------
788 ! Subroutine: syminv
789 ! Purpose: compute inverse of a symmetric matrix
790 ! Author: Original FORTRAN77 version by Michael Healy, modifications by AJ Miller, FORTRAN90 version by John Burkardt.
791 !----------------------------------------------------------------------
792 subroutine syminv(mpl,n,a,c)
794 implicit none
795 
796 ! Passed variables
797 type(mpl_type),intent(in) :: mpl ! MPI data
798 integer,intent(in) :: n ! Matrix rank
799 real(kind_real),intent(in) :: a(n,n) ! Matrix
800 real(kind_real),intent(out) :: c(n,n) ! Matrix inverse
801 
802 ! Local variables
803 integer :: nn,i,j,ij
804 real(kind_real),allocatable :: apack(:),cpack(:)
805 
806 ! Allocation
807 nn = (n*(n+1))/2
808 allocate(apack(nn))
809 allocate(cpack(nn))
810 
811 ! Pack matrix
812 ij = 0
813 do i=1,n
814  do j=1,i
815  ij = ij+1
816  apack(ij) = a(i,j)
817  end do
818 end do
819 
820 ! Matrix inversion
821 call asa007_syminv(mpl,n,nn,apack,cpack)
822 
823 ! Unpack matrix
824 ij = 0
825 do i=1,n
826  do j=1,i
827  ij = ij+1
828  c(i,j) = cpack(ij)
829  c(j,i) = c(i,j)
830  end do
831 end do
832 
833 end subroutine syminv
834 
835 end module tools_func
subroutine, public reduce_arc(lon_i, lat_i, lon_f, lat_f, maxdist, dist)
Definition: tools_func.F90:94
real(kind_real), parameter, public gc2gau
Definition: tools_func.F90:19
real(kind_real), parameter, public dmin
Definition: tools_func.F90:21
subroutine, public add(value, cumul, num, wgt)
Definition: tools_func.F90:185
integer, parameter, public ip
Definition: Type_Kinds.f90:97
subroutine, public syminv(mpl, n, a, c)
Definition: tools_func.F90:793
real(kind_real) function, public gc99(mpl, distnorm)
Definition: tools_func.F90:518
real(kind_real) function matern(mpl, M, x)
Definition: tools_func.F90:702
subroutine, public divide(value, num)
Definition: tools_func.F90:214
subroutine, public cholesky(mpl, n, a, u)
Definition: tools_func.F90:745
subroutine, public fit_diag_dble(mpl, nc3, nl0r, nl0, l0rl0_to_l0, disth, distv, rh, rv, rv_rfac, rv_coef, fit)
Definition: tools_func.F90:369
subroutine, public lonlatmod(lon, lat)
Definition: tools_func.F90:37
integer, parameter, public m
Definition: tools_func.F90:23
subroutine, public check_cond(d1, d2, nod, valid)
Definition: tools_func.F90:661
subroutine, public asa007_syminv(mpl, n, nn, a, c)
subroutine, public vector_product(v1, v2, vp)
Definition: tools_func.F90:131
subroutine, public sphere_dist(lon_i, lat_i, lon_f, lat_f, dist)
Definition: tools_func.F90:67
real(kind_real), parameter, public gau2gc
Definition: tools_func.F90:20
real(kind_real), parameter condmax
Definition: tools_func.F90:22
#define max(a, b)
Definition: mosaic_util.h:33
subroutine, public lct_d2h(mpl, D11, D22, D33, D12, H11, H22, H33, H12)
Definition: tools_func.F90:620
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
#define min(a, b)
Definition: mosaic_util.h:32
subroutine, public vector_triple_product(v1, v2, v3, p)
Definition: tools_func.F90:158
real(fp), parameter, public pi
subroutine, public fit_diag(mpl, nc3, nl0r, nl0, l0rl0_to_l0, disth, distv, rh, rv, fit)
Definition: tools_func.F90:235
subroutine, public fit_lct(mpl, nc, nl0, dx, dy, dz, dmask, nscales, D, coef, fit)
Definition: tools_func.F90:548