Ticket #784: nf_fwrite4d.F

File nf_fwrite4d.F, 20.5 KB (added by dwhitt, 6 years ago)
Line 
1#include "cppdefs.h"
2 MODULE nf_fwrite4d_mod
3!
4!svn $Id$
5!================================================== Hernan G. Arango ===
6! Copyright (c) 2002-2018 The ROMS/TOMS Group !
7! Licensed under a MIT/X style license !
8! See License_ROMS.txt !
9!=======================================================================
10! !
11! This function writes out a generic floating point 4D array into an !
12! output NetCDF file. !
13! !
14! On Input: !
15! !
16! ng Nested grid number. !
17! model Calling model identifier. !
18! ncid NetCDF file ID. !
19! ncvarid NetCDF variable ID. !
20! tindex NetCDF time record index to write. !
21! gtype Grid type. If negative, only write water points. !
22! LBi I-dimension Lower bound. !
23! UBi I-dimension Upper bound. !
24! LBj J-dimension Lower bound. !
25! UBj J-dimension Upper bound. !
26! LBk K-dimension Lower bound. !
27! UBk K-dimension Upper bound. !
28! LBt Time-dimension Lower bound. !
29! UBt Time-dimension Upoer bound. !
30! Amask land/Sea mask, if any (real). !
31! Ascl Factor to scale field before writing (real). !
32! A Field to write out (real). !
33! SetFillVal Logical switch to set fill value in land areas !
34! (optional). !
35! !
36! On Output: !
37! !
38! nf_fwrite4d Error flag (integer). !
39! !
40#ifdef POSITIVE_ZERO
41! Starting F95 zero values can be signed (-0 or +0) following the !
42! IEEE 754 floating point standard. This may produce different !
43! output data in serial and parallel applications. Since comparing !
44! serial and parallel output is essential for tracking parallel !
45! partition bugs, "positive zero" is enforced. !
46! !
47#endif
48!=======================================================================
49!
50 implicit none
51
52 CONTAINS
53
54#if defined PARALLEL_IO && defined DISTRIBUTE
55!
56!***********************************************************************
57 FUNCTION nf_fwrite4d (ng, model, ncid, ncvarid, tindex, gtype, &
58 & LBi, UBi, LBj, UBj, LBk, UBk, LBt, UBt, &
59 & Ascl, &
60# ifdef MASKING
61 & Amask, &
62# endif
63 & A, SetFillVal)
64!***********************************************************************
65!
66 USE mod_param
67 USE mod_parallel
68 USE mod_ncparam
69 USE mod_netcdf
70 USE mod_scalars
71
72# ifdef MASKING
73!
74 USE distribute_mod, ONLY : mp_collect
75# endif
76!
77! Imported variable declarations.
78!
79 logical, intent(in), optional :: SetFillVal
80
81 integer, intent(in) :: ng, model, ncid, ncvarid, tindex, gtype
82 integer, intent(in) :: LBi, UBi, LBj, UBj, LBk, UBk, LBt, UBt
83
84 real(r8), intent(in) :: Ascl
85
86# ifdef ASSUMED_SHAPE
87# ifdef MASKING
88 real(r8), intent(in) :: Amask(LBi:,LBj:)
89# endif
90 real(r8), intent(in) :: A(LBi:,LBj:,LBk:,LBt:)
91# else
92# ifdef MASKING
93 real(r8), intent(in) :: Amask(LBi:UBi,LBj:UBj)
94# endif
95 real(r8), intent(in) :: A(LBi:UBi,LBj:UBj,LBk:UBk,LBt:UBt)
96# endif
97!
98! Local variable declarations.
99!
100# ifdef MASKING
101 logical :: LandFill
102# endif
103 integer :: i, ic, j, jc, k, kc, l, lc, Npts
104 integer :: Imin, Imax, Jmin, Jmax, Kmin, Kmax
105 integer :: Ioff, Joff, Koff, Loff
106 integer :: Istr, Iend
107 integer :: Ilen, Isize, Jlen, Jsize, IJlen, IJKlen, Klen, Llen
108 integer :: MyType, status
109
110 integer, dimension(5) :: start, total
111
112 integer :: nf_fwrite4d
113
114 real(r8), parameter :: IniVal = 0.0_r8
115
116 real(r8), allocatable :: Awrk(:)
117!
118!-----------------------------------------------------------------------
119! Set starting and ending indices to process.
120!-----------------------------------------------------------------------
121!
122! Set first and last grid point according to staggered C-grid
123! classification.
124!
125 MyType=gtype
126
127 SELECT CASE (ABS(MyType))
128 CASE (p2dvar, p3dvar)
129 Imin=BOUNDS(ng)%Istr (MyRank)
130 Imax=BOUNDS(ng)%IendR(MyRank)
131 Jmin=BOUNDS(ng)%Jstr (MyRank)
132 Jmax=BOUNDS(ng)%JendR(MyRank)
133 Isize=IOBOUNDS(ng)%xi_psi
134 Jsize=IOBOUNDS(ng)%eta_psi
135 CASE (r2dvar, r3dvar)
136 Imin=BOUNDS(ng)%IstrR(MyRank)
137 Imax=BOUNDS(ng)%IendR(MyRank)
138 Jmin=BOUNDS(ng)%JstrR(MyRank)
139 Jmax=BOUNDS(ng)%JendR(MyRank)
140 Isize=IOBOUNDS(ng)%xi_rho
141 Jsize=IOBOUNDS(ng)%eta_rho
142 CASE (u2dvar, u3dvar)
143 Imin=BOUNDS(ng)%Istr (MyRank)
144 Imax=BOUNDS(ng)%IendR(MyRank)
145 Jmin=BOUNDS(ng)%JstrR(MyRank)
146 Jmax=BOUNDS(ng)%JendR(MyRank)
147 Isize=IOBOUNDS(ng)%xi_u
148 Jsize=IOBOUNDS(ng)%eta_u
149 CASE (v2dvar, v3dvar)
150 Imin=BOUNDS(ng)%IstrR(MyRank)
151 Imax=BOUNDS(ng)%IendR(MyRank)
152 Jmin=BOUNDS(ng)%Jstr (MyRank)
153 Jmax=BOUNDS(ng)%JendR(MyRank)
154 Isize=IOBOUNDS(ng)%xi_v
155 Jsize=IOBOUNDS(ng)%eta_v
156 CASE DEFAULT
157 Imin=BOUNDS(ng)%IstrR(MyRank)
158 Imax=BOUNDS(ng)%IendR(MyRank)
159 Jmin=BOUNDS(ng)%JstrR(MyRank)
160 Jmax=BOUNDS(ng)%JendR(MyRank)
161 Isize=IOBOUNDS(ng)%xi_rho
162 Jsize=IOBOUNDS(ng)%eta_rho
163 END SELECT
164
165 Ilen=Imax-Imin+1
166 Jlen=Jmax-Jmin+1
167 Klen=UBk-LBk+1
168 Llen=UBt-LBt+1
169
170# ifdef MASKING
171!
172! Set switch to replace land areas with fill value, spval.
173!
174 IF (PRESENT(SetFillVal)) THEN
175 LandFill=SetFillVal
176 ELSE
177 LandFill=tindex.gt.0
178 END IF
179# endif
180!
181!-----------------------------------------------------------------------
182! Parallel I/O: Pack tile data into 1D array in column-major order.
183# ifdef MASKING
184! Overwrite masked points with special value.
185# endif
186!-----------------------------------------------------------------------
187!
188 IF (gtype.gt.0) THEN
189!
190! Set offsets due the NetCDF dimensions. Recall that some output
191! variables not always start at one.
192!
193 SELECT CASE (ABS(MyType))
194 CASE (p2dvar, p3dvar)
195 Ioff=0
196 Joff=0
197 CASE (r2dvar, r3dvar)
198 Ioff=1
199 Joff=1
200 CASE (u2dvar, u3dvar)
201 Ioff=0
202 Joff=1
203 CASE (v2dvar, v3dvar)
204 Ioff=1
205 Joff=0
206 CASE DEFAULT
207 Ioff=1
208 Joff=1
209 END SELECT
210
211 IF (LBk.eq.0) THEN
212 Koff=1
213 ELSE
214 Koff=0
215 END IF
216
217 IF (LBt.eq.0) THEN
218 Loff=1
219 ELSE
220 Loff=0
221 END IF
222!
223! Allocate and initialize scratch work array.
224!
225 IJlen=Ilen*Jlen
226 IJKlen=IJlen*Klen
227 Npts=IJKlen*Llen
228
229 IF (.not.allocated(Awrk)) THEN
230 allocate ( Awrk(Npts) )
231 Awrk=IniVal
232 END IF
233!
234! Pack and scale tile data.
235!
236 ic=0
237 DO l=LBt,UBt
238 DO k=LBk,UBk
239 DO j=Jmin,Jmax
240 DO i=Imin,Imax
241 ic=ic+1
242 Awrk(ic)=A(i,j,k,l)*Ascl
243# ifdef POSITIVE_ZERO
244 IF (ABS(Awrk(ic)).eq.0.0_r8) THEN
245 Awrk(ic)=0.0_r8 ! impose positive zero
246 END IF
247# endif
248# ifdef MASKING
249 IF ((Amask(i,j).eq.0.0_r8).and.LandFill) THEN
250 Awrk(ic)=spval
251 END IF
252# endif
253 END DO
254 END DO
255 END DO
256 END DO
257!
258! Write out data: all parallel nodes write their own packed tile data.
259!
260 start(1)=Imin+Ioff
261 total(1)=Ilen
262 start(2)=Jmin+Joff
263 total(2)=Jlen
264 start(3)=LBk+Koff
265 total(3)=Klen
266 start(4)=LBt+Loff
267 total(4)=Llen
268 start(5)=tindex
269 total(5)=1
270
271 status=nf90_put_var(ncid, ncvarid, Awrk, start, total)
272 nf_fwrite4d=status
273 END IF
274
275# if defined WRITE_WATER && defined MASKING
276!
277!-----------------------------------------------------------------------
278! Parallel I/O: Remove land points and pack tile data into 1D array.
279!-----------------------------------------------------------------------
280!
281 IF (gtype.lt.0) THEN
282!
283! Set offsets due array packing into 1D array in column-major order.
284!
285 SELECT CASE (ABS(MyType))
286 CASE (p2dvar, p3dvar)
287 Ioff=0
288 Joff=1
289 CASE (r2dvar, r3dvar)
290 Ioff=1
291 Joff=0
292 CASE (u2dvar, u3dvar)
293 Ioff=0
294 Joff=0
295 CASE (v2dvar, v3dvar)
296 Ioff=1
297 Joff=1
298 CASE DEFAULT
299 Ioff=1
300 Joff=0
301 END SELECT
302
303 IF (LBk.eq.0) THEN
304 Koff=0
305 ELSE
306 Koff=1
307 END IF
308
309 IF (LBt.eq.0) THEN
310 Loff=0
311 ELSE
312 Loff=1
313 END IF
314!
315! Allocate and initialize scratch work array.
316!
317 IJlen=Isize*Jsize
318 IJKlen=IJlen*Klen
319 Npts=IJKlen*Llen
320
321 IF (.not.allocated(Awrk)) THEN
322 allocate ( Awrk(Npts) )
323 Awrk=IniVal
324 END IF
325!
326! Scale and gather data from all spawned nodes. Store data into a 1D
327! global array, packed in column-major order. Flag land point with
328! special value.
329!
330 DO l=LBt,UBt
331 lc=(l-Loff)*IJKlen
332 DO k=LBk,UBk
333 kc=(k-Koff)*IJlen+lc
334 DO j=Jmin,Jmax
335 jc=(j-Joff)*Isize+kc
336 DO i=Imin,Imax
337 ic=i+Ioff+jc
338 Awrk(ic)=A(i,j,k,l)*Ascl
339# ifdef POSITIVE_ZERO
340 IF (ABS(Awrk(ic)).eq.0.0_r8) THEN
341 Awrk(ic)=0.0_r8 ! impose positive zero
342 END IF
343# endif
344 IF (Amask(i,j).eq.0.0_r8) THEN
345 Awrk(ic)=spval
346 END IF
347 END DO
348 END DO
349 END DO
350 END DO
351!
352! Global reduction of work array.
353!
354 CALL mp_collect (ng, model, Npts, IniVal, Awrk)
355!
356! Remove land points.
357!
358 ic=0
359 DO i=1,Npts
360 IF (Awrk(i).lt.spval) THEN
361 ic=ic+1
362 Awrk(ic)=Awrk(i)
363 END IF
364 END DO
365 Npts=ic
366!
367! Write out data: all parallel nodes write a section of the packed
368! data.
369!
370 CALL tile_bounds_1d (ng, MyRank, Npts, Istr, Iend)
371
372 start(1)=Istr
373 total(1)=Iend-Istr+1
374 start(2)=tindex
375 total(2)=1
376
377 status=nf90_put_var(ncid, ncvarid, Awrk(Istr:), start, total)
378 nf_fwrite4d=status
379 END IF
380# endif
381!
382!-----------------------------------------------------------------------
383! Deallocate scratch work array.
384!-----------------------------------------------------------------------
385!
386 IF (allocated(Awrk)) THEN
387 deallocate (Awrk)
388 END IF
389
390 RETURN
391 END FUNCTION nf_fwrite4d
392
393#else
394
395!
396!***********************************************************************
397 FUNCTION nf_fwrite4d (ng, model, ncid, ncvarid, tindex, gtype, &
398 & LBi, UBi, LBj, UBj, LBk, UBk, LBt, UBt, &
399 & Ascl, &
400# ifdef MASKING
401 & Amask, &
402# endif
403 & A, SetFillVal)
404!***********************************************************************
405!
406 USE mod_param
407 USE mod_parallel
408 USE mod_ncparam
409 USE mod_netcdf
410 USE mod_scalars
411
412# ifdef DISTRIBUTE
413!
414 USE distribute_mod, ONLY : mp_bcasti
415# ifdef INLINE_2DIO
416 USE distribute_mod, ONLY : mp_gather2d
417# else
418 USE distribute_mod, ONLY : mp_gather3d
419# endif
420# endif
421!
422! Imported variable declarations.
423!
424 logical, intent(in), optional :: SetFillVal
425
426 integer, intent(in) :: ng, model, ncid, ncvarid, tindex, gtype
427 integer, intent(in) :: LBi, UBi, LBj, UBj, LBk, UBk, LBt, UBt
428
429 real(r8), intent(in) :: Ascl
430
431# ifdef ASSUMED_SHAPE
432# ifdef MASKING
433 real(r8), intent(in) :: Amask(LBi:,LBj:)
434# endif
435 real(r8), intent(in) :: A(LBi:,LBj:,LBk:,LBt:)
436# else
437# ifdef MASKING
438 real(r8), intent(in) :: Amask(LBi:UBi,LBj:UBj)
439# endif
440 real(r8), intent(in) :: A(LBi:UBi,LBj:UBj,LBk:UBk,LBt:UBt)
441# endif
442!
443! Local variable declarations.
444!
445# ifdef MASKING
446 logical :: LandFill
447# endif
448 integer :: i, j, k, ic, fourth, Npts
449 integer :: Imin, Imax, Jmin, Jmax, Kmin, Kmax, Koff, Loff
450 integer :: Ilen, Jlen, Klen, IJlen, MyType, status
451
452 integer, dimension(5) :: start, total
453
454 integer :: nf_fwrite4d
455
456# if defined INLINE_2DIO && defined DISTRIBUTE
457 real(r8), dimension((Lm(ng)+2)*(Mm(ng)+2)) :: Awrk
458# else
459 real(r8), dimension((Lm(ng)+2)*(Mm(ng)+2)*(UBk-LBk+1)) :: Awrk
460# endif
461!
462!-----------------------------------------------------------------------
463! Set starting and ending indices to process.
464!-----------------------------------------------------------------------
465!
466! Set first and last grid point according to staggered C-grid
467! classification. Set loops offsets.
468!
469 MyType=gtype
470
471 SELECT CASE (ABS(MyType))
472 CASE (p2dvar, p3dvar)
473 Imin=IOBOUNDS(ng)%ILB_psi
474 Imax=IOBOUNDS(ng)%IUB_psi
475 Jmin=IOBOUNDS(ng)%JLB_psi
476 Jmax=IOBOUNDS(ng)%JUB_psi
477 CASE (r2dvar, r3dvar)
478 Imin=IOBOUNDS(ng)%ILB_rho
479 Imax=IOBOUNDS(ng)%IUB_rho
480 Jmin=IOBOUNDS(ng)%JLB_rho
481 Jmax=IOBOUNDS(ng)%JUB_rho
482 CASE (u2dvar, u3dvar)
483 Imin=IOBOUNDS(ng)%ILB_u
484 Imax=IOBOUNDS(ng)%IUB_u
485 Jmin=IOBOUNDS(ng)%JLB_u
486 Jmax=IOBOUNDS(ng)%JUB_u
487 CASE (v2dvar, v3dvar)
488 Imin=IOBOUNDS(ng)%ILB_v
489 Imax=IOBOUNDS(ng)%IUB_v
490 Jmin=IOBOUNDS(ng)%JLB_v
491 Jmax=IOBOUNDS(ng)%JUB_v
492 CASE DEFAULT
493 Imin=IOBOUNDS(ng)%ILB_rho
494 Imax=IOBOUNDS(ng)%IUB_rho
495 Jmin=IOBOUNDS(ng)%JLB_rho
496 Jmax=IOBOUNDS(ng)%JUB_rho
497 END SELECT
498
499 Ilen=Imax-Imin+1
500 Jlen=Jmax-Jmin+1
501 Klen=UBk-LBk+1
502 IJlen=Ilen*Jlen
503
504 IF (LBk.eq.0) THEN
505 Koff=0
506 ELSE
507 Koff=1
508 END IF
509
510 IF (LBt.eq.0) THEN
511 Loff=1
512 ELSE
513 Loff=0
514 END IF
515
516# ifdef MASKING
517!
518! Set switch to replace land areas with fill value, spval.
519!
520 IF (PRESENT(SetFillVal)) THEN
521 LandFill=SetFillVal
522 ELSE
523 LandFill=tindex.gt.0
524 END IF
525# endif
526!
527! Initialize local array to avoid denormalized numbers. This
528! facilitates processing and debugging.
529!
530 Awrk=0.0_r8
531
532# ifdef DISTRIBUTE
533!
534!-----------------------------------------------------------------------
535! If distributed-memory set-up, collect tile data from all spawned
536! nodes and store it into a global scratch 1D array, packed in column-
537! major order.
538# ifdef MASKING
539# ifdef WRITE_WATER
540! Remove land points and pack water points into 1D-array.
541# else
542! Overwrite masked points with special value.
543# endif
544# endif
545!-----------------------------------------------------------------------
546!
547!
548 DO fourth=LBt,UBt
549# ifdef INLINE_2DIO
550
551! If appropriate, process 3D data level by level to reduce memory
552! requirements.
553!
554 DO k=LBk,UBk
555 CALL mp_gather2d (ng, model, LBi, UBi, LBj, UBj, &
556 & tindex, gtype, Ascl, &
557# ifdef MASKING
558 & Amask, &
559# endif
560 & A(:,:,k,fourth), Npts, Awrk, SetFillVal)
561# else
562 CALL mp_gather3d (ng, model, LBi, UBi, LBj, UBj, LBk, UBk, &
563 & tindex, gtype, Ascl, &
564# ifdef MASKING
565 & Amask, &
566# endif
567 & A(:,:,:,fourth), Npts, Awrk, SetFillVal)
568# endif
569
570!
571!-----------------------------------------------------------------------
572! Write output buffer into NetCDF file.
573!-----------------------------------------------------------------------
574!
575 nf_fwrite4d=nf90_noerr
576 IF (OutThread) THEN
577 IF (gtype.gt.0) THEN
578 start(1)=1
579 total(1)=Ilen
580 start(2)=1
581 total(2)=Jlen
582# ifdef INLINE_2DIO
583 start(3)=k-Koff+1
584 total(3)=1
585# else
586 start(3)=1
587 total(3)=Klen
588# endif
589 start(4)=fourth+Loff
590 total(4)=1
591 start(5)=tindex
592 total(5)=1
593# ifdef MASKING
594 ELSE
595 start(1)=1+(fourth+Loff-1)*Npts
596 total(1)=Npts
597 start(2)=tindex
598 total(2)=1
599# endif
600 END IF
601# ifdef POSITIVE_ZERO
602 DO ic=1,Npts
603 IF (ABS(Awrk(ic)).eq.0.0_r8) THEN
604 Awrk(ic)=0.0_r8 ! impose positive zero
605 END IF
606 END DO
607# endif
608 status=nf90_put_var(ncid, ncvarid, Awrk, start, total)
609 nf_fwrite4d=status
610 END IF
611# ifdef INLINE_2DIO
612 END DO
613# endif
614 END DO
615# else
616!
617!-----------------------------------------------------------------------
618! If serial or shared-memory applications and serial output, pack data
619! into a global 1D array in column-major order.
620# ifdef MASKING
621# ifdef WRITE_WATER
622! Remove land points and pack water points into 1D-array.
623# else
624! Overwrite masked points with special value.
625# endif
626# endif
627!-----------------------------------------------------------------------
628!
629! Process data as 3D slices.
630!
631 DO fourth=LBt,UBt
632 IF (gtype.gt.0) THEN
633 ic=0
634 Npts=IJlen*Klen
635 DO k=LBk,UBk
636 DO j=Jmin,Jmax
637 DO i=Imin,Imax
638 ic=ic+1
639 Awrk(ic)=A(i,j,k,fourth)*Ascl
640# ifdef MASKING
641 IF ((Amask(i,j).eq.0.0_r8).and.LandFill) THEN
642 Awrk(ic)=spval
643 END IF
644# endif
645 END DO
646 END DO
647 END DO
648# ifdef MASKING
649 ELSE
650 Npts=0
651 DO k=LBk,UBk
652 DO j=Jmin,Jmax
653 DO i=Imin,Imax
654 IF (Amask(i,j).gt.0.0_r8) THEN
655 Npts=Npts+1
656 Awrk(Npts)=A(i,j,k,fourth)*Ascl
657 END IF
658 END DO
659 END DO
660 END DO
661# endif
662 END IF
663!
664!-----------------------------------------------------------------------
665! Write output buffer into NetCDF file.
666!-----------------------------------------------------------------------
667!
668 nf_fwrite4d=nf90_noerr
669 IF (OutThread) THEN
670 IF (gtype.gt.0) THEN
671 start(1)=1
672 total(1)=Ilen
673 start(2)=1
674 total(2)=Jlen
675 start(3)=1
676 total(3)=Klen
677 start(4)=fourth+Loff
678 total(4)=1
679 start(5)=tindex
680 total(5)=1
681# ifdef MASKING
682 ELSE
683 start(1)=1+(fourth+Loff-1)*Npts
684 total(1)=Npts
685 start(2)=tindex
686 total(2)=1
687# endif
688 END IF
689# ifdef POSITIVE_ZERO
690 DO ic=1,Npts
691 IF (ABS(Awrk(ic)).eq.0.0_r8) THEN
692 Awrk(ic)=0.0_r8 ! impose positive zero
693 END IF
694 END DO
695# endif
696 status=nf90_put_var(ncid, ncvarid, Awrk, start, total)
697 END IF
698 END DO
699# endif
700# ifdef DISTRIBUTE
701!
702!-----------------------------------------------------------------------
703! Broadcast IO error flag to all nodes.
704!-----------------------------------------------------------------------
705!
706 CALL mp_bcasti (ng, model, status)
707# endif
708 nf_fwrite4d=status
709
710 RETURN
711 END FUNCTION nf_fwrite4d
712#endif
713 END MODULE nf_fwrite4d_mod