FV3 Bundle
type_linop.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_linop
3 ! Purpose: linear operator 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_linop
9 
10 use netcdf
11 !$ use omp_lib
12 use tools_kinds, only: kind_real
14 use tools_nc, only: ncfloat
15 use tools_qsort, only: qsort
16 use tools_repro, only: inf
17 use type_geom, only: geom_type
18 use type_kdtree, only: kdtree_type
19 use type_mesh, only: mesh_type
20 use type_mpl, only: mpl_type
21 use type_rng, only: rng_type
22 use fckit_mpi_module, only: fckit_mpi_status
23 
24 implicit none
25 
26 logical,parameter :: check_data = .false. ! Activate data check for all linear operations
27 integer,parameter :: reorder_max = 1000000 ! Maximum size of linear operation to allow reordering
28 integer,parameter :: nnatmax = 40 ! Maximum number of natural neighbors
29 real(kind_real),parameter :: s_inf = 1.0e-2_kind_real ! Minimum interpolation coefficient
30 
31 ! Linear operator derived type
33  character(len=1024) :: prefix ! Operator prefix (for I/O)
34  integer :: n_src ! Source vector size
35  integer :: n_dst ! Destination vector size
36  integer :: n_s ! Operator size
37  integer,allocatable :: row(:) ! Output indices
38  integer,allocatable :: col(:) ! Input indices
39  real(kind_real),allocatable :: s(:) ! Coefficients
40  integer :: nvec ! Size of the vector of linear operators with similar row and col
41  real(kind_real),allocatable :: svec(:,:) ! Coefficients of the vector of linear operators with similar row and col
42 contains
43  procedure :: alloc => linop_alloc
44  procedure :: dealloc => linop_dealloc
45  procedure :: copy => linop_copy
46  procedure :: reorder => linop_reorder
47  procedure :: read => linop_read
48  procedure :: write => linop_write
49  procedure :: apply => linop_apply
50  procedure :: apply_ad => linop_apply_ad
51  procedure :: apply_sym => linop_apply_sym
52  procedure :: add_op => linop_add_op
53  procedure :: gather => linop_gather
56  procedure :: linop_interp_grid
58  procedure :: interp_check_mask => linop_interp_check_mask
59  procedure :: interp_missing => linop_interp_missing
60 end type linop_type
61 
62 private
63 public :: linop_type
64 
65 contains
66 
67 !----------------------------------------------------------------------
68 ! Subroutine: linop_alloc
69 ! Purpose: linear operator allocation
70 !----------------------------------------------------------------------
71 subroutine linop_alloc(linop,nvec)
72 
73 implicit none
74 
75 ! Passed variables
76 class(linop_type),intent(inout) :: linop ! Linear operator
77 integer,intent(in),optional :: nvec ! Size of the vector of linear operators with similar row and col
78 
79 ! Vector size
80 if (present(nvec)) then
81  linop%nvec = nvec
82 else
83  call msi(linop%nvec)
84 end if
85 
86 ! Allocation
87 allocate(linop%row(linop%n_s))
88 allocate(linop%col(linop%n_s))
89 if (present(nvec)) then
90  allocate(linop%Svec(linop%n_s,linop%nvec))
91 else
92  allocate(linop%S(linop%n_s))
93 end if
94 
95 ! Initialization
96 if (linop%n_s>0) then
97  call msi(linop%row)
98  call msi(linop%col)
99  if (present(nvec)) then
100  if (linop%nvec>0) call msr(linop%Svec)
101  else
102  call msr(linop%S)
103  end if
104 end if
105 
106 end subroutine linop_alloc
107 
108 !----------------------------------------------------------------------
109 ! Subroutine: linop_dealloc
110 ! Purpose: linear operator deallocation
111 !----------------------------------------------------------------------
112 subroutine linop_dealloc(linop)
114 implicit none
115 
116 ! Passed variables
117 class(linop_type),intent(inout) :: linop ! Linear operator
118 
119 ! Release memory
120 if (allocated(linop%row)) deallocate(linop%row)
121 if (allocated(linop%col)) deallocate(linop%col)
122 if (allocated(linop%S)) deallocate(linop%S)
123 if (allocated(linop%Svec)) deallocate(linop%Svec)
124 
125 end subroutine linop_dealloc
126 
127 !----------------------------------------------------------------------
128 ! Function: linop_copy
129 ! Purpose: linear operator copy
130 !----------------------------------------------------------------------
131 type(linop_type) function linop_copy(linop)
133 implicit none
134 
135 ! Passed variables
136 class(linop_type),intent(in) :: linop ! Linear operator
137 
138 ! Copy attributes
139 linop_copy%prefix = trim(linop%prefix)
140 linop_copy%n_src = linop%n_src
141 linop_copy%n_dst = linop%n_dst
142 linop_copy%n_s = linop%n_s
143 
144 ! Deallocation
145 call linop_copy%dealloc
146 
147 ! Allocation
148 if (isnotmsi(linop%nvec)) then
149  call linop_copy%alloc(linop%nvec)
150 else
151  call linop_copy%alloc
152 end if
153 
154 ! Copy data
155 if (linop%n_s>0) then
156  linop_copy%row = linop%row
157  linop_copy%col = linop%col
158  if (isnotmsi(linop_copy%nvec)) then
159  if (linop_copy%nvec>0) linop_copy%Svec = linop%Svec
160  else
161  linop_copy%S = linop%S
162  end if
163 end if
164 
165 end function linop_copy
166 
167 !----------------------------------------------------------------------
168 ! Subroutine: linop_reorder
169 ! Purpose: reorder linear operator
170 !----------------------------------------------------------------------
171 subroutine linop_reorder(linop,mpl)
173 implicit none
174 
175 ! Passed variables
176 class(linop_type),intent(inout) :: linop ! Linear operator
177 type(mpl_type),intent(in) :: mpl ! MPI data
178 
179 ! Local variables
180 integer :: row,i_s_s,i_s_e,n_s,i_s
181 integer,allocatable :: order(:)
182 
183 if ((linop%n_s>0).and.(linop%n_s<reorder_max)) then
184  ! Sort with respect to row
185  allocate(order(linop%n_s))
186  call qsort(linop%n_s,linop%row,order)
187 
188  ! Sort col and S
189  linop%col = linop%col(order)
190  if (isnotmsi(linop%nvec)) then
191  if (linop%nvec>0) linop%Svec = linop%Svec(order,:)
192  else
193  linop%S = linop%S(order)
194  end if
195  deallocate(order)
196 
197  ! Sort with respect to col for each row
198  row = minval(linop%row)
199  i_s_s = 1
200  call msi(i_s_e)
201  do i_s=1,linop%n_s
202  if (linop%row(i_s)==row) then
203  i_s_e = i_s
204  else
205  n_s = i_s_e-i_s_s+1
206  allocate(order(n_s))
207  call qsort(n_s,linop%col(i_s_s:i_s_e),order)
208  order = order+i_s_s-1
209  if (isnotmsi(linop%nvec)) then
210  if (linop%nvec>0) linop%Svec(i_s_s:i_s_e,:) = linop%Svec(order,:)
211  else
212  linop%S(i_s_s:i_s_e) = linop%S(order)
213  end if
214  deallocate(order)
215  i_s_s = i_s+1
216  row = linop%row(i_s)
217  end if
218  end do
219 else
220  call mpl%warning('linear operator is too big, impossible to reorder')
221 end if
222 
223 end subroutine linop_reorder
224 
225 !----------------------------------------------------------------------
226 ! Subroutine: linop_read
227 ! Purpose: read linear operator from a NetCDF file
228 !----------------------------------------------------------------------
229 subroutine linop_read(linop,mpl,ncid)
231 implicit none
232 
233 ! Passed variables
234 class(linop_type),intent(inout) :: linop ! Linear operator
235 type(mpl_type),intent(in) :: mpl ! MPI data
236 integer,intent(in) :: ncid ! NetCDF file ID
237 
238 ! Local variables
239 integer :: info,nvec
240 integer :: n_s_id,row_id,col_id,S_id,Svec_id
241 character(len=1024) :: subr = 'linop_read'
242 
243 ! Get operator size
244 info = nf90_inq_dimid(ncid,trim(linop%prefix)//'_n_s',n_s_id)
245 if (info==nf90_noerr) then
246  call mpl%ncerr(subr,nf90_inquire_dimension(ncid,n_s_id,len=linop%n_s))
247 else
248  linop%n_s = 0
249 end if
250 
251 ! Get source/destination dimensions
252 call mpl%ncerr(subr,nf90_get_att(ncid,nf90_global,trim(linop%prefix)//'_n_src',linop%n_src))
253 call mpl%ncerr(subr,nf90_get_att(ncid,nf90_global,trim(linop%prefix)//'_n_dst',linop%n_dst))
254 call mpl%ncerr(subr,nf90_get_att(ncid,nf90_global,trim(linop%prefix)//'_nvec',nvec))
255 
256 ! Allocation
257 if (isnotmsi(nvec)) then
258  call linop%alloc(nvec)
259 else
260  call linop%alloc
261 end if
262 
263 if (linop%n_s>0) then
264  ! Get variables id
265  call mpl%ncerr(subr,nf90_inq_varid(ncid,trim(linop%prefix)//'_row',row_id))
266  call mpl%ncerr(subr,nf90_inq_varid(ncid,trim(linop%prefix)//'_col',col_id))
267  if (isnotmsi(linop%nvec)) then
268  if (linop%nvec>0) call mpl%ncerr(subr,nf90_inq_varid(ncid,trim(linop%prefix)//'_Svec',svec_id))
269  else
270  call mpl%ncerr(subr,nf90_inq_varid(ncid,trim(linop%prefix)//'_S',s_id))
271  end if
272 
273  ! Get variables
274  call mpl%ncerr(subr,nf90_get_var(ncid,row_id,linop%row))
275  call mpl%ncerr(subr,nf90_get_var(ncid,col_id,linop%col))
276  if (isnotmsi(linop%nvec)) then
277  if (linop%nvec>0) call mpl%ncerr(subr,nf90_get_var(ncid,svec_id,linop%Svec))
278  else
279  call mpl%ncerr(subr,nf90_get_var(ncid,s_id,linop%S))
280  end if
281 end if
282 
283 end subroutine linop_read
284 
285 !----------------------------------------------------------------------
286 ! Subroutine: linop_write
287 ! Purpose: write linear operator to a NetCDF file
288 !----------------------------------------------------------------------
289 subroutine linop_write(linop,mpl,ncid)
291 implicit none
292 
293 ! Passed variables
294 class(linop_type),intent(in) :: linop ! Linear operator
295 type(mpl_type),intent(in) :: mpl ! MPI data
296 integer,intent(in) :: ncid ! NetCDF file ID
297 
298 ! Local variables
299 integer :: n_s_id,nvec_id,row_id,col_id,S_id,Svec_id
300 character(len=1024) :: subr = 'linop_write'
301 
302 ! Start definition mode
303 call mpl%ncerr(subr,nf90_redef(ncid))
304 
305 ! Write source/destination dimensions
306 call mpl%ncerr(subr,nf90_put_att(ncid,nf90_global,trim(linop%prefix)//'_n_src',linop%n_src))
307 call mpl%ncerr(subr,nf90_put_att(ncid,nf90_global,trim(linop%prefix)//'_n_dst',linop%n_dst))
308 call mpl%ncerr(subr,nf90_put_att(ncid,nf90_global,trim(linop%prefix)//'_nvec',linop%nvec))
309 
310 if (linop%n_s>0) then
311  ! Define dimensions
312  call mpl%ncerr(subr,nf90_def_dim(ncid,trim(linop%prefix)//'_n_s',linop%n_s,n_s_id))
313  if (isnotmsi(linop%nvec).and.(linop%nvec>0)) call mpl%ncerr(subr,nf90_def_dim(ncid,trim(linop%prefix)//'_nvec',linop%nvec, &
314  & nvec_id))
315 
316  ! Define variables
317  call mpl%ncerr(subr,nf90_def_var(ncid,trim(linop%prefix)//'_row',nf90_int,(/n_s_id/),row_id))
318  call mpl%ncerr(subr,nf90_def_var(ncid,trim(linop%prefix)//'_col',nf90_int,(/n_s_id/),col_id))
319  if (isnotmsi(linop%nvec)) then
320  if (linop%nvec>0) call mpl%ncerr(subr,nf90_def_var(ncid,trim(linop%prefix)//'_Svec',ncfloat,(/n_s_id,nvec_id/),svec_id))
321  else
322  call mpl%ncerr(subr,nf90_def_var(ncid,trim(linop%prefix)//'_S',ncfloat,(/n_s_id/),s_id))
323  end if
324 
325  ! End definition mode
326  call mpl%ncerr(subr,nf90_enddef(ncid))
327 
328  ! Put variables
329  call mpl%ncerr(subr,nf90_put_var(ncid,row_id,linop%row))
330  call mpl%ncerr(subr,nf90_put_var(ncid,col_id,linop%col))
331  if (isnotmsi(linop%nvec)) then
332  if (linop%nvec>0) call mpl%ncerr(subr,nf90_put_var(ncid,svec_id,linop%Svec))
333  else
334  call mpl%ncerr(subr,nf90_put_var(ncid,s_id,linop%S))
335  end if
336 else
337  ! End definition mode
338  call mpl%ncerr(subr,nf90_enddef(ncid))
339 end if
340 
341 end subroutine linop_write
342 
343 !----------------------------------------------------------------------
344 ! Subroutine: linop_apply
345 ! Purpose: apply linear operator
346 !----------------------------------------------------------------------
347 subroutine linop_apply(linop,mpl,fld_src,fld_dst,ivec,mssrc,msdst)
349 implicit none
350 
351 ! Passed variables
352 class(linop_type),intent(in) :: linop ! Linear operator
353 type(mpl_type),intent(in) :: mpl ! MPI data
354 real(kind_real),intent(in) :: fld_src(linop%n_src) ! Source vector
355 real(kind_real),intent(out) :: fld_dst(linop%n_dst) ! Destination vector
356 integer,intent(in),optional :: ivec ! Index of the vector of linear operators with similar row and col
357 logical,intent(in),optional :: mssrc ! Check for missing source
358 logical,intent(in),optional :: msdst ! Check for missing destination
359 
360 ! Local variables
361 integer :: i_s,i_dst
362 logical :: lmssrc,lmsdst,valid
363 logical,allocatable :: missing(:)
364 
365 if (check_data) then
366  ! Check linear operation
367  if (minval(linop%col)<1) call mpl%abort('col<1 for linear operation '//trim(linop%prefix))
368  if (maxval(linop%col)>linop%n_src) call mpl%abort('col>n_src for linear operation '//trim(linop%prefix))
369  if (minval(linop%row)<1) call mpl%abort('row<1 for linear operation '//trim(linop%prefix))
370  if (maxval(linop%row)>linop%n_dst) call mpl%abort('row>n_dst for linear operation '//trim(linop%prefix))
371  if (present(ivec)) then
372  if (any(isnan(linop%Svec))) call mpl%abort('NaN in Svec for linear operation '//trim(linop%prefix))
373  else
374  if (any(isnan(linop%S))) call mpl%abort('NaN in S for linear operation '//trim(linop%prefix))
375  end if
376 
377  ! Check input
378  if (any(fld_src>huge(1.0))) call mpl%abort('Overflowing number in fld_src for linear operation '//trim(linop%prefix))
379  if (any(isnan(fld_src))) call mpl%abort('NaN in fld_src for linear operation '//trim(linop%prefix))
380 end if
381 
382 ! Initialization
383 fld_dst = 0.0
384 lmssrc = .false.
385 if (present(mssrc)) lmssrc = mssrc
386 lmsdst = .true.
387 if (present(msdst)) lmsdst = msdst
388 if (lmsdst) then
389  allocate(missing(linop%n_dst))
390  missing = .true.
391 end if
392 
393 ! Apply weights
394 do i_s=1,linop%n_s
395  if (lmssrc) then
396  ! Check for missing source (WARNING: source-dependent => no adjoint)
397  valid = isnotmsr(fld_src(linop%col(i_s)))
398  else
399  ! Source independent
400  valid = .true.
401  end if
402 
403  if (valid) then
404  if (present(ivec)) then
405  fld_dst(linop%row(i_s)) = fld_dst(linop%row(i_s))+linop%Svec(i_s,ivec)*fld_src(linop%col(i_s))
406  else
407  fld_dst(linop%row(i_s)) = fld_dst(linop%row(i_s))+linop%S(i_s)*fld_src(linop%col(i_s))
408  end if
409 
410  ! Check for missing destination
411  if (lmsdst) missing(linop%row(i_s)) = .false.
412  end if
413 end do
414 
415 if (lmsdst) then
416  ! Missing destination values
417  do i_dst=1,linop%n_dst
418  if (missing(i_dst)) call msr(fld_dst(i_dst))
419  end do
420 end if
421 
422 if (check_data) then
423  ! Check output
424  if (any(isnan(fld_dst))) call mpl%abort('NaN in fld_dst for linear operation '//trim(linop%prefix))
425 end if
426 
427 end subroutine linop_apply
428 
429 !----------------------------------------------------------------------
430 ! Subroutine: linop_apply_ad
431 ! Purpose: apply linear operator, adjoint
432 !----------------------------------------------------------------------
433 subroutine linop_apply_ad(linop,mpl,fld_dst,fld_src,ivec)
435 implicit none
436 
437 ! Passed variables
438 class(linop_type),intent(in) :: linop ! Linear operator
439 type(mpl_type),intent(in) :: mpl ! MPI data
440 real(kind_real),intent(in) :: fld_dst(linop%n_dst) ! Destination vector
441 real(kind_real),intent(out) :: fld_src(linop%n_src) ! Source vector
442 integer,intent(in),optional :: ivec ! Index of the vector of linear operators with similar row and col
443 
444 ! Local variables
445 integer :: i_s
446 
447 if (check_data) then
448  ! Check linear operation
449  if (minval(linop%col)<1) call mpl%abort('col<1 for adjoint linear operation '//trim(linop%prefix))
450  if (maxval(linop%col)>linop%n_src) call mpl%abort('col>n_src for adjoint linear operation '//trim(linop%prefix))
451  if (minval(linop%row)<1) call mpl%abort('row<1 for adjoint linear operation '//trim(linop%prefix))
452  if (maxval(linop%row)>linop%n_dst) call mpl%abort('row>n_dst for adjoint linear operation '//trim(linop%prefix))
453  if (present(ivec)) then
454  if (any(isnan(linop%Svec))) call mpl%abort('NaN in Svec for adjoint linear operation '//trim(linop%prefix))
455  else
456  if (any(isnan(linop%S))) call mpl%abort('NaN in S for adjoint linear operation '//trim(linop%prefix))
457  end if
458 
459  ! Check input
460  if (any(fld_dst>huge(1.0))) call mpl%abort('Overflowing number in fld_dst for adjoint linear operation '//trim(linop%prefix))
461  if (any(isnan(fld_dst))) call mpl%abort('NaN in fld_dst for adjoint linear operation '//trim(linop%prefix))
462 end if
463 
464 ! Initialization
465 fld_src = 0.0
466 
467 ! Apply weights
468 do i_s=1,linop%n_s
469  if (present(ivec)) then
470  fld_src(linop%col(i_s)) = fld_src(linop%col(i_s))+linop%Svec(i_s,ivec)*fld_dst(linop%row(i_s))
471  else
472  fld_src(linop%col(i_s)) = fld_src(linop%col(i_s))+linop%S(i_s)*fld_dst(linop%row(i_s))
473  end if
474 end do
475 
476 if (check_data) then
477  ! Check output
478  if (any(isnan(fld_src))) call mpl%abort('NaN in fld_src for adjoint linear operation '//trim(linop%prefix))
479 end if
480 
481 end subroutine linop_apply_ad
482 
483 !----------------------------------------------------------------------
484 ! Subroutine: linop_apply_sym
485 ! Purpose: apply linear operator, symmetric
486 !----------------------------------------------------------------------
487 subroutine linop_apply_sym(linop,mpl,fld,ivec)
489 implicit none
490 
491 ! Passed variables
492 class(linop_type),intent(in) :: linop ! Linear operator
493 type(mpl_type),intent(in) :: mpl ! MPI data
494 real(kind_real),intent(inout) :: fld(linop%n_src) ! Source/destination vector
495 integer,intent(in),optional :: ivec ! Index of the vector of linear operators with similar row and col
496 
497 ! Local variables
498 integer :: i_s,ithread
499 real(kind_real) :: fld_arr(linop%n_dst,mpl%nthread)
500 
501 if (check_data) then
502  ! Check linear operation
503  if (minval(linop%col)<1) call mpl%abort('col<1 for symmetric linear operation '//trim(linop%prefix))
504  if (maxval(linop%col)>linop%n_src) call mpl%abort('col>n_src for symmetric linear operation '//trim(linop%prefix))
505  if (minval(linop%row)<1) call mpl%abort('row<1 for symmetric linear operation '//trim(linop%prefix))
506  if (maxval(linop%row)>linop%n_src) call mpl%abort('row>n_dst for symmetric linear operation '//trim(linop%prefix))
507  if (present(ivec)) then
508  if (any(isnan(linop%Svec))) call mpl%abort('NaN in Svec for symmetric linear operation '//trim(linop%prefix))
509  else
510  if (any(isnan(linop%S))) call mpl%abort('NaN in S for symmetric linear operation '//trim(linop%prefix))
511  end if
512 
513  ! Check input
514  if (any(fld>huge(1.0))) call mpl%abort('Overflowing number in fld for symmetric linear operation '//trim(linop%prefix))
515  if (any(isnan(fld))) call mpl%abort('NaN in fld for symmetric linear operation '//trim(linop%prefix))
516 end if
517 
518 ! Apply weights
519 fld_arr = 0.0
520 !$omp parallel do schedule(static) private(i_s,ithread)
521 do i_s=1,linop%n_s
522  ithread = 1
523 !$ ithread = omp_get_thread_num()+1
524  if (present(ivec)) then
525  fld_arr(linop%row(i_s),ithread) = fld_arr(linop%row(i_s),ithread)+linop%Svec(i_s,ivec)*fld(linop%col(i_s))
526  if (linop%col(i_s)/=linop%row(i_s)) fld_arr(linop%col(i_s),ithread) = fld_arr(linop%col(i_s),ithread) &
527  & +linop%Svec(i_s,ivec)*fld(linop%row(i_s))
528  else
529  fld_arr(linop%row(i_s),ithread) = fld_arr(linop%row(i_s),ithread)+linop%S(i_s)*fld(linop%col(i_s))
530  if (linop%col(i_s)/=linop%row(i_s)) fld_arr(linop%col(i_s),ithread) = fld_arr(linop%col(i_s),ithread) &
531  & +linop%S(i_s)*fld(linop%row(i_s))
532  end if
533 end do
534 !$omp end parallel do
535 
536 ! Sum over threads
537 fld = 0.0
538 do ithread=1,mpl%nthread
539  fld = fld+fld_arr(:,ithread)
540 end do
541 
542 if (check_data) then
543  ! Check output
544  if (any(isnan(fld))) call mpl%abort('NaN in fld for symmetric linear operation '//trim(linop%prefix))
545 end if
546 
547 end subroutine linop_apply_sym
548 
549 !----------------------------------------------------------------------
550 ! Subroutine: linop_add_op
551 ! Purpose: add operation
552 !----------------------------------------------------------------------
553 subroutine linop_add_op(linop,n_s,row,col,S)
555 implicit none
556 
557 ! Passed variables
558 class(linop_type),intent(inout) :: linop ! Linear operator
559 integer,intent(inout) :: n_s ! Number of operations
560 integer,intent(in) :: row ! Row index
561 integer,intent(in) :: col ! Column index
562 real(kind_real),intent(in) :: S ! Value
563 
564 ! Local variables
565 type(linop_type) :: linop_tmp
566 
567 ! Update
568 n_s = n_s+1
569 if (n_s>linop%n_s) then
570  ! Copy
571  linop_tmp = linop%copy()
572 
573  ! Reallocate larger linear operation
574  call linop%dealloc
575  linop%n_s = 2*linop_tmp%n_s
576  call linop%alloc
577 
578  ! Copy data
579  linop%row(1:linop_tmp%n_s) = linop_tmp%row
580  linop%col(1:linop_tmp%n_s) = linop_tmp%col
581  linop%S(1:linop_tmp%n_s) = linop_tmp%S
582 
583  ! Release memory
584  call linop_tmp%dealloc
585 end if
586 
587 ! New operation
588 linop%row(n_s) = row
589 linop%col(n_s) = col
590 linop%S(n_s) = s
591 
592 end subroutine linop_add_op
593 
594 !----------------------------------------------------------------------
595 ! Subroutine: linop_gather
596 ! Purpose: gather data from OpenMP threads
597 !----------------------------------------------------------------------
598 subroutine linop_gather(linop,mpl,n_s_arr,linop_arr)
600 implicit none
601 
602 ! Passed variables
603 class(linop_type),intent(inout) :: linop ! Linear operator
604 type(mpl_type),intent(in) :: mpl ! MPI data
605 integer,intent(in) :: n_s_arr(mpl%nthread) ! Number of operations
606 type(linop_type),intent(in) :: linop_arr(mpl%nthread) ! Linear operator array
607 
608 ! Local variables
609 integer :: ithread,offset
610 
611 ! Total number of operations
612 linop%n_s = sum(n_s_arr)
613 
614 ! Allocation
615 call linop%alloc
616 
617 ! Gather data
618 offset = 0
619 do ithread=1,mpl%nthread
620  linop%row(offset+1:offset+n_s_arr(ithread)) = linop_arr(ithread)%row(1:n_s_arr(ithread))
621  linop%col(offset+1:offset+n_s_arr(ithread)) = linop_arr(ithread)%col(1:n_s_arr(ithread))
622  linop%S(offset+1:offset+n_s_arr(ithread)) = linop_arr(ithread)%S(1:n_s_arr(ithread))
623  offset = offset+n_s_arr(ithread)
624 end do
625 
626 end subroutine linop_gather
627 
628 !----------------------------------------------------------------------
629 ! Subroutine: linop_interp_from_lat_lon
630 ! Purpose: compute horizontal interpolation from source latitude/longitude
631 !----------------------------------------------------------------------
632 subroutine linop_interp_from_lat_lon(linop,mpl,rng,n_src,lon_src,lat_src,mask_src,n_dst,lon_dst,lat_dst,mask_dst,interp_type)
634 implicit none
635 
636 ! Passed variables
637 class(linop_type),intent(inout) :: linop ! Linear operator
638 type(mpl_type),intent(inout) :: mpl ! MPI data
639 type(rng_type),intent(inout) :: rng ! Random number generator
640 integer,intent(in) :: n_src ! Source size
641 real(kind_real),intent(in) :: lon_src(n_src) ! Source longitudes
642 real(kind_real),intent(in) :: lat_src(n_src) ! Source latitudes
643 logical,intent(in) :: mask_src(n_src) ! Source mask
644 integer,intent(in) :: n_dst ! Destination size
645 real(kind_real),intent(in) :: lon_dst(n_dst) ! Destination longitudes
646 real(kind_real),intent(in) :: lat_dst(n_dst) ! Destination latitudes
647 logical,intent(in) :: mask_dst(n_dst) ! Destination mask
648 character(len=*),intent(in) :: interp_type ! Interpolation type
649 
650 ! Local variables
651 integer :: n_src_eff,i_src,i_src_eff
652 integer,allocatable :: src_eff_to_src(:)
653 real(kind_real),allocatable :: lon_src_eff(:),lat_src_eff(:)
654 logical,allocatable :: mask_src_eff(:)
655 type(kdtree_type) :: kdtree
656 type(mesh_type) :: mesh
657 
658 ! Count non-missing source points
659 n_src_eff = count(mask_src)
660 
661 ! Allocation
662 allocate(src_eff_to_src(n_src_eff))
663 allocate(lon_src_eff(n_src_eff))
664 allocate(lat_src_eff(n_src_eff))
665 allocate(mask_src_eff(n_src_eff))
666 
667 ! Conversion
668 i_src_eff = 0
669 do i_src=1,n_src
670  if (mask_src(i_src)) then
671  i_src_eff = i_src_eff+1
672  src_eff_to_src(i_src_eff) = i_src
673  end if
674 end do
675 lon_src_eff = lon_src(src_eff_to_src)
676 lat_src_eff = lat_src(src_eff_to_src)
677 mask_src_eff = .true.
678 
679 ! Create mesh
680 call mesh%create(mpl,rng,n_src_eff,lon_src_eff,lat_src_eff)
681 
682 ! Compute KD-tree
683 call kdtree%create(mpl,n_src_eff,lon_src_eff,lat_src_eff)
684 
685 ! Compute interpolation
686 call linop%interp(mpl,mesh,kdtree,n_src_eff,mask_src_eff,n_dst,lon_dst,lat_dst,mask_dst,interp_type)
687 
688 ! Effective points conversion
689 linop%n_src = n_src
690 linop%col = src_eff_to_src(linop%col)
691 
692 ! Release memory
693 call kdtree%dealloc
694 
695 end subroutine linop_interp_from_lat_lon
696 
697 !----------------------------------------------------------------------
698 ! Subroutine: linop_interp_from_mesh_kdtree
699 ! Purpose: compute horizontal interpolation from source mesh and kdtree
700 !----------------------------------------------------------------------
701 subroutine linop_interp_from_mesh_kdtree(linop,mpl,mesh,kdtree,n_src,mask_src,n_dst,lon_dst,lat_dst,mask_dst,interp_type)
703 implicit none
704 
705 ! Passed variables
706 class(linop_type),intent(inout) :: linop ! Linear operator
707 type(mpl_type),intent(inout) :: mpl ! MPI data
708 type(mesh_type),intent(in) :: mesh ! Mesh
709 type(kdtree_type),intent(in) :: kdtree ! KD-tree
710 integer,intent(in) :: n_src ! Source size
711 logical,intent(in) :: mask_src(n_src) ! Source mask
712 integer,intent(in) :: n_dst ! Destination size
713 real(kind_real),intent(in) :: lon_dst(n_dst) ! Destination longitudes
714 real(kind_real),intent(in) :: lat_dst(n_dst) ! Destination latitudes
715 logical,intent(in) :: mask_dst(n_dst) ! Destination mask
716 character(len=*),intent(in) :: interp_type ! Interpolation type
717 
718 ! Local variables
719 integer :: i,i_src,i_dst,nn_index(1),n_s,ib(3),nnat,inat,np,iproc,offset,i_s
720 integer :: i_dst_s(mpl%nproc),i_dst_e(mpl%nproc),n_dst_loc(mpl%nproc),i_dst_loc,proc_to_n_s(mpl%nproc)
721 integer,allocatable :: natis(:),row(:),col(:)
722 real(kind_real) :: nn_dist(1),b(3)
723 real(kind_real),allocatable :: area_polygon(:),area_polygon_new(:),natwgt(:),S(:)
724 logical :: loop
725 logical,allocatable :: missing(:)
726 type(mesh_type) :: meshnew
727 type(fckit_mpi_status) :: status
728 
729 ! MPI splitting
730 call mpl%split(n_dst,i_dst_s,i_dst_e,n_dst_loc)
731 
732 ! Allocation
733 call msi(np)
734 if (trim(interp_type)=='bilin') then
735  ! Bilinear interpolation
736  np = 3
737 elseif (trim(interp_type)=='natural') then
738  ! Natural neighbors
739  np = nnatmax
740  allocate(area_polygon(mesh%n))
741  allocate(area_polygon_new(nnatmax))
742  allocate(natis(mesh%n))
743  allocate(natwgt(nnatmax))
744 else
745  call mpl%abort('wrong interpolation type')
746 end if
747 allocate(row(np*n_dst_loc(mpl%myproc)))
748 allocate(col(np*n_dst_loc(mpl%myproc)))
749 allocate(s(np*n_dst_loc(mpl%myproc)))
750 
751 if (trim(interp_type)=='natural') then
752  ! Compute polygons areas
753  do i_src=1,mesh%n
754  natis(i_src) = i_src
755  end do
756  call mesh%polygon(mesh%n,natis,area_polygon)
757 end if
758 
759 ! Compute interpolation
760 write(mpl%info,'(a10,a)',advance='no') '','Compute interpolation: '
761 call flush(mpl%info)
762 call mpl%prog_init(n_dst_loc(mpl%myproc))
763 n_s = 0
764 do i_dst_loc=1,n_dst_loc(mpl%myproc)
765  ! Indices
766  i_dst = i_dst_s(mpl%myproc)+i_dst_loc-1
767 
768  if (mask_dst(i_dst)) then
769  ! Find nearest neighbor
770  call kdtree%find_nearest_neighbors(lon_dst(i_dst),lat_dst(i_dst),1,nn_index,nn_dist)
771 
772  if (abs(nn_dist(1))>0.0) then
773  ! Compute barycentric coordinates
774  call mesh%barycentric(lon_dst(i_dst),lat_dst(i_dst),nn_index(1),b,ib)
775  if (sum(b)>0.0) b = b/sum(b)
776  do i=1,3
777  if (inf(b(i),s_inf)) b(i) = 0.0
778  end do
779  if (sum(b)>0.0) b = b/sum(b)
780 
781  if (all(ib>0)) then
782  if (all(mask_src(mesh%order(ib)))) then
783  ! Valid interpolation
784  if (trim(interp_type)=='bilin') then
785  ! Bilinear interpolation
786  do i=1,3
787  if (b(i)>0.0) then
788  n_s = n_s+1
789  row(n_s) = i_dst
790  col(n_s) = mesh%order(ib(i))
791  s(n_s) = b(i)
792  end if
793  end do
794  elseif (trim(interp_type)=='natural') then
795  ! Natural neighbors interpolation
796 
797  ! Copy mesh
798  meshnew = mesh%copy()
799 
800  ! Add a node
801  call meshnew%addnode(mpl,lon_dst(i_dst),lat_dst(i_dst))
802 
803  ! Find natural neighbors
804  i_src = meshnew%lend(meshnew%n)
805  loop = .true.
806  nnat = 0
807  do while (loop)
808  nnat = nnat+1
809  natis(nnat) = abs(meshnew%list(i_src))
810  i_src = meshnew%lptr(i_src)
811  loop = (i_src/=meshnew%lend(meshnew%n))
812  end do
813 
814  if (all(mask_src(natis(1:nnat)))) then
815  ! Compute natural neighbors polygons areas
816  call meshnew%polygon(nnat,natis(1:nnat),area_polygon_new(1:nnat))
817 
818  ! Compute weight
819  natwgt = 0.0
820  natwgt(1:nnat) = area_polygon(natis(1:nnat))-area_polygon_new(1:nnat)
821  if (sum(natwgt(1:nnat))>0.0) natwgt(1:nnat) = natwgt(1:nnat)/sum(natwgt(1:nnat))
822  do inat=1,nnat
823  if (inf(natwgt(inat),s_inf)) natwgt(inat) = 0.0
824  end do
825  if (sum(natwgt(1:nnat))>0.0) natwgt(1:nnat) = natwgt(1:nnat)/sum(natwgt(1:nnat))
826 
827  ! Add interpolation element
828  do inat=1,nnat
829  if (natwgt(inat)>0.0) then
830  n_s = n_s+1
831  row(n_s) = i_dst
832  col(n_s) = mesh%order(natis(inat))
833  s(n_s) = natwgt(inat)
834  end if
835  end do
836  end if
837  end if
838  end if
839  end if
840  else
841  ! Subsampled point
842  n_s = n_s+1
843  row(n_s) = i_dst
844  col(n_s) = nn_index(1)
845  s(n_s) = 1.0
846  end if
847  end if
848 
849  ! Update
850  call mpl%prog_print(i_dst_loc)
851 end do
852 write(mpl%info,'(a)') '100%'
853 call flush(mpl%info)
854 
855 ! Communication
856 call mpl%f_comm%allgather(n_s,proc_to_n_s)
857 
858 ! Allocation
859 linop%n_s = sum(proc_to_n_s)
860 linop%n_src = n_src
861 linop%n_dst = n_dst
862 call linop%alloc
863 
864 ! Communication
865 if (mpl%main) then
866  offset = 0
867  do iproc=1,mpl%nproc
868  if (proc_to_n_s(iproc)>0) then
869  if (iproc==mpl%ioproc) then
870  ! Copy data
871  linop%row(offset+1:offset+proc_to_n_s(iproc)) = row(1:proc_to_n_s(iproc))
872  linop%col(offset+1:offset+proc_to_n_s(iproc)) = col(1:proc_to_n_s(iproc))
873  linop%S(offset+1:offset+proc_to_n_s(iproc)) = s(1:proc_to_n_s(iproc))
874  else
875  ! Receive data on ioproc
876  call mpl%f_comm%receive(linop%row(offset+1:offset+proc_to_n_s(iproc)),iproc-1,mpl%tag,status)
877  call mpl%f_comm%receive(linop%col(offset+1:offset+proc_to_n_s(iproc)),iproc-1,mpl%tag+1,status)
878  call mpl%f_comm%receive(linop%S(offset+1:offset+proc_to_n_s(iproc)),iproc-1,mpl%tag+2,status)
879  end if
880  end if
881 
882  ! Update offset
883  offset = offset+proc_to_n_s(iproc)
884  end do
885 else
886  if (n_s>0) then
887  ! Send data to ioproc
888  call mpl%f_comm%send(row(1:n_s),mpl%ioproc-1,mpl%tag)
889  call mpl%f_comm%send(col(1:n_s),mpl%ioproc-1,mpl%tag+1)
890  call mpl%f_comm%send(s(1:n_s),mpl%ioproc-1,mpl%tag+2)
891  end if
892 end if
893 call mpl%update_tag(3)
894 
895 ! Broadcast data
896 call mpl%f_comm%broadcast(linop%row,mpl%ioproc-1)
897 call mpl%f_comm%broadcast(linop%col,mpl%ioproc-1)
898 call mpl%f_comm%broadcast(linop%S,mpl%ioproc-1)
899 
900 ! Deal with missing points
901 call linop%interp_missing(mpl,n_dst,lon_dst,lat_dst,mask_dst,interp_type)
902 
903 ! Check interpolation
904 allocate(missing(n_dst))
905 missing = .false.
906 do i_dst=1,n_dst
907  if (mask_dst(i_dst)) missing(i_dst) = .true.
908 end do
909 do i_s=1,linop%n_s
910  missing(linop%row(i_s)) = .false.
911 end do
912 if (any(missing)) call mpl%abort('missing destination points in interp_from_mesh_kdtree')
913 
914 end subroutine linop_interp_from_mesh_kdtree
915 
916 !----------------------------------------------------------------------
917 ! Subroutine: linop_interp_grid
918 ! Purpose: compute horizontal grid interpolation
919 !----------------------------------------------------------------------
920 subroutine linop_interp_grid(linop,mpl,rng,geom,il0i,nc1,c1_to_c0,mask_check,vbot,vtop,interp_type,interp_base)
922 implicit none
923 
924 ! Passed variables
925 class(linop_type),intent(inout) :: linop ! Linear operator
926 type(mpl_type),intent(inout) :: mpl ! MPI data
927 type(rng_type),intent(inout) :: rng ! Random number generator
928 type(geom_type),intent(in) :: geom ! Geometry
929 integer,intent(in) :: il0i ! Level
930 integer,intent(in) :: nc1 ! Subset Sc1 size
931 integer,intent(in) :: c1_to_c0(nc1) ! Subset Sc1 to subset Sc0
932 logical,intent(in) :: mask_check ! Mask check key
933 integer,intent(in) :: vbot(nc1) ! Bottom level
934 integer,intent(in) :: vtop(nc1) ! Top level
935 character(len=*),intent(in) :: interp_type ! Interpolation type
936 type(linop_type),intent(inout) :: interp_base ! Linear operator (base interpolation)
937 
938 ! Local variables
939 integer :: ic0,ic1,jc0,jc1,i_s
940 real(kind_real) :: renorm(geom%nc0)
941 real(kind_real),allocatable :: lon_c1(:),lat_c1(:),lon_col(:),lat_col(:)
942 logical :: test_c0(geom%nc0)
943 logical,allocatable :: mask_c1(:),mask_extra(:),valid(:)
944 
945 if (.not.allocated(interp_base%row)) then
946  ! Allocation
947  allocate(lon_c1(nc1))
948  allocate(lat_c1(nc1))
949  allocate(mask_c1(nc1))
950 
951  ! Initialization
952  lon_c1 = geom%lon(c1_to_c0)
953  lat_c1 = geom%lat(c1_to_c0)
954  mask_c1 = geom%mask_hor_c0(c1_to_c0)
955 
956  ! Compute base interpolation
957  call interp_base%interp(mpl,rng,nc1,lon_c1,lat_c1,mask_c1,geom%nc0,geom%lon,geom%lat,geom%mask_hor_c0,interp_type)
958 end if
959 
960 ! Allocation
961 allocate(valid(interp_base%n_s))
962 allocate(mask_extra(nc1))
963 
964 ! Check mask
965 do i_s=1,interp_base%n_s
966  ic0 = interp_base%row(i_s)
967  jc1 = interp_base%col(i_s)
968  jc0 = c1_to_c0(jc1)
969  valid(i_s) = geom%mask_c0(ic0,il0i).and.geom%mask_c0(jc0,il0i)
970 end do
971 
972 if (mask_check) then
973  write(mpl%info,'(a10,a,i3,a)',advance='no') '','Sublevel ',il0i,': '
974  call flush(mpl%info)
975 
976  ! Allocation
977  allocate(lon_col(nc1))
978  allocate(lat_col(nc1))
979 
980  ! Initialization
981  lon_col = geom%lon(c1_to_c0)
982  lat_col = geom%lat(c1_to_c0)
983 
984  ! Check mask boundaries
985  call interp_base%interp_check_mask(mpl,geom,valid,il0i,lon_col=lon_col,lat_col=lat_col)
986 end if
987 
988 if (geom%nl0i>1) then
989  ! Check extrapolated points because of masks vertical variations (unsampled levels)
990  mask_extra = .false.
991  do ic1=1,nc1
992  ic0 = c1_to_c0(ic1)
993  if (geom%mask_c0(ic0,il0i).and.((il0i<vbot(ic1)).or.(il0i>vtop(ic1)))) mask_extra(ic1) = .true.
994  end do
995 
996  ! Remove operations for extrapolated points
997  do i_s=1,interp_base%n_s
998  if (valid(i_s)) then
999  ic1 = interp_base%col(i_s)
1000  if (mask_extra(ic1)) valid(i_s) = .false.
1001  end if
1002  end do
1003  if (count(mask_extra)>0) then
1004  write(mpl%info,'(a10,a,i5)') '','Extrapolated points: ',count(mask_extra)
1005  call flush(mpl%info)
1006  end if
1007 else
1008  mask_extra = .false.
1009 end if
1010 
1011 ! Renormalization
1012 renorm = 0.0
1013 do i_s=1,interp_base%n_s
1014  if (valid(i_s)) renorm(interp_base%row(i_s)) = renorm(interp_base%row(i_s))+interp_base%S(i_s)
1015 end do
1016 
1017 ! Initialize linear operator
1018 linop%n_src = nc1
1019 linop%n_dst = geom%nc0
1020 linop%n_s = count(valid)
1021 call linop%alloc
1022 linop%n_s = 0
1023 do i_s=1,interp_base%n_s
1024  if (valid(i_s)) then
1025  linop%n_s = linop%n_s+1
1026  linop%row(linop%n_s) = interp_base%row(i_s)
1027  linop%col(linop%n_s) = interp_base%col(i_s)
1028  linop%S(linop%n_s) = interp_base%S(i_s)/renorm(interp_base%row(i_s))
1029  end if
1030 end do
1031 
1032 ! Release memory
1033 call interp_base%dealloc
1034 
1035 ! Deal with missing points
1036 call linop%interp_missing(mpl,geom%nc0,geom%lon,geom%lat,geom%mask_c0(:,il0i),interp_type)
1037 
1038 ! Check interpolation
1039 test_c0 = geom%mask_c0(:,il0i)
1040 do i_s=1,linop%n_s
1041  test_c0(linop%row(i_s)) = .false.
1042 end do
1043 if (any(test_c0)) call mpl%abort('error with the grid interpolation row')
1044 
1045 ! Release memory
1046 deallocate(valid)
1047 deallocate(mask_extra)
1048 
1049 end subroutine linop_interp_grid
1050 
1051 !----------------------------------------------------------------------
1052 ! Subroutine: linop_interp_check_mask
1053 ! Purpose: check mask boundaries for interpolations
1054 !----------------------------------------------------------------------
1055 subroutine linop_interp_check_mask(linop,mpl,geom,valid,il0,lon_row,lat_row,lon_col,lat_col)
1057 implicit none
1058 
1059 ! Passed variables
1060 class(linop_type),intent(inout) :: linop ! Linear operator
1061 type(mpl_type),intent(inout) :: mpl ! MPI data
1062 type(geom_type),intent(in) :: geom ! Geometry
1063 logical,intent(inout) :: valid(linop%n_s) ! Valid points
1064 real(kind_real),intent(in),optional :: lon_row(linop%n_dst) ! Row longitude (Sc0 longitude if missing)
1065 real(kind_real),intent(in),optional :: lat_row(linop%n_dst) ! Row latitude (Sc0 latitude if missing)
1066 real(kind_real),intent(in),optional :: lon_col(linop%n_src) ! Column longitude (Sc0 longitude if missing)
1067 real(kind_real),intent(in),optional :: lat_col(linop%n_src) ! Column latitude (Sc0 latitude if missing)
1068 
1069 ! Local variables
1070 integer :: i_s,il0,iproc
1071 integer :: i_s_s(mpl%nproc),i_s_e(mpl%nproc),n_s_loc(mpl%nproc),i_s_loc
1072 real(kind_real) :: llon_row,llat_row,llon_col,llat_col
1073 type(fckit_mpi_status) :: status
1074 
1075 ! MPI splitting
1076 call mpl%split(linop%n_s,i_s_s,i_s_e,n_s_loc)
1077 
1078 ! Check that interpolations are not crossing mask boundaries
1079 call mpl%prog_init(n_s_loc(mpl%myproc))
1080 !$omp parallel do schedule(static) private(i_s_loc,i_s,llon_row,llat_row,llon_col,llat_col)
1081 do i_s_loc=1,n_s_loc(mpl%myproc)
1082  ! Indices
1083  i_s = i_s_s(mpl%myproc)+i_s_loc-1
1084 
1085  if (valid(i_s)) then
1086  ! Row lon/lat
1087  if (present(lon_row).and.present(lat_row)) then
1088  llon_row = lon_row(linop%row(i_s))
1089  llat_row = lat_row(linop%row(i_s))
1090  else
1091  llon_row = geom%lon(linop%row(i_s))
1092  llat_row = geom%lat(linop%row(i_s))
1093  end if
1094 
1095  ! Column lon/lat
1096  if (present(lon_col).and.present(lat_col)) then
1097  llon_col = lon_col(linop%col(i_s))
1098  llat_col = lat_col(linop%col(i_s))
1099  else
1100  llon_col = geom%lon(linop%col(i_s))
1101  llat_col = geom%lat(linop%col(i_s))
1102  end if
1103 
1104  ! Check if arc is crossing boundary arcs
1105  call geom%check_arc(il0,llon_row,llat_row,llon_col,llat_col,valid(i_s))
1106  end if
1107 
1108  ! Update
1109  call mpl%prog_print(i_s_loc)
1110 end do
1111 !$omp end parallel do
1112 write(mpl%info,'(a)') '100%'
1113 call flush(mpl%info)
1114 
1115 ! Communication
1116 if (mpl%main) then
1117  do iproc=1,mpl%nproc
1118  if (n_s_loc(iproc)>0) then
1119  if (iproc/=mpl%ioproc) then
1120  ! Receive data on ioproc
1121  call mpl%f_comm%receive(valid(i_s_s(iproc):i_s_e(iproc)),iproc-1,mpl%tag,status)
1122  end if
1123  end if
1124  end do
1125 else
1126  if (n_s_loc(mpl%myproc)>0) then
1127  ! Send data to ioproc
1128  call mpl%f_comm%send(valid(i_s_s(mpl%myproc):i_s_e(mpl%myproc)),mpl%ioproc-1,mpl%tag)
1129  end if
1130 end if
1131 call mpl%update_tag(1)
1132 
1133 ! Broadcast data
1134 call mpl%f_comm%broadcast(valid,mpl%ioproc-1)
1135 
1136 end subroutine linop_interp_check_mask
1137 
1138 !----------------------------------------------------------------------
1139 ! Subroutine: linop_interp_missing
1140 ! Purpose: deal with missing interpolation points
1141 !----------------------------------------------------------------------
1142 subroutine linop_interp_missing(linop,mpl,n_dst,lon_dst,lat_dst,mask_dst,interp_type)
1144 implicit none
1145 
1146 ! Passed variables
1147 class(linop_type),intent(inout) :: linop ! Linear operator (interpolation)
1148 type(mpl_type),intent(in) :: mpl ! MPI data
1149 integer,intent(in) :: n_dst ! Destination size
1150 real(kind_real),intent(in) :: lon_dst(n_dst) ! Destination longitude
1151 real(kind_real),intent(in) :: lat_dst(n_dst) ! Destination latitude
1152 logical,intent(in) :: mask_dst(n_dst) ! Destination mask
1153 character(len=*),intent(in) :: interp_type ! Interpolation type
1154 
1155 ! Local variables
1156 integer :: i_dst,i_s
1157 integer :: nn(1)
1158 real(kind_real) :: dum(1)
1159 logical :: missing(n_dst),lmask(n_dst),found
1160 type(linop_type) :: interp_tmp
1161 type(kdtree_type) :: kdtree
1162 
1163 ! Find missing points
1164 missing = .false.
1165 do i_dst=1,n_dst
1166  if (mask_dst(i_dst)) missing(i_dst) = .true.
1167 end do
1168 do i_s=1,linop%n_s
1169  missing(linop%row(i_s)) = .false.
1170 end do
1171 
1172 if (count(missing)>0) then
1173  write(mpl%info,'(a10,a,i6,a)') '','Deal with ',count(missing),' missing interpolation points'
1174 
1175  ! Allocate temporary interpolation
1176  if (trim(interp_type)=='bilin') then
1177  interp_tmp%n_s = linop%n_s+3*count(missing)
1178  elseif (trim(interp_type)=='natural') then
1179  interp_tmp%n_s = linop%n_s+40*count(missing)
1180  else
1181  call mpl%abort('wrong interpolation')
1182  end if
1183  call interp_tmp%alloc
1184 
1185  ! Fill arrays
1186  interp_tmp%row(1:linop%n_s) = linop%row
1187  interp_tmp%col(1:linop%n_s) = linop%col
1188  interp_tmp%S(1:linop%n_s) = linop%S
1189 
1190  ! Reset size
1191  interp_tmp%n_s = linop%n_s
1192 
1193  ! Mask
1194  lmask = mask_dst.and.(.not.missing)
1195 
1196  ! Compute KD-tree
1197  call kdtree%create(mpl,n_dst,lon_dst,lat_dst,mask=lmask)
1198 
1199  do i_dst=1,n_dst
1200  if (missing(i_dst)) then
1201  ! Compute nearest neighbor
1202  call kdtree%find_nearest_neighbors(lon_dst(i_dst),lat_dst(i_dst),1,nn,dum)
1203 
1204  ! Copy data
1205  found = .false.
1206  do i_s=1,linop%n_s
1207  if (linop%row(i_s)==nn(1)) then
1208  found = .true.
1209  interp_tmp%n_s = interp_tmp%n_s+1
1210  interp_tmp%row(interp_tmp%n_s) = i_dst
1211  interp_tmp%col(interp_tmp%n_s) = linop%col(i_s)
1212  interp_tmp%S(interp_tmp%n_s) = linop%S(i_s)
1213  end if
1214  end do
1215  if (.not.found) call mpl%abort('missing point not found')
1216  end if
1217  end do
1218 
1219  ! Reallocate interpolation
1220  call linop%dealloc
1221  linop%n_s = interp_tmp%n_s
1222  call linop%alloc
1223 
1224  ! Fill arrays
1225  linop%row = interp_tmp%row(1:linop%n_s)
1226  linop%col = interp_tmp%col(1:linop%n_s)
1227  linop%S = interp_tmp%S(1:linop%n_s)
1228 
1229  ! Release memory
1230  call kdtree%dealloc
1231  call interp_tmp%dealloc
1232 end if
1233 
1234 end subroutine linop_interp_missing
1235 
1236 end module type_linop
subroutine linop_alloc(linop, nvec)
Definition: type_linop.F90:72
integer, parameter nnatmax
Definition: type_linop.F90:28
subroutine linop_interp_missing(linop, mpl, n_dst, lon_dst, lat_dst, mask_dst, interp_type)
subroutine linop_gather(linop, mpl, n_s_arr, linop_arr)
Definition: type_linop.F90:599
subroutine linop_interp_from_lat_lon(linop, mpl, rng, n_src, lon_src, lat_src, mask_src, n_dst, lon_dst, lat_dst, mask_dst, interp_type)
Definition: type_linop.F90:633
subroutine, public copy(self, rhs)
Definition: qg_fields.F90:225
subroutine linop_read(linop, mpl, ncid)
Definition: type_linop.F90:230
subroutine linop_interp_from_mesh_kdtree(linop, mpl, mesh, kdtree, n_src, mask_src, n_dst, lon_dst, lat_dst, mask_dst, interp_type)
Definition: type_linop.F90:702
type(linop_type) function linop_copy(linop)
Definition: type_linop.F90:132
subroutine linop_interp_check_mask(linop, mpl, geom, valid, il0, lon_row, lat_row, lon_col, lat_col)
subroutine linop_apply_sym(linop, mpl, fld, ivec)
Definition: type_linop.F90:488
subroutine linop_apply_ad(linop, mpl, fld_dst, fld_src, ivec)
Definition: type_linop.F90:434
integer, parameter reorder_max
Definition: type_linop.F90:27
subroutine linop_write(linop, mpl, ncid)
Definition: type_linop.F90:290
subroutine linop_dealloc(linop)
Definition: type_linop.F90:113
subroutine linop_reorder(linop, mpl)
Definition: type_linop.F90:172
subroutine linop_interp_grid(linop, mpl, rng, geom, il0i, nc1, c1_to_c0, mask_check, vbot, vtop, interp_type, interp_base)
Definition: type_linop.F90:921
subroutine linop_apply(linop, mpl, fld_src, fld_dst, ivec, mssrc, msdst)
Definition: type_linop.F90:348
subroutine linop_add_op(linop, n_s, row, col, S)
Definition: type_linop.F90:554
real(kind_real), parameter s_inf
Definition: type_linop.F90:29
logical, parameter check_data
Definition: type_linop.F90:26
integer, parameter, public kind_real
logical function, public inf(x, y)
Definition: tools_repro.F90:47
integer, parameter, public ncfloat
Definition: tools_nc.F90:27