MODULE module_quilt_outbuf_ops 7 !<DESCRIPTION> !<PRE> ! This module contains routines and data structures used by the I/O quilt ! servers to assemble fields ("quilting") and write them to disk. !</PRE> !</DESCRIPTION> INTEGER, PARAMETER :: tabsize = 5 ! The number of entries in outpatch_table (up to a maximum of tabsize) INTEGER, SAVE :: num_entries ! ARP, for PNC-enabled quilting, 02/06/2010 TYPE varpatch LOGICAL :: forDeletion ! TRUE if patch to be ! deleted INTEGER, DIMENSION(3) :: PatchStart, PatchEnd, PatchExtent REAL, POINTER, DIMENSION(:,:,:) :: rptr INTEGER, POINTER, DIMENSION(:,:,:) :: iptr END TYPE varpatch ! With PNC-enabled quilting, each table entry consists of a series of ! 'npatch' patches (one for each of the compute PEs that this IOServer has ! as clients). We attempt to stitch these together before finally ! writing the data to disk. TYPE outpatchlist CHARACTER*80 :: VarName, DateStr, MemoryOrder, & Stagger, DimNames(3) INTEGER, DIMENSION(3) :: DomainStart, DomainEnd INTEGER :: FieldType ! Total no. of patches in the list PatchList INTEGER :: nPatch ! How many of the patches remain active in PatchList INTEGER :: nActivePatch TYPE(varpatch), ALLOCATABLE, DIMENSION(:) :: PatchList ! TYPE(varpatch), DIMENSION(tabsize) :: PatchList END TYPE outpatchlist TYPE(outpatchlist), DIMENSION(tabsize), SAVE :: outpatch_table ! List of which of the initial set of patches saved by the IOServer have ! been successfully stitched together. Without any stitching, each patch's ! entry contains just itself: ! JoinedPatches(1,ipatch) = ipatch ! If jpatch is then stitched to ipatch then we do: ! JoinedPatches(2,ipatch) = jpatch ! and so on. INTEGER, ALLOCATABLE, DIMENSION(:,:), SAVE :: JoinedPatches ! The no. of original patches to be stitched together to make each new patch ! i.e. if the 2nd new patch consists of 4 of the original patches stitched ! together then: ! PatchCount(2) = 4 INTEGER, ALLOCATABLE, DIMENSION(:), SAVE :: PatchCount ! endARP, for PNC-enabled quilting, 02/06/2010 TYPE outrec CHARACTER*80 :: VarName, DateStr, MemoryOrder, & Stagger, DimNames(3) INTEGER :: ndim INTEGER, DIMENSION(3) :: DomainStart, DomainEnd INTEGER :: FieldType REAL, POINTER, DIMENSION(:,:,:) :: rptr INTEGER, POINTER, DIMENSION(:,:,:) :: iptr END TYPE outrec TYPE(outrec), DIMENSION(tabsize) :: outbuf_table CONTAINS SUBROUTINE init_outbuf 2 !<DESCRIPTION> !<PRE> ! This routine re-initializes module data structures. !</PRE> !</DESCRIPTION> IMPLICIT NONE INTEGER :: i, j DO i = 1, tabsize #ifdef PNETCDF_QUILT ! This section for PNC-enabled IO quilting outpatch_table(i)%VarName = "" outpatch_table(i)%DateStr = "" outpatch_table(i)%MemoryOrder = "" outpatch_table(i)%Stagger = "" outpatch_table(i)%DimNames(1:3) = "" outpatch_table(i)%DomainStart(1:3) = 0 outpatch_table(i)%DomainEnd(1:3) = 0 ! We don't free any memory here - that is done immediately after the ! write of each patch is completed DO j = 1, outpatch_table(i)%npatch outpatch_table(i)%PatchList(j)%forDeletion = .FALSE. outpatch_table(i)%PatchList(j)%PatchStart(:) = 0 outpatch_table(i)%PatchList(j)%PatchEnd(:) = 0 outpatch_table(i)%PatchList(j)%PatchExtent(:)= 0 IF (ALLOCATED(outpatch_table(i)%PatchList)) THEN IF (ASSOCIATED(outpatch_table(i)%PatchList(j)%rptr)) & NULLIFY( outpatch_table(i)%PatchList(j)%rptr ) IF (ASSOCIATED(outpatch_table(i)%PatchList(j)%iptr)) & NULLIFY( outpatch_table(i)%PatchList(j)%iptr ) DEALLOCATE(outpatch_table(i)%PatchList) ENDIF END DO outpatch_table(i)%npatch = 0 outpatch_table(i)%nActivePatch = 0 #else outbuf_table(i)%VarName = "" outbuf_table(i)%DateStr = "" outbuf_table(i)%MemoryOrder = "" outbuf_table(i)%Stagger = "" outbuf_table(i)%DimNames(1) = "" outbuf_table(i)%DimNames(2) = "" outbuf_table(i)%DimNames(3) = "" outbuf_table(i)%ndim = 0 NULLIFY( outbuf_table(i)%rptr ) NULLIFY( outbuf_table(i)%iptr ) #endif ENDDO !write(0,*)'initializing num_entries to 0 ' num_entries = 0 END SUBROUTINE init_outbuf #ifdef PNETCDF_QUILT SUBROUTINE write_outbuf_pnc ( DataHandle, io_form_arg, local_comm, & 1,7 mytask, ntasks ) !<DESCRIPTION> !<PRE> ! This routine writes all of the records stored in outpatch_table to the ! file referenced by DataHandle using pNetCDF. The patches constituting ! each record are stitched together as far as is possible before ! the pNetCDF I/O routines are called to accomplish the write. ! ! It then re-initializes module data structures. !</PRE> !</DESCRIPTION> USE module_state_description IMPLICIT NONE INCLUDE 'mpif.h' #include "wrf_io_flags.h" INTEGER , INTENT(IN) :: DataHandle, io_form_arg, & local_comm, mytask, ntasks INTEGER :: ii, jj INTEGER :: DomainDesc ! dummy INTEGER :: Status INTEGER :: ipatch, icnt INTEGER, ALLOCATABLE, DIMENSION(:) :: count_buf INTEGER :: min_count LOGICAL :: do_indep_write ! If no. of patches differs between ! IO Servers then we will have to ! switch pnetcdf into ! independent-writes mode for some ! of them CHARACTER*256 :: mess DomainDesc = 0 ALLOCATE(count_buf(ntasks), Stat=Status) IF(Status /= 0)THEN CALL wrf_error_fatal("write_outbuf_pnc: allocate failed") END IF WRITE(mess,"('write_outbuf_pnc: table has ', I3,' entries')") num_entries CALL wrf_message(mess) DO ii = 1, num_entries WRITE(mess,*)'write_outbuf_pnc: writing ', & TRIM(outpatch_table(ii)%DateStr)," ", & TRIM(outpatch_table(ii)%VarName)," ", & TRIM(outpatch_table(ii)%MemoryOrder) CALL wrf_message(mess) SELECT CASE ( io_form_arg ) CASE ( IO_PNETCDF ) ! Situation is more complicated in this case since field data stored ! as a list of patches rather than in one array of global-domain ! extent. ! PatchStart(1) - PatchEnd(1) is dimension with unit stride. ! Quilt patches back together where possible in order to minimise ! number of individual writes CALL stitch_outbuf_patches(ii) ! Check how many patches each of the other IO servers has - we can ! only use pNetCDF in collective mode for the same no. of writes ! on each IO server. Any other patches will have to be written in ! independent mode. do_indep_write = .FALSE. count_buf(:) = 0 min_count = outpatch_table(ii)%nActivePatch CALL MPI_AllGather(min_count, 1, MPI_INTEGER, & count_buf, 1, MPI_INTEGER, & local_comm, Status) ! Work out the minimum no. of patches on any IO Server and whether ! or not we will have to enter independent IO mode. min_count = outpatch_table(ii)%nActivePatch DO jj=1,ntasks, 1 IF(count_buf(jj) < min_count) min_count = count_buf(jj) IF(outpatch_table(ii)%nActivePatch /= count_buf(jj)) do_indep_write = .TRUE. END DO ! WRITE(mess,*) 'ARPDBG: Min. no. of patches is ', min_count ! CALL wrf_message(mess) ! WRITE(mess,*) 'ARPDBG: I have ',count_buf(mytask+1),' patches.' ! CALL wrf_message(mess) IF ( outpatch_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN ! Loop over the patches in this field up to the number that ! every IO Server has. This is slightly tricky now ! that some of them may be 'deleted.' ipatch = 0 icnt = 0 DO WHILE ( icnt < min_count ) ipatch = ipatch + 1 IF(outpatch_table(ii)%PatchList(ipatch)%forDeletion) CYCLE icnt = icnt + 1 WRITE (mess, "('Calling write for patch: ',I3, ' Start = ',3I4)") ipatch, outpatch_table(ii)%PatchList(ipatch)%PatchStart(1:3) CALL wrf_message(mess) WRITE (mess,"(29x,'End = ',3I4)") outpatch_table(ii)%PatchList(ipatch)%PatchEnd(1:3) CALL wrf_message(mess) CALL ext_pnc_write_field ( DataHandle , & TRIM(outpatch_table(ii)%DateStr), & TRIM(outpatch_table(ii)%VarName), & outpatch_table(ii)%PatchList(ipatch)%rptr, & outpatch_table(ii)%FieldType, &!* local_comm, local_comm, DomainDesc , & TRIM(outpatch_table(ii)%MemoryOrder), & TRIM(outpatch_table(ii)%Stagger), &!* outpatch_table(ii)%DimNames , &!* outpatch_table(ii)%DomainStart, & outpatch_table(ii)%DomainEnd, & ! ARP supply magic number as MemoryStart and ! MemoryEnd to signal that this routine is ! being called from quilting. -998899, & -998899, & outpatch_table(ii)%PatchList(ipatch)%PatchStart,& outpatch_table(ii)%PatchList(ipatch)%PatchEnd, & Status ) ! Free memory associated with this patch DEALLOCATE(outpatch_table(ii)%PatchList(ipatch)%rptr) END DO IF( do_indep_write )THEN ! We must do the next few patches (if any) in independent IO ! mode as not all of the IO Servers have the same no. of ! patches. ! outpatch_table(ii)%nActivePatch holds the no. of live patches ! for this IO Server CALL ext_pnc_start_independent_mode(DataHandle, Status) DO WHILE ( icnt<outpatch_table(ii)%nActivePatch ) ipatch = ipatch + 1 IF(outpatch_table(ii)%PatchList(ipatch)%forDeletion) CYCLE icnt = icnt + 1 CALL ext_pnc_write_field ( DataHandle , & TRIM(outpatch_table(ii)%DateStr), & TRIM(outpatch_table(ii)%VarName), & outpatch_table(ii)%PatchList(ipatch)%rptr, & outpatch_table(ii)%FieldType, &!* local_comm, local_comm, DomainDesc , & TRIM(outpatch_table(ii)%MemoryOrder), & TRIM(outpatch_table(ii)%Stagger), &!* outpatch_table(ii)%DimNames , &!* outpatch_table(ii)%DomainStart, & outpatch_table(ii)%DomainEnd, & ! ARP supply magic number as MemoryStart and ! MemoryEnd to signal that this routine is ! being called from quilting. -998899, & -998899, & outpatch_table(ii)%PatchList(ipatch)%PatchStart,& outpatch_table(ii)%PatchList(ipatch)%PatchEnd, & Status ) ! Free memory associated with this patch DEALLOCATE(outpatch_table(ii)%PatchList(ipatch)%rptr) END DO ! End of patches that not every IO Server has so can switch ! back to collective mode. CALL ext_pnc_end_independent_mode(DataHandle, Status) END IF ! Additional patches ELSE IF ( outpatch_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN ! Loop over the patches in this field up to the number that ! every IO Server has. This is slightly tricky now ! that some of them may be 'deleted.' ipatch = 0 icnt = 0 DO WHILE ( icnt < min_count ) ipatch = ipatch + 1 IF(outpatch_table(ii)%PatchList(ipatch)%forDeletion) CYCLE icnt = icnt + 1 CALL ext_pnc_write_field ( DataHandle , & TRIM(outpatch_table(ii)%DateStr), & TRIM(outpatch_table(ii)%VarName), & outpatch_table(ii)%PatchList(ipatch)%iptr, & outpatch_table(ii)%FieldType, &!* local_comm, local_comm, DomainDesc, & TRIM(outpatch_table(ii)%MemoryOrder), & TRIM(outpatch_table(ii)%Stagger), &!* outpatch_table(ii)%DimNames , &!* outpatch_table(ii)%DomainStart, & outpatch_table(ii)%DomainEnd, & ! ARP supply magic number as MemoryStart and ! MemoryEnd to signal that this routine is ! being called from quilting. -998899, & -998899, & outpatch_table(ii)%PatchList(ipatch)%PatchStart, & outpatch_table(ii)%PatchList(ipatch)%PatchEnd, & Status ) ! Free memory associated with this patch DEALLOCATE(outpatch_table(ii)%PatchList(ipatch)%iptr) END DO IF( do_indep_write )THEN ! We have to do the next few patches in independent IO mode as ! not all of the IO Servers have this many patches. ! outpatch_table(ii)%npatch holds the no. of live patches for ! this IO Server CALL ext_pnc_start_independent_mode(DataHandle, Status) DO WHILE ( icnt<outpatch_table(ii)%nActivePatch ) ipatch = ipatch + 1 IF(outpatch_table(ii)%PatchList(ipatch)%forDeletion) CYCLE icnt = icnt + 1 CALL ext_pnc_write_field ( DataHandle , & TRIM(outpatch_table(ii)%DateStr), & TRIM(outpatch_table(ii)%VarName), & outpatch_table(ii)%PatchList(ipatch)%iptr, & outpatch_table(ii)%FieldType, &!* local_comm, local_comm, DomainDesc , & TRIM(outpatch_table(ii)%MemoryOrder), & TRIM(outpatch_table(ii)%Stagger), &!* outpatch_table(ii)%DimNames , &!* outpatch_table(ii)%DomainStart, & outpatch_table(ii)%DomainEnd, & ! ARP supply magic number as MemoryStart and ! MemoryEnd to signal that this routine is ! being called from quilting. -998899, & -998899, & outpatch_table(ii)%PatchList(ipatch)%PatchStart,& outpatch_table(ii)%PatchList(ipatch)%PatchEnd, & Status ) ! Free memory associated with this patch DEALLOCATE(outpatch_table(ii)%PatchList(ipatch)%iptr) END DO ! End of patches that not every IO Server has so can switch ! back to collective mode. CALL ext_pnc_end_independent_mode(DataHandle, Status) ENDIF ! Have additional patches ENDIF CASE DEFAULT END SELECT ENDDO ! Loop over output buffers ! Reset the table of output buffers CALL init_outbuf() DEALLOCATE(count_buf) END SUBROUTINE write_outbuf_pnc #endif SUBROUTINE write_outbuf ( DataHandle , io_form_arg ) 1,7 !<DESCRIPTION> !<PRE> ! This routine writes all of the records stored in outbuf_table to the ! file referenced by DataHandle using format specified by io_form_arg. ! This routine calls the package-specific I/O routines to accomplish ! the write. ! It then re-initializes module data structures. !</PRE> !</DESCRIPTION> USE module_state_description IMPLICIT NONE #include "wrf_io_flags.h" INTEGER , INTENT(IN) :: DataHandle, io_form_arg INTEGER :: ii,ds1,de1,ds2,de2,ds3,de3 INTEGER :: Comm, IOComm, DomainDesc ! dummy INTEGER :: Status CHARACTER*256 :: mess Comm = 0 ; IOComm = 0 ; DomainDesc = 0 DO ii = 1, num_entries WRITE(mess,*)'writing ', & TRIM(outbuf_table(ii)%DateStr)," ", & TRIM(outbuf_table(ii)%VarName)," ", & TRIM(outbuf_table(ii)%MemoryOrder) ds1 = outbuf_table(ii)%DomainStart(1) ; de1 = outbuf_table(ii)%DomainEnd(1) ds2 = outbuf_table(ii)%DomainStart(2) ; de2 = outbuf_table(ii)%DomainEnd(2) ds3 = outbuf_table(ii)%DomainStart(3) ; de3 = outbuf_table(ii)%DomainEnd(3) SELECT CASE ( io_form_arg ) #ifdef NETCDF CASE ( IO_NETCDF ) IF ( outbuf_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN CALL ext_ncd_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%rptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ELSE IF ( outbuf_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN CALL ext_ncd_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%iptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ENDIF #endif #ifdef YYY CASE ( IO_YYY ) IF ( outbuf_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN CALL ext_yyy_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%rptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ELSE IF ( outbuf_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN CALL ext_yyy_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%iptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ENDIF #endif #ifdef GRIB1 CASE ( IO_GRIB1 ) IF ( outbuf_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN CALL ext_gr1_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%rptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ELSE IF ( outbuf_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN CALL ext_gr1_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%iptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ENDIF #endif #ifdef GRIB2 CASE ( IO_GRIB2 ) IF ( outbuf_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN CALL ext_gr2_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%rptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ELSE IF ( outbuf_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN CALL ext_gr2_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%iptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ENDIF #endif #ifdef INTIO CASE ( IO_INTIO ) IF ( outbuf_table(ii)%FieldType .EQ. WRF_FLOAT ) THEN CALL ext_int_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%rptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ELSE IF ( outbuf_table(ii)%FieldType .EQ. WRF_INTEGER ) THEN CALL ext_int_write_field ( DataHandle , & TRIM(outbuf_table(ii)%DateStr), & TRIM(outbuf_table(ii)%VarName), & outbuf_table(ii)%iptr(ds1:de1,ds2:de2,ds3:de3), & outbuf_table(ii)%FieldType, & !* Comm, IOComm, DomainDesc , & TRIM(outbuf_table(ii)%MemoryOrder), & TRIM(outbuf_table(ii)%Stagger), & !* outbuf_table(ii)%DimNames , & !* outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & outbuf_table(ii)%DomainStart, & outbuf_table(ii)%DomainEnd, & Status ) ENDIF #endif CASE DEFAULT END SELECT IF ( ASSOCIATED( outbuf_table(ii)%rptr) ) DEALLOCATE(outbuf_table(ii)%rptr) IF ( ASSOCIATED( outbuf_table(ii)%iptr) ) DEALLOCATE(outbuf_table(ii)%iptr) NULLIFY( outbuf_table(ii)%rptr ) NULLIFY( outbuf_table(ii)%iptr ) ENDDO CALL init_outbuf END SUBROUTINE write_outbuf SUBROUTINE stitch_outbuf_patches(ibuf) 1,11 USE module_timing IMPLICIT none INTEGER, INTENT(in) :: ibuf !<DESCRIPTION> !<PRE> ! This routine does the "output quilting" for the case where quilting has been ! built to use Parallel NetCDF. Unlike store_patch_in_outbuf() we do not have ! data for the whole domain --- instead we aim to quilt as much of the data as ! possible in order to reduce the number of separate writes that we must do. !</PRE> !</DESCRIPTION> #include "wrf_io_flags.h" INTEGER :: ipatch, jpatch, ii INTEGER :: ierr INTEGER :: npatches INTEGER, DIMENSION(3) :: newExtent, pos INTEGER, ALLOCATABLE, DIMENSION(:,:) :: OldPatchStart INTEGER, POINTER, DIMENSION(:,:,:) :: ibuffer REAL, POINTER, DIMENSION(:,:,:) :: rbuffer CHARACTER*256 :: mess integer i,j ! CALL start_timing() IF(LEN_TRIM(outpatch_table(ibuf)%MemoryOrder) < 2)THEN ! This field is a scalar or 1D array. Such quantities are replicated ! across compute nodes and therefore we need only keep a single ! patch - delete all but the first in the list IF ( outpatch_table(ibuf)%FieldType .EQ. WRF_FLOAT ) THEN DO jpatch=2,outpatch_table(ibuf)%npatch,1 outpatch_table(ibuf)%PatchList(jpatch)%forDeletion = .TRUE. outpatch_table(ibuf)%nActivePatch = & outpatch_table(ibuf)%nActivePatch - 1 DEALLOCATE(outpatch_table(ibuf)%PatchList(jpatch)%rptr) END DO ELSE IF ( outpatch_table(ibuf)%FieldType .EQ. WRF_INTEGER ) THEN DO jpatch=2,outpatch_table(ibuf)%npatch,1 outpatch_table(ibuf)%PatchList(jpatch)%forDeletion = .TRUE. outpatch_table(ibuf)%nActivePatch = & outpatch_table(ibuf)%nActivePatch - 1 DEALLOCATE(outpatch_table(ibuf)%PatchList(jpatch)%iptr) END DO ELSE CALL wrf_error_fatal("stitch_outbuf_patches: unrecognised Field Type") END IF ! CALL end_timing("stitch_outbuf_patches: deleting replicated patches") RETURN END IF ! Field is scalar or 1D ! Otherwise, this field _is_ distributed across compute PEs and therefore ! it's worth trying to stitch patches together... ALLOCATE(OldPatchStart(3,outpatch_table(ibuf)%npatch), & JoinedPatches(outpatch_table(ibuf)%npatch, & outpatch_table(ibuf)%npatch), & PatchCount(outpatch_table(ibuf)%npatch), & Stat=ierr) IF(ierr /= 0)THEN CALL wrf_message('stitch_outbuf_patches: unable to stitch patches as allocate failed.') RETURN END IF JoinedPatches(:,:) = -1 ! Initialise these arrays to catch failures in the above allocate on ! linux-based systems (e.g. Cray XE) where allocation only actually ! performed when requested memory is touched. PatchCount(:) = 0 OldPatchStart(:,:) = 0 NULLIFY(ibuffer) NULLIFY(rbuffer) DO jpatch=1,outpatch_table(ibuf)%npatch,1 ! Each patch consists of just itself initially JoinedPatches(1,jpatch) = jpatch PatchCount(jpatch) = 1 ! Store the location of each patch for use after we've decided how to ! stitch them together OldPatchStart(:,jpatch) = outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(:) END DO ! Search through patches to find pairs that we can stitch together ipatch = 1 OUTER: DO WHILE(ipatch < outpatch_table(ibuf)%npatch) IF( outpatch_table(ibuf)%PatchList(ipatch)%forDeletion )THEN ipatch = ipatch + 1 CYCLE OUTER END IF INNER: DO jpatch=ipatch+1,outpatch_table(ibuf)%npatch,1 IF(outpatch_table(ibuf)%PatchList(jpatch)%forDeletion )THEN CYCLE INNER END IF ! Look for patches that can be concatenated with ipatch in the first ! dimension (preferred since that is contiguous in memory in F90) ! ________________ ____________ ! | | | | ! Startx(j) Endx(j) Startx(i) Endx(i) ! IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) - 1) )THEN ! Patches contiguous in first dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3)) )THEN ! We can concatenate these two patches in first dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 1')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF ! ______________ ____________ ! | | | | ! Startx(i) Endx(i) Startx(j) Endx(j) ! IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) + 1))THEN ! Patches contiguous in first dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3)) )THEN ! We can concatenate these two patches in first dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 1')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF ! Try the second dimension IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) - 1))THEN ! Patches contiguous in second dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3)) )THEN ! We can concatenate these two patches in second dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 2')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2) + 1) )THEN ! Patches contiguous in second dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3)) )THEN ! We can concatenate these two patches in second dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 2')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF ! Try the third dimension IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) - 1) )THEN ! Patches contiguous in second dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2)) )THEN ! We can concatenate these two patches in the third dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 3')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(3) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF IF(outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3) == & (outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3) + 1))THEN ! Patches contiguous in second dimension - do they have the same ! extents in the other two dimensions? IF( (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(1) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2)== & outpatch_table(ibuf)%PatchList(ipatch)%PatchStart(2) ) .AND.& (outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2) == & outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(2)) )THEN ! We can concatenate these two patches in the third dimension ! WRITE(mess,"('Can concatenate patches ',I3,' and ',I3,' in dim 3')") ipatch, jpatch ! CALL wrf_message(mess) ! Grow patch ipatch to include jpatch outpatch_table(ibuf)%PatchList(ipatch)%PatchEnd(3) = & outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) CALL merge_patches(ibuf, ipatch, jpatch) ! Go again... ipatch = 1 CYCLE OUTER END IF END IF END DO INNER ipatch = ipatch + 1 END DO OUTER npatches = 0 DO jpatch=1,outpatch_table(ibuf)%npatch,1 IF ( outpatch_table(ibuf)%PatchList(jpatch)%forDeletion ) CYCLE ! WRITE(mess,"('Patch ',I3,': [',I3,': ',I3,'],[',I3,':',I3,'],[',I3,':',I3,']')") jpatch, outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(1), & ! outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(1), & ! outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(2), & ! outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(2), & ! outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(3), & ! outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(3) ! CALL wrf_message(mess) ! Count how many patches we're left with npatches = npatches + 1 ! If no patches have been merged together to make this patch then we ! don't have to do any more with it IF(PatchCount(jpatch) == 1) CYCLE ! Get the extent of this patch newExtent(:) = outpatch_table(ibuf)%PatchList(jpatch)%PatchEnd(:) - & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(:) + 1 ! Allocate a buffer to hold all of its data IF ( outpatch_table(ibuf)%FieldType .EQ. WRF_FLOAT ) THEN ALLOCATE(rbuffer(newExtent(1), newExtent(2), newExtent(3)), & Stat=ierr) ELSE IF ( outpatch_table(ibuf)%FieldType .EQ. WRF_INTEGER ) THEN ALLOCATE(ibuffer(newExtent(1), newExtent(2), newExtent(3)), & Stat=ierr) END IF IF(ierr /= 0)THEN CALL wrf_error_fatal('stitch_outbuf_patches: unable to stitch patches as allocate for merge buffer failed.') RETURN END IF ! Copy data into this buffer from each of the patches that are being ! stitched together IF( ASSOCIATED(rbuffer) )THEN ! CALL start_timing() DO ipatch=1,PatchCount(jpatch),1 ii = JoinedPatches(ipatch, jpatch) ! Work out where to put it - the PatchList(i)%PatchStart() has been ! updated to hold the start of the newly quilted patch i. It will ! therefore be less than or equal to the starts of each of the ! constituent patches. pos(:) = OldPatchStart(:,ii) - & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(:) + 1 ! Do the copy - can use the PatchExtent data here because that ! wasn't modified during the stitching of the patches. rbuffer(pos(1): pos(1)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(1)-1, & pos(2): pos(2)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(2)-1, & pos(3): pos(3)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(3)-1 ) & = & outpatch_table(ibuf)%PatchList(ii)%rptr(:, :, :) ! Having copied the data from this patch, we can free-up the ! associated buffer DEALLOCATE(outpatch_table(ibuf)%PatchList(ii)%rptr) END DO ! CALL end_timing("Data copy into new real patch") ! Re-assign the pointer associated with this patch to the new, ! larger, buffer containing the quilted patches outpatch_table(ibuf)%PatchList(jpatch)%rptr => rbuffer ! Unset the original pointer to this buffer NULLIFY(rbuffer) ELSE IF( ASSOCIATED(ibuffer) )THEN ! CALL start_timing() DO ipatch=1,PatchCount(jpatch),1 ii = JoinedPatches(ipatch, jpatch) ! Work out where to put it pos(:) = OldPatchStart(:,ii) - & outpatch_table(ibuf)%PatchList(jpatch)%PatchStart(:) + 1 ! Do the copy - can use the PatchExtent data here because that ! wasn't modified during the stitching of the patches. ibuffer(pos(1): & pos(1)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(1)-1, & pos(2): & pos(2)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(2)-1, & pos(3): & pos(3)+outpatch_table(ibuf)%PatchList(ii)%PatchExtent(3)-1 ) = & outpatch_table(ibuf)%PatchList(ii)%iptr(:, :, :) DEALLOCATE(outpatch_table(ibuf)%PatchList(ii)%iptr) END DO ! CALL end_timing("Data copy into new integer patch") ! Re-assign the pointer associated with this patch to the new, ! larger, buffer containing the quilted patches outpatch_table(ibuf)%PatchList(jpatch)%iptr => ibuffer NULLIFY(ibuffer) END IF END DO WRITE(mess,*) "--------------------------" CALL wrf_message(mess) ! Record how many patches we're left with outpatch_table(ibuf)%nPatch = npatches DEALLOCATE(OldPatchStart, JoinedPatches, PatchCount) ! CALL end_timing("stitch patches") END SUBROUTINE stitch_outbuf_patches !------------------------------------------------------------------------- SUBROUTINE merge_patches(itab, ipatch, jpatch) 6 INTEGER, INTENT(in) :: itab, ipatch, jpatch ! Merge patch jpatch into patch ipatch and then 'delete' jpatch INTEGER :: ii ! Keep track of which patches we've merged: ipatch takes ! on all of the original patches which currently make up ! jpatch. DO ii=1,PatchCount(jpatch),1 PatchCount(ipatch) = PatchCount(ipatch) + 1 JoinedPatches(PatchCount(ipatch),ipatch) = JoinedPatches(ii,jpatch) END DO ! and mark patch jpatch for deletion outpatch_table(itab)%PatchList(jpatch)%forDeletion = .TRUE. ! decrement the count of active patches outpatch_table(itab)%nActivePatch = outpatch_table(itab)%nActivePatch - 1 END SUBROUTINE merge_patches END MODULE module_quilt_outbuf_ops ! don't let other programs see the definition of this; type mismatches ! on inbuf will result; may want to make a module program at some point SUBROUTINE store_patch_in_outbuf( inbuf_r, inbuf_i, DateStr, VarName , FieldType, MemoryOrder, Stagger, DimNames, & 2,4 DomainStart , DomainEnd , & MemoryStart , MemoryEnd , & PatchStart , PatchEnd ) !<DESCRIPTION> !<PRE> ! This routine does the "output quilting". ! ! It stores a patch in the appropriate location in a domain-sized array ! within an element of the outbuf_table data structure. DateStr, VarName, and ! MemoryOrder are used to uniquely identify which element of outbuf_table is ! associated with this array. If no element is associated, then this routine ! first assigns an unused element and allocates space within that element for ! the globally-sized array. This routine also stores DateStr, VarName, ! FieldType, MemoryOrder, Stagger, DimNames, DomainStart, and DomainEnd within ! the same element of outbuf. !</PRE> !</DESCRIPTION> USE module_quilt_outbuf_ops IMPLICIT NONE #include "wrf_io_flags.h" INTEGER , INTENT(IN) :: FieldType REAL , DIMENSION(*) , INTENT(IN) :: inbuf_r INTEGER , DIMENSION(*) , INTENT(IN) :: inbuf_i INTEGER , DIMENSION(3) , INTENT(IN) :: DomainStart , DomainEnd , MemoryStart , MemoryEnd , PatchStart , PatchEnd CHARACTER*(*) , INTENT(IN) :: DateStr , VarName, MemoryOrder , Stagger, DimNames(3) ! Local CHARACTER*256 :: mess INTEGER :: l,m,n,ii,jj LOGICAL :: found ! Find the VarName if it's in the buffer already ii = 1 found = .false. DO WHILE ( .NOT. found .AND. ii .LE. num_entries ) !TBH: need to test other attributes too! IF ( TRIM(VarName) .EQ. TRIM(outbuf_table(ii)%VarName) ) THEN IF ( TRIM(DateStr) .EQ. TRIM(outbuf_table(ii)%DateStr) ) THEN IF ( TRIM(MemoryOrder) .EQ. TRIM(outbuf_table(ii)%MemoryOrder) ) THEN found = .true. ELSE CALL wrf_error_fatal("store_patch_in_outbuf: memory order disagreement") ENDIF ELSE CALL wrf_error_fatal("store_patch_in_outbuf: multiple dates in buffer") ENDIF ELSE ii = ii + 1 ENDIF ENDDO IF ( .NOT. found ) THEN num_entries = num_entries + 1 IF ( FieldType .EQ. WRF_FLOAT ) THEN ALLOCATE( outbuf_table(num_entries)%rptr(DomainStart(1):DomainEnd(1), & DomainStart(2):DomainEnd(2),DomainStart(3):DomainEnd(3)) ) ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN ALLOCATE( outbuf_table(num_entries)%iptr(DomainStart(1):DomainEnd(1), & DomainStart(2):DomainEnd(2),DomainStart(3):DomainEnd(3)) ) ELSE write(mess,*)"store_patch_in_outbuf: unsupported type ", FieldType CALL wrf_error_fatal(mess) ENDIF outbuf_table(num_entries)%VarName = TRIM(VarName) outbuf_table(num_entries)%DateStr = TRIM(DateStr) outbuf_table(num_entries)%MemoryOrder = TRIM(MemoryOrder) outbuf_table(num_entries)%Stagger = TRIM(Stagger) outbuf_table(num_entries)%DimNames(1) = TRIM(DimNames(1)) outbuf_table(num_entries)%DimNames(2) = TRIM(DimNames(2)) outbuf_table(num_entries)%DimNames(3) = TRIM(DimNames(3)) outbuf_table(num_entries)%DomainStart = DomainStart outbuf_table(num_entries)%DomainEnd = DomainEnd outbuf_table(num_entries)%FieldType = FieldType ii = num_entries ENDIF jj = 1 IF ( FieldType .EQ. WRF_FLOAT ) THEN DO n = PatchStart(3),PatchEnd(3) DO m = PatchStart(2),PatchEnd(2) DO l = PatchStart(1),PatchEnd(1) outbuf_table(ii)%rptr(l,m,n) = inbuf_r(jj) jj = jj + 1 ENDDO ENDDO ENDDO ENDIF IF ( FieldType .EQ. WRF_INTEGER ) THEN DO n = PatchStart(3),PatchEnd(3) DO m = PatchStart(2),PatchEnd(2) DO l = PatchStart(1),PatchEnd(1) outbuf_table(ii)%iptr(l,m,n) = inbuf_i(jj) jj = jj + 1 ENDDO ENDDO ENDDO ENDIF RETURN END SUBROUTINE store_patch_in_outbuf ! don't let other programs see the definition of this; type mismatches ! on inbuf will result; may want to make a module program at some point SUBROUTINE store_patch_in_outbuf_pnc( inbuf_r, inbuf_i, DateStr, VarName , & 2,14 FieldType, MemoryOrder, Stagger, & DimNames , & DomainStart , DomainEnd , & MemoryStart , MemoryEnd , & PatchStart , PatchEnd , & ntasks ) !<DESCRIPTION> !<PRE> ! This routine stores a patch in an array within an element of the ! outpatch_table%PatchList data structure. DateStr, VarName, and ! MemoryOrder are used to uniquely identify which element of outpatch_table is ! associated with this array. If no element is associated, then this routine ! first assigns an unused element and allocates space within that element. ! This routine also stores DateStr, VarName, ! FieldType, MemoryOrder, Stagger, DimNames, DomainStart, and DomainEnd within ! the same element of outpatch. !</PRE> !</DESCRIPTION> USE module_quilt_outbuf_ops, Only: outpatch_table, tabsize, num_entries USE module_timing IMPLICIT NONE #include "wrf_io_flags.h" INTEGER , INTENT(IN) :: FieldType REAL , DIMENSION(*), INTENT(IN) :: inbuf_r INTEGER , DIMENSION(*), INTENT(IN) :: inbuf_i INTEGER , DIMENSION(3), INTENT(IN) :: DomainStart, DomainEnd, MemoryStart,& MemoryEnd , PatchStart , PatchEnd CHARACTER*(*) , INTENT(IN) :: DateStr , VarName, MemoryOrder , & Stagger, DimNames(3) INTEGER, INTENT(IN) :: ntasks ! Number of compute tasks associated with ! this IO server ! Local CHARACTER*256 :: mess INTEGER :: l,m,n,ii,jj,ipatch,ierr LOGICAL :: found ! CALL start_timing() ! Find the VarName if it's in the buffer already ii = 1 found = .false. DO WHILE ( .NOT. found .AND. ii .LE. num_entries ) !TBH: need to test other attributes too! IF ( TRIM(VarName) .EQ. TRIM(outpatch_table(ii)%VarName) ) THEN IF ( TRIM(DateStr) .EQ. TRIM(outpatch_table(ii)%DateStr) ) THEN IF ( TRIM(MemoryOrder) .EQ. TRIM(outpatch_table(ii)%MemoryOrder) ) THEN found = .true. ELSE CALL wrf_error_fatal("store_patch_in_outbuf_pnc: memory order disagreement") ENDIF ELSE CALL wrf_error_fatal("store_patch_in_outbuf_pnc: multiple dates in buffer") ENDIF ELSE ii = ii + 1 ENDIF ENDDO IF ( .NOT. found ) THEN num_entries = num_entries + 1 IF(num_entries > tabsize)THEN WRITE(mess,*) 'Number of entries in outpatch_table has exceeded tabsize (',tabsize,') in module_quilt_outbuf_ops::store_patch_in_outbuf_pnc' CALL wrf_error_fatal(mess) END IF outpatch_table(num_entries)%npatch = 0 outpatch_table(num_entries)%VarName = TRIM(VarName) outpatch_table(num_entries)%DateStr = TRIM(DateStr) outpatch_table(num_entries)%MemoryOrder = TRIM(MemoryOrder) outpatch_table(num_entries)%Stagger = TRIM(Stagger) outpatch_table(num_entries)%DimNames(1) = TRIM(DimNames(1)) outpatch_table(num_entries)%DimNames(2) = TRIM(DimNames(2)) outpatch_table(num_entries)%DimNames(3) = TRIM(DimNames(3)) outpatch_table(num_entries)%DomainStart = DomainStart outpatch_table(num_entries)%DomainEnd = DomainEnd outpatch_table(num_entries)%FieldType = FieldType ! Allocate the table for the list of patches for this output - it ! will have as many entries as there are compute tasks associated with ! this IO server. IF ( ALLOCATED(outpatch_table(num_entries)%PatchList) ) & DEALLOCATE(outpatch_table(num_entries)%PatchList) ALLOCATE(outpatch_table(num_entries)%PatchList(ntasks), Stat=ierr) IF(ierr /= 0)THEN WRITE(mess,*)'num_entries ',num_entries,' ntasks ',ntasks,' ierr ',ierr CALL wrf_message(mess) WRITE(mess,*)'Allocation for ',ntasks, & ' patches in store_patch_in_outbuf_pnc() failed.' CALL wrf_error_fatal( mess ) ENDIF ! Initialise the list of patches DO ii=1, ntasks, 1 outpatch_table(num_entries)%PatchList(ii)%forDeletion = .FALSE. NULLIFY(outpatch_table(num_entries)%PatchList(ii)%rptr) NULLIFY(outpatch_table(num_entries)%PatchList(ii)%iptr) outpatch_table(num_entries)%PatchList(ii)%PatchStart(:) = 0 outpatch_table(num_entries)%PatchList(ii)%PatchEnd(:) = 0 outpatch_table(num_entries)%PatchList(ii)%PatchExtent(:) = 0 END DO ii = num_entries WRITE(mess,*)'Adding field entry no. ',num_entries CALL wrf_message(mess) WRITE(mess,*)'Variable = ',TRIM(VarName) CALL wrf_message(mess) WRITE(mess,*)'Domain start = ',DomainStart(:) CALL wrf_message(mess) WRITE(mess,*)'Domain end = ',DomainEnd(:) CALL wrf_message(mess) ENDIF ! We only store > 1 patch if the field has two or more dimensions. Scalars ! and 1D arrays are replicated across compute nodes and therefore we only ! need keep a single patch. IF(LEN_TRIM(outpatch_table(ii)%MemoryOrder) >= 2 .OR. & outpatch_table(ii)%npatch < 1)THEN ! Add another patch outpatch_table(ii)%npatch = outpatch_table(ii)%npatch + 1 outpatch_table(ii)%nActivePatch = outpatch_table(ii)%npatch ipatch = outpatch_table(ii)%npatch outpatch_table(ii)%PatchList(ipatch)%PatchStart(:) = PatchStart(:) outpatch_table(ii)%PatchList(ipatch)%PatchEnd(:) = PatchEnd(:) outpatch_table(ii)%PatchList(ipatch)%PatchExtent(:)= PatchEnd(:) - PatchStart(:) + 1 ierr = 0 IF ( FieldType .EQ. WRF_FLOAT ) THEN ALLOCATE( outpatch_table(ii)%PatchList(ipatch)%rptr( & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(1), & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(2), & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(3)),& Stat=ierr) ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN ALLOCATE( outpatch_table(ii)%PatchList(ipatch)%iptr( & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(1), & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(2), & outpatch_table(ii)%PatchList(ipatch)%PatchExtent(3)),& Stat=ierr) ELSE WRITE(mess,*)"store_patch_in_outbuf_pnc: unsupported type ", FieldType CALL wrf_error_fatal(mess) ENDIF IF(ierr /= 0)THEN WRITE(mess,*)"store_patch_in_outbuf_pnc: failed to allocate memory to hold patch for var. ", TRIM(VarName) CALL wrf_error_fatal(mess) END IF jj = 1 WRITE(mess,"('Variable ',(A),', patch ',I3,': (',I3,':',I3,',',I3,':',I3,',',I3,':',I3,')')")& TRIM(outpatch_table(ii)%VarName), & ipatch, & PatchStart(1),PatchEnd(1), & PatchStart(2),PatchEnd(2), & PatchStart(3),PatchEnd(3) CALL wrf_message(mess) IF ( FieldType .EQ. WRF_FLOAT ) THEN DO n = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(3),1 DO m = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(2),1 DO l = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(1),1 outpatch_table(ii)%PatchList(ipatch)%rptr(l,m,n) = inbuf_r(jj) jj = jj + 1 ENDDO ENDDO ENDDO ENDIF IF ( FieldType .EQ. WRF_INTEGER ) THEN DO n = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(3),1 DO m = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(2),1 DO l = 1,outpatch_table(ii)%PatchList(ipatch)%PatchExtent(1),1 outpatch_table(ii)%PatchList(ipatch)%iptr(l,m,n) = inbuf_i(jj) jj = jj + 1 ENDDO ENDDO ENDDO ENDIF END IF ! We need to add another patch ! CALL end_timing("store patch in outbuf") RETURN END SUBROUTINE store_patch_in_outbuf_pnc !call add_to_bufsize_for_field( VarName, hdrbufsize+chunksize ) SUBROUTINE add_to_bufsize_for_field( VarName, Nbytes ) 10,1 !<DESCRIPTION> !<PRE> ! This routine is a wrapper for C routine add_to_bufsize_for_field_c() that ! is used to accumulate buffer sizes. Buffer size Nbytes is added to the ! curent buffer size for the buffer named VarName. Any buffer space ! associated with VarName is freed. If a buffer named VarName does not exist, ! a new one is assigned and its size is set to Nbytes. !</PRE> !</DESCRIPTION> USE module_quilt_outbuf_ops IMPLICIT NONE CHARACTER*(*) , INTENT(IN) :: VarName INTEGER , INTENT(IN) :: Nbytes ! Local CHARACTER*256 :: mess INTEGER :: i, ierr INTEGER :: VarNameAsInts( 256 ) VarNameAsInts( 1 ) = len(trim(VarName)) DO i = 2, len(trim(VarName)) + 1 VarNameAsInts( i ) = ICHAR( VarName(i-1:i-1) ) ENDDO CALL add_to_bufsize_for_field_c ( VarNameAsInts, Nbytes ) RETURN END SUBROUTINE add_to_bufsize_for_field SUBROUTINE store_piece_of_field( inbuf, VarName, Nbytes ) 10,2 !<DESCRIPTION> !<PRE> ! This routine is a wrapper for C routine store_piece_of_field_c() that ! is used to store pieces of a field in an internal buffer. Nbytes bytes of ! buffer inbuf are appended to the end of the internal buffer named VarName. ! An error occurs if either an internal buffer named VarName does not exist or ! if there are fewer than Nbytes bytes left in the internal buffer. !</PRE> !</DESCRIPTION> USE module_quilt_outbuf_ops IMPLICIT NONE INTEGER , INTENT(IN) :: Nbytes INTEGER , DIMENSION(*) , INTENT(IN) :: inbuf CHARACTER*(*) , INTENT(IN) :: VarName ! Local CHARACTER*256 :: mess INTEGER :: i, ierr INTEGER :: VarNameAsInts( 256 ) VarNameAsInts( 1 ) = len(trim(VarName)) DO i = 2, len(trim(VarName)) + 1 VarNameAsInts( i ) = ICHAR( VarName(i-1:i-1) ) ENDDO CALL store_piece_of_field_c ( inbuf, VarNameAsInts, Nbytes, ierr ) IF ( ierr .NE. 0 ) CALL wrf_error_fatal ( "store_piece_of_field" ) RETURN END SUBROUTINE store_piece_of_field SUBROUTINE retrieve_pieces_of_field( outbuf, VarName, obufsz, Nbytes_tot, lret ) 4,1 !<DESCRIPTION> !<PRE> ! This routine is a wrapper for C routine retrieve_pieces_of_field_c() that ! is used to extract the entire contents (i.e. all previously stored pieces of ! fields) of the next internal buffer. The name associated with this internal ! buffer is returned in VarName. The number of bytes read is returned in ! Nbytes_tot. Bytes are stored in outbuf whose size (in bytes) is obufsz. ! If there are more than obufsz bytes left in the next internal buffer, then ! only obufsz bytes are returned and the rest are discarded (probably an error ! in the making!). The internal buffer is then freed. Flag lret is set to ! .TRUE. iff there are more fields left to extract. !</PRE> !</DESCRIPTION> USE module_quilt_outbuf_ops IMPLICIT NONE INTEGER , INTENT(IN) :: obufsz INTEGER , INTENT(OUT) :: Nbytes_tot INTEGER , DIMENSION(*) , INTENT(OUT) :: outbuf CHARACTER*(*) , INTENT(OUT) :: VarName LOGICAL :: lret ! true if more, false if not ! Local CHARACTER*256 :: mess INTEGER :: i, iret INTEGER :: VarNameAsInts( 256 ) CALL retrieve_pieces_of_field_c ( outbuf, VarNameAsInts, obufsz, Nbytes_tot, iret ) IF ( iret .NE. 0 ) THEN lret = .FALSE. ELSE lret = .TRUE. VarName = ' ' DO i = 2, VarNameAsInts(1) + 1 VarName(i-1:i-1) = CHAR(VarNameAsInts( i )) ENDDO ENDIF RETURN END SUBROUTINE retrieve_pieces_of_field