FV3 Bundle
type_obsop.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_obsop
3 ! Purpose: observation operator data 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_obsop
9 
10 use netcdf
12 use tools_func, only: sphere_dist
13 use tools_kinds, only: kind_real
15 use tools_nc, only: ncfloat
16 use tools_qsort, only: qsort
17 use type_com, only: com_type
18 use type_geom, only: geom_type
19 use type_linop, only: linop_type
20 use type_mpl, only: mpl_type
21 use type_nam, only: nam_type
22 use type_rng, only: rng_type
23 use fckit_mpi_module, only: fckit_mpi_sum,fckit_mpi_min,fckit_mpi_max,fckit_mpi_status
24 
25 implicit none
26 
27 logical,parameter :: test_no_obs = .false. ! Test observation operator with no observation on the last MPI task
28 
29 ! Observation operator data derived type
31  ! Observations
32  integer :: nobs ! Number of observations
33  real(kind_real),allocatable :: lonobs(:) ! Observations longitudes
34  real(kind_real),allocatable :: latobs(:) ! Observations latitudes
35  integer,allocatable :: obsa_to_obs(:) ! Local to global observation
36 
37  ! Required data to apply an observation operator
38 
39  ! Number of points
40  integer :: nc0b ! Halo B size
41 
42  ! Number of observations
43  integer :: nobsa ! Local number of observations
44 
45  ! Interpolation data
46  type(linop_type) :: h ! Interpolation data
47 
48  ! Communication data
49  type(com_type) :: com ! Communication data
50 contains
51  procedure :: dealloc => obsop_dealloc
52  procedure :: read => obsop_read
53  procedure :: write => obsop_write
54  procedure :: generate => obsop_generate
55  procedure :: from => obsop_from
56  procedure :: run_obsop => obsop_run_obsop
57  procedure :: run_obsop_tests => obsop_run_obsop_tests
58  procedure :: apply => obsop_apply
59  procedure :: apply_ad => obsop_apply_ad
60  procedure :: test_adjoint => obsop_test_adjoint
61  procedure :: test_accuracy => obsop_test_accuracy
62 end type obsop_type
63 
64 private
65 public :: obsop_type
66 
67 contains
68 
69 !----------------------------------------------------------------------
70 ! Subroutine: obsop_dealloc
71 ! Purpose: observation operator deallocation
72 !----------------------------------------------------------------------
73 subroutine obsop_dealloc(obsop)
74 
75 implicit none
76 
77 ! Passed variables
78 class(obsop_type),intent(inout) :: obsop ! Observation operator data
79 
80 ! Release memory
81 if (allocated(obsop%lonobs)) deallocate(obsop%lonobs)
82 if (allocated(obsop%latobs)) deallocate(obsop%latobs)
83 if (allocated(obsop%obsa_to_obs)) deallocate(obsop%obsa_to_obs)
84 call obsop%h%dealloc
85 call obsop%com%dealloc
86 
87 end subroutine obsop_dealloc
88 
89 !----------------------------------------------------------------------
90 ! Subroutine: obsop_read
91 ! Purpose: read observations locations
92 !----------------------------------------------------------------------
93 subroutine obsop_read(obsop,mpl,nam)
94 
95 implicit none
96 
97 ! Passed variables
98 class(obsop_type),intent(inout) :: obsop ! Observation operator data
99 type(mpl_type),intent(in) :: mpl ! MPI data
100 type(nam_type),intent(in) :: nam ! Namelist
101 
102 ! Local variables
103 integer :: ncid
104 character(len=1024) :: filename
105 character(len=1024) :: subr = 'obsop_read'
106 
107 ! Create file
108 write(filename,'(a,a,i4.4,a,i4.4,a)') trim(nam%prefix),'_obs_',mpl%nproc,'-',mpl%myproc,'.nc'
109 call mpl%ncerr(subr,nf90_open(trim(nam%datadir)//'/'//trim(filename),nf90_nowrite,ncid))
110 
111 ! Get attributes
112 call mpl%ncerr(subr,nf90_get_att(ncid,nf90_global,'nc0b',obsop%nc0b))
113 call mpl%ncerr(subr,nf90_get_att(ncid,nf90_global,'nobsa',obsop%nobsa))
114 
115 ! Read interpolation
116 obsop%h%prefix = 'o'
117 call obsop%h%read(mpl,ncid)
118 
119 ! Read communication
120 call obsop%com%read(mpl,ncid,'com')
121 
122 ! Close file
123 call mpl%ncerr(subr,nf90_close(ncid))
124 
125 end subroutine obsop_read
126 
127 !----------------------------------------------------------------------
128 ! Subroutine: obsop_write
129 ! Purpose: write observations locations
130 !----------------------------------------------------------------------
131 subroutine obsop_write(obsop,mpl,nam)
133 implicit none
134 
135 ! Passed variables
136 class(obsop_type),intent(inout) :: obsop ! Observation operator data
137 type(mpl_type),intent(in) :: mpl ! MPI data
138 type(nam_type),intent(in) :: nam ! Namelist
139 
140 ! Local variables
141 integer :: ncid
142 character(len=1024) :: filename
143 character(len=1024) :: subr = 'obsop_write'
144 
145 ! Create file
146 write(filename,'(a,a,i4.4,a,i4.4,a)') trim(nam%prefix),'_obs_',mpl%nproc,'-',mpl%myproc,'.nc'
147 call mpl%ncerr(subr,nf90_create(trim(nam%datadir)//'/'//trim(filename),or(nf90_clobber,nf90_64bit_offset),ncid))
148 
149 ! Write namelist parameters
150 call nam%ncwrite(mpl,ncid)
151 
152 ! Write attributes
153 call mpl%ncerr(subr,nf90_put_att(ncid,nf90_global,'nc0b',obsop%nc0b))
154 call mpl%ncerr(subr,nf90_put_att(ncid,nf90_global,'nobsa',obsop%nobsa))
155 
156 ! End definition mode
157 call mpl%ncerr(subr,nf90_enddef(ncid))
158 
159 ! Write interpolation
160 call obsop%h%write(mpl,ncid)
161 
162 ! Write communication
163 call obsop%com%write(mpl,ncid)
164 
165 ! Close file
166 call mpl%ncerr(subr,nf90_close(ncid))
167 
168 end subroutine obsop_write
169 
170 !----------------------------------------------------------------------
171 ! Subroutine: obsop_generate
172 ! Purpose: generate observations locations
173 !----------------------------------------------------------------------
174 subroutine obsop_generate(obsop,mpl,rng,nam,geom)
176 implicit none
177 
178 ! Passed variables
179 class(obsop_type),intent(inout) :: obsop ! Observation operator data
180 type(mpl_type),intent(in) :: mpl ! MPI data
181 type(rng_type),intent(inout) :: rng ! Random number generator
182 type(nam_type),intent(in) :: nam ! Namelist
183 type(geom_type),intent(in) :: geom ! Geometry
184 
185 ! Local variables
186 integer :: iobs,jobs,iproc,iobsa,nproc_max
187 integer,allocatable :: order(:),obs_to_proc(:)
188 real(kind_real),allocatable :: lonobs(:),latobs(:),list(:)
189 
190 ! Check observation number
191 if (nam%nobs<1) call mpl%abort('nobs should be positive for offline observation operator')
192 
193 ! Allocation
194 allocate(lonobs(nam%nobs))
195 allocate(latobs(nam%nobs))
196 allocate(obs_to_proc(nam%nobs))
197 
198 if (mpl%main) then
199  ! Generate random observation network
200  if (abs(maxval(geom%area)-4.0*pi)<1.0e-1) then
201  ! Limited-area domain
202  call rng%rand_real(minval(geom%lon),maxval(geom%lon),lonobs)
203  call rng%rand_real(minval(geom%lat),maxval(geom%lat),latobs)
204  else
205  ! Global domain
206  call rng%rand_real(-pi,pi,lonobs)
207  call rng%rand_real(-1.0_kind_real,1.0_kind_real,latobs)
208  latobs = 0.5*pi-acos(latobs)
209  end if
210 end if
211 
212 ! Broadcast data
213 call mpl%f_comm%broadcast(lonobs,mpl%ioproc-1)
214 call mpl%f_comm%broadcast(latobs,mpl%ioproc-1)
215 
216 ! Split observations between processors
217 if (test_no_obs.and.(mpl%nproc==1)) call mpl%abort('at least 2 MPI tasks required for test_no_obs')
218 if (mpl%main) then
219  ! Allocation
220  allocate(list(nam%nobs))
221  allocate(order(nam%nobs))
222 
223  ! Generate random order
224  call rng%rand_real(0.0_kind_real,1.0_kind_real,list)
225  call qsort(nam%nobs,list,order)
226 
227  ! Split observations
228  iproc = 1
229  if (test_no_obs) then
230  nproc_max = mpl%nproc-1
231  else
232  nproc_max = mpl%nproc
233  end if
234  do iobs=1,nam%nobs
235  jobs = order(iobs)
236  obs_to_proc(jobs) = iproc
237  iproc = iproc+1
238  if (iproc>nproc_max) iproc = 1
239  end do
240 end if
241 
242 ! Broadcast
243 call mpl%f_comm%broadcast(obs_to_proc,mpl%ioproc-1)
244 obsop%nobsa = count(obs_to_proc==mpl%myproc)
245 
246 ! Release memory
247 call obsop%dealloc
248 
249 ! Allocation
250 allocate(obsop%lonobs(obsop%nobsa))
251 allocate(obsop%latobs(obsop%nobsa))
252 
253 ! Copy local observations
254 iobsa = 0
255 do iobs=1,nam%nobs
256  iproc = obs_to_proc(iobs)
257  if (mpl%myproc==iproc) then
258  iobsa = iobsa+1
259  obsop%lonobs(iobsa) = lonobs(iobs)
260  obsop%latobs(iobsa) = latobs(iobs)
261  end if
262 end do
263 
264 end subroutine obsop_generate
265 
266 !----------------------------------------------------------------------
267 ! Subroutine: obsop_from
268 ! Purpose: copy observation operator data
269 !----------------------------------------------------------------------
270 subroutine obsop_from(obsop,nobsa,lonobs,latobs)
272 implicit none
273 
274 ! Passed variables
275 class(obsop_type),intent(inout) :: obsop ! Observation operator data
276 integer,intent(in) :: nobsa ! Number of observations
277 real(kind_real),intent(in) :: lonobs(nobsa) ! Observations longitudes (in degrees)
278 real(kind_real),intent(in) :: latobs(nobsa) ! Observations latitudes (in degrees)
279 
280 ! Get size
281 obsop%nobsa = nobsa
282 
283 ! Release memory
284 call obsop%dealloc
285 
286 ! Allocation
287 allocate(obsop%lonobs(obsop%nobsa))
288 allocate(obsop%latobs(obsop%nobsa))
289 
290 if (obsop%nobsa>0) then
291  ! Copy
292  obsop%lonobs = lonobs*deg2rad
293  obsop%latobs = latobs*deg2rad
294 end if
295 
296 end subroutine obsop_from
297 
298 !----------------------------------------------------------------------
299 ! Subroutine: obsop_run_obsop
300 ! Purpose: observation operator driver
301 !----------------------------------------------------------------------
302 subroutine obsop_run_obsop(obsop,mpl,rng,nam,geom)
304 implicit none
305 
306 ! Passed variables
307 class(obsop_type),intent(inout) :: obsop ! Observation operator data
308 type(mpl_type),intent(inout) :: mpl ! MPI data
309 type(rng_type),intent(inout) :: rng ! Random number generator
310 type(nam_type),intent(in) :: nam ! Namelist
311 type(geom_type),intent(in) :: geom ! Geometry
312 
313 ! Local variables
314 integer :: offset,iobs,jobs,iobsa,iproc,i_s,ic0,ic0b,i,ic0a,delta,nres,ind(1),lunit
315 integer :: imin(1),imax(1),nmoves,imoves
316 integer,allocatable :: nop(:),iop(:),srcproc(:,:),srcic0(:,:),order(:),nobs_to_move(:),nobs_to_move_tmp(:),obs_moved(:,:)
317 integer,allocatable :: obs_to_proc(:),obs_to_obsa(:),proc_to_nobsa(:),c0b_to_c0(:),c0_to_c0b(:),c0a_to_c0b(:)
318 real(kind_real) :: N_max,C_max
319 real(kind_real),allocatable :: lonobs(:),latobs(:),list(:)
320 logical,allocatable :: maskobs(:),lcheck_nc0b(:)
321 type(fckit_mpi_status) :: status
322 type(linop_type) :: hfull
323 
324 ! Allocation
325 allocate(proc_to_nobsa(mpl%nproc))
326 
327 ! Get global number of observations
328 call mpl%f_comm%allgather(obsop%nobsa,proc_to_nobsa)
329 obsop%nobs = sum(proc_to_nobsa)
330 
331 ! Print input
332 write(mpl%info,'(a7,a)') '','Number of observations per MPI task on input:'
333 do iproc=1,mpl%nproc
334  write(mpl%info,'(a10,a,i3,a,i8)') '','Task ',iproc,': ',proc_to_nobsa(iproc)
335 end do
336 write(mpl%info,'(a10,a,i8)') '','Total : ',obsop%nobs
337 
338 ! Allocation
339 allocate(lonobs(obsop%nobs))
340 allocate(latobs(obsop%nobs))
341 
342 ! Get observations coordinates
343 if (mpl%main) then
344  offset = 0
345  do iproc=1,mpl%nproc
346  if (proc_to_nobsa(iproc)>0) then
347  if (iproc==mpl%ioproc) then
348  ! Copy data
349  lonobs(offset+1:offset+proc_to_nobsa(iproc)) = obsop%lonobs
350  latobs(offset+1:offset+proc_to_nobsa(iproc)) = obsop%latobs
351  else
352  ! Receive data on ioproc
353  call mpl%f_comm%receive(lonobs(offset+1:offset+proc_to_nobsa(iproc)),iproc-1,mpl%tag,status)
354  call mpl%f_comm%receive(latobs(offset+1:offset+proc_to_nobsa(iproc)),iproc-1,mpl%tag+1,status)
355  end if
356  end if
357 
358  ! Update offset
359  offset = offset+proc_to_nobsa(iproc)
360  end do
361 else
362  if (obsop%nobsa>0) then
363  ! Send data to ioproc
364  call mpl%f_comm%send(obsop%lonobs,mpl%ioproc-1,mpl%tag)
365  call mpl%f_comm%send(obsop%latobs,mpl%ioproc-1,mpl%tag+1)
366  end if
367 end if
368 call mpl%update_tag(2)
369 
370 ! Broadcast data
371 call mpl%f_comm%broadcast(lonobs,mpl%ioproc-1)
372 call mpl%f_comm%broadcast(latobs,mpl%ioproc-1)
373 
374 ! Allocation
375 allocate(maskobs(obsop%nobs))
376 allocate(nop(obsop%nobs))
377 allocate(iop(obsop%nobs))
378 allocate(obs_to_proc(obsop%nobs))
379 allocate(obs_to_obsa(obsop%nobs))
380 
381 ! Compute interpolation
382 hfull%prefix = 'o'
383 write(mpl%info,'(a7,a)') '','Single level:'
384 call flush(mpl%info)
385 maskobs = .true.
386 call hfull%interp(mpl,geom%mesh,geom%kdtree,geom%nc0,geom%mask_hor_c0,obsop%nobs,lonobs,latobs,maskobs,nam%obsop_interp)
387 
388 ! Count interpolation points
389 nop = 0
390 do i_s=1,hfull%n_s
391  iobs = hfull%row(i_s)
392  nop(iobs) = nop(iobs)+1
393 end do
394 
395 ! Allocation
396 allocate(srcproc(maxval(nop),obsop%nobs))
397 allocate(srcic0(maxval(nop),obsop%nobs))
398 
399 ! Find grid points origin
400 iop = 0
401 call msi(srcproc)
402 call msi(srcic0)
403 do i_s=1,hfull%n_s
404  ic0 = hfull%col(i_s)
405  iproc = geom%c0_to_proc(ic0)
406  iobs = hfull%row(i_s)
407  iop(iobs) = iop(iobs)+1
408  srcproc(iop(iobs),iobs) = iproc
409  srcic0(iop(iobs),iobs) = ic0
410 end do
411 
412 ! Generate observation distribution on processors
413 select case (trim(nam%obsdis))
414 case('')
415  ! Observations distributed as provided by user
416  write(mpl%info,'(a7,a)') '','Observations distributed as provided by user'
417  call flush(mpl%info)
418 
419  ! Fill obs_to_proc
420  iobs = 0
421  do iproc=1,mpl%nproc
422  do iobsa=1,proc_to_nobsa(iproc)
423  iobs = iobs+1
424  obs_to_proc(iobs) = iproc
425  end do
426  end do
427 case('random')
428  ! Observations randomly distributed
429  write(mpl%info,'(a7,a)') '','Observations randomly distributed'
430  call flush(mpl%info)
431 
432  if (mpl%main) then
433  ! Allocation
434  allocate(list(obsop%nobs))
435  allocate(order(obsop%nobs))
436 
437  ! Generate random order
438  call rng%rand_real(0.0_kind_real,1.0_kind_real,list)
439  call qsort(obsop%nobs,list,order)
440 
441  ! Split observations
442  iproc = 1
443  do iobs=1,obsop%nobs
444  jobs = order(iobs)
445  obs_to_proc(jobs) = iproc
446  iproc = iproc+1
447  if (iproc>mpl%nproc) iproc = 1
448  end do
449  end if
450 
451  ! Broadcast
452  call mpl%f_comm%broadcast(obs_to_proc,mpl%ioproc-1)
453 case ('local','adjusted')
454  ! Grid-based repartition
455  do iobs=1,obsop%nobs
456  ! Set observation proc
457  if (isnotmsi(srcproc(2,iobs)).and.(srcproc(2,iobs)==srcproc(3,iobs))) then
458  ! Set to second point proc
459  obs_to_proc(iobs) = srcproc(2,iobs)
460  else
461  ! Set to first point proc
462  obs_to_proc(iobs) = srcproc(1,iobs)
463  end if
464  end do
465 
466  if (trim(nam%obsdis)=='adjusted') then
467  ! Observations distribution adjusted
468  write(mpl%info,'(a7,a)') '','Observations distribution adjusted'
469  call flush(mpl%info)
470 
471  ! Allocation
472  allocate(nobs_to_move(mpl%nproc))
473 
474  ! Proc to nobsa
475  proc_to_nobsa = 0
476  do iobs=1,obsop%nobs
477  ! Concerned proc
478  iproc = obs_to_proc(iobs)
479 
480  ! Number of observations per proc
481  proc_to_nobsa(iproc) = proc_to_nobsa(iproc)+1
482  end do
483 
484  ! Target nobsa, nobsa to move
485  nres = obsop%nobs
486  do iproc=1,mpl%nproc
487  delta = obsop%nobs/mpl%nproc
488  if (nres>(mpl%nproc-iproc+1)*delta) delta = delta+1
489  nobs_to_move(iproc) = delta
490  nres = nres-delta
491  end do
492  nobs_to_move = int(real(proc_to_nobsa-nobs_to_move,kind_real))
493  if (sum(nobs_to_move)>0) then
494  ind = maxloc(nobs_to_move)
495  elseif (sum(nobs_to_move)<0) then
496  ind = minloc(nobs_to_move)
497  else
498  ind = 1
499  end if
500  nobs_to_move(ind(1)) = nobs_to_move(ind(1))-sum(nobs_to_move)
501 
502  ! Select observations to move
503  allocate(nobs_to_move_tmp(mpl%nproc))
504  allocate(obs_moved(maxval(abs(nobs_to_move)),mpl%nproc))
505  call msi(obs_moved)
506  nobs_to_move_tmp = nobs_to_move
507  do iobs=1,obsop%nobs
508  iproc = obs_to_proc(iobs)
509  if (nobs_to_move_tmp(iproc)>0) then
510  ! Move this observation from iproc
511  obs_moved(nobs_to_move_tmp(iproc),iproc) = iobs
512  nobs_to_move_tmp(iproc) = nobs_to_move_tmp(iproc)-1
513  end if
514  end do
515 
516  ! Select destination proc
517  do while (any(nobs_to_move<0))
518  imin = minloc(nobs_to_move)
519  imax = maxloc(nobs_to_move)
520  nmoves = min(nobs_to_move(imax(1)),-nobs_to_move(imin(1)))
521  do imoves=1,nmoves
522  iobs = obs_moved(nobs_to_move(imax(1)),imax(1))
523  call msi(obs_moved(nobs_to_move(imax(1)),imax(1)))
524  obs_to_proc(iobs) = imin(1)
525  nobs_to_move(imax(1)) = nobs_to_move(imax(1))-1
526  nobs_to_move(imin(1)) = nobs_to_move(imin(1))+1
527  end do
528  end do
529 
530  ! Release memory
531  deallocate(nobs_to_move)
532  else
533  ! Observations distribution based on grid distribution
534  write(mpl%info,'(a7,a)') '','Observations distribution based on grid distribution'
535  call flush(mpl%info)
536  end if
537 case default
538  call mpl%abort('wrong obsdis')
539 end select
540 
541 ! Allocation
542 obsop%nobsa = count(obs_to_proc==mpl%myproc)
543 allocate(obsop%obsa_to_obs(obsop%nobsa))
544 
545 ! Fill proc_to_nobsa, obs_to_obsa and obsa_to_obs
546 proc_to_nobsa = 0
547 do iobs=1,obsop%nobs
548  ! Concerned proc
549  iproc = obs_to_proc(iobs)
550 
551  ! Number of observations per proc
552  proc_to_nobsa(iproc) = proc_to_nobsa(iproc)+1
553 
554  ! Observations local index
555  iobsa = proc_to_nobsa(iproc)
556  obs_to_obsa(iobs) = iobsa
557  if (iproc==mpl%myproc) obsop%obsa_to_obs(iobsa) = iobs
558 end do
559 
560 ! Allocation
561 allocate(lcheck_nc0b(geom%nc0))
562 
563 ! Count number of local interpolation operations
564 obsop%h%n_s = 0
565 do i_s=1,hfull%n_s
566  iobs = hfull%row(i_s)
567  iproc = obs_to_proc(iobs)
568  if (iproc==mpl%myproc) obsop%h%n_s = obsop%h%n_s+1
569 end do
570 
571 ! Count halo points
572 lcheck_nc0b = .false.
573 do ic0a=1,geom%nc0a
574  ic0 = geom%c0a_to_c0(ic0a)
575  if (geom%c0_to_proc(ic0)==mpl%myproc) lcheck_nc0b(ic0) = .true.
576 end do
577 do iobs=1,obsop%nobs
578  iproc = obs_to_proc(iobs)
579  if (iproc==mpl%myproc) then
580  do i=1,iop(iobs)
581  ic0 = srcic0(i,iobs)
582  lcheck_nc0b(ic0) = .true.
583  end do
584  end if
585 end do
586 obsop%nc0b = count(lcheck_nc0b)
587 
588 ! Define halo points
589 allocate(c0b_to_c0(obsop%nc0b))
590 allocate(c0_to_c0b(geom%nc0))
591 call msi(c0_to_c0b)
592 ic0b = 0
593 do ic0=1,geom%nc0
594  if (lcheck_nc0b(ic0)) then
595  ic0b = ic0b+1
596  c0b_to_c0(ic0b) = ic0
597  c0_to_c0b(ic0) = ic0b
598  end if
599 end do
600 
601 ! Split interpolation data
602 obsop%h%prefix = 'o'
603 obsop%h%n_src = obsop%nc0b
604 obsop%h%n_dst = obsop%nobsa
605 call obsop%h%alloc
606 obsop%h%n_s = 0
607 do i_s=1,hfull%n_s
608  iobs = hfull%row(i_s)
609  ic0 = hfull%col(i_s)
610  iproc = obs_to_proc(iobs)
611  if (iproc==mpl%myproc) then
612  obsop%h%n_s = obsop%h%n_s+1
613  obsop%h%row(obsop%h%n_s) = obs_to_obsa(iobs)
614  obsop%h%col(obsop%h%n_s) = c0_to_c0b(ic0)
615  obsop%h%S(obsop%h%n_s) = hfull%S(i_s)
616  end if
617 end do
618 
619 ! Inter-halo conversions
620 allocate(c0a_to_c0b(geom%nc0a))
621 do ic0a=1,geom%nc0a
622  ic0 = geom%c0a_to_c0(ic0a)
623  ic0b = c0_to_c0b(ic0)
624  c0a_to_c0b(ic0a) = ic0b
625 end do
626 
627 ! Setup communications
628 call obsop%com%setup(mpl,'com',geom%nc0,geom%nc0a,obsop%nc0b,c0b_to_c0,c0a_to_c0b,geom%c0_to_proc,geom%c0_to_c0a)
629 
630 ! Compute scores
631 call mpl%f_comm%allreduce(real(obsop%com%nhalo,kind_real),C_max,fckit_mpi_max())
632 c_max = c_max/(3.0*real(obsop%nobs,kind_real)/real(mpl%nproc,kind_real))
633 n_max = real(maxval(proc_to_nobsa),kind_real)/(real(obsop%nobs,kind_real)/real(mpl%nproc,kind_real))
634 
635 ! Print output
636 write(mpl%info,'(a7,a)') '','Number of observations per MPI task on output:'
637 do iproc=1,mpl%nproc
638  write(mpl%info,'(a10,a,i3,a,i8)') '','Task ',iproc,': ',proc_to_nobsa(iproc)
639 end do
640 write(mpl%info,'(a7,a,f5.1,a)') '','Observation repartition imbalance: ',100.0*real(maxval(proc_to_nobsa) & & -minval(proc_to_nobsa),kind_real)/(real(sum(proc_to_nobsa),kind_real)/real(mpl%nproc,kind_real)),' %'
641 write(mpl%info,'(a7,a,i3)') '','Number of grid points, halo size and number of received values for MPI task: ',mpl%myproc
642 write(mpl%info,'(a10,i8,a,i8,a,i8)') '',obsop%com%nred,' / ',obsop%com%next,' / ',obsop%com%nhalo
643 write(mpl%info,'(a7,a,f10.2,a,f10.2)') '','Scores (N_max / C_max):',n_max,' / ',c_max
644 call flush(mpl%info)
645 
646 ! Write observations
647 call obsop%write(mpl,nam)
648 
649 if (mpl%main) then
650  ! Write scores
651  if (mpl%nproc>1) then
652  call mpl%newunit(lunit)
653  open(unit=lunit,file=trim(nam%datadir)//'/'//trim(nam%prefix)//'_obs_scores_'//trim(nam%obsdis)//'.dat',status='replace')
654  write(lunit,*) n_max,c_max
655  close(unit=lunit)
656  end if
657 end if
658 
659 end subroutine obsop_run_obsop
660 
661 !----------------------------------------------------------------------
662 ! Subroutine: obsop_run_obsop_tests
663 ! Purpose: observation operator tests driver
664 !----------------------------------------------------------------------
665 subroutine obsop_run_obsop_tests(obsop,mpl,rng,geom)
666 
667 implicit none
668 
669 ! Passed variables
670 class(obsop_type),intent(inout) :: obsop ! Observation operator data
671 type(mpl_type),intent(inout) :: mpl ! MPI data
672 type(rng_type),intent(inout) :: rng ! Random number generator
673 type(geom_type),intent(in) :: geom ! Geometry
674 
675 ! Test adjoints
676 write(mpl%info,'(a)') '-------------------------------------------------------------------'
677 write(mpl%info,'(a)') '--- Test observation operator adjoint'
678 call flush(mpl%info)
679 call obsop%test_adjoint(mpl,rng,geom)
680 
681 if (allocated(obsop%obsa_to_obs)) then
682  ! Test precision
683  write(mpl%info,'(a)') '-------------------------------------------------------------------'
684  write(mpl%info,'(a)') '--- Test observation operator precision'
685  call flush(mpl%info)
686  call obsop%test_accuracy(mpl,geom)
687 end if
688 
689 end subroutine obsop_run_obsop_tests
690 
691 !----------------------------------------------------------------------
692 ! Subroutine: obsop_apply
693 ! Purpose: observation operator interpolation
694 !----------------------------------------------------------------------
695 subroutine obsop_apply(obsop,mpl,geom,fld,obs)
696 
697 implicit none
698 
699 ! Passed variables
700 class(obsop_type),intent(in) :: obsop ! Observation operator data
701 type(mpl_type),intent(in) :: mpl ! MPI data
702 type(geom_type),intent(in) :: geom ! Geometry
703 real(kind_real),intent(in) :: fld(geom%nc0a,geom%nl0) ! Field
704 real(kind_real),intent(out) :: obs(obsop%nobsa,geom%nl0) ! Observations columns
705 
706 ! Local variables
707 integer :: il0
708 real(kind_real) :: fld_ext(obsop%nc0b,geom%nl0)
709 
710 ! Halo extension
711 call obsop%com%ext(mpl,geom%nl0,fld,fld_ext)
712 
713 if (obsop%nobsa>0) then
714  ! Horizontal interpolation
715  !$omp parallel do schedule(static) private(il0)
716  do il0=1,geom%nl0
717  call obsop%h%apply(mpl,fld_ext(:,il0),obs(:,il0))
718  end do
719  !$omp end parallel do
720 end if
721 
722 end subroutine obsop_apply
723 
724 !----------------------------------------------------------------------
725 ! Subroutine: obsop_apply_ad
726 ! Purpose: observation operator interpolation adjoint
727 !----------------------------------------------------------------------
728 subroutine obsop_apply_ad(obsop,mpl,geom,obs,fld)
729 
730 implicit none
731 
732 ! Passed variables
733 class(obsop_type),intent(in) :: obsop ! Observation operator data
734 type(mpl_type),intent(in) :: mpl ! MPI data
735 type(geom_type),intent(in) :: geom ! Geometry
736 real(kind_real),intent(in) :: obs(obsop%nobsa,geom%nl0) ! Observations columns
737 real(kind_real),intent(out) :: fld(geom%nc0a,geom%nl0) ! Field
738 
739 ! Local variables
740 integer :: il0
741 real(kind_real) :: fld_ext(obsop%nc0b,geom%nl0)
742 
743 if (obsop%nobsa>0) then
744  ! Horizontal interpolation
745  !$omp parallel do schedule(static) private(il0)
746  do il0=1,geom%nl0
747  call obsop%h%apply_ad(mpl,obs(:,il0),fld_ext(:,il0))
748  end do
749  !$omp end parallel do
750 else
751  ! No observation on this task
752  fld_ext = 0.0
753 end if
754 
755 ! Halo reduction
756 call obsop%com%red(mpl,geom%nl0,fld_ext,fld)
757 
758 end subroutine obsop_apply_ad
759 
760 !----------------------------------------------------------------------
761 ! Subroutine: obsop_test_adjoint
762 ! Purpose: test observation operator adjoints accuracy
763 !----------------------------------------------------------------------
764 subroutine obsop_test_adjoint(obsop,mpl,rng,geom)
765 
766 implicit none
767 
768 ! Passed variables
769 class(obsop_type),intent(inout) :: obsop ! Observation operator data
770 type(mpl_type),intent(in) :: mpl ! MPI data
771 type(rng_type),intent(inout) :: rng ! Random number generator
772 type(geom_type),intent(in) :: geom ! Geometry
773 
774 ! Local variables
775 real(kind_real) :: sum1,sum2_loc,sum2
776 real(kind_real) :: fld(geom%nc0a,geom%nl0),fld_save(geom%nc0a,geom%nl0)
777 real(kind_real),allocatable :: yobs(:,:),yobs_save(:,:)
778 
779 ! Allocation
780 allocate(yobs(obsop%nobsa,geom%nl0))
781 allocate(yobs_save(obsop%nobsa,geom%nl0))
782 
783 ! Generate random fields
784 call rng%rand_real(0.0_kind_real,1.0_kind_real,fld_save)
785 if (obsop%nobsa>0) call rng%rand_real(0.0_kind_real,1.0_kind_real,yobs_save)
786 
787 ! Apply direct and adjoint obsservation operators
788 call obsop%apply(mpl,geom,fld_save,yobs)
789 call obsop%apply_ad(mpl,geom,yobs_save,fld)
790 
791 ! Compute adjoint test
792 call mpl%dot_prod(fld,fld_save,sum1)
793 if (obsop%nobsa>0) then
794  sum2_loc = sum(yobs*yobs_save)
795 else
796  sum2_loc = 0.0
797 end if
798 call mpl%f_comm%allreduce(sum2_loc,sum2,fckit_mpi_sum())
799 
800 ! Print results
801 write(mpl%info,'(a7,a,e15.8,a,e15.8,a,e15.8)') '','Observation operator adjoint test: ', &
802  & sum1,' / ',sum2,' / ',2.0*abs(sum1-sum2)/abs(sum1+sum2)
803 call flush(mpl%info)
804 
805 end subroutine obsop_test_adjoint
806 
807 !----------------------------------------------------------------------
808 ! Subroutine: obsop_test_accuracy
809 ! Purpose: test observation operator accuracy
810 !----------------------------------------------------------------------
811 subroutine obsop_test_accuracy(obsop,mpl,geom)
812 
813 implicit none
814 
815 ! Passed variables
816 class(obsop_type),intent(inout) :: obsop ! Observation operator data
817 type(mpl_type),intent(in) :: mpl ! MPI data
818 type(geom_type),intent(in) :: geom ! Geometry
819 
820 ! Local variables
821 integer :: ic0a,ic0,iobsa
822 integer :: iprocmax(1),iobsamax(1),iobsmax(1)
823 real(kind_real) :: ylonmax(1),ylatmax(1)
824 real(kind_real) :: norm,distmin,distmax,distsum
825 real(kind_real) :: norm_tot,distmin_tot,proc_to_distmax(mpl%nproc),distsum_tot
826 real(kind_real),allocatable :: lon(:,:),lat(:,:)
827 real(kind_real),allocatable :: ylon(:,:),ylat(:,:)
828 real(kind_real),allocatable :: dist(:)
829 
830 ! Allocation
831 allocate(lon(geom%nc0a,geom%nl0))
832 allocate(lat(geom%nc0a,geom%nl0))
833 allocate(ylon(obsop%nobsa,geom%nl0))
834 allocate(ylat(obsop%nobsa,geom%nl0))
835 allocate(dist(obsop%nobsa))
836 
837 ! Initialization
838 do ic0a=1,geom%nc0a
839  ic0 = geom%c0a_to_c0(ic0a)
840  lon(ic0a,:) = geom%lon(ic0)
841  lat(ic0a,:) = geom%lat(ic0)
842 end do
843 
844 ! Apply obsop
845 call obsop%apply(mpl,geom,lon,ylon)
846 call obsop%apply(mpl,geom,lat,ylat)
847 
848 if (obsop%nobsa>0) then
849  ! Remove points close to the longitude discontinuity and to the poles
850  call msr(dist)
851  do iobsa=1,obsop%nobsa
852  if ((abs(obsop%lonobs(iobsa))<0.8*pi).and.(abs(obsop%latobs(iobsa))<0.4*pi)) then
853  call sphere_dist(ylon(iobsa,1),ylat(iobsa,1),obsop%lonobs(iobsa),obsop%latobs(iobsa),dist(iobsa))
854  dist(iobsa) = dist(iobsa)*reqkm
855  end if
856  end do
857  norm = real(count(isnotmsr(dist)),kind_real)
858  if (norm>0) then
859  distmin = minval(dist,mask=isnotmsr(dist))
860  distmax = maxval(dist,mask=isnotmsr(dist))
861  distsum = sum(dist,mask=isnotmsr(dist))
862  else
863  distmin = huge(1.0)
864  distmax = 0.0
865  distsum = 0.0
866  end if
867 else
868  ! No observation on this task
869  norm = 0
870  distmin = huge(1.0)
871  distmax = 0.0
872  distsum = 0.0
873 end if
874 
875 ! Gather results
876 call mpl%f_comm%allreduce(norm,norm_tot,fckit_mpi_sum())
877 call mpl%f_comm%allreduce(distmin,distmin_tot,fckit_mpi_min())
878 call mpl%f_comm%allgather(distmax,proc_to_distmax)
879 call mpl%f_comm%allreduce(distsum,distsum_tot,fckit_mpi_sum())
880 
881 ! Maximum error detail
882 iprocmax = maxloc(proc_to_distmax)
883 if (iprocmax(1)==mpl%myproc) then
884  iobsamax = maxloc(dist,mask=isnotmsr(dist))
885  iobsmax = obsop%obsa_to_obs(iobsamax)
886  ylonmax = ylon(iobsamax,1)
887  ylatmax = ylat(iobsamax,1)
888 end if
889 
890 ! Broadcast results
891 call mpl%f_comm%broadcast(iobsmax,iprocmax(1)-1)
892 call mpl%f_comm%broadcast(ylonmax,iprocmax(1)-1)
893 call mpl%f_comm%broadcast(ylatmax,iprocmax(1)-1)
894 
895 ! Print results
896 if (norm_tot>0.0) then
897  write(mpl%info,'(a7,a,f10.2,a,f10.2,a,f10.2,a)') '','Interpolation error (min/mean/max): ',distmin_tot, &
898  & ' km / ',distsum_tot/norm_tot,' km / ',maxval(proc_to_distmax),' km'
899  write(mpl%info,'(a7,a)') '','Max. interpolation error location (lon/lat): '
900  write(mpl%info,'(a10,a14,f10.2,a,f10.2,a)') '','Observation: ',obsop%lonobs(iobsmax(1))*rad2deg, &
901  & ' deg. / ' ,obsop%latobs(iobsmax(1))*rad2deg,' deg.'
902  write(mpl%info,'(a10,a14,f10.2,a,f10.2,a)') '','Interpolation:',ylonmax*rad2deg, &
903  & ' deg. / ' ,ylatmax*rad2deg,' deg.'
904  call flush(mpl%info)
905 else
906  call mpl%abort('all observations are out of the test windows')
907 end if
908 
909 end subroutine obsop_test_accuracy
910 
911 end module type_obsop
912 
subroutine obsop_dealloc(obsop)
Definition: type_obsop.F90:74
real(kind_real), parameter, public rad2deg
Definition: tools_const.F90:17
subroutine obsop_test_adjoint(obsop, mpl, rng, geom)
Definition: type_obsop.F90:766
subroutine obsop_read(obsop, mpl, nam)
Definition: type_obsop.F90:94
subroutine obsop_write(obsop, mpl, nam)
Definition: type_obsop.F90:132
subroutine obsop_apply(obsop, mpl, geom, fld, obs)
Definition: type_obsop.F90:697
real(kind_real), parameter, public msvalr
Definition: tools_const.F90:24
subroutine obsop_test_accuracy(obsop, mpl, geom)
Definition: type_obsop.F90:813
subroutine obsop_run_obsop_tests(obsop, mpl, rng, geom)
Definition: type_obsop.F90:667
subroutine, public sphere_dist(lon_i, lat_i, lon_f, lat_f, dist)
Definition: tools_func.F90:67
real(kind_real), parameter, public deg2rad
Definition: tools_const.F90:16
subroutine obsop_apply_ad(obsop, mpl, geom, obs, fld)
Definition: type_obsop.F90:730
subroutine obsop_run_obsop(obsop, mpl, rng, nam, geom)
Definition: type_obsop.F90:303
subroutine obsop_generate(obsop, mpl, rng, nam, geom)
Definition: type_obsop.F90:175
logical, parameter test_no_obs
Definition: type_obsop.F90:27
integer, parameter, public kind_real
#define min(a, b)
Definition: mosaic_util.h:32
integer, parameter, public ncfloat
Definition: tools_nc.F90:27
subroutine obsop_from(obsop, nobsa, lonobs, latobs)
Definition: type_obsop.F90:271
real(fp), parameter, public pi
real(kind_real), parameter, public reqkm
Definition: tools_const.F90:19