FV3 Bundle
type_geom.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_geom
3 ! Purpose: geometry 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_geom
9 
10 use netcdf
13 use tools_kinds, only: kind_real
15 use tools_nc, only: ncfloat
16 use tools_qsort, only: qsort
17 use tools_stripack, only: areas,trans
18 use type_com, only: com_type
19 use type_kdtree, only: kdtree_type
20 use type_mesh, only: mesh_type
21 use type_mpl, only: mpl_type
22 use type_nam, only: nam_type
23 use type_rng, only: rng_type
24 use fckit_mpi_module, only: fckit_mpi_sum,fckit_mpi_min,fckit_mpi_max,fckit_mpi_status
25 
26 implicit none
27 
28 integer,parameter :: nredmax = 10 ! Maximum number of similar redundant points
29 real(kind_real),parameter :: distminred = 1.0e-12 ! Minimum distance (in rad) to consider points as redundant
30 logical,parameter :: test_no_point = .false. ! Test BUMP with no grid point on the last MPI task
31 
32 ! Geometry derived type
34  ! Offline geometry data
35  integer :: nlon ! Longitude size
36  integer :: nlat ! Latitude size
37  integer :: nlev ! Number of levels
38  integer,allocatable :: c0_to_lon(:) ! Subset Sc0 to longitude index
39  integer,allocatable :: c0_to_lat(:) ! Subset Sc0 to latgitude index
40  integer,allocatable :: c0_to_tile(:) ! Subset Sc0 to tile index
41 
42  ! Number of points and levels
43  integer :: nmg ! Number of model grid points
44  integer :: nc0 ! Number of points in subset Sc0
45  integer :: nl0 ! Number of levels in subset Sl0f
46  integer :: nl0i ! Number of independent levels in subset Sl0
47 
48  ! Basic geometry data
49  real(kind_real),allocatable :: lon(:) ! Longitudes
50  real(kind_real),allocatable :: lat(:) ! Latitudes
51  real(kind_real),allocatable :: area(:) ! Domain area
52  real(kind_real),allocatable :: vunit(:,:) ! Vertical unit
53  real(kind_real),allocatable :: vunitavg(:) ! Averaged vertical unit
54  real(kind_real),allocatable :: disth(:) ! Horizontal distance
55 
56  ! Masks
57  logical,allocatable :: mask_hor_mg(:) ! Horizontal mask on model grid, global
58  logical,allocatable :: mask_c0(:,:) ! Mask on subset Sc0, global
59  logical,allocatable :: mask_c0a(:,:) ! Mask on subset Sc0, halo A
60  logical,allocatable :: mask_hor_c0(:) ! Union of horizontal masks on subset Sc0, global
61  logical,allocatable :: mask_hor_c0a(:) ! Union of horizontal masks on subset Sc0, halo A
62  logical,allocatable :: mask_ver_c0(:) ! Union of vertical masks
63  integer,allocatable :: nc0_mask(:) ! Horizontal mask size on subset Sc0
64  logical :: mask_del ! Remove subset Sc0 points that are masked for all level
65 
66  ! Mesh
67  type(mesh_type) :: mesh ! Mesh
68 
69  ! KD-tree
70  type(kdtree_type) :: kdtree ! KD-tree
71 
72  ! Boundary nodes
73  integer,allocatable :: nbnd(:) ! Number of boundary nodes
74  real(kind_real),allocatable :: xbnd(:,:,:) ! Boundary nodes, x-coordinate
75  real(kind_real),allocatable :: ybnd(:,:,:) ! Boundary nodes, y-coordinate
76  real(kind_real),allocatable :: zbnd(:,:,:) ! Boundary nodes, z-coordinate
77  real(kind_real),allocatable :: vbnd(:,:,:) ! Boundary nodes, orthogonal vector
78 
79  ! Gripoints and subset Sc0
80  integer,allocatable :: redundant(:) ! Redundant points array
81  integer,allocatable :: c0_to_mg(:) ! Subset Sc0 to model grid
82  integer,allocatable :: mg_to_c0(:) ! Model grid to subset Sc0
83 
84  ! Dirac information
85  integer :: ndir ! Number of valid Dirac points
86  real(kind_real),allocatable :: londir(:) ! Dirac longitude
87  real(kind_real),allocatable :: latdir(:) ! Dirac latitude
88  integer,allocatable :: iprocdir(:) ! Dirac processor
89  integer,allocatable :: ic0adir(:) ! Dirac gridpoint
90  integer,allocatable :: il0dir(:) ! Dirac level
91  integer,allocatable :: ivdir(:) ! Dirac variable
92  integer,allocatable :: itsdir(:) ! Dirac timeslot
93 
94  ! MPI distribution
95  integer :: nmga ! Halo A size for model grid
96  integer :: nc0a ! Halo A size for subset Sc0
97  integer,allocatable :: mg_to_proc(:) ! Model grid to local task
98  integer,allocatable :: mg_to_mga(:) ! Model grid, global to halo A
99  integer,allocatable :: mga_to_mg(:) ! Model grid, halo A to global
100  integer,allocatable :: proc_to_nmga(:) ! Halo A size for each proc
101  integer,allocatable :: c0_to_proc(:) ! Subset Sc0 to local task
102  integer,allocatable :: c0_to_c0a(:) ! Subset Sc0, global to halo A
103  integer,allocatable :: c0a_to_c0(:) ! Subset Sc0, halo A to global
104  integer,allocatable :: proc_to_nc0a(:) ! Halo A size for each proc
105  integer,allocatable :: mga_to_c0(:) ! Model grid, halo A to subset Sc0, global
106  integer,allocatable :: c0a_to_mga(:) ! Subset Sc0 to model grid, halo A
107  type(com_type) :: com_mg ! Communication between subset Sc0 and model grid
108 contains
109  procedure :: alloc => geom_alloc
110  procedure :: dealloc => geom_dealloc
111  procedure :: setup_online => geom_setup_online
112  procedure :: find_sc0 => geom_find_sc0
113  procedure :: init => geom_init
114  procedure :: define_mask => geom_define_mask
115  procedure :: compute_area => geom_compute_area
116  procedure :: define_dirac => geom_define_dirac
117  procedure :: define_distribution => geom_define_distribution
118  procedure :: check_arc => geom_check_arc
119  procedure :: copy_c0a_to_mga => geom_copy_c0a_to_mga
120  procedure :: copy_mga_to_c0a => geom_copy_mga_to_c0a
121  procedure :: compute_deltas => geom_compute_deltas
122 end type geom_type
123 
124 private
125 public :: geom_type
126 
127 contains
128 
129 !----------------------------------------------------------------------
130 ! Subroutine: geom_alloc
131 ! Purpose: geometry allocation
132 !----------------------------------------------------------------------
133 subroutine geom_alloc(geom)
135 implicit none
136 
137 ! Passed variables
138 class(geom_type),intent(inout) :: geom ! Geometry
139 
140 ! Allocation
141 allocate(geom%c0_to_proc(geom%nc0))
142 allocate(geom%c0_to_c0a(geom%nc0))
143 allocate(geom%c0_to_lon(geom%nc0))
144 allocate(geom%c0_to_lat(geom%nc0))
145 allocate(geom%c0_to_tile(geom%nc0))
146 allocate(geom%lon(geom%nc0))
147 allocate(geom%lat(geom%nc0))
148 allocate(geom%area(geom%nl0))
149 allocate(geom%vunit(geom%nc0,geom%nl0))
150 allocate(geom%vunitavg(geom%nl0))
151 allocate(geom%mask_c0(geom%nc0,geom%nl0))
152 allocate(geom%mask_hor_c0(geom%nc0))
153 allocate(geom%mask_ver_c0(geom%nl0))
154 allocate(geom%nc0_mask(geom%nl0))
155 
156 ! Initialization
157 call msi(geom%c0_to_proc)
158 call msi(geom%c0_to_c0a)
159 call msi(geom%c0_to_lon)
160 call msi(geom%c0_to_lat)
161 call msi(geom%c0_to_tile)
162 call msr(geom%lon)
163 call msr(geom%lat)
164 call msr(geom%area)
165 call msr(geom%vunit)
166 call msr(geom%vunitavg)
167 geom%mask_c0 = .false.
168 geom%mask_hor_c0 = .false.
169 geom%mask_ver_c0 = .false.
170 call msi(geom%nc0_mask)
171 
172 end subroutine geom_alloc
173 
174 !----------------------------------------------------------------------
175 ! Subroutine: geom_dealloc
176 ! Purpose: geometry deallocation
177 !----------------------------------------------------------------------
178 subroutine geom_dealloc(geom)
180 implicit none
181 
182 ! Passed variables
183 class(geom_type),intent(inout) :: geom ! Geometry
184 
185 ! Release memory
186 if (allocated(geom%c0_to_lon)) deallocate(geom%c0_to_lon)
187 if (allocated(geom%c0_to_lat)) deallocate(geom%c0_to_lat)
188 if (allocated(geom%c0_to_tile)) deallocate(geom%c0_to_tile)
189 if (allocated(geom%lon)) deallocate(geom%lon)
190 if (allocated(geom%lat)) deallocate(geom%lat)
191 if (allocated(geom%area)) deallocate(geom%area)
192 if (allocated(geom%vunit)) deallocate(geom%vunit)
193 if (allocated(geom%vunitavg)) deallocate(geom%vunitavg)
194 if (allocated(geom%disth)) deallocate(geom%disth)
195 if (allocated(geom%mask_c0)) deallocate(geom%mask_c0)
196 if (allocated(geom%mask_c0a)) deallocate(geom%mask_c0a)
197 if (allocated(geom%mask_hor_c0)) deallocate(geom%mask_hor_c0)
198 if (allocated(geom%mask_hor_c0a)) deallocate(geom%mask_hor_c0a)
199 if (allocated(geom%mask_ver_c0)) deallocate(geom%mask_ver_c0)
200 if (allocated(geom%nc0_mask)) deallocate(geom%nc0_mask)
201 call geom%mesh%dealloc
202 call geom%kdtree%dealloc
203 if (allocated(geom%redundant)) deallocate(geom%redundant)
204 if (allocated(geom%nbnd)) deallocate(geom%nbnd)
205 if (allocated(geom%xbnd)) deallocate(geom%xbnd)
206 if (allocated(geom%ybnd)) deallocate(geom%ybnd)
207 if (allocated(geom%zbnd)) deallocate(geom%zbnd)
208 if (allocated(geom%vbnd)) deallocate(geom%vbnd)
209 if (allocated(geom%c0_to_mg)) deallocate(geom%c0_to_mg)
210 if (allocated(geom%mg_to_c0)) deallocate(geom%mg_to_c0)
211 if (allocated(geom%mg_to_proc)) deallocate(geom%mg_to_proc)
212 if (allocated(geom%mg_to_mga)) deallocate(geom%mg_to_mga)
213 if (allocated(geom%mga_to_mg)) deallocate(geom%mga_to_mg)
214 if (allocated(geom%proc_to_nmga)) deallocate(geom%proc_to_nmga)
215 if (allocated(geom%c0_to_proc)) deallocate(geom%c0_to_proc)
216 if (allocated(geom%c0_to_c0a)) deallocate(geom%c0_to_c0a)
217 if (allocated(geom%c0a_to_c0)) deallocate(geom%c0a_to_c0)
218 if (allocated(geom%proc_to_nc0a)) deallocate(geom%proc_to_nc0a)
219 if (allocated(geom%mga_to_c0)) deallocate(geom%mga_to_c0)
220 if (allocated(geom%c0a_to_mga)) deallocate(geom%c0a_to_mga)
221 call geom%com_mg%dealloc
222 
223 end subroutine geom_dealloc
224 
225 !----------------------------------------------------------------------
226 ! Subroutine: geom_setup_online
227 ! Purpose: setup online geometry
228 !----------------------------------------------------------------------
229 subroutine geom_setup_online(geom,mpl,rng,nam,nmga,nl0,lon,lat,area,vunit,lmask)
231 implicit none
232 
233 ! Passed variables
234 class(geom_type),intent(inout) :: geom ! Geometry
235 type(mpl_type),intent(inout) :: mpl ! MPI data
236 type(rng_type),intent(inout) :: rng ! Random number generator
237 type(nam_type),intent(in) :: nam ! Namelist
238 integer,intent(in) :: nmga ! Halo A size
239 integer,intent(in) :: nl0 ! Number of levels in subset Sl0
240 real(kind_real),intent(in) :: lon(nmga) ! Longitudes
241 real(kind_real),intent(in) :: lat(nmga) ! Latitudes
242 real(kind_real),intent(in) :: area(nmga) ! Area
243 real(kind_real),intent(in) :: vunit(nmga,nl0) ! Vertical unit
244 logical,intent(in) :: lmask(nmga,nl0) ! Mask
245 
246 ! Local variables
247 integer :: ic0,ic0a,il0,offset,iproc,img,imga
248 integer,allocatable :: order(:),order_inv(:)
249 real(kind_real),allocatable :: lon_mg(:),lat_mg(:),area_mg(:),vunit_mg(:,:),list(:)
250 logical,allocatable :: lmask_mg(:,:)
251 type(fckit_mpi_status) :: status
252 
253 ! Copy geometry variables
254 geom%nmga = nmga
255 geom%nl0 = nl0
256 geom%nlev = nl0
257 
258 ! Allocation
259 allocate(geom%proc_to_nmga(mpl%nproc))
260 
261 ! Communication
262 call mpl%f_comm%allgather(geom%nmga,geom%proc_to_nmga)
263 
264 ! Global number of model grid points
265 geom%nmg = sum(geom%proc_to_nmga)
266 
267 ! Allocation
268 allocate(lon_mg(geom%nmg))
269 allocate(lat_mg(geom%nmg))
270 allocate(area_mg(geom%nmg))
271 allocate(vunit_mg(geom%nmg,geom%nl0))
272 allocate(lmask_mg(geom%nmg,geom%nl0))
273 allocate(geom%mg_to_proc(geom%nmg))
274 allocate(geom%mg_to_mga(geom%nmg))
275 allocate(geom%mga_to_mg(geom%nmga))
276 
277 ! Communication of model grid points
278 if (mpl%main) then
279  ! Allocation
280  offset = 0
281  do iproc=1,mpl%nproc
282  if (iproc==mpl%ioproc) then
283  ! Copy data
284  lon_mg(offset+1:offset+geom%proc_to_nmga(iproc)) = lon
285  lat_mg(offset+1:offset+geom%proc_to_nmga(iproc)) = lat
286  area_mg(offset+1:offset+geom%proc_to_nmga(iproc)) = area
287  do il0=1,geom%nl0
288  vunit_mg(offset+1:offset+geom%proc_to_nmga(iproc),il0) = vunit(:,il0)
289  lmask_mg(offset+1:offset+geom%proc_to_nmga(iproc),il0) = lmask(:,il0)
290  end do
291  else
292  ! Receive data on ioproc
293  call mpl%f_comm%receive(lon_mg(offset+1:offset+geom%proc_to_nmga(iproc)),iproc-1,mpl%tag,status)
294  call mpl%f_comm%receive(lat_mg(offset+1:offset+geom%proc_to_nmga(iproc)),iproc-1,mpl%tag+1,status)
295  call mpl%f_comm%receive(area_mg(offset+1:offset+geom%proc_to_nmga(iproc)),iproc-1,mpl%tag+2,status)
296  do il0=1,geom%nl0
297  call mpl%f_comm%receive(vunit_mg(offset+1:offset+geom%proc_to_nmga(iproc),il0),iproc-1,mpl%tag+2+il0,status)
298  call mpl%f_comm%receive(lmask_mg(offset+1:offset+geom%proc_to_nmga(iproc),il0),iproc-1, &
299  & mpl%tag+2+geom%nl0+il0,status)
300  end do
301  end if
302 
303  ! Update offset
304  offset = offset+geom%proc_to_nmga(iproc)
305  end do
306 else
307  ! Send data to ioproc
308  call mpl%f_comm%send(lon,mpl%ioproc-1,mpl%tag)
309  call mpl%f_comm%send(lat,mpl%ioproc-1,mpl%tag+1)
310  call mpl%f_comm%send(area,mpl%ioproc-1,mpl%tag+2)
311  do il0=1,geom%nl0
312  call mpl%f_comm%send(vunit(:,il0),mpl%ioproc-1,mpl%tag+2+il0)
313  call mpl%f_comm%send(lmask(:,il0),mpl%ioproc-1,mpl%tag+2+geom%nl0+il0)
314  end do
315 end if
316 call mpl%update_tag(3+2*geom%nl0)
317 
318 if (mpl%main) then
319  ! Convert to radians
320  lon_mg = lon_mg*deg2rad
321  lat_mg = lat_mg*deg2rad
322 end if
323 
324 ! Broadcast data
325 call mpl%f_comm%broadcast(lon_mg,mpl%ioproc-1)
326 call mpl%f_comm%broadcast(lat_mg,mpl%ioproc-1)
327 call mpl%f_comm%broadcast(area_mg,mpl%ioproc-1)
328 call mpl%f_comm%broadcast(vunit_mg,mpl%ioproc-1)
329 call mpl%f_comm%broadcast(lmask_mg,mpl%ioproc-1)
330 
331 ! Find subset Sc0 points
332 call geom%find_sc0(mpl,rng,lon_mg,lat_mg,lmask_mg,.true.,nam%mask_check,.false.)
333 
334 ! Allocation
335 call geom%alloc
336 allocate(geom%proc_to_nc0a(mpl%nproc))
337 allocate(list(geom%nc0))
338 allocate(order(geom%nc0))
339 allocate(order_inv(geom%nc0))
340 
341 ! Model grid conversions and Sc0 size on halo A
342 img = 0
343 geom%proc_to_nc0a = 0
344 do iproc=1,mpl%nproc
345  do imga=1,geom%proc_to_nmga(iproc)
346  img = img+1
347  geom%mg_to_proc(img) = iproc
348  geom%mg_to_mga(img) = imga
349  if (iproc==mpl%myproc) geom%mga_to_mg(imga) = img
350  if (geom%mask_hor_mg(img)) geom%proc_to_nc0a(iproc) = geom%proc_to_nc0a(iproc)+1
351  end do
352 end do
353 geom%nc0a = geom%proc_to_nc0a(mpl%myproc)
354 
355 ! Subset Sc0 conversions
356 allocate(geom%c0a_to_c0(geom%nc0a))
357 ic0 = 0
358 do iproc=1,mpl%nproc
359  do ic0a=1,geom%proc_to_nc0a(iproc)
360  ic0 = ic0+1
361  geom%c0_to_proc(ic0) = iproc
362  if (iproc==mpl%myproc) geom%c0a_to_c0(ic0a) = ic0
363  end do
364 end do
365 call mpl%glb_to_loc_index(geom%nc0a,geom%c0a_to_c0,geom%nc0,geom%c0_to_c0a)
366 
367 ! Inter-halo conversions
368 allocate(geom%mga_to_c0(geom%nmga))
369 allocate(geom%c0a_to_mga(geom%nc0a))
370 do imga=1,geom%nmga
371  img = geom%mga_to_mg(imga)
372  ic0 = geom%mg_to_c0(img)
373  geom%mga_to_c0(imga) = ic0
374 end do
375 do ic0a=1,geom%nc0a
376  ic0 = geom%c0a_to_c0(ic0a)
377  img = geom%c0_to_mg(ic0)
378  imga = geom%mg_to_mga(img)
379  geom%c0a_to_mga(ic0a) = imga
380 end do
381 
382 ! Deal with mask on redundant points
383 do il0=1,geom%nl0
384  do img=1,geom%nmg
385  if (isnotmsi(geom%redundant(img))) lmask_mg(img,il0) = lmask_mg(img,il0).or.lmask_mg(geom%redundant(img),il0)
386  end do
387 end do
388 
389 ! Remove redundant points
390 geom%lon = lon_mg(geom%c0_to_mg)
391 geom%lat = lat_mg(geom%c0_to_mg)
392 do il0=1,geom%nl0
393  geom%area(il0) = sum(area_mg(geom%c0_to_mg),lmask_mg(geom%c0_to_mg,il0))/req**2
394  geom%vunit(:,il0) = vunit_mg(geom%c0_to_mg,il0)
395  geom%mask_c0(:,il0) = lmask_mg(geom%c0_to_mg,il0)
396 end do
397 
398 ! Reorder points based on lon/lat
399 do ic0=1,geom%nc0
400  list(ic0) = aint(abs(geom%lon(ic0))*1.0e6)+abs(geom%lat(ic0))*1.0e-1
401  if (geom%lon(ic0)<0.0) list(ic0) = list(ic0)+2.0e7
402  if (geom%lat(ic0)<0.0) list(ic0) = list(ic0)+1.0e7
403 end do
404 call qsort(geom%nc0,list,order)
405 do ic0=1,geom%nc0
406  order_inv(order(ic0)) = ic0
407 end do
408 geom%c0_to_proc = geom%c0_to_proc(order)
409 geom%c0_to_c0a = geom%c0_to_c0a(order)
410 geom%c0a_to_c0 = order_inv(geom%c0a_to_c0)
411 geom%lon = geom%lon(order)
412 geom%lat = geom%lat(order)
413 do il0=1,geom%nl0
414  geom%vunit(:,il0) = geom%vunit(order,il0)
415  geom%mask_c0(:,il0) = geom%mask_c0(order,il0)
416 end do
417 geom%c0_to_mg = geom%c0_to_mg(order)
418 geom%mg_to_c0 = order_inv(geom%mg_to_c0)
419 geom%mga_to_c0 = order_inv(geom%mga_to_c0)
420 
421 ! Other masks
422 allocate(geom%mask_c0a(geom%nc0a,geom%nl0))
423 allocate(geom%mask_hor_c0a(geom%nc0a))
424 geom%mask_c0a = geom%mask_c0(geom%c0a_to_c0,:)
425 geom%mask_hor_c0 = any(geom%mask_c0,dim=2)
426 geom%mask_hor_c0a = geom%mask_hor_c0(geom%c0a_to_c0)
427 geom%mask_ver_c0 = any(geom%mask_c0,dim=1)
428 geom%nc0_mask = count(geom%mask_c0,dim=1)
429 
430 ! Setup redundant points communication
431 call geom%com_mg%setup(mpl,'com_mg',geom%nc0,geom%nc0a,geom%nmga,geom%mga_to_c0,geom%c0a_to_mga,geom%c0_to_proc,geom%c0_to_c0a)
432 
433 end subroutine geom_setup_online
434 
435 !----------------------------------------------------------------------
436 ! Subroutine: geom_find_sc0
437 ! Purpose: find subset Sc0 points
438 !----------------------------------------------------------------------
439 subroutine geom_find_sc0(geom,mpl,rng,lon,lat,lmask,red_check,mask_check,mask_del)
441 implicit none
442 
443 ! Passed variables
444 class(geom_type),intent(inout) :: geom ! Geometry
445 type(mpl_type),intent(in) :: mpl ! MPI data
446 type(rng_type),intent(inout) :: rng ! Random number generator
447 real(kind_real),intent(in) :: lon(geom%nmg) ! Longitudes
448 real(kind_real),intent(in) :: lat(geom%nmg) ! Latitudes
449 logical,intent(in) :: lmask(geom%nmg,geom%nl0) ! Mask
450 logical,intent(in) :: red_check ! Check redundant points
451 logical,intent(in) :: mask_check ! Check mask boundaries
452 logical,intent(in) :: mask_del ! Delete masked points
453 
454 ! Local variables
455 integer :: img,ic0,ired,nn_index(nredmax),nc0full,ic0full,jc0full,kc0full,i,j,k,iend,ibnd,il0
456 integer,allocatable :: c0full_to_mg(:),mg_to_c0full(:),ic0full_bnd(:,:,:)
457 real(kind_real) :: nn_dist(nredmax),latbnd(2),lonbnd(2),v1(3),v2(3)
458 real(kind_real),allocatable :: lon_c0full(:),lat_c0full(:)
459 logical :: init
460 logical,allocatable :: lmask_c0full(:,:)
461 type(kdtree_type) :: kdtree
462 type(mesh_type) :: mesh
463 
464 ! Allocation
465 allocate(geom%mask_hor_mg(geom%nmg))
466 allocate(geom%redundant(geom%nmg))
467 allocate(geom%mg_to_c0(geom%nmg))
468 
469 ! Initialization
470 call msi(geom%redundant)
471 
472 ! Look for redundant points
473 if (red_check) then
474  write(mpl%info,'(a7,a)') '','Look for redundant points in the model grid'
475 
476  ! Create KD-tree
477  call kdtree%create(mpl,geom%nmg,lon,lat)
478 
479  ! Find redundant points
480  do img=1,geom%nmg
481  ! Find nearest neighbors
482  call kdtree%find_nearest_neighbors(lon(img),lat(img),nredmax,nn_index,nn_dist)
483 
484  ! Count redundant points
485  do ired=1,nredmax
486  if ((nn_dist(ired)>distminred).or.(nn_index(ired)>=img)) nn_index(ired) = geom%nmg+1
487  end do
488 
489  if (any(nn_index<=geom%nmg)) then
490  ! Redundant point
491  geom%redundant(img) = minval(nn_index)
492  end if
493  end do
494 
495  ! Check for successive redundant points
496  do img=1,geom%nmg
497  if (isnotmsi(geom%redundant(img))) then
498  do while (isnotmsi(geom%redundant(geom%redundant(img))))
499  geom%redundant(img) = geom%redundant(geom%redundant(img))
500  end do
501  end if
502  end do
503 
504  ! Release memory
505  call kdtree%dealloc
506 end if
507 
508 ! Horizontal model grid mask
509 geom%mask_hor_mg = ismsi(geom%redundant)
510 
511 ! Allocation
512 nc0full = count(geom%mask_hor_mg)
513 allocate(c0full_to_mg(nc0full))
514 allocate(mg_to_c0full(geom%nmg))
515 allocate(lon_c0full(nc0full))
516 allocate(lat_c0full(nc0full))
517 allocate(lmask_c0full(nc0full,geom%nl0))
518 
519 ! Conversion
520 ic0full = 0
521 do img=1,geom%nmg
522  if (ismsi(geom%redundant(img))) then
523  ic0full = ic0full+1
524  c0full_to_mg(ic0full) = img
525  mg_to_c0full(img) = ic0full
526  end if
527 end do
528 lon_c0full = lon(c0full_to_mg)
529 lat_c0full = lat(c0full_to_mg)
530 lmask_c0full = lmask(c0full_to_mg,:)
531 
532 ! Delete points flag
533 geom%mask_del = mask_del.and.(any(.not.lmask_c0full))
534 
535 if (geom%mask_del.or.mask_check) then
536  ! Create mesh
537  call mesh%create(mpl,rng,nc0full,lon_c0full,lat_c0full)
538 
539  ! Allocation
540  allocate(geom%nbnd(geom%nl0))
541  allocate(ic0full_bnd(2,mesh%n,geom%nl0))
542 
543  ! Find border points
544  do il0=1,geom%nl0
545  geom%nbnd(il0) = 0
546  do i=1,mesh%n
547  ! Check mask points only
548  ic0full = mesh%order(i)
549  if (.not.lmask_c0full(ic0full,il0)) then
550  iend = mesh%lend(i)
551  init = .true.
552  do while ((iend/=mesh%lend(i)).or.init)
553  j = abs(mesh%list(iend))
554  k = abs(mesh%list(mesh%lptr(iend)))
555  jc0full = mesh%order(j)
556  kc0full = mesh%order(k)
557  if (.not.lmask_c0full(jc0full,il0).and.lmask_c0full(kc0full,il0)) then
558  ! Create a new boundary arc
559  geom%nbnd(il0) = geom%nbnd(il0)+1
560  if (geom%nbnd(il0)>mesh%n) call mpl%abort('too many boundary arcs')
561  ic0full_bnd(1,geom%nbnd(il0),il0) = ic0full
562  ic0full_bnd(2,geom%nbnd(il0),il0) = jc0full
563  end if
564  iend = mesh%lptr(iend)
565  init = .false.
566  end do
567  end if
568  end do
569  end do
570 
571  ! Allocation
572  allocate(geom%xbnd(2,maxval(geom%nbnd),geom%nl0))
573  allocate(geom%ybnd(2,maxval(geom%nbnd),geom%nl0))
574  allocate(geom%zbnd(2,maxval(geom%nbnd),geom%nl0))
575  allocate(geom%vbnd(3,maxval(geom%nbnd),geom%nl0))
576 
577  do il0=1,geom%nl0
578  ! Compute boundary arcs
579  do ibnd=1,geom%nbnd(il0)
580  latbnd = lat_c0full(ic0full_bnd(:,ibnd,il0))
581  lonbnd = lon_c0full(ic0full_bnd(:,ibnd,il0))
582  call trans(2,latbnd,lonbnd,geom%xbnd(:,ibnd,il0),geom%ybnd(:,ibnd,il0),geom%zbnd(:,ibnd,il0))
583  end do
584  do ibnd=1,geom%nbnd(il0)
585  v1 = (/geom%xbnd(1,ibnd,il0),geom%ybnd(1,ibnd,il0),geom%zbnd(1,ibnd,il0)/)
586  v2 = (/geom%xbnd(2,ibnd,il0),geom%ybnd(2,ibnd,il0),geom%zbnd(2,ibnd,il0)/)
587  call vector_product(v1,v2,geom%vbnd(:,ibnd,il0))
588  end do
589  end do
590 end if
591 
592 if (geom%mask_del) then
593  ! Remove subset Sc0 points that are masked for all levels
594  do img=1,geom%nmg
595  if (geom%mask_hor_mg(img)) then
596  ic0full = mg_to_c0full(img)
597  geom%mask_hor_mg(img) = any(lmask_c0full(ic0full,:))
598  end if
599  end do
600 end if
601 
602 ! Allocation
603 geom%nc0 = count(geom%mask_hor_mg)
604 allocate(geom%c0_to_mg(geom%nc0))
605 
606 ! Initialization
607 call msi(geom%mg_to_c0)
608 
609 ! Conversion
610 ic0 = 0
611 do img=1,geom%nmg
612  if (geom%mask_hor_mg(img)) then
613  ic0 = ic0+1
614  geom%c0_to_mg(ic0) = img
615  geom%mg_to_c0(img) = ic0
616  end if
617 end do
618 
619  ! Deal with redundant points
620 do img=1,geom%nmg
621  if (isnotmsi(geom%redundant(img))) geom%mg_to_c0(img) = geom%mg_to_c0(geom%redundant(img))
622 end do
623 
624 ! Print results
625 if (red_check) write(mpl%info,'(a7,a,i6,a,f6.2,a)') '','Number of redundant points: ',(geom%nmg-nc0full), &
626  & ' (',real(geom%nmg-nc0full,kind_real)/real(geom%nmg,kind_real)*100.0,'%)'
627 if (mask_del) write(mpl%info,'(a7,a,i6,a,f6.2,a)') '','Number of masked points :',(nc0full-geom%nc0), &
628  & ' (',real(nc0full-geom%nc0,kind_real)/real(geom%nmg,kind_real)*100.0,'%)'
629 if (red_check.and.mask_del) write(mpl%info,'(a7,a,i6,a,f6.2,a)') '','Total number of removed points:',(geom%nmg-geom%nc0), &
630  & ' (',real(geom%nmg-geom%nc0,kind_real)/real(geom%nmg,kind_real)*100.0,'%)'
631 call flush(mpl%info)
632 write(mpl%info,'(a7,a,i8)') '','Model grid size: ',geom%nmg
633 if (red_check) write(mpl%info,'(a7,a,i8)') '','Non-redundant grid size: ',nc0full
634 write(mpl%info,'(a7,a,i8)') '','Subset Sc0 size: ',geom%nc0
635 
636 end subroutine geom_find_sc0
637 
638 !----------------------------------------------------------------------
639 ! Subroutine: geom_init
640 ! Purpose: initialize geometry
641 !----------------------------------------------------------------------
642 subroutine geom_init(geom,mpl,rng,nam)
644 implicit none
645 
646 ! Passed variables
647 class(geom_type),intent(inout) :: geom ! Geometry
648 type(mpl_type),intent(in) :: mpl ! MPI data
649 type(rng_type),intent(inout) :: rng ! Random number generator
650 type(nam_type),intent(in) :: nam ! Namelist
651 
652 ! Local variables
653 integer :: ic0,il0,jc3,iproc
654 logical :: same_mask
655 
656 ! Set longitude and latitude bounds
657 do ic0=1,geom%nc0
658  call lonlatmod(geom%lon(ic0),geom%lat(ic0))
659 end do
660 
661 ! Define mask
662 call geom%define_mask(mpl,nam)
663 
664 ! Averaged vertical unit
665 do il0=1,geom%nl0
666  if (geom%mask_ver_c0(il0)) then
667  geom%vunitavg(il0) = sum(geom%vunit(:,il0),geom%mask_c0(:,il0))/real(geom%nc0_mask(il0),kind_real)
668  else
669  geom%vunitavg(il0) = 0.0
670  end if
671 end do
672 
673 ! Create mesh
674 call geom%mesh%create(mpl,rng,geom%nc0,geom%lon,geom%lat)
675 call geom%mesh%bnodes
676 
677 ! Compute area
678 if (.not.isanynotmsr(geom%area)) call geom%compute_area(mpl)
679 
680 ! Check whether the mask is the same for all levels
681 same_mask = .true.
682 do il0=2,geom%nl0
683  same_mask = same_mask.and.(all((geom%mask_c0(:,il0).and.geom%mask_c0(:,1)) &
684  & .or.(.not.geom%mask_c0(:,il0).and..not.geom%mask_c0(:,1))))
685 end do
686 
687 ! Define number of independent levels
688 if (same_mask) then
689  geom%nl0i = 1
690 else
691  geom%nl0i = geom%nl0
692 end if
693 write(mpl%info,'(a7,a,i3)') '','Number of independent levels: ',geom%nl0i
694 call flush(mpl%info)
695 
696 ! Create KD-tree
697 call geom%kdtree%create(mpl,geom%nc0,geom%lon,geom%lat)
698 
699 ! Horizontal distance
700 allocate(geom%disth(nam%nc3))
701 do jc3=1,nam%nc3
702  geom%disth(jc3) = real(jc3-1,kind_real)*nam%dc
703 end do
704 
705 ! Define dirac points
706 if (nam%ndir>0) call geom%define_dirac(nam)
707 
708 ! Print summary
709 write(mpl%info,'(a10,a,f7.1,a,f7.1)') '','Min. / max. longitudes:',minval(geom%lon)*rad2deg,' / ',maxval(geom%lon)*rad2deg
710 write(mpl%info,'(a10,a,f7.1,a,f7.1)') '','Min. / max. latitudes: ',minval(geom%lat)*rad2deg,' / ',maxval(geom%lat)*rad2deg
711 write(mpl%info,'(a10,a)') '','Unmasked area (% of Earth area) / masked points / vertical unit:'
712 do il0=1,geom%nl0
713  write(mpl%info,'(a13,a,i3,a,f5.1,a,f5.1,a,f12.1,a)') '','Level ',nam%levs(il0),' ~> ',geom%area(il0)/(4.0*pi)*100.0,'% / ', &
714  & real(count(.not.geom%mask_c0(:,il0)),kind_real)/real(geom%nc0,kind_real)*100.0,'% / ',geom%vunitavg(il0),' '//trim(mpl%vunitchar)
715 end do
716 write(mpl%info,'(a7,a)') '','Distribution summary:'
717 do iproc=1,mpl%nproc
718  write(mpl%info,'(a10,a,i3,a,i8,a)') '','Proc #',iproc,': ',geom%proc_to_nc0a(iproc),' grid-points'
719 end do
720 call flush(mpl%info)
721 
722 end subroutine geom_init
723 
724 !----------------------------------------------------------------------
725 ! Subroutine: geom_define_mask
726 ! Purpose: define mask
727 !----------------------------------------------------------------------
728 subroutine geom_define_mask(geom,mpl,nam)
730 implicit none
731 
732 ! Passed variables
733 class(geom_type),intent(inout) :: geom ! Geometry
734 type(mpl_type),intent(in) :: mpl ! MPI data
735 type(nam_type),intent(in) :: nam ! Namelist
736 
737 ! Local variables
738 integer :: latmin,latmax,il0,ic0,ildw
739 integer :: ncid,nlon_id,nlon_test,nlat_id,nlat_test,mask_id
740 real(kind_real) :: dist
741 real(kind_real),allocatable :: hydmask(:,:)
742 logical :: mask_test
743 character(len=3) :: il0char
744 character(len=1024) :: subr = 'geom_define_mask'
745 
746 ! Mask restriction
747 if (nam%mask_type(1:3)=='lat') then
748  ! Latitude mask
749  read(nam%mask_type(4:6),'(i3)') latmin
750  read(nam%mask_type(7:9),'(i3)') latmax
751  if (latmin>=latmax) call mpl%abort('latmin should be lower than latmax')
752  do il0=1,geom%nl0
753  geom%mask_c0(:,il0) = geom%mask_c0(:,il0).and.(geom%lat>=real(latmin,kind_real)*deg2rad) &
754  & .and.(geom%lat<=real(latmax,kind_real)*deg2rad)
755  end do
756 elseif (trim(nam%mask_type)=='hyd') then
757  ! Read from hydrometeors mask file
758  call mpl%ncerr(subr,nf90_open(trim(nam%datadir)//'/'//trim(nam%prefix)//'_hyd.nc',nf90_nowrite,ncid))
759  if (trim(nam%model)=='aro') then
760  call mpl%ncerr(subr,nf90_inq_dimid(ncid,'X',nlon_id))
761  call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nlon_id,len=nlon_test))
762  call mpl%ncerr(subr,nf90_inq_dimid(ncid,'Y',nlat_id))
763  call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nlat_id,len=nlat_test))
764  if ((nlon_test/=geom%nlon).or.(nlat_test/=geom%nlat)) call mpl%abort('wrong dimensions in the mask')
765  allocate(hydmask(geom%nlon,geom%nlat))
766  do il0=1,geom%nl0
767  write(il0char,'(i3.3)') nam%levs(il0)
768  call mpl%ncerr(subr,nf90_inq_varid(ncid,'S'//il0char//'MASK',mask_id))
769  call mpl%ncerr(subr,nf90_get_var(ncid,mask_id,hydmask,(/1,1/),(/geom%nlon,geom%nlat/)))
770  geom%mask_c0(:,il0) = geom%mask_c0(:,il0).and.pack(real(hydmask,kind(1.0))>nam%mask_th,mask=.true.)
771  end do
772  deallocate(hydmask)
773  call mpl%ncerr(subr,nf90_close(ncid))
774  end if
775 elseif (trim(nam%mask_type)=='ldwv') then
776  ! Compute distance to the vertical diagnostic points
777  do ic0=1,geom%nc0
778  if (geom%mask_hor_c0(ic0)) then
779  mask_test = .false.
780  do ildw=1,nam%nldwv
781  call sphere_dist(nam%lon_ldwv(ildw),nam%lat_ldwv(ildw),geom%lon(ic0),geom%lat(ic0),dist)
782  mask_test = mask_test.or.(dist<1.1*nam%local_rad)
783  end do
784  do il0=1,geom%nl0
785  if (geom%mask_c0(ic0,il0)) geom%mask_c0(ic0,:) = mask_test
786  end do
787  end if
788  end do
789 end if
790 
791 end subroutine geom_define_mask
792 
793 !----------------------------------------------------------------------
794 ! Subroutine: geom_compute_area
795 ! Purpose: compute domain area
796 !----------------------------------------------------------------------
797 subroutine geom_compute_area(geom,mpl)
799 implicit none
800 
801 ! Passed variables
802 class(geom_type),intent(inout) :: geom ! Geometry
803 type(mpl_type),intent(in) :: mpl ! MPI data
804 
805 ! Local variables
806 integer :: il0,it
807 real(kind_real) :: area,frac
808 
809 write(mpl%info,'(a7,a)') '','Compute area (might take a long time)'
810 
811 ! Create triangles list
812 if (.not.allocated(geom%mesh%ltri)) call geom%mesh%trlist
813 
814 ! Compute area
815 geom%area = 0.0
816 do it=1,geom%mesh%nt
817  area = areas((/geom%mesh%x(geom%mesh%ltri(1,it)),geom%mesh%y(geom%mesh%ltri(1,it)),geom%mesh%z(geom%mesh%ltri(1,it))/), &
818  & (/geom%mesh%x(geom%mesh%ltri(2,it)),geom%mesh%y(geom%mesh%ltri(2,it)),geom%mesh%z(geom%mesh%ltri(2,it))/), &
819  & (/geom%mesh%x(geom%mesh%ltri(3,it)),geom%mesh%y(geom%mesh%ltri(3,it)),geom%mesh%z(geom%mesh%ltri(3,it))/))
820  do il0=1,geom%nl0
821  frac = real(count(geom%mask_c0(geom%mesh%order(geom%mesh%ltri(1:3,it)),il0)),kind_real)/3.0
822  geom%area(il0) = geom%area(il0)+frac*area
823  end do
824 end do
825 
826 end subroutine geom_compute_area
827 
828 !----------------------------------------------------------------------
829 ! Subroutine: geom_define_dirac
830 ! Purpose: define dirac indices
831 !----------------------------------------------------------------------
832 subroutine geom_define_dirac(geom,nam)
834 implicit none
835 
836 ! Passed variables
837 class(geom_type),intent(inout) :: geom ! Geometry
838 type(nam_type),intent(in) :: nam ! Namelist
839 
840 ! Local variables
841 integer :: idir,il0,nn_index(1),ic0dir,il0dir
842 real(kind_real) :: nn_dist(1)
843 logical :: valid
844 
845 ! Allocation
846 allocate(geom%londir(nam%ndir))
847 allocate(geom%latdir(nam%ndir))
848 allocate(geom%iprocdir(nam%ndir))
849 allocate(geom%ic0adir(nam%ndir))
850 allocate(geom%il0dir(nam%ndir))
851 allocate(geom%ivdir(nam%ndir))
852 allocate(geom%itsdir(nam%ndir))
853 
854 ! Initialization
855 geom%ndir = 0
856 do idir=1,nam%ndir
857  ! Find level
858  do il0=1,geom%nl0
859  if (nam%levs(il0)==nam%levdir(idir)) il0dir = il0
860  end do
861 
862  ! Find nearest neighbor
863  call geom%kdtree%find_nearest_neighbors(nam%londir(idir),nam%latdir(idir),1,nn_index,nn_dist)
864  ic0dir = nn_index(1)
865 
866  ! Check arc
867  if (geom%mask_del) then
868  call geom%check_arc(il0dir,nam%londir(idir),nam%latdir(idir),geom%lon(ic0dir),geom%lat(ic0dir),valid)
869  else
870  valid = geom%mask_c0(ic0dir,il0dir)
871  end if
872 
873  if (valid) then
874  ! Add valid dirac point
875  geom%ndir = geom%ndir+1
876  geom%londir(geom%ndir) = nam%londir(idir)
877  geom%latdir(geom%ndir) = nam%latdir(idir)
878  geom%iprocdir(geom%ndir) = geom%c0_to_proc(ic0dir)
879  geom%ic0adir(geom%ndir) = geom%c0_to_c0a(ic0dir)
880  geom%il0dir(geom%ndir) = il0dir
881  geom%ivdir(geom%ndir) = nam%ivdir(idir)
882  geom%itsdir(geom%ndir) = nam%itsdir(idir)
883  end if
884 end do
885 
886 end subroutine geom_define_dirac
887 
888 !----------------------------------------------------------------------
889 ! Subroutine: geom_define_distribution
890 ! Purpose: define local distribution
891 !----------------------------------------------------------------------
892 subroutine geom_define_distribution(geom,mpl,nam,rng)
894 implicit none
895 
896 ! Passed variables
897 class(geom_type),intent(inout) :: geom ! Geometry
898 type(mpl_type),intent(inout) :: mpl ! MPI data
899 type(nam_type),intent(in) :: nam ! Namelist
900 type(rng_type),intent(inout) :: rng ! Random number generator
901 
902 ! Local variables
903 integer :: ic0,il0,info,iproc,ic0a,nc0a,ny,nres,iy,delta,ix
904 integer :: ncid,nc0_id,c0_to_proc_id,c0_to_c0a_id,lon_id,lat_id
905 integer :: c0_reorder(geom%nc0),nn_index(1)
906 integer,allocatable :: center_to_c0(:),nx(:),ic0a_arr(:)
907 real(kind_real) :: nn_dist(1),dlat,dlon
908 real(kind_real),allocatable :: rh_c0(:),lon_center(:),lat_center(:)
909 logical,allocatable :: mask_hor_c0(:)
910 character(len=4) :: nprocchar
911 character(len=1024) :: filename_nc
912 character(len=1024) :: subr = 'geom_define_distribution'
913 type(kdtree_type) :: kdtree
914 
915 if (mpl%nproc==1) then
916  ! All points on a single processor
917  geom%c0_to_proc = 1
918  do ic0=1,geom%nc0
919  geom%c0_to_c0a(ic0) = ic0
920  end do
921 elseif (mpl%nproc>1) then
922  if (mpl%main) then
923  ! Open file
924  write(nprocchar,'(i4.4)') mpl%nproc
925  filename_nc = trim(nam%prefix)//'_distribution_'//nprocchar//'.nc'
926  info = nf90_open(trim(nam%datadir)//'/'//trim(filename_nc),nf90_nowrite,ncid)
927  end if
928  call mpl%f_comm%broadcast(info,mpl%ioproc-1)
929 
930  if (info==nf90_noerr) then
931  ! Read local distribution
932  write(mpl%info,'(a7,a,i4,a)') '','Read local distribution for: ',mpl%nproc,' MPI tasks'
933  call flush(mpl%info)
934 
935  if (mpl%main) then
936  ! Get variables ID
937  call mpl%ncerr(subr,nf90_inq_varid(ncid,'c0_to_proc',c0_to_proc_id))
938  call mpl%ncerr(subr,nf90_inq_varid(ncid,'c0_to_c0a',c0_to_c0a_id))
939 
940  ! Read varaibles
941  call mpl%ncerr(subr,nf90_get_var(ncid,c0_to_proc_id,geom%c0_to_proc))
942  call mpl%ncerr(subr,nf90_get_var(ncid,c0_to_c0a_id,geom%c0_to_c0a))
943 
944  ! Close file
945  call mpl%ncerr(subr,nf90_close(ncid))
946  end if
947 
948  ! Broadcast distribution
949  call mpl%f_comm%broadcast(geom%c0_to_proc,mpl%ioproc-1)
950  call mpl%f_comm%broadcast(geom%c0_to_c0a,mpl%ioproc-1)
951 
952  ! Check
953  if (maxval(geom%c0_to_proc)>mpl%nproc) call mpl%abort('wrong distribution')
954  else
955  ! Generate a distribution
956 
957  ! Allocation
958  allocate(lon_center(mpl%nproc))
959  allocate(lat_center(mpl%nproc))
960  allocate(ic0a_arr(mpl%nproc))
961 
962  ! Define distribution centers
963  if (.false.) then
964  ! Using a random sampling
965 
966  ! Allocation
967  allocate(mask_hor_c0(geom%nc0))
968  allocate(rh_c0(geom%nc0))
969  allocate(center_to_c0(mpl%nproc))
970 
971  ! Initialization
972  mask_hor_c0 = any(geom%mask_c0,dim=2)
973  rh_c0 = 1.0
974 
975  ! Compute sampling
976  write(mpl%info,'(a7,a)',advance='no') '','Define distribution centers:'
977  call rng%initialize_sampling(mpl,geom%nc0,geom%lon,geom%lat,mask_hor_c0,rh_c0,nam%ntry,nam%nrep,mpl%nproc,center_to_c0)
978 
979  ! Define centers coordinates
980  lon_center = geom%lon(center_to_c0)
981  lat_center = geom%lat(center_to_c0)
982  else
983  ! Using a regular splitting
984 
985  ! Allocation
986  ny = nint(sqrt(real(mpl%nproc,kind_real)))
987  if (ny**2<mpl%nproc) ny = ny+1
988  allocate(nx(ny))
989  nres = mpl%nproc
990  do iy=1,ny
991  delta = mpl%nproc/ny
992  if (nres>(ny-iy+1)*delta) delta = delta+1
993  nx(iy) = delta
994  nres = nres-delta
995  end do
996  if (sum(nx)/=mpl%nproc) call mpl%abort('wrong number of tiles in define_distribution')
997  dlat = (maxval(geom%lat)-minval(geom%lat))/ny
998  iproc = 0
999  do iy=1,ny
1000  dlon = (maxval(geom%lon)-minval(geom%lon))/nx(iy)
1001  do ix=1,nx(iy)
1002  iproc = iproc+1
1003  lat_center(iproc) = minval(geom%lat)+(real(iy,kind_real)-0.5)*dlat
1004  lon_center(iproc) = minval(geom%lon)+(real(ix,kind_real)-0.5)*dlon
1005  end do
1006  end do
1007  end if
1008 
1009  if (mpl%main) then
1010  ! Define kdtree
1011  call kdtree%create(mpl,mpl%nproc,lon_center,lat_center)
1012 
1013  ! Local processor
1014  do ic0=1,geom%nc0
1015  call kdtree%find_nearest_neighbors(geom%lon(ic0),geom%lat(ic0),1,nn_index,nn_dist)
1016  geom%c0_to_proc(ic0) = nn_index(1)
1017  end do
1018 
1019  ! Local index
1020  ic0a_arr = 0
1021  do ic0=1,geom%nc0
1022  iproc = geom%c0_to_proc(ic0)
1023  ic0a_arr(iproc) = ic0a_arr(iproc)+1
1024  geom%c0_to_c0a(ic0) = ic0a_arr(iproc)
1025  end do
1026  end if
1027 
1028  ! Broadcast distribution
1029  call mpl%f_comm%broadcast(geom%c0_to_proc,mpl%ioproc-1)
1030  call mpl%f_comm%broadcast(geom%c0_to_c0a,mpl%ioproc-1)
1031 
1032 
1033  if (test_no_point) then
1034  ! Count points on the penultimate processor
1035  nc0a = count(geom%c0_to_proc==mpl%nproc-1)
1036 
1037  ! Move all point from the last to the penultimate processor
1038  do ic0=1,geom%nc0
1039  if (geom%c0_to_proc(ic0)==mpl%nproc) then
1040  nc0a = nc0a+1
1041  geom%c0_to_proc(ic0) = mpl%nproc-1
1042  geom%c0_to_c0a(ic0) = nc0a
1043  end if
1044  end do
1045  end if
1046 
1047  ! Write distribution
1048  if (mpl%main) then
1049  ! Create file
1050  call mpl%ncerr(subr,nf90_create(trim(nam%datadir)//'/'//trim(filename_nc),or(nf90_clobber,nf90_64bit_offset),ncid))
1051 
1052  ! Write namelist parameters
1053  call nam%ncwrite(mpl,ncid)
1054 
1055  ! Define dimension
1056  call mpl%ncerr(subr,nf90_def_dim(ncid,'nc0',geom%nc0,nc0_id))
1057 
1058  ! Define variables
1059  call mpl%ncerr(subr,nf90_def_var(ncid,'lon',ncfloat,(/nc0_id/),lon_id))
1060  call mpl%ncerr(subr,nf90_def_var(ncid,'lat',ncfloat,(/nc0_id/),lat_id))
1061  call mpl%ncerr(subr,nf90_def_var(ncid,'c0_to_proc',nf90_int,(/nc0_id/),c0_to_proc_id))
1062  call mpl%ncerr(subr,nf90_def_var(ncid,'c0_to_c0a',nf90_int,(/nc0_id/),c0_to_c0a_id))
1063 
1064  ! End definition mode
1065  call mpl%ncerr(subr,nf90_enddef(ncid))
1066 
1067  ! Write variables
1068  call mpl%ncerr(subr,nf90_put_var(ncid,lon_id,geom%lon*rad2deg))
1069  call mpl%ncerr(subr,nf90_put_var(ncid,lat_id,geom%lat*rad2deg))
1070  call mpl%ncerr(subr,nf90_put_var(ncid,c0_to_proc_id,geom%c0_to_proc))
1071  call mpl%ncerr(subr,nf90_put_var(ncid,c0_to_c0a_id,geom%c0_to_c0a))
1072 
1073  ! Close file
1074  call mpl%ncerr(subr,nf90_close(ncid))
1075  end if
1076  end if
1077 end if
1078 
1079 ! Size of tiles
1080 allocate(geom%proc_to_nc0a(mpl%nproc))
1081 do iproc=1,mpl%nproc
1082  geom%proc_to_nc0a(iproc) = count(geom%c0_to_proc==iproc)
1083 end do
1084 geom%nc0a = geom%proc_to_nc0a(mpl%myproc)
1085 
1086 ! Conversion
1087 allocate(geom%c0a_to_c0(geom%nc0a))
1088 ic0a = 0
1089 do ic0=1,geom%nc0
1090  if (geom%c0_to_proc(ic0)==mpl%myproc) then
1091  ic0a = ic0a+1
1092  geom%c0a_to_c0(ic0a) = ic0
1093  end if
1094 end do
1095 
1096 ! Reorder Sc0 points to improve communication efficiency
1097 do ic0=1,geom%nc0
1098  iproc = geom%c0_to_proc(ic0)
1099  ic0a = geom%c0_to_c0a(ic0)
1100  if (iproc==1) then
1101  c0_reorder(ic0) = ic0a
1102  else
1103  c0_reorder(ic0) = sum(geom%proc_to_nc0a(1:iproc-1))+ic0a
1104  end if
1105 end do
1106 geom%c0_to_lon(c0_reorder) = geom%c0_to_lon
1107 geom%c0_to_lat(c0_reorder) = geom%c0_to_lat
1108 geom%c0_to_tile(c0_reorder) = geom%c0_to_tile
1109 geom%lon(c0_reorder) = geom%lon
1110 geom%lat(c0_reorder) = geom%lat
1111 do il0=1,geom%nl0
1112  geom%vunit(c0_reorder,il0) = geom%vunit(:,il0)
1113  geom%mask_c0(c0_reorder,il0) = geom%mask_c0(:,il0)
1114 end do
1115 geom%c0_to_proc(c0_reorder) = geom%c0_to_proc
1116 geom%c0_to_c0a(c0_reorder) = geom%c0_to_c0a
1117 do ic0a=1,geom%nc0a
1118  geom%c0a_to_c0(ic0a) = c0_reorder(geom%c0a_to_c0(ic0a))
1119 end do
1120 
1121 ! Other masks
1122 allocate(geom%mask_c0a(geom%nc0a,geom%nl0))
1123 allocate(geom%mask_hor_c0a(geom%nc0a))
1124 geom%mask_c0a = geom%mask_c0(geom%c0a_to_c0,:)
1125 geom%mask_hor_c0 = any(geom%mask_c0,dim=2)
1126 geom%mask_hor_c0a = geom%mask_hor_c0(geom%c0a_to_c0)
1127 geom%mask_ver_c0 = any(geom%mask_c0,dim=1)
1128 geom%nc0_mask = count(geom%mask_c0,dim=1)
1129 
1130 end subroutine geom_define_distribution
1131 
1132 !----------------------------------------------------------------------
1133 ! Subroutine: geom_check_arc
1134 ! Purpose: check if an arc is crossing boundaries
1135 !----------------------------------------------------------------------
1136 subroutine geom_check_arc(geom,il0,lon_s,lat_s,lon_e,lat_e,valid)
1138 implicit none
1139 
1140 ! Passed variables
1141 class(geom_type),intent(in) :: geom ! Geometry
1142 integer,intent(in) :: il0 ! Level
1143 real(kind_real),intent(in) :: lon_s ! First point longitude
1144 real(kind_real),intent(in) :: lat_s ! First point latitude
1145 real(kind_real),intent(in) :: lon_e ! Second point longitude
1146 real(kind_real),intent(in) :: lat_e ! Second point latitude
1147 logical,intent(out) :: valid ! True for valid arcs
1148 
1149 ! Local variables
1150 integer :: ibnd
1151 real(kind_real) :: x(2),y(2),z(2),v1(3),v2(3),va(3),vp(3),t(4)
1152 
1153 ! Transform to cartesian coordinates
1154 call trans(2,(/lat_s,lat_e/),(/lon_s,lon_e/),x,y,z)
1155 
1156 ! Compute arc orthogonal vector
1157 v1 = (/x(1),y(1),z(1)/)
1158 v2 = (/x(2),y(2),z(2)/)
1159 call vector_product(v1,v2,va)
1160 
1161 ! Check if arc is crossing boundary arcs
1162 valid = .true.
1163 do ibnd=1,geom%nbnd(il0)
1164  call vector_product(va,geom%vbnd(:,ibnd,il0),vp)
1165  v1 = (/x(1),y(1),z(1)/)
1166  call vector_triple_product(v1,va,vp,t(1))
1167  v1 = (/x(2),y(2),z(2)/)
1168  call vector_triple_product(v1,va,vp,t(2))
1169  v1 = (/geom%xbnd(1,ibnd,il0),geom%ybnd(1,ibnd,il0),geom%zbnd(1,ibnd,il0)/)
1170  call vector_triple_product(v1,geom%vbnd(:,ibnd,il0),vp,t(3))
1171  v1 = (/geom%xbnd(2,ibnd,il0),geom%ybnd(2,ibnd,il0),geom%zbnd(2,ibnd,il0)/)
1172  call vector_triple_product(v1,geom%vbnd(:,ibnd,il0),vp,t(4))
1173  t(1) = -t(1)
1174  t(3) = -t(3)
1175  if (all(t>0).or.(all(t<0))) then
1176  valid = .false.
1177  exit
1178  end if
1179 end do
1180 
1181 end subroutine geom_check_arc
1182 
1183 !----------------------------------------------------------------------
1184 ! Subroutine: geom_copy_c0a_to_mga
1185 ! Purpose: copy from subset Sc0 to model grid, halo A
1186 !----------------------------------------------------------------------
1187 subroutine geom_copy_c0a_to_mga(geom,mpl,fld_c0a,fld_mga)
1189 implicit none
1190 
1191 ! Passed variables
1192 class(geom_type),intent(in) :: geom ! Geometry
1193 type(mpl_type),intent(in) :: mpl ! MPI data
1194 real(kind_real),intent(in) :: fld_c0a(geom%nc0a,geom%nl0) ! Field on subset Sc0, halo A
1195 real(kind_real),intent(out) :: fld_mga(geom%nmga,geom%nl0) ! Field on model grid, halo A
1196 
1197 if (geom%nc0==geom%nmg) then
1198  ! Model grid and subset Sc0 are identical
1199  fld_mga = fld_c0a
1200 else
1201  ! Extend subset Sc0 to model grid
1202  call geom%com_mg%ext(mpl,geom%nl0,fld_c0a,fld_mga)
1203 end if
1204 
1205 write(mpl%info,*) 'c0a_to_mga',mpl%myproc,maxval(fld_c0a),maxval(fld_mga),sum(fld_c0a),sum(fld_mga)
1206 
1207 end subroutine geom_copy_c0a_to_mga
1208 
1209 !----------------------------------------------------------------------
1210 ! Subroutine: geom_copy_mga_to_c0a
1211 ! Purpose: copy from model grid to subset Sc0, halo A
1212 !----------------------------------------------------------------------
1213 subroutine geom_copy_mga_to_c0a(geom,mpl,fld_mga,fld_c0a)
1215 implicit none
1216 
1217 ! Passed variables
1218 class(geom_type),intent(in) :: geom ! Geometry
1219 type(mpl_type),intent(in) :: mpl ! MPI data
1220 real(kind_real),intent(in) :: fld_mga(geom%nmga,geom%nl0) ! Field on model grid, halo A
1221 real(kind_real),intent(out) :: fld_c0a(geom%nc0a,geom%nl0) ! Field on subset Sc0, halo A
1222 
1223 ! Local variables
1224 integer :: ic0a,imga,il0
1225 real(kind_real) :: fld_mga_zero(geom%nmga,geom%nl0)
1226 
1227 if (geom%nc0==geom%nmg) then
1228  ! Model grid and subset Sc0 are identical
1229  fld_c0a = fld_mga
1230 else
1231  ! Initialization
1232  fld_mga_zero = fld_mga
1233  do ic0a=1,geom%nc0a
1234  imga = geom%c0a_to_mga(ic0a)
1235  fld_mga_zero(imga,:) = 0.0
1236  end do
1237 
1238  ! Reduce model grid to subset Sc0
1239  call geom%com_mg%red(mpl,geom%nl0,fld_mga_zero,fld_c0a)
1240 
1241 write(mpl%info,*) 'mga_to_c0a',mpl%myproc,maxval(fld_mga_zero),maxval(fld_c0a),sum(fld_mga_zero),sum(fld_c0a)
1242 
1243  ! Copy non-redundant points
1244  do il0=1,geom%nl0
1245  do ic0a=1,geom%nc0a
1246  imga = geom%c0a_to_mga(ic0a)
1247  if (abs(fld_mga(imga,il0)-fld_c0a(ic0a,il0))>0.0) then
1248  ! Values are different
1249  if (ismsr(fld_c0a(ic0a,il0))) then
1250  ! Subset Sc0 value is missing
1251  fld_c0a(ic0a,il0) = fld_mga(imga,il0)
1252  elseif (ismsr(fld_mga(imga,il0))) then
1253  ! Nothing to do
1254  else
1255  ! Both values are not missing, check for zero value
1256  if (.not.(abs(fld_c0a(ic0a,il0))>0.0)) then
1257  ! Subset Sc0 value is zero
1258  fld_c0a(ic0a,il0) = fld_mga(imga,il0)
1259  elseif (.not.(abs(fld_mga(imga,il0))>0.0)) then
1260  ! Nothing to do
1261  else
1262  call mpl%abort('both redundant values are different, not missing and nonzero')
1263  end if
1264  end if
1265  end if
1266  end do
1267  end do
1268 end if
1269 
1270 write(mpl%info,*) 'mga_to_c0a',mpl%myproc,maxval(fld_mga),maxval(fld_c0a),sum(fld_mga),sum(fld_c0a)
1271 
1272 end subroutine geom_copy_mga_to_c0a
1273 
1274 !----------------------------------------------------------------------
1275 ! Subroutine: geom_compute_deltas
1276 ! Purpose: compute deltas for LCT definition
1277 !----------------------------------------------------------------------
1278 subroutine geom_compute_deltas(geom,ic0,il0,jc0,jl0,dx,dy,dz)
1280 implicit none
1281 
1282 ! Passed variables
1283 class(geom_type),intent(in) :: geom ! Geometry
1284 integer,intent(in) :: ic0 ! First horizontal index
1285 integer,intent(in) :: il0 ! First vertical index
1286 integer,intent(in) :: jc0 ! Second horizontal index
1287 integer,intent(in) :: jl0 ! Second vertical index
1288 real(kind_real),intent(out) :: dx ! Longitude delta
1289 real(kind_real),intent(out) :: dy ! Latitude delta
1290 real(kind_real),intent(out) :: dz ! Altitude delta
1291 
1292 ! Compute deltas
1293 dx = geom%lon(jc0)-geom%lon(ic0)
1294 dy = geom%lat(jc0)-geom%lat(ic0)
1295 call lonlatmod(dx,dy)
1296 dx = dx*cos(geom%lat(ic0))
1297 dz = real(geom%vunit(ic0,jl0)-geom%vunit(ic0,il0),kind_real)
1298 
1299 end subroutine geom_compute_deltas
1300 
1301 end module type_geom
subroutine geom_dealloc(geom)
Definition: type_geom.F90:179
real(kind_real), parameter, public rad2deg
Definition: tools_const.F90:17
subroutine geom_compute_deltas(geom, ic0, il0, jc0, jl0, dx, dy, dz)
Definition: type_geom.F90:1279
subroutine geom_copy_c0a_to_mga(geom, mpl, fld_c0a, fld_mga)
Definition: type_geom.F90:1188
subroutine, public trans(n, rlat, rlon, x, y, z)
subroutine geom_define_distribution(geom, mpl, nam, rng)
Definition: type_geom.F90:893
subroutine, public lonlatmod(lon, lat)
Definition: tools_func.F90:37
subroutine geom_define_dirac(geom, nam)
Definition: type_geom.F90:833
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
logical, parameter test_no_point
Definition: type_geom.F90:30
integer, parameter nredmax
Definition: type_geom.F90:28
subroutine geom_compute_area(geom, mpl)
Definition: type_geom.F90:798
real(kind_real), parameter distminred
Definition: type_geom.F90:29
real(kind_real), parameter, public deg2rad
Definition: tools_const.F90:16
real(kind=kind_real), parameter req
Earth radius at equator (m)
subroutine geom_alloc(geom)
Definition: type_geom.F90:134
subroutine geom_init(geom, mpl, rng, nam)
Definition: type_geom.F90:643
subroutine geom_check_arc(geom, il0, lon_s, lat_s, lon_e, lat_e, valid)
Definition: type_geom.F90:1137
subroutine geom_find_sc0(geom, mpl, rng, lon, lat, lmask, red_check, mask_check, mask_del)
Definition: type_geom.F90:440
real(kind_real) function, public areas(v1, v2, v3)
subroutine geom_copy_mga_to_c0a(geom, mpl, fld_mga, fld_c0a)
Definition: type_geom.F90:1214
subroutine geom_setup_online(geom, mpl, rng, nam, nmga, nl0, lon, lat, area, vunit, lmask)
Definition: type_geom.F90:230
integer, parameter, public kind_real
subroutine, public vector_triple_product(v1, v2, v3, p)
Definition: tools_func.F90:158
integer, parameter, public ncfloat
Definition: tools_nc.F90:27
subroutine geom_define_mask(geom, mpl, nam)
Definition: type_geom.F90:729
real(fp), parameter, public pi
real(kind_real), parameter, public reqkm
Definition: tools_const.F90:19