UPP (upp-srw-2.2.0)
Loading...
Searching...
No Matches
IFI.F
1module upp_ifi_mod
2
3#ifdef USE_IFI
4 ! Use the libIFI API modules.
5 use ifi_type_mod
6 use ifi_mod
7#endif
8
9 use iso_c_binding, only: c_bool, c_int64_t, c_ptr, c_double, c_float
10 implicit none
11
12 private
13
14 public run_ifi, set_ifi_dims, ifi_real_t, write_ifi_debug_files, &
15 first_supported_ifi_hour, last_supported_ifi_hour
16
17 logical :: write_ifi_debug_files = .false.
18 real, parameter :: first_supported_ifi_hour = 1
19 real, parameter :: last_supported_ifi_hour = 21
20
21#ifndef USE_IFI
22 ! Stubs need their own version of the ifi_real_t since it is defined in ifi_type_mod
23 integer, parameter :: ifi_real_t = c_float
24
25#else
26 ! Actual IFI code needs a few more module-scope variables.
27
28 real, parameter :: feet2meters = 0.3048
29
30 type(IFIConfig) :: ifi_config
31 logical :: have_read_ifi_config = .false.
32
33 ! Communication-related variables for gathers and
34 ! scatters. Gathering is done in the i direction first, all to one
35 ! rank in each row. Then that rank in each row gathers to the
36 ! destination rank. Scatter follows the same process, in reverse.
37
38 integer :: ifi_mpi_real_kind=-999
39 integer :: row_comm = -999 ! communicator for i-direction messages
40 integer :: col_comm = -999 ! communicator for j-direction messages
41 integer :: row_comm_rank=-999, col_comm_rank=-999 ! Rank in the i & j comm communicators
42 integer :: row_comm_size=-999, col_comm_size=-999 ! Size of the i & j comm communicators
43 integer :: grid_rank ! rank within mpi_comm_comp
44
45 integer, allocatable :: ista_grid(:) ! ista for each rank in mpi_comm_comp in i direction
46 integer, allocatable :: jsta_grid(:) ! jsta for each rank in mpi_comm_comp in j direction
47
48 real, allocatable :: rearrange(:,:) ! for rearranging data on local processor
49 integer, allocatable :: row_comm_count(:) ! scatterv&gatherv receive counts for i-direction collectives
50 integer, allocatable :: row_comm_displ(:) ! scatterv&gatherv displacements for i-direction collectives
51 integer, allocatable :: row_ista(:) ! i starts for rearranging data (icomm_size)
52 integer, allocatable :: row_iend(:) ! i ends for rearranging data (icomm_size)
53 real, allocatable :: rearrange_row1d(:) ! for rearranging data for an entire row (roots of icomm communicators)
54
55 real, allocatable :: rearrange_row2d(:,:) ! for rearranging data for an entire row (roots of icomm communicators)
56 integer, allocatable :: col_comm_count(:) ! scatterv&gatherv receive counts for j-direction collectives
57 integer, allocatable :: col_comm_displ(:) ! scatterv&gatherv displacements for j-direction collectives
58 integer, allocatable :: col_jsta(:) ! j starts for rearranging data (jcomm_size)
59 integer, allocatable :: col_jend(:) ! j ends for rearranging data (jcomm_size)
60
61 integer, allocatable :: isize_grid(:) ! iend-ista+1 for each gridpoint
62#endif
63
64contains !==============================================================
65
66 subroutine send_missing_data(ient)
67 ! Fills IFI fields in index ient (of IGET) with missing values.
68 !
69 ! This subroutine is both in the stub and actual IFI code.
70 ! The stubs always send missing data, while the actual code
71 ! sends missing data for unsupported hours.
72 use ctlblk_mod, only: ifi_nflight, ifi_flight_levels
73 use ctlblk_mod, only: spval, ista, iend, jsta, jend, lm, im, cfld, datapd, fld_info, ifi_flight_levels, jsta_2l, jend_2u,me
74 use rqstfld_mod, only: iget, iavblfld, lvlsxml, lvls
75 implicit none
76
77 integer, intent(in) :: ient
78
79 ! Locals
80 integer :: i,j,k
81
82 logical, save :: wrote_message = .false. ! guarded by an OMP CRITICAL below
83
84 if(size(iget)<ient) then
85 return
86 endif
87
88 if(iget(ient)<1 .or. iget(ient)>size(lvls,2)) then
89 return
90 endif
91
92 ! Go level-by-level writing grib2 if requested
93 do k=1,ifi_nflight
94 if(k>size(lvls,1)) then
95 write(0,*) 'ERROR: LVLS is too small to handle ',ifi_nflight,' vertical levels'
96 return
97 endif
98 if(lvls(k,iget(ient))>0) then
99 if(.not.wrote_message) then
100 !$OMP CRITICAL
101 if(.not.wrote_message.and.me==0) then
102 write(*,*) 'This post cannot produce IFI icing products because it was not compiled with libIFI.'
103 wrote_message = .true.
104 endif
105 !$OMP END CRITICAL
106 endif
107
108 cfld = cfld+1
109 fld_info(cfld)%ifld = iavblfld(iget(ient))
110 fld_info(cfld)%lvl = k
111
112 !$OMP PARALLEL DO PRIVATE(i,j) COLLAPSE(2)
113 do j=jsta,jend
114 do i=ista,iend
115 datapd(i-ista+1,j-jsta+1,cfld) = spval
116 enddo
117 enddo
118 endif
119 enddo
120 end subroutine send_missing_data
121
122#ifndef USE_IFI
123! IFI stubs
124
125 subroutine set_ifi_dims()
126 use ctlblk_mod, only: ifi_nflight, ifi_flight_levels
127 implicit none
128 integer :: i
129
130 ! Bogus fill value for flight levels to prevent a crash
131 ifi_nflight = 60
132 if(allocated(ifi_flight_levels)) then
133 deallocate(ifi_flight_levels)
134 endif
135 allocate(ifi_flight_levels(ifi_nflight))
136 do i=1,ifi_nflight
137 ifi_flight_levels(i) = 500*i
138 enddo
139 end subroutine set_ifi_dims
140
141 ! --------------------------------------------------------------------
142
143 subroutine run_ifi()
144 ! Fill any requested IFI fields with missing data.
145 call send_missing_data(1007) ! ICE_PROB = missing
146 call send_missing_data(1008) ! SLD = missing
147 call send_missing_data(1009) ! ICE_SEV_CAT = missing
148 call send_missing_data(1010) ! ICE_SEV_CAT = missing
149 end subroutine run_ifi
150
151!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
152
153#else
154
155!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
156
157! Actual IFI code
158
159 subroutine make_communicators
160 use ctlblk_mod, only: jsta, jend, ista, iend, mpi_comm_comp, im, jm
161 use mpi
162 use iso_c_binding, only: c_sizeof
163 implicit none
164
165 integer, allocatable :: key(:) ! ensure ranks are in a consistent order
166 integer :: size, ierr, local_count, accum, irank, i
167 real(kind=ifi_real_t) :: testreal
168
169 if(allocated(jsta_grid)) then
170 return ! Already made communicators
171 endif
172
173 ! Use the right MPI type for the ifi real kind
174 if(c_sizeof(testreal)==4) then
175 ifi_mpi_real_kind=mpi_real4
176 else
177 ifi_mpi_real_kind=mpi_real8
178 endif
179
180 ! Get the correct size of the communicator before we try to split it.
181 call mpi_comm_size(mpi_comm_comp,size,ierr)
182 call mpi_comm_rank(mpi_comm_comp,grid_rank,ierr)
183
184! 929 format('Rank ',I0,' ista=',I0,' jsta=',I0)
185! print 929,grid_rank,ista,jsta
186
187 if(allocated(ista_grid)) then
188 deallocate(ista_grid)
189 deallocate(jsta_grid)
190 deallocate(rearrange)
191 deallocate(rearrange_row1d)
192 deallocate(rearrange_row2d)
193 deallocate(row_ista)
194 deallocate(row_iend)
195 deallocate(row_comm_count)
196 deallocate(row_comm_displ)
197 deallocate(col_jsta)
198 deallocate(col_jend)
199 deallocate(col_comm_count)
200 deallocate(col_comm_displ)
201 endif
202
203 ! Get the start locations on every rank. We need this for the key,
204 ! and for determining root ranks.
205 allocate(ista_grid(size))
206 allocate(jsta_grid(size))
207 ista_grid=-1
208 jsta_grid=-1
209 call mpi_allgather(ista,1,mpi_integer,ista_grid,1,mpi_integer,mpi_comm_comp,ierr)
210 call mpi_allgather(jsta,1,mpi_integer,jsta_grid,1,mpi_integer,mpi_comm_comp,ierr)
211
212 ! Ensure ranks are arranged in a consistent order.
213 allocate(key(size))
214 do i=1,size
215 if(ista_grid(i)==-1) then
216 write(0,*) 'invalid ista_grid ',ista_grid(i)
217 call mpi_abort(mpi_comm_world,1,ierr)
218 endif
219 if(jsta_grid(i)==-1) then
220 write(0,*) 'invalid jsta_grid ',jsta_grid(i)
221 call mpi_abort(mpi_comm_world,1,ierr)
222 endif
223 key(i) = ista_grid(i) + im*(jsta_grid(i)-1)
224 enddo
225
226 ! Create a communicator for row gather/scatter (all ranks that share a jsta)
227 if(all(jsta_grid==jsta_grid(1))) then
228 row_comm = mpi_comm_comp
229 else
230 call mpi_comm_split(mpi_comm_comp,jsta_grid(grid_rank+1),key(grid_rank+1),row_comm,ierr)
231 endif
232 if(row_comm==mpi_comm_null .or. row_comm==0) then
233 write(0,*) 'MPI_Comm_split gave MPI_COMM_NULL for row_comm'
234 call mpi_abort(mpi_comm_world,1,ierr)
235 endif
236 call mpi_comm_rank(row_comm,row_comm_rank,ierr)
237 call mpi_comm_size(row_comm,row_comm_size,ierr)
238
239 ! Create a communicator for column gather/scatter (all ranks that share an ista)
240 if(all(ista_grid==ista_grid(1))) then
241 col_comm = mpi_comm_comp
242 else
243 call mpi_comm_split(mpi_comm_comp,ista_grid(grid_rank+1),key(grid_rank+1),col_comm,ierr)
244 endif
245 if(col_comm==mpi_comm_null .or. col_comm==0) then
246 write(0,*) 'MPI_Comm_split gave MPI_COMM_NULL for col_comm'
247 call mpi_abort(mpi_comm_world,1,ierr)
248 endif
249 call mpi_comm_rank(col_comm,col_comm_rank,ierr)
250 call mpi_comm_size(col_comm,col_comm_size,ierr)
251
252 ! Done with the key.
253 deallocate(key)
254
255 ! Allocate the arrays used to rearrange data.
256 allocate(rearrange(ista:iend,jsta:jend))
257 allocate(rearrange_row1d(im*(jend-jsta+1)))
258 allocate(rearrange_row2d(1:im,jsta:jend))
259
260 ! Calculate information for row MPI_Gatherv and MPI_Scatterv calls.
261 allocate(row_ista(row_comm_size))
262 call mpi_allgather(ista,1,mpi_integer,row_ista,1,mpi_integer,row_comm,ierr)
263 allocate(row_iend(row_comm_size))
264 call mpi_allgather(iend,1,mpi_integer,row_iend,1,mpi_integer,row_comm,ierr)
265 allocate(row_comm_count(row_comm_size))
266 allocate(row_comm_displ(row_comm_size))
267 call get_count_and_displ(row_comm,row_comm_size,(iend-ista+1)*(jend-jsta+1), &
268 row_comm_count,row_comm_displ)
269
270 ! Calculate information for j-direction MPI_Gatherv and MPI_Scatterv calls.
271 allocate(col_jsta(col_comm_size))
272 call mpi_allgather(jsta,1,mpi_integer,col_jsta,1,mpi_integer,col_comm,ierr)
273 allocate(col_jend(col_comm_size))
274 call mpi_allgather(jend,1,mpi_integer,col_jend,1,mpi_integer,col_comm,ierr)
275 allocate(col_comm_count(col_comm_size))
276 allocate(col_comm_displ(col_comm_size))
277 call get_count_and_displ(col_comm,col_comm_size,im*(jend-jsta+1), &
278 col_comm_count,col_comm_displ)
279
280 end subroutine make_communicators
281
282 ! --------------------------------------------------------------------
283
284 subroutine get_count_and_displ(comm,comm_size,here,counts,displacements)
285 use mpi
286 implicit none
287 integer, intent(in) :: comm,comm_size,here
288 integer, intent(inout) :: counts(comm_size),displacements(comm_size)
289 integer :: accum, ierr, i
290
291 ! Get counts from all ranks
292 call mpi_allgather(here,1,mpi_integer,counts,1,mpi_integer,comm,ierr)
293
294 ! Displacements are a cumulative sum starting at 0 for rank 0
295 accum=0
296 do i=1,comm_size
297 displacements(i) = accum
298 accum = accum+counts(i)
299 end do
300 end subroutine get_count_and_displ
301
302 ! --------------------------------------------------------------------
303
304 subroutine find_comm_roots(global_rank,row_root,col_root)
305 ! Find the roots of the row and column communicators for a gather
306 ! or scatter with the specified rank as the location of the global
307 ! array.
308 use mpi
309 implicit none
310
311 integer, intent(in) :: global_rank
312 integer, intent(inout) :: row_root,col_root
313 integer :: r, destination_ista, destination_jsta, ierr
314
315 ! print *,'find roots for rank ',global_rank
316
317 destination_ista=ista_grid(global_rank+1)
318 destination_jsta=jsta_grid(global_rank+1)
319
320 row_root=-1
321 do r=1,row_comm_size
322 if(row_ista(r)==destination_ista) then
323 row_root=r-1
324 exit
325 endif
326 enddo
327
328 col_root=-1
329 do r=1,col_comm_size
330 if(col_jsta(r)==destination_jsta) then
331 col_root=r-1
332 exit
333 endif
334 enddo
335
336! 201 format('root is row_root=',I0,' col_root=',I0,' for rank ',I0)
337! print 201,row_root,col_root,global_rank
338
339 if(col_root==-1) then
340 write(0,'(A,I0)') 'ABORT: Could not find column root rank for rank ',global_rank
341 call mpi_abort(mpi_comm_world,1,ierr)
342 endif
343 if(row_root==-1) then
344 write(0,'(A,I0)') 'ABORT: Could not find row root rank for rank ',global_rank
345 call mpi_abort(mpi_comm_world,1,ierr)
346 endif
347 end subroutine find_comm_roots
348
349 ! --------------------------------------------------------------------
350
351 subroutine global_gather(local,global,global_rank,ista_local,iend_local,jsta_local,jend_local)
352 ! Gather from local arrays on all ranks to global array on specified rank
353 use mpi
354 use ctlblk_mod, only: jsta, jend, ista, iend, im, jm
355 use iso_c_binding, only: c_sizeof
356 implicit none
357
358 real(kind=ifi_real_t), intent(in) :: local(ista_local:iend_local,jsta_local:jend_local)
359 real(kind=ifi_real_t), intent(out) :: global(im,jm)
360 integer, intent(in) :: global_rank,ista_local,iend_local,jsta_local,jend_local
361
362 integer :: i,j,r,inindex,row_root,col_root, destination_ista, destination_jsta,ni_rank,nj_rank,idxstart, ierr
363
364 if(col_comm==0 .or. col_comm==mpi_comm_null) then
365 write(0,*) 'somehow, col_comm became invalid ',col_comm
366 call mpi_abort(mpi_comm_world,1,ierr)
367 endif
368
369 if(row_comm==0 .or. row_comm==mpi_comm_null) then
370 write(0,*) 'somehow, row_comm became invalid ',row_comm
371 call mpi_abort(mpi_comm_world,1,ierr)
372 endif
373
374 ! Find out who is the root of the i and j direction communications.
375 call find_comm_roots(global_rank,row_root,col_root)
376
377 ! Store the data in a contiguous local array
378 !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j)
379 do j=jsta,jend
380 do i=ista,iend
381 rearrange(i,j) = local(i,j)
382 enddo
383 enddo
384
385 ! Gather across all ranks in row
386 call mpi_gatherv(rearrange,(jend-jsta+1)*(iend-ista+1),ifi_mpi_real_kind, &
387 rearrange_row1d,row_comm_count,row_comm_displ,ifi_mpi_real_kind,row_root,row_comm,ierr)
388
389 if(row_comm_rank==row_root) then
390 ! Rearrange from i-j-rank dimensions to i-j dimensions.
391 idxstart=1
392 do r=1,row_comm_size
393
394 ni_rank=row_iend(r)-row_ista(r)+1
395 nj_rank=jend-jsta+1
396 ! print *,'r,ista,iend=',r,row_ista(r),row_iend(r)
397
398 !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(inindex)
399 do j=0,nj_rank-1
400 do i=0,ni_rank-1
401 inindex = idxstart + i + ni_rank*j
402 rearrange_row2d(i+row_ista(r),j+jsta) = rearrange_row1d(inindex)
403 enddo
404 enddo
405
406 idxstart = idxstart + ni_rank*nj_rank
407 enddo
408
409 ! Gather along columns
410 ! print *,'global gatherv to rank ',grid_rank
411 call mpi_gatherv(rearrange_row2d,im*(jend-jsta+1),ifi_mpi_real_kind, &
412 global,col_comm_count,col_comm_displ,ifi_mpi_real_kind,col_root,col_comm,ierr)
413 endif
414
415 ! print *,'global_gather success'
416 end subroutine global_gather
417
418 ! --------------------------------------------------------------------
419
420 subroutine global_scatter(local,global,global_rank,ista_local,iend_local,jsta_local,jend_local)
421 ! Scatter from global array at specified rank to local arrays on all ranks.
422 ! NOTE: Does NOT update halo regions!
423 use mpi
424 use ctlblk_mod, only: jsta, jend, ista, iend, im, jm
425 use iso_c_binding, only: c_sizeof
426 implicit none
427
428 real(kind=ifi_real_t), intent(out) :: local(ista_local:iend_local,jsta_local:jend_local)
429 real(kind=ifi_real_t), intent(in) :: global(im,jm)
430 integer, intent(in) :: global_rank,ista_local,iend_local,jsta_local,jend_local
431
432 integer :: i,j,r,outindex,idxstart,ni_rank,nj_rank,col_root,ierr,row_root
433
434 if(col_comm==0 .or. col_comm==mpi_comm_null) then
435 write(0,*) 'somehow, col_comm became invalid ',col_comm
436 call mpi_abort(mpi_comm_world,1,ierr)
437 endif
438
439 if(row_comm==0 .or. row_comm==mpi_comm_null) then
440 write(0,*) 'somehow, row_comm became invalid ',row_comm
441 call mpi_abort(mpi_comm_world,1,ierr)
442 endif
443
444 ! Find out who is the root of the i and j direction communications.
445 call find_comm_roots(global_rank,row_root,col_root)
446
447 if(row_comm_rank==row_root) then
448 ! Distribute in J direction.
449 ! print *,'global scatterv from rank ',grid_rank
450 call mpi_scatterv(global, col_comm_count, col_comm_displ, ifi_mpi_real_kind, &
451 rearrange_row2d ,im*(jend-jsta+1), ifi_mpi_real_kind, col_root, col_comm, ierr)
452 ! Rearrange from i-j dimensions to i-j-rank dimensions
453 idxstart=1
454 do r=1,row_comm_size
455
456 ni_rank=row_iend(r)-row_ista(r)+1
457 nj_rank=jend-jsta+1
458
459 !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(outindex)
460 do j=0,nj_rank-1
461 do i=0,ni_rank-1
462 outindex = idxstart + i + ni_rank*j
463 rearrange_row1d(outindex) = rearrange_row2d(i+row_ista(r),j+jsta)
464 enddo
465 enddo
466
467 idxstart = idxstart + ni_rank*nj_rank
468 enddo
469 endif
470
471 ! Distribute across row
472 call mpi_scatterv(rearrange_row1d,row_comm_count,row_comm_displ,ifi_mpi_real_kind, &
473 rearrange,(iend-ista+1)*(jend-jsta+1),ifi_mpi_real_kind,row_root,row_comm,ierr)
474
475 ! Copy back to contiguous local array.
476 ! NOTE: Does NOT update the halo!
477 !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j)
478 do j=jsta,jend
479 do i=ista,iend
480 local(i,j) = rearrange(i,j)
481 enddo
482 enddo
483
484 ! print *,'global_scatter success'
485
486 end subroutine global_scatter
487
488 ! --------------------------------------------------------------------
489
490 subroutine ifi_check(status,error_message)
491 use mpi, only: mpi_abort, mpi_comm_world
492 implicit none
493 integer(c_int64_t), intent(in) :: status
494 character(*), intent(in) :: error_message
495 integer :: ierr
496
497 ! Exit program if status is non-zero
498 if(status/=0) then
499 write(0,'("IFI Failed: ",A)') trim(error_message)
500 call mpi_abort(mpi_comm_world,1,ierr)
501 endif
502 end subroutine ifi_check
503
504 ! --------------------------------------------------------------------
505
506 subroutine read_ifi_config()
507 implicit none
508 ! Only read the config if we haven't already:
509 if(.not.have_read_ifi_config) then
510 call ifi_check(ifi_config%init(),'read IFI config')
511 have_read_ifi_config = .true.
512 endif
513 end subroutine read_ifi_config
514
515 ! --------------------------------------------------------------------
516
517 subroutine set_ifi_dims()
518 use ctlblk_mod, only: ifi_nflight, ifi_flight_levels
519 implicit none
520
521 ! Warning: do not deallocate config_flight_levels_feet.
522 ! The IFI C++ library manages that array.
523 integer(kind=c_int64_t), pointer :: config_flight_levels_feet(:)
524 integer :: i
525
526 ! Make sure the config was read in
527 call read_ifi_config()
528
529 ! Get the flight levels
530 call ifi_check(ifi_config%get_flight_levels_feet(config_flight_levels_feet), &
531 'cannot get flight levels in feet from IFI config')
532
533 ! Convert from integer to real:
534 ifi_nflight = size(config_flight_levels_feet)
535 if(allocated(ifi_flight_levels)) then
536 deallocate(ifi_flight_levels)
537 endif
538 allocate(ifi_flight_levels(ifi_nflight))
539 ifi_flight_levels = config_flight_levels_feet
540
541! print '(A)','IFI flight levels:'
542! 38 format(' ifi_flight_level[',I0,'] = ',F15.5)
543! do i=1,ifi_nflight
544! print 38,i,ifi_flight_levels(i)
545! enddo
546
547 end subroutine set_ifi_dims
548
549 ! --------------------------------------------------------------------
550
551 subroutine send_data(vars,name,ient)
552 use ctlblk_mod, only: spval, jsta, jend, lm, cfld, datapd, fld_info, ifi_flight_levels, jsta_2l, jend_2u, ista, iend, ista_2l, iend_2u
553 use rqstfld_mod, only: iget, iavblfld, lvlsxml, lvls
554 implicit none
555
556 class(IFIData) :: vars
557 integer, intent(in) :: ient
558 character(*), intent(in) :: name
559
560 ! Locals
561 integer(c_int64_t) :: &
562 ims,ime,jms,jme,kms,kme, ids,ide,jds,jde,kds,kde, ips,ipe,jps,jpe,kps,kpe
563 real(kind=ifi_real_t), pointer :: data(:)
564 logical(c_bool) :: missing_value_is_set
565 real(kind=ifi_real_t) :: missing_value
566 integer :: i,j,k,nj_local,jpad,ilen,jlen,kstartm1,jstartm1,iloc,ndata,ji,ni_local,ipad
567
568 if(.not. iget(ient)>0) then
569 return
570 endif
571
572 ! WARNING: do not deallocate the data pointer. It is managed by the IFI C++ library.
573 data => vars%get_data(trim(name),missing_value_is_set,missing_value, &
574 ims,ime,jms,jme,kms,kme, ids,ide,jds,jde,kds,kde, ips,ipe,jps,jpe,kps,kpe)
575 if(.not.missing_value_is_set) then
576 missing_value = missing
577 endif
578
579 ! Get dimensions and do some sanity checks
580 nj_local = jpe-jps+1
581 ni_local = ipe-ips+1
582 jpad = jps-jms
583 ipad = ips-ims
584 if(nj_local/=jend-jsta+1 .or. jpad/=jsta-jsta_2l .or. ni_local/=iend-ista+1 .or. ipad/=ista-ista_2l) then
58594 format(' ',a,' = ',i0,', ',i0)
58683 format('Warning: ',a,': IFI output j bounds do not match UPP j bounds')
587 print 83,trim(name)
588 print 94,'jps,jsta',jps,jsta
589 print 94,'jpe,jend',jpe,jend
590 print 94,'jms,jsta_2l',jms,jsta_2l
591 print 94,'jme,jend_2u',jme,jend_2u
592 print 94,'jpad,jsta-jsta_2l',jpad,jsta-jsta_2l
593 print 94,'nj_local,jend-jsta+1',nj_local,jend-jsta+1
594 print 94,'ips,ista',ips,ista
595 print 94,'ipe,iend',ipe,iend
596 print 94,'ims,ista_2l',ims,ista_2l
597 print 94,'ime,iend_2u',ime,iend_2u
598 print 94,'ipad,ista-ista_2l',ipad,ista-ista_2l
599 print 94,'ni_local,iend-ista+1',ni_local,iend-ista+1
600 !call ifi_check(-1,'Internal error: IFI output j bounds do not match UPP j bounds')
601 end if
602 ilen=ime-ims+1
603 jlen=jme-jms+1
604 ndata=ilen*jlen*(kme-kms+1)
605
606 ! Go level-by-level writing grib2 if requested
607 do k=kps,kpe
608 kstartm1=ilen*jlen*(k-kms)
609 if(lvls(k,iget(ient))>0) then
610 cfld = cfld+1
611 fld_info(cfld)%ifld = iavblfld(iget(ient))
612 fld_info(cfld)%lvl = k ! ifi_flight_levels(k)*feet2meters
613 !$OMP PARALLEL DO PRIVATE(i,j,iloc,jstartm1) COLLAPSE(2)
614 do j=jps,jpe
615 do i=ips,ipe
616 jstartm1 = kstartm1 + (j-jms)*ilen
617 iloc = jstartm1+(i-ims)+1
618 if(iloc<1 .or. iloc>size(data)) then
619 call ifi_check(-1,'Internal error: out of bounds in send_data.')
620 endif
621 if(data(iloc)==missing_value) then
622 datapd(i-ips+1,j-jps+1,cfld) = spval
623 else
624 datapd(i-ips+1,j-jps+1,cfld) = data(iloc)
625 endif
626 enddo
627 enddo
628 endif
629 enddo
630 end subroutine send_data
631
632 ! --------------------------------------------------------------------
633
634 subroutine gather_ifi(local_buf_c,global_buf_c,receiving_rank) bind(C)
635 use iso_c_binding, only: c_float, c_double,c_int64_t,c_f_pointer
636 use mpi
637 use ctlblk_mod, only: me, mpi_comm_comp, jsta, jend, im, jm, icnt, idsp, ista, iend
638 implicit none
639
640 integer(kind=c_int64_t), value :: receiving_rank
641 type(c_ptr), value :: global_buf_c, local_buf_c
642 real(kind=ifi_real_t), pointer :: global_buf(:,:), local_buf(:,:)
643
644 integer :: type, iret
645
646 call c_f_pointer(local_buf_c,local_buf,(/ iend-ista+1, jend-jsta+1 /))
647 if(me==receiving_rank) then
648 call c_f_pointer(global_buf_c,global_buf,(/ im, jm /))
649 else
650 call c_f_pointer(global_buf_c,global_buf,(/ 1,1 /))
651 endif
652
653 call gather_for_write(local_buf,global_buf,receiving_rank)
654
655 end subroutine gather_ifi
656
657 ! --------------------------------------------------------------------
658
659 subroutine gather_for_write(local_buf,global_buf,receiving_rank_c) bind(C)
660 use iso_c_binding, only: c_float, c_double,c_int64_t,c_f_pointer
661 use mpi
662 use ctlblk_mod, only: me, mpi_comm_comp, jsta, jend, im, jm, ista, ista, iend
663 implicit none
664
665 integer(kind=c_int64_t), value :: receiving_rank_c
666 real(kind=ifi_real_t) :: global_buf(:,:), local_buf(:,:)
667 integer :: receiving_rank
668 receiving_rank = receiving_rank_c
669 call global_gather(local_buf,global_buf,receiving_rank,ista,iend,jsta,jend)
670 end subroutine gather_for_write
671
672 ! --------------------------------------------------------------------
673
674 subroutine scatter_ifi(local_buf_c,global_buf_c,sending_rank_c)bind(C)
675 use iso_c_binding, only: c_float, c_double, c_int64_t, c_f_pointer
676 use mpi
677 use ctlblk_mod, only: jsta, jend, im, jm, ista, iend, me
678 implicit none
679
680 integer(kind=c_int64_t), value :: sending_rank_c
681 type(c_ptr), value :: global_buf_c, local_buf_c
682 real(kind=ifi_real_t), pointer :: global_buf(:,:), local_buf(:,:)
683 integer :: sending_rank
684
685 sending_rank=sending_rank_c
686
687 call c_f_pointer(local_buf_c,local_buf,(/ iend-ista+1, jend-jsta+1 /))
688 if(me==sending_rank) then
689 call c_f_pointer(global_buf_c,global_buf,(/ im, jm /))
690 else
691 call c_f_pointer(global_buf_c,global_buf,(/ 1,1 /))
692 endif
693
694 call global_scatter(local_buf,global_buf,sending_rank,ista,iend,jsta,jend)
695 end subroutine scatter_ifi
696
697 ! --------------------------------------------------------------------
698
699 subroutine run_ifi()
700 use ctlblk_mod, only: spval, lm, lp1, im, jsta_2l,jend_2u, itprec, ifhr, ifmin, grib, &
701 jm, jsta,jend, me, num_procs, mpi_comm_comp, ista, iend, ista_2l, iend_2u, dtq2
702 use vrbls3d, only: &
703 zmid, & ! = IFI "HGT"
704 zint, & ! zint(:,:,LM+1) = IFI "HGT_surface"
705 qqi, & ! = IFI "CIMIXR"
706 qqg, & ! = IFI "GRLE"
707 qqw, & ! = IFI "CLMR"
708 pmid, & ! = IFI "PRES"
709 qqr, & ! "RWMR"
710 q, & ! "SPFH"
711 t, & ! "TMP"
712 qqs, & ! "SNMR"
713 omga ! "VVEL"
714 use vrbls2d, only : &
715 ifi_apcp, & ! IFI "APCP_surface" over ITPREC bucket time
716 cape, cin, &
717 avgprec_cont ! Backup plan for points where IFI_APCP is invalid or 0
718 use rqstfld_mod, only: iget
719 use iso_c_binding, only: c_bool, c_int64_t
720
721 implicit none
722
723 INTERFACE ! implemented in EXCH_c_float.f
724 SUBROUTINE exch_c_float(A) BIND(C)
725 use ifi_type_mod, only: ifi_real_t
726 implicit none
727 real(kind=ifi_real_t) :: a(*)
728 END SUBROUTINE exch_c_float
729 END INTERFACE
730
731 type(IFIData) :: hybr_vars, pres_vars, derived_vars, fip_algo_vars, flight_vars, cat_vars
732 type(IFIAlgo) :: algo
733 character(88) :: outfile
734 real :: to_hourly
735 real(c_double) :: fcst_lead_sec
736 integer :: i, j
737
738 if(ifhr < first_supported_ifi_hour .or. ifhr > last_supported_ifi_hour) then
7391838 format('You requested IFI fields for hour ',i0)
740 print 1838, ifhr
7411839 format('IFI fields are only available from hours ',i0,' through ',i0,'.')
742 print 1839, first_supported_ifi_hour, last_supported_ifi_hour
7431840 format(a)
744 print 1840, 'Will output missing data for IFI fields for this time.'
745 call send_missing_data(1007)
746 call send_missing_data(1008)
747 call send_missing_data(1009)
748 call send_missing_data(1010)
749 return
750 endif
751
752 ! FIXME: Once the post is i-decomposed, replace the appropriate 1..im in this routine.
753
754 if(grib/='grib2' .or. (iget(1007)<=0 .and. iget(1008)<=0 .and. iget(1009)<=0 .and. iget(1010)<=0)) then
755 if(me==0) then
756 print '(A)','IFI fields were not requested; skipping IFI'
757 endif
758 return ! nothing to do
759 endif
760
761 if(me==0) then
762 print '(A)','Running libIFI to get icing products.'
763 call ifi_print_copyright()
764
765 if(write_ifi_debug_files) then
766 print '(A)','Will output IFI debug files.'
767 else
768 print '(A)','Will NOT output IFI debug files.'
769 endif
770 endif
771
772 if(write_ifi_debug_files) then
773 call make_communicators()
774 endif
775
776 ! Read config and initialize input data structures:
777
778 call read_ifi_config()
779 call ifi_check(hybr_vars%init(int(1,c_int64_t),int(im,c_int64_t),int(1,c_int64_t),int(jm,c_int64_t),&
780 int(1,c_int64_t),int(lm,c_int64_t),int( ista,c_int64_t),int(iend,c_int64_t),int(jsta,c_int64_t),&
781 int(jend,c_int64_t),int(1,c_int64_t),int(lm,c_int64_t)),&
782 'could not initialize IFI input data structures')
783
784 ! Copy 2D vars to IFI internal structures:
785
786 call ifi_check(hybr_vars%add_ij_var('topography',int(ista_2l,c_int64_t),int(iend_2u,c_int64_t),&
787 int(jsta_2l,c_int64_t),int(jend_2u,c_int64_t), &
788 zint(:,:,lp1)),'could not send topography to IFI')
789
790 to_hourly=3600*1000.0/dtq2
791!$OMP PARALLEL DO COLLAPSE(2)
792 do j=jsta,jend
793 do i=ista,iend
794 if(ifi_apcp(i,j)>9e9 .or. ifi_apcp(i,j)<-9e9 .or. ifi_apcp(i,j)==0) then
795 ifi_apcp(i,j) = avgprec_cont(i,j)*to_hourly
796 else if(itprec>1e-5) then
797 ifi_apcp(i,j) = ifi_apcp(i,j)/itprec
798 endif
799 enddo
800 enddo
801
802 call ifi_check(hybr_vars%add_ij_var('APCP_surface',int(ista_2l,c_int64_t),int(iend_2u,c_int64_t),&
803 int(jsta_2l,c_int64_t),int(jend_2u,c_int64_t), &
804 ifi_apcp),'could not send IFI_APCP to IFI')
805 call ifi_check(hybr_vars%add_ij_var('CAPE_surface',int(ista_2l,c_int64_t),int(iend_2u,c_int64_t), &
806 int(jsta_2l,c_int64_t),int(jend_2u,c_int64_t),cape), &
807 'could not send CAPE to IFI')
808 call ifi_check(hybr_vars%add_ij_var('CIN_surface',int(ista_2l,c_int64_t),int(iend_2u,c_int64_t), &
809 int(jsta_2l,c_int64_t),int(jend_2u,c_int64_t),cin), &
810 'could not send CIN to IFI')
811
812 ! Copy 3D vars to IFI internal structures, inverting K dimension
813
814 call add_ijk_var('HGT','zmid',zmid)
815 call add_ijk_var('CIMIXR','QQI',qqi)
816 call add_ijk_var('CLMR','QQW',qqw)
817 call add_ijk_var('GRLE','QQG',qqg)
818 call add_ijk_var('PRES','pmid',pmid)
819 call add_ijk_var('RWMR','QQR',qqr)
820 call add_ijk_var('SPFH','Q',q)
821 call add_ijk_var('TMP','T',t)
822 call add_ijk_var('SNMR','QQS',qqs)
823 call add_ijk_var('VVEL','OMGA',omga)
824
825308 format(a,'_',i0,'.nc')
826
827 fcst_lead_sec=ifhr*3600.0+ifmin*60.0
828
829 if(write_ifi_debug_files) then
830 write(outfile,308) 'hybr_vars',me
831 call write_fip_output(hybr_vars,fcst_lead_sec,trim(outfile),.true.,'z0','z1')
832 endif
833
834 ! Initialize the IFI algorithm
835
836 call ifi_check(algo%init(ifi_config,fcst_lead_sec,hybr_vars,me,num_procs,mpi_comm_comp), &
837 'could not initialize IFI algorithm')
838
839 ! Run the IFI algorithm
840
841 call ifi_check(algo%calc_exner(),'calc_exner() failed')
842 call ifi_check(algo%hybrid_to_pressure(),'hybrid_to_pressure() failed')
843
844 call ifi_check(algo%get_pres_vars(pres_vars),'get_pres_vars()')
845 if(write_ifi_debug_files) then
846 write(outfile,308) 'pres_vars',me
847 call write_fip_output(pres_vars,fcst_lead_sec,trim(outfile),.true.,'z1','z0')
848 endif
849
850 call ifi_check(algo%discard_hybrid_level_vars(),'discard_hybrid_level_vars() failed')
851
852 call ifi_check(algo%derive_fields(),'derive_fields() failed')
853
854 call ifi_check(algo%get_derived_vars(derived_vars),'get_derived_vars()')
855 if(write_ifi_debug_files) then
856 write(outfile,308) 'derived_vars',me
857 call write_fip_output(derived_vars,fcst_lead_sec,trim(outfile),.false.,'z1','z0')
858 endif
859
860 call ifi_check(algo%run_fip_algo(),'run_fip_algo() failed')
861
862 call ifi_check(algo%get_fip_algo_vars(pres_vars),'get_fip_algo_vars()')
863 if(write_ifi_debug_files) then
864 write(outfile,308) 'fip_algo_vars',me
865 call write_fip_output(pres_vars,fcst_lead_sec,trim(outfile),.false.,'z1','z0')
866 endif
867
868 call ifi_check(algo%discard_pressure_level_vars(),'discard_pressure_level_vars() failed')
869 call ifi_check(algo%discard_derived_vars(),'discard_derived_vars() failed')
870 call ifi_check(algo%pressure_to_flight(),'pressure_to_flight() failed')
871
872 call ifi_check(algo%get_flight_vars(flight_vars),'get_flight_vars()')
873 if(write_ifi_debug_files) then
874 write(outfile,308) 'flight_vars',me
875 call write_fip_output(flight_vars,fcst_lead_sec,trim(outfile),.false.,'z1','z0')
876 endif
877
878 call ifi_check(algo%discard_fip_algo_vars(),'discard_fip_algo_vars() failed')
879 call ifi_check(algo%make_icing_category(),'make_icing_category() failed')
880
881 ! Get the final output fields:
882
883 call ifi_check(algo%get_cat_vars(cat_vars),'get_cat_vars()')
884
885 if(write_ifi_debug_files) then
886 write(outfile,308) 'cat_vars',me
887 call write_fip_output(cat_vars,fcst_lead_sec,trim(outfile),.false.,'z1','z0')
888 endif
889
890 call send_data(cat_vars,'ICE_PROB',1007)
891 call send_data(cat_vars,'SLD',1008)
892 call send_data(cat_vars,'ICE_SEV_CAT',1009)
893 call send_data(cat_vars,'WMO_ICE_SEV_CAT',1010)
894
895 ! When this subroutine ends, a Fortran-2003-compliant compiler
896 ! will free all memory IFI uses, by calling the destructors (final
897 ! routines) for cat_vars, hybr_vars, algo, and config.
898
899 contains
900
901 subroutine add_ijk_var(ifi_name,upp_name,upp_var)
902 implicit none
903 character(len=*), intent(in) :: ifi_name,upp_name
904 real, intent(in) :: upp_var(ISTA_2L:IEND_2U,JSTA_2L:JEND_2U,LM)
905 real(kind=ifi_real_t) :: ifi_var(ista_2l:iend_2u,jsta_2l:jend_2u,lm)
906 integer i,j,k
907
908!$OMP PARALLEL DO COLLAPSE(2)
909 do k=1,lm
910 do j=jsta_2l,jend_2u
911 do i=ista_2l,iend_2u
912 ifi_var(i,j,k) = upp_var(i,j,lm-k+1)
913 enddo
914 enddo
915 enddo
916
917 call ifi_check(hybr_vars%add_ijk_var(ifi_name,int(ista_2l,c_int64_t),int(iend_2u,c_int64_t),&
918 int(jsta_2l,c_int64_t),int(jend_2u,c_int64_t),&
919 int(1,c_int64_t),int(lm,c_int64_t),ifi_var), &
920 'could not send '//ifi_name//' ('//upp_name//') to IFI')
921 end subroutine add_ijk_var
922
923 end subroutine run_ifi
924
925!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
926
927 subroutine find_range(var,count,missing_value_is_set,missing_value,min_not_miss,max_not_miss,all_missing)
928 USE ieee_arithmetic
929 use mpi
930 use ctlblk_mod, only: mpi_comm_comp
931 implicit none
932 real(kind=ifi_real_t), intent(in) :: var(*)
933 real(kind=ifi_real_t), intent(in) :: missing_value
934 real(kind=ifi_real_t), intent(out) :: min_not_miss,max_not_miss
935 integer, intent(in) :: count
936 logical, intent(out) :: all_missing
937 logical(c_bool), intent(in) :: missing_value_is_set
938
939 real(kind=ifi_real_t) :: minv,maxv,epsilon, global_minv,global_maxv
940 integer :: i,type,iret
941 real(kind=ifi_real_t), parameter :: zero = 0
942
943 print *,'find range begin'
944
945 if(missing_value_is_set) then
946 epsilon = abs(missing_value)*1e-4
947 if(.not. epsilon+1>epsilon) then
948 epsilon=1
949 endif
950 endif
951
952 ! Initialize to out-of-bounds values so they'll stay that way if no valid values are found:
953 minv = 1e30
954 maxv = -1e30
955
956 ! Find the min and max values in the array:
957 if(missing_value_is_set) then
958 !$OMP PARALLEL DO REDUCTION(min:minv) REDUCTION(max:maxv)
959 do i=1,count
960 if(.not. abs(var(i)-missing_value)>epsilon .and. var(i)<9e9 .and. var(i)>-9e9) then
961 minv=min(minv,var(i))
962 maxv=max(maxv,var(i))
963 endif
964 end do
965 else
966 !$OMP PARALLEL DO REDUCTION(min:minv) REDUCTION(max:maxv)
967 do i=1,count
968 if(var(i)+1>var(i)) then
969 if(var(i)<9e9 .and. var(i)>-9e9) then
970 minv=min(minv,var(i))
971 maxv=max(maxv,var(i))
972 endif
973 endif
974 end do
975 endif
976
977 if(ifi_real_t==c_double) then
978 type=mpi_real8
979 else
980 type=mpi_real4
981 endif
982
983 ! call MPI_Allreduce(minv,global_minv,1,type,MPI_MIN,mpi_comm_comp,iret)
984 ! call MPI_Allreduce(maxv,global_maxv,1,type,MPI_MAX,mpi_comm_comp,iret)
985 global_minv=minv
986 global_maxv=maxv
987
988 ! If min or max are inf, -inf, or NaN, assume all values are missing.
989 all_missing = global_minv<-9e9 .or. global_minv>9e9 .or. global_maxv<-9e9 .or. global_maxv>9e9
990
991 print *,'range min,max = ',global_minv,global_maxv
992
993 if(all_missing) then
994 min_not_miss=-1
995 max_not_miss=1
996 else
997 min_not_miss=global_minv
998 max_not_miss=global_maxv
999 endif
1000
1001 print *,'find range end'
1002 end subroutine find_range
1003
1004!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1005
1006 subroutine nc_check(code,file,message)
1007 use netcdf
1008 use mpi
1009 implicit none
1010 integer, intent(in) :: code
1011 character(*), intent(in) :: file, message
1012 integer :: i, ierr
1013 if(code/=0) then
1014 write(0,93) trim(file),trim(message),trim(nf90_strerror(code)),code
1015 call mpi_abort(mpi_comm_world,1,ierr)
1016 endif
1017 93 format(a,': ',a,': ',a,'(',i0,')')
1018 end subroutine nc_check
1019
1020 subroutine write_fip_output(ifi_data,fcst_lead_sec,output_file,rename,z2dname,z3dname)
1021 use ifi_mod
1022 use iso_c_binding
1023 use netcdf
1024 use ctlblk_mod, only: me
1025 implicit none
1026 integer, parameter :: maxname=80
1027 integer, parameter :: maxvars=999
1028 logical, intent(in) :: rename
1029 double precision, intent(in) :: fcst_lead_sec
1030 character(len=*), intent(in) :: z2dname,z3dname
1031 type var_info
1032 character(len=maxname) :: varname, outname
1033 integer :: varid, ndims, dims(4), dimids(4), count
1034 real(ifi_real_t), pointer :: data(:)
1035 integer :: ims,ime,jms,jme,kms,kme
1036 integer :: ids,ide,jds,jde,kds,kde
1037 integer :: ips,ipe,jps,jpe,kps,kpe
1038 logical :: should_dealloc
1039 real(kind=ifi_real_t) :: missing_value
1040 real(kind=ifi_real_t) :: min_not_miss,max_not_miss
1041 logical(c_bool) :: missing_value_is_set
1042 logical :: all_missing
1043 end type var_info
1044 character(len=*), intent(in) :: output_file
1045
1046 type(IFIData) :: ifi_data
1047 integer(c_int64_t) :: ids,ide, jds,jde, kds,kde
1048 integer(c_int64_t) :: ips,ipe, jps,jpe, kps,kpe
1049 integer(c_int64_t) :: ims,ime, jms,jme, kms,kme
1050 integer :: count
1051
1052 character(len=200) :: varname,nextname
1053
1054 integer :: dimids(4), ncid, ivar, id, nvars, nx0, ny0, nz0, ntime, dims(4)
1055 type(var_info),target :: var_data(maxvars)
1056
1057 if(me==0) then
1058 write(*,'(A,A)') trim(output_file),': writing IFI debug data'
1059 endif
1060
1061 call ifi_check(ifi_data%get_dims(ids,ide, jds,jde, kds,kde, ips,ipe, jps,jpe, kps,kpe),"get_dims")
1062
1063 nx0=ide-ids+1
1064 ny0=jde-jds+1
1065 nz0=kde-kds+1
1066 ntime=1
1067
1068 dims = (/nx0,ny0,nz0,ntime/)
1069
1070 varname=' ' ! special value indicating "start at the beginning"
1071
1072 ivar=0
1073 do while(ivar<maxvars)
1074 nextname='internal_error_nextname_was_not_updated'
1075 call ifi_check(ifi_data%next_varname(varname,nextname),"next_varname")
1076 varname=nextname
1077
1078 if(len_trim(nextname)<1) then
1079 exit
1080 else
1081 ivar=ivar+1
1082 endif
1083
1084 if(ivar==1 .and. me==0) then
108559 format(a,': write to file')
1086 print 59,output_file
1087
1088 call nc_check(nf90_create(output_file,ior(nf90_noclobber,nf90_64bit_offset),ncid),&
1089 output_file,'nf90_create')
1090
1091 call nc_check(nf90_put_att(ncid,nf90_global,"fcst_lead_sec",fcst_lead_sec),&
1092 output_file,"nf90_put_att fcst_lead_sec")
1093
1094 call nc_check(nf90_def_dim(ncid,"x0",nx0,dimids(1)),output_file,"nf90_def_dim x0")
1095 call nc_check(nf90_def_dim(ncid,"y0",ny0,dimids(2)),output_file,"nf90_def_dim y0")
1096 call nc_check(nf90_def_dim(ncid,z3dname,nz0,dimids(3)),output_file,"nf90_def_dim "//z3dname)
1097 call nc_check(nf90_def_dim(ncid,"time",ntime,dimids(4)),output_file,"nf90_def_dim time")
1098 endif
1099
1100 var_data(ivar)%varname = varname
1101 var_data(ivar)%outname = varname
1102 var_data(ivar)%should_dealloc = .false.
1103 var_data(ivar)%data => read_var(var_data(ivar),trim(varname),var_data(ivar)%should_dealloc)
1104 if(me==0) then
1105 call set_dims(var_data(ivar),dims,dimids)
1106 var_data(ivar)%varid = def_var(var_data(ivar),rename)
1107 endif
1108 enddo
1109
1110 if(me/=0) then
1111 ! Ranks that are not writing are done now
1112 return
1113 endif
1114
1115 nvars=ivar
1116
1117 if(nvars<1) then
1118 write(*,"(A,A)") output_file,': no data to write!'
1119 return
1120 endif
1121
1122 call nc_check(nf90_enddef(ncid),output_file,'nf90_enddef')
1123
1124 write(*,*) 'before write loop ',me
1125 do ivar=1,nvars
112624 format(' put var ',a)
1127 print 24,trim(var_data(ivar)%varname)
1128
1129 call find_range(var_data(ivar)%data,var_data(ivar)%count, &
1130 var_data(ivar)%missing_value_is_set,var_data(ivar)%missing_value,&
1131 var_data(ivar)%min_not_miss,var_data(ivar)%max_not_miss, &
1132 var_data(ivar)%all_missing)
1133 call write_var(var_data(ivar))
1134 if(var_data(ivar)%should_dealloc) then
1135 deallocate(var_data(ivar)%data)
1136 endif
1137 nullify(var_data(ivar)%data)
1138 enddo
1139
1140 call nc_check(nf90_close(ncid),output_file,"nf90_close")
1141 contains
1142
1143 subroutine set_dims(var,dims,dimids)
1144 implicit none
1145 type(var_info), intent(inout), target :: var
1146 integer, intent(in) :: dims(:), dimids(:)
1147 character(len=:), pointer :: varname
1148
1149 varname=>var%varname(1:len_trim(var%varname))
1150
1151 if(me/=0) then
1152 ! Ranks that are not writing are done now
1153 return
1154 endif
1155
1156 ! These must also be in IFITest.cc IFITest::write_netcdf
1157
1158 if(varname=='x' .or. varname=='x0') then
1159 var%dims=(/nx0,1,1,1/)
1160 var%dimids=(/dimids(1),-1,-1,-1/)
1161 var%ndims=1
1162 else if(varname=='y' .or. varname=='y0') then
1163 var%dims=(/ny0,1,1,1/)
1164 var%dimids=(/dimids(2),-1,-1,-1/)
1165 var%ndims=1
1166 else if(varname=='z' .or. varname=='z0' .or. varname=='z1' .or. &
1167 varname=='pressure_levels' .or. varname=='exner_levels') then
1168 var%dims=(/nz0,1,1,1/)
1169 var%dimids=(/dimids(3),-1,-1,-1/)
1170 var%ndims=1
1171 else if(varname=='latitude' .or. varname=='longitude') then
1172 var%dims=(/nx0,ny0,1,1/)
1173 var%dimids=(/dimids(1),dimids(2),-1,-1/)
1174 var%ndims=2
1175 else if(varname=='time') then
1176 var%dims=(/ntime,1,1,1/)
1177 var%dimids=(/dimids(4),-1,-1,-1/)
1178 var%ndims=1
1179 else if(kme<=kms) then
1180 var%dims=(/nx0,ny0,ntime,1/)
1181 var%dimids=(/dimids(1),dimids(2),dimids(4),-1/)
1182 var%ndims=3
1183 else
1184 var%dims=(/nx0,ny0,nz0,ntime/)
1185 var%dimids=dimids
1186 var%ndims=4
1187 endif
1188
1189 var%count=var%dims(1)*var%dims(2)*var%dims(3)*var%dims(4)
1190 end subroutine set_dims
1191
1192 subroutine write_var(var)
1193 implicit none
1194 type(var_info), intent(inout) :: var
1195
1196 integer :: ones(var%ndims), dims(var%ndims)
1197 real(ifi_real_t) :: put(var%count)
1198 integer :: i,j,k,n,ilen,jlen,m,imlen,jmlen
1199
1200 ones = 1
1201 dims = var%dims(1:var%ndims)
1202
1203 if(me/=0) then
1204 ! Ranks that are not writing are done now
1205 return
1206 endif
1207
1208 call nc_check(nf90_put_var(ncid=ncid,varid=var%varid,values=var%data, &
1209 start=ones,count=dims), &
1210 output_file,"nf90_put_var "//trim(var%outname))
1211
1212 call nc_check(nf90_put_att(ncid,var%varid,"min_value",var%min_not_miss), &
1213 output_file,"nf90_put_att "//trim(var%outname)//" min_value")
1214
1215 call nc_check(nf90_put_att(ncid,var%varid,"max_value",var%max_not_miss), &
1216 output_file,"nf90_put_att "//trim(var%outname)//" max_value")
1217
1218 end subroutine write_var
1219
1220 integer function def_var(var,rename)
1221 use iso_c_binding, only: c_float
1222 implicit none
1223 logical :: rename
1224 type(var_info), intent(inout) :: var
1225
1226 integer :: varid, xtype, dimids(var%ndims)
1227 character(len=100) :: outname
1228
1229 if(rename) then
1230 var%outname=var%varname
1231 select case(trim(var%varname))
1232 case('CIMIXR')
1233 var%outname = 'ICMR'
1234 case('SPFH')
1235 var%outname = 'MIXR'
1236 case('GRLE')
1237 var%outname = 'GRMR'
1238 case('CAPE_surface')
1239 var%outname = 'CAPE'
1240 case('CIN_surface')
1241 var%outname = 'CIN'
1242 case('CLMR')
1243 var%outname = 'CLWMR'
1244 case('APCP_surface')
1245 var%outname = 'APCP1Hr'
1246 end select
1247 endif
1248
1249 var%ims=ims ; var%ime=ime ; var%jms=jms ; var%jme=jme ; var%kms=kms ; var%kme=kme
1250 var%ids=ids ; var%ide=ide ; var%jds=jds ; var%jde=jde ; var%kds=kds ; var%kde=kde
1251 var%ips=ips ; var%ipe=ipe ; var%jps=jps ; var%jpe=jpe ; var%kps=kps ; var%kpe=kpe
1252
1253 dimids = var%dimids(1:var%ndims)
1254
1255 if(ifi_real_t==c_float) then
1256 xtype = nf90_float
1257 else
1258 xtype = nf90_double
1259 endif
1260
1261 call nc_check(nf90_def_var(ncid,trim(var%outname),xtype,dimids,def_var), &
1262 output_file,"nf90_def_var "//trim(var%outname))
1263
1264 call nc_check(nf90_put_att(ncid,def_var,"min_value",var%min_not_miss), &
1265 output_file,"nf90_put_att "//trim(var%outname)//" min_value")
1266
1267 call nc_check(nf90_put_att(ncid,def_var,"max_value",var%max_not_miss), &
1268 output_file,"nf90_put_att "//trim(var%outname)//" max_value")
1269
1270 if(var%missing_value_is_set) then
1271 call nc_check(nf90_put_att(ncid,def_var,"_FillValue",-9999.0), &
1272 output_file,"nf90_put_att "//trim(var%outname)//" _FillValue")
1273 end if
1274
1275 end function def_var
1276
1277 function read_var(var,varname,should_dealloc)
1278 use mpi
1279 implicit none
1280 type(var_info), intent(inout) :: var
1281 real(kind=ifi_real_t), pointer :: read_var(:)
1282 real(kind=ifi_real_t), pointer :: local_data_1d(:)
1283 character(len=*), intent(in) :: varname
1284 logical, intent(out) :: should_dealloc
1285
1286 real(kind=ifi_real_t), allocatable :: local_data(:,:),global_data(:,:)
1287 real(kind=ifi_real_t), pointer :: global_data_1d(:)
1288
1289 integer :: nxny_local,nxny_global,nz,count,i,j,k
1290 integer :: local_ilen,local_jlen,local_klen,local_index
1291 integer :: global_ilen,global_jlen,global_klen,global_index, ierr
1292 type(c_ptr) :: global_cptr,local_cptr
1293
1294 local_data_1d => ifi_data%get_data(trim(varname),var%missing_value_is_set,var%missing_value, &
1295 ims,ime,jms,jme,kms,kme, ids,ide,jds,jde,kds,kde, ips,ipe,jps,jpe,kps,kpe)
1296
1297 global_ilen=ide-ids+1
1298 global_jlen=jde-jds+1
1299 global_klen=kde-kds+1
1300 local_ilen=ipe-ips+1
1301 local_jlen=jpe-jps+1
1302 local_klen=kpe-kps+1
1303
1304 if(.not.associated(local_data_1d)) then
1305 write(0,38) trim(varname)
130638 format("IFI did not produce ",a," variable.")
1307 call mpi_abort(mpi_comm_world,1,ierr)
1308 end if
1309
1310 count=global_ilen*global_jlen*global_klen
1311 if(count<=0) then
1312 write(0,39) trim(varname),count
131339 format("IFI variable ",a," had no data (size=",i0,")")
1314 call mpi_abort(mpi_comm_world,1,ierr)
1315 endif
1316
1317 if(ids==ide .or. jds==jde) then
1318 ! This is a 1D variable so we write it as is from rank 0
1319 read_var => local_data_1d
1320 should_dealloc = .false.
1321 return
1322 endif
1323
132440 format('var ',a,' has bad ',a,'. Got: ',i0,' but expected: ',i0)
1325
1326 if(global_ilen /= nx0) then
1327 write(0,40) trim(varname),'global_ilen',global_ilen,nx0
1328 call mpi_abort(mpi_comm_world,1,ierr)
1329 endif
1330
1331 if(global_jlen /= ny0) then
1332 write(0,40) trim(varname),'global_jlen',global_jlen,ny0
1333 call mpi_abort(mpi_comm_world,1,ierr)
1334 endif
1335
1336 if(global_klen/=1 .and. global_klen /= nz0) then
1337 write(0,40) trim(varname),'global_klen',global_klen,nz0
1338 call mpi_abort(mpi_comm_world,1,ierr)
1339 endif
1340
1341
1342 allocate(local_data(local_ilen,local_jlen))
1343 if(me==0) then
1344 allocate(global_data(global_ilen,global_jlen))
1345 allocate(global_data_1d(global_ilen*global_jlen*global_klen))
1346 else
1347 allocate(global_data(1,1))
1348 allocate(global_data_1d(1))
1349 endif
1350
1351 do k=kds,kde
1352 do j=jps,jpe
1353 do i=ips,ipe
1354 local_data(i-ips+1,j-jps+1) = local_data_1d( 1 + (i-ims) + ((j-jms) + (k-kms)*(jme-jms+1))*(ime-ims+1) )
1355 enddo
1356 enddo
1357 call gather_for_write(local_data,global_data,0)
1358 if(me==0) then
1359 do j=1,global_jlen
1360 do i=1,global_ilen
1361 global_data_1d(1 + (i-1) + ((j-1) + (k-kds)*global_jlen)*global_ilen) = &
1362 global_data(i,j)
1363 enddo
1364 enddo
1365 endif
1366 enddo
1367
1368 ! Ranks that are not writing data are done.
1369 if(me/=0) then
1370 ! This rank does not have the global data, and will not use the data anyway,
1371 ! but it wants an array, so we'll send it the local data that is managed
1372 ! internally by libIFI.
1373 read_var => local_data_1d
1374 should_dealloc = .false.
1375 deallocate(global_data_1d)
1376 else
1377 read_var => global_data_1d
1378 should_dealloc = .true.
1379 endif
1380
1381 deallocate(global_data)
1382 deallocate(local_data)
1383 end function read_var
1384 end subroutine write_fip_output
1385
1386#endif
1387
1388end module upp_ifi_mod
subroutine exch_c_float(a)
EXCH_c_float Subroutines that exchange one halo row.