FV3 Bundle
type_kdtree.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_kdtree
3 ! Purpose: KD-tree derived type
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 type_kdtree
9 
10 use tools_kdtree2, only: kdtree2,kdtree2_result,kdtree2_create,kdtree2_destroy, &
12 use tools_kinds, only: kind_real
13 use tools_missing, only: msi,isnotmsi
14 use tools_qsort, only: qsort
15 use tools_repro, only: rth
16 use tools_stripack, only: trans,scoord
17 use type_mpl, only: mpl_type
18 
19 implicit none
20 
21 ! KD-tree derived type
23  type(kdtree2),pointer :: tp ! KD-tree pointer
24  integer,allocatable :: from_eff(:) ! Effective index conversion
25 contains
26  procedure :: create => kdtree_create
27  procedure :: dealloc => kdtree_dealloc
28  procedure :: find_nearest_neighbors => kdtree_find_nearest_neighbors
29  procedure :: count_nearest_neighbors => kdtree_count_nearest_neighbors
30 end type kdtree_type
31 
32 private
33 public :: kdtree_type
34 
35 contains
36 
37 !----------------------------------------------------------------------
38 ! Subroutine: kdtree_create
39 ! Purpose: create a KD-tree
40 !----------------------------------------------------------------------
41 subroutine kdtree_create(kdtree,mpl,n,lon,lat,mask,sort,rearrange)
42 
43 implicit none
44 
45 ! Passed variables
46 class(kdtree_type),intent(inout) :: kdtree ! KD-tree
47 type(mpl_type),intent(in) :: mpl ! MPI data
48 integer,intent(in) :: n ! Number of points
49 real(kind_real),intent(in) :: lon(n) ! Points longitudes
50 real(kind_real),intent(in) :: lat(n) ! Points latitudes
51 logical,intent(in),optional :: mask(n) ! Mask
52 logical,intent(in),optional :: sort ! Sorting flag
53 logical,intent(in),optional :: rearrange ! Rearranging flag
54 
55 ! Local variable
56 integer :: neff,i,ieff
57 real(kind_real),allocatable :: input_data(:,:)
58 logical :: lmask(n),lsort,lrearrange
59 
60 ! Mask
61 if (present(mask)) then
62  lmask = mask
63 else
64  lmask = .true.
65 end if
66 
67 ! Sorting flag
68 if (present(sort)) then
69  lsort = sort
70 else
71  lsort = .true.
72 end if
73 
74 ! Rearranging flag
75 if (present(rearrange)) then
76  lrearrange = rearrange
77 else
78  lrearrange = .true.
79 end if
80 
81 ! Effective tree size
82 neff = count(lmask)
83 
84 ! Check size
85 if (neff<1) call mpl%abort('mask should have at least one valid point to create a kdtree')
86 
87 ! Allocation
88 allocate(input_data(3,neff))
89 allocate(kdtree%from_eff(neff))
90 
91 ! Loop over points
92 ieff = 0
93 do i=1,n
94  if (lmask(i)) then
95  ieff = ieff+1
96 
97  ! Transform to cartesian coordinates
98  call trans(1,lat(i),lon(i),input_data(1,ieff),input_data(2,ieff),input_data(3,ieff))
99 
100  ! Conversion
101  kdtree%from_eff(ieff) = i
102  end if
103 end do
104 
105 ! Create KD-tree
106 kdtree%tp => kdtree2_create(input_data,sort=lsort,rearrange=lrearrange)
107 
108 end subroutine kdtree_create
109 
110 !----------------------------------------------------------------------
111 ! Subroutine: kdtree_dealloc
112 ! Purpose: deallocate KD-tree
113 !----------------------------------------------------------------------
114 subroutine kdtree_dealloc(kdtree)
116 implicit none
117 
118 ! Passed variables
119 class(kdtree_type),intent(inout) :: kdtree ! KD-tree
120 
121 if (allocated(kdtree%from_eff)) then
122  ! Deallocation
123  deallocate(kdtree%from_eff)
124 
125  ! Delete KD-tree
126  call kdtree2_destroy(kdtree%tp)
127 end if
128 
129 end subroutine kdtree_dealloc
130 
131 !----------------------------------------------------------------------
132 ! Subroutine: kdtree_find_nearest_neighbors
133 ! Purpose: find nearest neighbors using a KD-tree
134 !----------------------------------------------------------------------
135 subroutine kdtree_find_nearest_neighbors(kdtree,lon,lat,nn,nn_index,nn_dist)
137 implicit none
138 
139 ! Passed variables
140 class(kdtree_type),intent(in) :: kdtree ! KD-tree
141 real(kind_real),intent(in) :: lon ! Point longitude
142 real(kind_real),intent(in) :: lat ! Point latitude
143 integer,intent(in) :: nn ! Number of nearest neighbors to find
144 integer,intent(out) :: nn_index(nn) ! Neareast neighbors index
145 real(kind_real),intent(out) :: nn_dist(nn) ! Neareast neighbors distance
146 
147 ! Local variables
148 integer :: i,j,nid
149 integer,allocatable :: order(:)
150 real(kind_real) :: lontmp(1),lattmp(1)
151 real(kind_real) :: qv(3)
152 type(kdtree2_result) :: results(nn)
153 
154 
155 ! Copy lon/lat
156 lontmp(1) = lon
157 lattmp(1) = lat
158 
159 ! Transform to cartesian coordinates
160 call trans(1,lattmp,lontmp,qv(1),qv(2),qv(3))
161 
162 ! Find nearest neighbors
163 call kdtree2_n_nearest(kdtree%tp,qv,nn,results)
164 do i=1,nn
165  nn_index(i) = kdtree%from_eff(results(i)%idx)
166  nn_dist(i) = results(i)%sdis
167 end do
168 
169 ! Indistinguishability threshold for cross-plateform reproducibility
170 i = 1
171 do while (i<nn)
172  ! Count indistinguishable neighbors
173  nid = 1
174  do j=i+1,nn
175  if (abs(nn_dist(i)-nn_dist(j))<rth*nn_dist(i)) nid = nid+1
176  end do
177 
178  ! Reorder
179  if (nid>1) then
180  allocate(order(nid))
181  call qsort(nid,nn_index(i:i+nid-1),order)
182  do j=1,nid
183  nn_dist(i+j-1) = nn_dist(i+order(j)-1)
184  end do
185  deallocate(order)
186  end if
187 
188  ! Update
189  i = i+nid
190 end do
191 
192 end subroutine kdtree_find_nearest_neighbors
193 
194 !----------------------------------------------------------------------
195 ! Subroutine: kdtree_count_nearest_neighbors
196 ! Purpose: count nearest neighbors using a KD-tree
197 !----------------------------------------------------------------------
198 subroutine kdtree_count_nearest_neighbors(kdtree,lon,lat,sr,nn)
200 implicit none
201 
202 ! Passed variables
203 class(kdtree_type),intent(in) :: kdtree ! KD-tree
204 real(kind_real),intent(in) :: lon ! Point longitude
205 real(kind_real),intent(in) :: lat ! Point latitude
206 real(kind_real),intent(in) :: sr ! Spherical radius
207 integer,intent(out) :: nn ! Number of nearest neighbors found
208 
209 ! Local variables
210 real(kind_real) :: lontmp(1),lattmp(1)
211 real(kind_real) :: qv(3),brsq
212 
213 ! Copy lon/lat
214 lontmp(1) = lon
215 lattmp(1) = lat
216 
217 ! Transform to cartesian coordinates
218 call trans(1,lattmp,lontmp,qv(1),qv(2),qv(3))
219 
220 ! Convert spherical radius to squared ball radius
221 brsq = (1.0-cos(sr))**2+sin(sr)**2
222 
223 ! Count nearest neighbors
224 nn = kdtree2_r_count(kdtree%tp,qv,brsq)
225 
226 end subroutine kdtree_count_nearest_neighbors
227 
228 end module type_kdtree
integer function, public kdtree2_r_count(tp, qv, r2)
subroutine kdtree_dealloc(kdtree)
subroutine kdtree_create(kdtree, mpl, n, lon, lat, mask, sort, rearrange)
Definition: type_kdtree.F90:42
subroutine, public kdtree2_destroy(tp)
subroutine, public create(self, geom, vars)
Linked list implementation.
Definition: qg_fields.F90:76
subroutine, public trans(n, rlat, rlon, x, y, z)
real(kind_real), parameter, public rth
Definition: tools_repro.F90:15
type(kdtree2) function, pointer, public kdtree2_create(input_data, sort, rearrange)
subroutine kdtree_count_nearest_neighbors(kdtree, lon, lat, sr, nn)
subroutine kdtree_find_nearest_neighbors(kdtree, lon, lat, nn, nn_index, nn_dist)
subroutine, public kdtree2_n_nearest(tp, qv, nn, results)
subroutine, public scoord(px, py, pz, plat, plon, pnrm)
integer, parameter, public kind_real