!/*************************************************************************** !* The HDF5 WRF IO module was written by the the HDF Group at NCSA, the * !* National Center for Supercomputing Applications. * !* HDF Group * !* National Center for Supercomputing Applications * !* University of Illinois at Urbana-Champaign * !* 605 E. Springfield, Champaign IL 61820 * !* http://hdf.ncsa.uiuc.edu/ * !* * !* Copyright 2004 by the Board of Trustees, University of Illinois, * !* * !* Redistribution or use of this IO module, with or without modification, * !* is permitted for any purpose, including commercial purposes. * !* * !* This software is an unsupported prototype. Use at your own risk. * !* http://hdf.ncsa.uiuc.edu/apps/WRF-ROMS * !* * !* This work was funded by the MEAD expedition at the National Center * !* for Supercomputing Applications, NCSA. For more information see: * !* http://www.ncsa.uiuc.edu/expeditions/MEAD * !* * !* * !****************************************************************************/ module wrf_phdf5_data use HDF5 integer , parameter :: FATAL = 1 integer , parameter :: WARN = 1 integer , parameter :: WrfDataHandleMax = 99 integer , parameter :: MaxDims = 2000 ! = NF_MAX_VARS integer , parameter :: MaxTabDims = 100 ! temporary,changable integer , parameter :: MaxVars = 2000 integer , parameter :: MaxTimes = 9999 ! temporary, changable integer , parameter :: MaxTimeSLen = 6 ! not exceed 1,000,000 timestamp integer , parameter :: DateStrLen = 19 integer , parameter :: VarNameLen = 31 integer , parameter :: NO_DIM = 0 integer , parameter :: NVarDims = 4 integer , parameter :: NMDVarDims = 2 integer , parameter :: CompDsetSize = 64256 ! set to 63K character (8) , parameter :: NO_NAME = 'NULL' character(4) , parameter :: hdf5_true ='TRUE' character(5) , parameter :: hdf5_false ='FALSE' integer , parameter :: MemOrdLen = 3 character (DateStrLen) , parameter :: ZeroDate = '0000-00-00-00:00:00' #include "wrf_io_flags.h" ! This is a hack. WRF IOAPI no longer supports WRF_CHARACTER. Rip this out! integer, parameter :: WRF_CHARACTER = 1080 character (120) :: msg ! derived data type for dimensional table type :: dim_scale character (len = 256) :: dim_name integer :: length integer :: unlimited end type dim_scale type :: wrf_phdf5_data_handle character (256) :: FileName integer :: FileStatus integer :: Comm integer(hid_t) :: FileID integer(hid_t) :: GroupID integer(hid_t) :: DimGroupID integer(hid_t) :: EnumID character (256) :: GroupName character (256) :: DimGroupName logical :: Free logical :: Write character (5) :: TimesName integer :: TimeIndex integer :: MaxTimeCount integer :: CurrentTime !Only used for read integer :: NumberTimes !Only used for read character (DateStrLen), pointer :: Times(:) integer(hid_t) :: TimesID integer(hid_t) :: str_id integer , pointer :: DimLengths(:) integer , pointer :: DimIDs(:) character (31) , pointer :: DimNames(:) integer :: DimUnlimID character (9) :: DimUnlimName type (dim_scale) , pointer :: DIMTABLE(:) integer , dimension(NVarDims) :: DimID integer , dimension(NVarDims) :: Dimension ! integer , pointer :: MDDsetIDs(:) integer , pointer :: MDVarDimLens(:) character (256) , pointer :: MDVarNames(:) integer(hid_t) , pointer :: TgroupIDs(:) integer(hid_t) , pointer :: DsetIDs(:) integer(hid_t) , pointer :: MDDsetIDs(:) ! integer(hid_t) :: DimTableID integer , pointer :: VarDimLens(:,:) character (VarNameLen), pointer :: VarNames(:) integer :: CurrentVariable !Only used for read integer :: NumVars ! first_operation is set to .TRUE. when a new handle is allocated ! or when open-for-write or open-for-read are committed. It is set ! to .FALSE. when the first field is read or written. logical :: first_operation end type wrf_phdf5_data_handle type(wrf_phdf5_data_handle),target :: WrfDataHandles(WrfDataHandleMax) end module wrf_phdf5_data module ext_phdf5_support_routines implicit none CONTAINS subroutine allocHandle(DataHandle,DH,Comm,Status) use wrf_phdf5_data use HDF5 include 'wrf_status_codes.h' integer ,intent(out) :: DataHandle type(wrf_phdf5_data_handle),pointer:: DH integer ,intent(IN) :: Comm integer ,intent(out) :: Status integer :: i integer :: j integer :: stat integer(hid_t) :: enum_type ! character (256) :: NullName ! NullName = char(0) do i=1,WrfDataHandleMax if(WrfDataHandles(i)%Free) then DH => WrfDataHandles(i) DataHandle = i DH%MaxTimeCount = 1 DH%FileID = -1 DH%GroupID = -1 DH%DimGroupID = -1 call SetUp_EnumID(enum_type,Status) if(Status /= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal enum ALLOCATION ERROR in ',__FILE__,', line',__LINE__ call wrf_debug ( FATAL , msg) return endif DH%EnumID = enum_type allocate(DH%Times(MaxTimes), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',__FILE__,', line',__LINE__ call wrf_debug ( FATAL , msg) return endif ! wait in the future ! DH%Times(1:MaxTimes) = NullName allocate(DH%DimLengths(MaxDims), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line',__LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%DimIDs(MaxDims), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%DimNames(MaxDims), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%DIMTABLE(MaxTabDims), STAT = stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif do j =1,MaxTabDims DH%DIMTABLE(j)%dim_name = NO_NAME DH%DIMTABLE(j)%unlimited = -1 enddo allocate(DH%MDDsetIDs(MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%MDVarDimLens(MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%MDVarNames(MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%DsetIDs(MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif DH%DsetIDs = -1 allocate(DH%TgroupIDs(MaxTimes), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif DH%TgroupIDs = -1 allocate(DH%VarDimLens(NVarDims-1,MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif allocate(DH%VarNames(MaxVars), STAT=stat) if(stat/= 0) then Status = WRF_HDF5_ERR_ALLOCATION write(msg,*) 'Fatal ALLOCATION ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( FATAL , msg) return endif exit endif if(i==WrfDataHandleMax) then Status = WRF_HDF5_ERR_TOO_MANY_FILES write(msg,*) 'Warning TOO MANY FILES in ',"__FILE__",', line', __LINE__ call wrf_debug ( WARN , msg) return endif enddo DH%Free =.false. DH%Comm = Comm DH%Write =.false. DH%first_operation = .TRUE. Status = WRF_NO_ERR end subroutine allocHandle ! Obtain data handler subroutine GetDH(DataHandle,DH,Status) use wrf_phdf5_data include 'wrf_status_codes.h' integer ,intent(in) :: DataHandle type(wrf_phdf5_data_handle) ,pointer :: DH integer ,intent(out) :: Status if(DataHandle < 1 .or. DataHandle > WrfDataHandleMax) then Status = WRF_HDF5_ERR_BAD_DATA_HANDLE return endif DH => WrfDataHandles(DataHandle) if(DH%Free) then Status = WRF_HDF5_ERR_BAD_DATA_HANDLE return endif Status = WRF_NO_ERR return end subroutine GetDH ! Set up eumerate datatype for possible logical type subroutine SetUp_EnumID(enum_type,Status) use wrf_phdf5_data use HDF5 implicit none include 'wrf_status_codes.h' integer(hid_t) ,intent(out) :: enum_type integer ,intent(out) :: Status integer :: hdf5err integer, dimension(2) :: data data(1) = 1 data(2) = 0 call h5tenum_create_f(H5T_NATIVE_INTEGER,enum_type,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call h5tenum_insert_f(enum_type,hdf5_true,data(1),hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call h5tenum_insert_f(enum_type,hdf5_false,data(2),Status) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif Status = WRF_NO_ERR return end subroutine SetUp_EnumID ! Returns .TRUE. iff it is OK to write time-independent domain metadata to the ! file referenced by DataHandle. If DataHandle is invalid, .FALSE. is ! returned. LOGICAL FUNCTION phdf5_ok_to_put_dom_ti( DataHandle ) use wrf_phdf5_data include 'wrf_status_codes.h' INTEGER, INTENT(IN) :: DataHandle CHARACTER*80 :: fname INTEGER :: filestate INTEGER :: Status LOGICAL :: dryrun, first_output, retval call ext_phdf5_inquire_filename( DataHandle, fname, filestate, Status ) IF ( Status /= WRF_NO_ERR ) THEN write(msg,*) 'Warning Status = ',Status,' in ',__FILE__, & ', line', __LINE__ call wrf_debug ( WARN , TRIM(msg) ) retval = .FALSE. ELSE dryrun = ( filestate .EQ. WRF_FILE_OPENED_NOT_COMMITTED ) first_output = phdf5_is_first_operation( DataHandle ) retval = .NOT. dryrun .AND. first_output ENDIF phdf5_ok_to_put_dom_ti = retval RETURN END FUNCTION phdf5_ok_to_put_dom_ti ! Returns .TRUE. iff it is OK to read time-independent domain metadata from the ! file referenced by DataHandle. If DataHandle is invalid, .FALSE. is ! returned. LOGICAL FUNCTION phdf5_ok_to_get_dom_ti( DataHandle ) use wrf_phdf5_data include 'wrf_status_codes.h' INTEGER, INTENT(IN) :: DataHandle CHARACTER*80 :: fname INTEGER :: filestate INTEGER :: Status LOGICAL :: dryrun, retval call ext_phdf5_inquire_filename( DataHandle, fname, filestate, Status ) IF ( Status /= WRF_NO_ERR ) THEN write(msg,*) 'Warning Status = ',Status,' in ',__FILE__, & ', line', __LINE__ call wrf_debug ( WARN , TRIM(msg) ) retval = .FALSE. ELSE dryrun = ( filestate .EQ. WRF_FILE_OPENED_NOT_COMMITTED ) retval = .NOT. dryrun ENDIF phdf5_ok_to_get_dom_ti = retval RETURN END FUNCTION phdf5_ok_to_get_dom_ti ! Returns .TRUE. iff nothing has been read from or written to the file ! referenced by DataHandle. If DataHandle is invalid, .FALSE. is returned. LOGICAL FUNCTION phdf5_is_first_operation( DataHandle ) use wrf_phdf5_data INCLUDE 'wrf_status_codes.h' INTEGER, INTENT(IN) :: DataHandle TYPE(wrf_phdf5_data_handle) ,POINTER :: DH INTEGER :: Status LOGICAL :: retval CALL GetDH( DataHandle, DH, Status ) IF ( Status /= WRF_NO_ERR ) THEN write(msg,*) 'Warning Status = ',Status,' in ',__FILE__, & ', line', __LINE__ call wrf_debug ( WARN , TRIM(msg) ) retval = .FALSE. ELSE retval = DH%first_operation ENDIF phdf5_is_first_operation = retval RETURN END FUNCTION phdf5_is_first_operation end module ext_phdf5_support_routines !module wrf_phdf5_opt_data ! integer ,parameter :: MaxOptVars = 100 !end module wrf_phdf5_opt_data !module opt_data_module !use wrf_phdf5_opt_data ! type :: field ! logical :: Free ! integer,pointer :: darrays(:) ! integer :: index ! end type field ! type(field),target :: fieldhandle(MaxOptVars) !end module opt_data_module !module opt_support_module ! implicit none !contains ! subroutine alloc_opt_handle(ODH) ! use opt_data_module ! type(field),pointer::DH ! integer :: i ! do i =1,MaxOptVars ! DH=>fieldhandle(i) ! DH%index = 0 ! enddo !end module opt_support_module ! check the date, only use the length subroutine DateCheck(Date,Status) use wrf_phdf5_data include 'wrf_status_codes.h' character*(*) ,intent(in) :: Date integer ,intent(out) :: Status if(len(Date) /= DateStrLen) then Status = WRF_HDF5_ERR_DATESTR_BAD_LENGTH else Status = WRF_NO_ERR endif return end subroutine DateCheck ! This routine is for meta-data time dependent varible attribute subroutine GetName(Element,Var,Name,Status) use wrf_phdf5_data include 'wrf_status_codes.h' character*(*) ,intent(in) :: Element character*(*) ,intent(in) :: Var character*(*) ,intent(out) :: Name integer ,intent(out) :: Status character (VarNameLen) :: VarName character (1) :: c integer :: i integer, parameter :: upper_to_lower =IACHAR('a')-IACHAR('A') VarName = Var Name = 'MD___'//trim(Element)//VarName do i=1,len(Name) c=Name(i:i) if('A'<=c .and. c <='Z') Name(i:i)=achar(iachar(c)+upper_to_lower) if(c=='-'.or.c==':') Name(i:i)='_' enddo Status = WRF_NO_ERR return end subroutine GetName ! Obtain TimeIndex subroutine GetDataTimeIndex(IO,DataHandle,DateStr,TimeIndex,Status) use HDF5 use wrf_phdf5_data use ext_phdf5_support_routines implicit none include 'wrf_status_codes.h' character (*) ,intent(in) :: IO integer ,intent(in) :: DataHandle character*(*) ,intent(in) :: DateStr character (DateStrLen), pointer :: TempTimes(:) integer ,intent(out) :: TimeIndex integer ,intent(out) :: Status type(wrf_phdf5_data_handle) ,pointer :: DH integer :: VStart(2) integer :: VCount(2) integer :: stat integer :: i integer :: PreTimeCount integer :: rank integer(hsize_t), dimension(1) :: chunk_dims =(/1/) integer(hsize_t), dimension(1) :: dims integer(hsize_t), dimension(1) :: hdf5_maxdims integer(hsize_t), dimension(1) :: offset integer(hsize_t), dimension(1) :: count integer(hsize_t), dimension(1) :: sizes INTEGER(HID_T) :: dset_id ! Dataset ID INTEGER(HID_T) :: dspace_id ! Dataspace ID INTEGER(HID_T) :: fspace_id ! Dataspace ID INTEGER(HID_T) :: crp_list ! chunk ID integer(hid_t) :: str_id ! string ID integer :: hdf5err integer(hid_t) :: group_id character(Len = 512) :: groupname ! for debug character(len=100) :: buf integer(size_t) :: name_size integer(size_t) :: datelen_size ! suppose the output will not exceed 100,0000 timesteps. character(Len = MaxTimeSLen) :: tname ! DH => WrfDataHandles(DataHandle), don't know why NetCDF doesn't use GetDH call GetDH(DataHandle,DH,Status) if(Status /= WRF_NO_ERR) then write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call DateCheck(DateStr,Status) if(Status /= WRF_NO_ERR) then Status = WRF_HDF5_ERR_DATESTR_ERROR write(msg,*) 'Warning DATE STRING ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( WARN , msg) return endif if(IO == 'write') then TimeIndex = DH%TimeIndex if(TimeIndex <= 0) then TimeIndex = 1 elseif(DateStr < DH%Times(TimeIndex)) then Status = WRF_HDF5_ERR_DATE_LT_LAST_DATE write(msg,*) 'Warning DATE < LAST DATE in ',"__FILE__",', line', __LINE__ call wrf_debug ( WARN , msg) return elseif(DateStr == DH%Times(TimeIndex)) then Status = WRF_NO_ERR return else TimeIndex = TimeIndex + 1 ! If exceeding the maximum timestep, updating the maximum timestep if(TimeIndex > MaxTimes*(DH%MaxTimeCount)) then PreTimeCount = DH%MaxTimeCount allocate(TempTimes(PreTimeCount*MaxTimes)) TempTimes(1:MaxTimes*PreTimeCount)=DH%Times(1:MaxTimes & *PreTimeCount) DH%MaxTimeCount = DH%MaxTimeCount +1 deallocate(DH%Times) allocate(DH%Times(DH%MaxTimeCount*MaxTimes)) DH%Times(1:MaxTimes*PreTimeCount)=TempTimes(1:MaxTimes & *PreTimeCount) deallocate(TempTimes) endif endif DH%TimeIndex = TimeIndex DH%Times(TimeIndex) = DateStr ! From NetCDF implementation, keep it in case it can be used. ! VStart(1) = 1 ! VStart(2) = TimeIndex ! VCount(1) = DateStrLen ! VCount(2) = 1 ! create memory dataspace id and file dataspace id dims(1) = 1 count(1) = 1 offset(1) = TimeIndex -1 sizes(1) = TimeIndex ! create group id for different time stamp call numtochar(TimeIndex,tname) groupname = 'TIME_STAMP_'//tname ! call h5gn_members_f(DH%GroupID,DH%GroupName,nmembers,hdf5err) ! do i = 0, nmembers - 1 ! call h5gget_obj_info_idx_f(DH%GroupID,DH%GroupName,i,ObjName, ObjType, & ! hdf5err) ! if(ObjName(1:17) == groupname) then ! call h5gopen_f(DH%GroupID,groupname,tgroupid,hdf5err) ! exit ! endif ! enddo if(DH%Tgroupids(TimeIndex) == -1) then call h5gcreate_f(DH%groupid,groupname,group_id,hdf5err) if(hdf5err .lt. 0) then Status = WRF_HDF5_ERR_GROUP write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif DH%Tgroupids(TimeIndex) = group_id else ! call h5gopen_f(DH%groupid,groupname,group_id, group_id = DH%Tgroupids(TimeIndex) endif call h5screate_simple_f(1,dims,dspace_id,hdf5err,dims) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASPACE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif ! create HDF5 string handler for time if(TimeIndex == 1) then call h5tcopy_f(H5T_NATIVE_CHARACTER, str_id, hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif datelen_size = DateStrLen call h5tset_size_f(str_id,datelen_size,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif else str_id = DH%str_id endif call h5dcreate_f(group_id,DH%TimesName,str_id,dspace_id,& DH%TimesID, hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_CREATE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif ! write the data in memory space to file space CALL h5dwrite_f(DH%TimesID,str_id,DateStr,dims,hdf5err,dspace_id,dspace_id) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_WRITE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif if(TimeIndex == 1) then DH%str_id = str_id endif call h5sclose_f(dspace_id,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASPACE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call h5dclose_f(DH%TimesID,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_GENERAL write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif else ! This is for IO read ! Find the timeIndex(very expensive for large ! time stamp, should use hashing table) do i=1,MaxTimes*DH%MaxTimeCount ! For handling reading maximum timestamp greater than 9000 in the future ! if(DH%Times(i) == NullName) then ! Status = WRF_HDF5_ERR_TIME ! write(msg,*) 'Warning TIME ',DateStr,' NOT FOUND in ',"__FILE__",& ! ', line', __LINE__ ! call wrf_debug ( WARN , msg) ! return ! endif if(DH%Times(i) == DateStr) then Status = WRF_NO_ERR TimeIndex = i exit endif ! Need a recursive function to handle this ! This is a potential bug if(i == MaxTimes*DH%MaxTimeCount) then ! PreTimeCount = DH%MaxTimeCount ! allocate(TempTimes(PreTimeCount*MaxTimes)) ! TempTimes(1:MaxTimes*PreTimeCount)=DH%Times(1:MaxTimes & ! *PreTimeCount) ! DH%MaxTimeCount = DH%MaxTimeCount +1 ! deallocate(DH%Times) ! allocate(DH%Times(DH%MaxTimeCount*MaxTimes)) ! DH%Times(1:MaxTimes*PreTimeCount)=TempTimes(1:MaxTimes & ! *PreTimeCount) ! deallocate(TempTimes) Status = WRF_HDF5_ERR_TIME write(msg,*) 'Warning TIME ',DateStr,' NOT FOUND in ',"__FILE__",& ', line', __LINE__ call wrf_debug ( WARN , msg) return endif enddo ! do the hyperslab selection endif return end subroutine GetDataTimeIndex subroutine GetAttrTimeIndex(IO,DataHandle,DateStr,TimeIndex,Status) use HDF5 use wrf_phdf5_data use ext_phdf5_support_routines implicit none include 'wrf_status_codes.h' character (*) ,intent(in) :: IO integer ,intent(in) :: DataHandle character*(*) ,intent(in) :: DateStr character (DateStrLen), pointer :: TempTimes(:) integer ,intent(out) :: TimeIndex integer ,intent(out) :: Status type(wrf_phdf5_data_handle) ,pointer :: DH integer :: VStart(2) integer :: VCount(2) integer :: stat integer :: i integer :: PreTimeCount integer :: rank integer(hsize_t), dimension(1) :: chunk_dims =(/1/) integer(hsize_t), dimension(1) :: dims integer(hsize_t), dimension(1) :: hdf5_maxdims integer(hsize_t), dimension(1) :: offset integer(hsize_t), dimension(1) :: count integer(hsize_t), dimension(1) :: sizes INTEGER(HID_T) :: dset_id ! Dataset ID INTEGER(HID_T) :: dspace_id ! Dataspace ID INTEGER(HID_T) :: fspace_id ! Dataspace ID INTEGER(HID_T) :: crp_list ! chunk ID integer(hid_t) :: str_id ! string ID integer :: hdf5err integer(size_t) :: datelen_size integer(hid_t) :: group_id character(Len = 512) :: groupname ! suppose the output will not exceed 100,0000 timesteps. character(Len = MaxTimeSLen) :: tname ! DH => WrfDataHandles(DataHandle), don't know why NetCDF doesn't use GetDH call GetDH(DataHandle,DH,Status) if(Status /= WRF_NO_ERR) then write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call DateCheck(DateStr,Status) if(Status /= WRF_NO_ERR) then Status = WRF_HDF5_ERR_DATESTR_ERROR write(msg,*) 'Warning DATE STRING ERROR in ',"__FILE__",', line', __LINE__ call wrf_debug ( WARN , msg) return endif if(IO == 'write') then TimeIndex = DH%TimeIndex if(TimeIndex <= 0) then TimeIndex = 1 elseif(DateStr < DH%Times(TimeIndex)) then Status = WRF_HDF5_ERR_DATE_LT_LAST_DATE write(msg,*) 'Warning DATE < LAST DATE in ',"__FILE__",', line', __LINE__ call wrf_debug ( WARN , msg) return elseif(DateStr == DH%Times(TimeIndex)) then Status = WRF_NO_ERR return else TimeIndex = TimeIndex + 1 ! If exceeding the maximum timestep, updating the maximum timestep if(TimeIndex > MaxTimes*(DH%MaxTimeCount)) then PreTimeCount = DH%MaxTimeCount allocate(TempTimes(PreTimeCount*MaxTimes)) TempTimes(1:MaxTimes*PreTimeCount)=DH%Times(1:MaxTimes & *PreTimeCount) DH%MaxTimeCount = DH%MaxTimeCount +1 deallocate(DH%Times) allocate(DH%Times(DH%MaxTimeCount*MaxTimes)) DH%Times(1:MaxTimes*PreTimeCount)=TempTimes(1:MaxTimes & *PreTimeCount) deallocate(TempTimes) endif endif DH%TimeIndex = TimeIndex DH%Times(TimeIndex) = DateStr ! From NetCDF implementation, keep it in case it can be used. ! VStart(1) = 1 ! VStart(2) = TimeIndex ! VCount(1) = DateStrLen ! VCount(2) = 1 ! create memory dataspace id and file dataspace id dims(1) = 1 count(1) = 1 offset(1) = TimeIndex -1 sizes(1) = TimeIndex ! create group id for different time stamp call numtochar(TimeIndex,tname) groupname = 'TIME_STAMP_'//tname if(DH%Tgroupids(TimeIndex) == -1) then call h5gcreate_f(DH%groupid,groupname,group_id,hdf5err) if(hdf5err .lt. 0) then Status = WRF_HDF5_ERR_GROUP write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif DH%Tgroupids(TimeIndex) = group_id else ! call h5gopen_f(DH%groupid,groupname,group_id, group_id = DH%Tgroupids(TimeIndex) endif call h5screate_simple_f(1,dims,dspace_id,hdf5err,dims) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASPACE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif ! create HDF5 string handler for time if(TimeIndex == 1) then call h5tcopy_f(H5T_NATIVE_CHARACTER, str_id, hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif datelen_size = DateStrLen call h5tset_size_f(str_id,datelen_size,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATATYPE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif else str_id = DH%str_id endif call h5dcreate_f(group_id,DH%TimesName,str_id,dspace_id,& DH%TimesID, hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_CREATE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif ! write the data in memory space to file space CALL h5dwrite_f(DH%TimesID,str_id,DateStr,dims,hdf5err,dspace_id,dspace_id) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_WRITE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif if(TimeIndex == 1) then DH%str_id = str_id endif call h5sclose_f(dspace_id,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASPACE write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif call h5dclose_f(DH%TimesID,hdf5err) if(hdf5err.lt.0) then Status = WRF_HDF5_ERR_DATASET_GENERAL write(msg,*) 'Warning Status = ',Status,' in ',__FILE__,', line', __LINE__ call wrf_debug ( WARN , msg) return endif else ! This is for IO read ! Find the timeIndex(very expensive for large ! time stamp, should use hashing table) do i=1,MaxTimes*DH%MaxTimeCount if(DH%Times(i) == DateStr) then Status = WRF_NO_ERR TimeIndex = i exit endif ! Need a recursive function to handle this ! This is a potential bug if(i == MaxTimes*DH%MaxTimeCount) then Status = WRF_HDF5_ERR_TIME write(msg,*) 'Warning TIME ',DateStr,' NOT FOUND in ',"__FILE__",& ', line', __LINE__ call wrf_debug ( WARN , msg) return endif enddo ! do the hyperslab selection endif return end subroutine GetAttrTimeIndex ! Obtain the rank of the dimension subroutine GetDim(MemoryOrder,NDim,Status) include 'wrf_status_codes.h' character*(*) ,intent(in) :: MemoryOrder integer ,intent(out) :: NDim integer ,intent(out) :: Status character*3 :: MemOrd call LowerCase(MemoryOrder,MemOrd) select case (MemOrd) case ('xyz','xzy','yxz','yzx','zxy','zyx','xsz','xez','ysz','yez') NDim = 3 case ('xy','yx','xs','xe','ys','ye') NDim = 2 case ('z','c','0') NDim = 1 case default Status = WRF_HDF5_ERR_BAD_MEMORYORDER return end select Status = WRF_NO_ERR return end subroutine GetDim ! Obtain the index for transposing subroutine GetIndices(NDim,Start,End,i1,i2,j1,j2,k1,k2) integer ,intent(in) :: NDim integer ,dimension(*),intent(in) :: Start,End integer ,intent(out) :: i1,i2,j1,j2,k1,k2 i1=1 i2=1 j1=1 j2=1 k1=1 k2=1 i1 = Start(1) i2 = End (1) if(NDim == 1) return j1 = Start(2) j2 = End (2) if(NDim == 2) return k1 = Start(3) k2 = End (3) return end subroutine GetIndices ! shuffling the memory order to XYZ order subroutine ExtOrder(MemoryOrder,Vector,Status) use wrf_phdf5_data include 'wrf_status_codes.h' character*(*) ,intent(in) :: MemoryOrder integer,dimension(*) ,intent(inout) :: Vector integer ,intent(out) :: Status integer :: NDim integer,dimension(NVarDims) :: temp character*3 :: MemOrd call GetDim(MemoryOrder,NDim,Status) temp(1:NDim) = Vector(1:NDim) call LowerCase(MemoryOrder,MemOrd) select case (MemOrd) case ('xyz','xsz','xez','ysz','yez','xy','xs','xe','ys','ye','z','c') continue case ('0') Vector(1) = 1 case ('xzy') Vector(2) = temp(3) Vector(3) = temp(2) case ('yxz') Vector(1) = temp(2) Vector(2) = temp(1) case ('yzx') Vector(1) = temp(3) Vector(2) = temp(1) Vector(3) = temp(2) case ('zxy') Vector(1) = temp(2) Vector(2) = temp(3) Vector(3) = temp(1) case ('zyx') Vector(1) = temp(3) Vector(3) = temp(1) case ('yx') Vector(1) = temp(2) Vector(2) = temp(1) case default Status = WRF_HDF5_ERR_BAD_MEMORYORDER return end select Status = WRF_NO_ERR return end subroutine ExtOrder ! shuffling the dimensional name order subroutine ExtOrderStr(MemoryOrder,Vector,ROVector,Status) use wrf_phdf5_data include 'wrf_status_codes.h' character*(*) ,intent(in) :: MemoryOrder character*(*),dimension(*) ,intent(in) :: Vector character(256),dimension(NVarDims),intent(out) :: ROVector integer ,intent(out) :: Status integer :: NDim character*3 :: MemOrd call GetDim(MemoryOrder,NDim,Status) ROVector(1:NDim) = Vector(1:NDim) call LowerCase(MemoryOrder,MemOrd) select case (MemOrd) case ('xyz','xsz','xez','ysz','yez','xy','xs','xe','ys','ye','z','c') continue case ('0') ROVector(1) = 'ext_scalar' case ('xzy') ROVector(2) = Vector(3) ROVector(3) = Vector(2) case ('yxz') ROVector(1) = Vector(2) ROVector(2) = Vector(1) case ('yzx') ROVector(1) = Vector(3) ROVector(2) = Vector(1) ROVector(3) = Vector(2) case ('zxy') ROVector(1) = Vector(2) ROVector(2) = Vector(3) ROVector(3) = Vector(1) case ('zyx') ROVector(1) = Vector(3) ROVector(3) = Vector(1) case ('yx') ROVector(1) = Vector(2) ROVector(2) = Vector(1) case default Status = WRF_HDF5_ERR_BAD_MEMORYORDER return end select Status = WRF_NO_ERR return end subroutine ExtOrderStr subroutine LowerCase(MemoryOrder,MemOrd) character*(*) ,intent(in) :: MemoryOrder character*(*) ,intent(out) :: MemOrd character*3 :: c integer ,parameter :: upper_to_lower =IACHAR('a')-IACHAR('A') integer :: i,N MemOrd = ' ' N = len(MemoryOrder) MemOrd(1:N) = MemoryOrder(1:N) do i=1,N c = MemoryOrder(i:i) if('A'<=c .and. c <='Z') MemOrd(i:i)=achar(iachar(c)+upper_to_lower) enddo return end subroutine LowerCase subroutine UpperCase(MemoryOrder,MemOrd) character*(*) ,intent(in) :: MemoryOrder character*(*) ,intent(out) :: MemOrd character*3 :: c integer ,parameter :: lower_to_upper =IACHAR('A')-IACHAR('a') integer :: i,N MemOrd = ' ' N = len(MemoryOrder) MemOrd(1:N) = MemoryOrder(1:N) do i=1,N c = MemoryOrder(i:i) if('a'<=c .and. c <='z') MemOrd(i:i)=achar(iachar(c)+lower_to_upper) enddo return end subroutine UpperCase ! subroutine used in transpose routine subroutine reorder (MemoryOrder,MemO) character*(*) ,intent(in) :: MemoryOrder character*3 ,intent(out) :: MemO character*3 :: MemOrd integer :: N,i,i1,i2,i3 MemO = MemoryOrder N = len_trim(MemoryOrder) if(N == 1) return call lowercase(MemoryOrder,MemOrd) i1 = 1 i3 = 1 do i=2,N if(ichar(MemOrd(i:i)) < ichar(MemOrd(i1:i1))) I1 = i if(ichar(MemOrd(i:i)) > ichar(MemOrd(i3:i3))) I3 = i enddo if(N == 2) then i2=i3 else i2 = 6-i1-i3 endif MemO(1:1) = MemoryOrder(i1:i1) MemO(2:2) = MemoryOrder(i2:i2) if(N == 3) MemO(3:3) = MemoryOrder(i3:i3) if(MemOrd(i1:i1) == 's' .or. MemOrd(i1:i1) == 'e') then MemO(1:N-1) = MemO(2:N) MemO(N:N ) = MemoryOrder(i1:i1) endif return end subroutine reorder subroutine Transpose(IO,MemoryOrder,di, Field,l1,l2,m1,m2,n1,n2 & ,XField,x1,x2,y1,y2,z1,z2 & ,i1,i2,j1,j2,k1,k2 ) character*(*) ,intent(in) :: IO character*(*) ,intent(in) :: MemoryOrder integer ,intent(in) :: l1,l2,m1,m2,n1,n2 integer ,intent(in) :: di integer ,intent(in) :: x1,x2,y1,y2,z1,z2 integer ,intent(in) :: i1,i2,j1,j2,k1,k2 integer ,intent(inout) :: Field(di,l1:l2,m1:m2,n1:n2) !jm 010827 integer ,intent(inout) :: XField(di,x1:x2,y1:y2,z1:z2) integer ,intent(inout) :: XField(di,(i2-i1+1)*(j2-j1+1)*(k2-k1+1)) character*3 :: MemOrd character*3 :: MemO integer ,parameter :: MaxUpperCase=IACHAR('Z') integer :: i,j,k,ix,jx,kx call LowerCase(MemoryOrder,MemOrd) select case (MemOrd) !#define XDEX(A,B,C) A-A ## 1+1+(A ## 2-A ## 1+1)*((B-B ## 1)+(C-C ## 1)*(B ## 2-B ## 1+1)) case ('xzy') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(i-i1+1+(i2-i1+1)*((k-k1)+(j-j1)*(k2-k1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(i-i1+1+(i2-i1+1)*((k-k1)+(j-j1)*(k2-k1+1)))) endif enddo enddo enddo return case ('xyz','xsz','xez','ysz','yez','xy','xs','xe','ys','ye','z','c','0') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(i-i1+1+(i2-i1+1)*((j-j1)+(k-k1)*(j2-j1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(i-i1+1+(i2-i1+1)*((j-j1)+(k-k1)*(j2-j1+1)))) endif enddo enddo enddo return case ('yxz') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(j-j1+1+(j2-j1+1)*((i-i1)+(k-k1)*(i2-i1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(j-j1+1+(j2-j1+1)*((i-i1)+(k-k1)*(i2-i1+1)))) endif enddo enddo enddo return case ('zxy') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(k-k1+1+(k2-k1+1)*((i-i1)+(j-j1)*(i2-i1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(k-k1+1+(k2-k1+1)*((i-i1)+(j-j1)*(i2-i1+1)))) endif enddo enddo enddo return case ('yzx') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(j-j1+1+(j2-j1+1)*((k-k1)+(i-i1)*(k2-k1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(j-j1+1+(j2-j1+1)*((k-k1)+(i-i1)*(k2-k1+1)))) endif enddo enddo enddo return case ('zyx') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(k-k1+1+(k2-k1+1)*((j-j1)+(i-i1)*(j2-j1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(k-k1+1+(k2-k1+1)*((j-j1)+(i-i1)*(j2-j1+1)))) endif enddo enddo enddo return case ('yx') ix=0 jx=0 kx=0 call reorder(MemoryOrder,MemO) if(IACHAR(MemO(1:1)) > MaxUpperCase) ix=i2+i1 if(IACHAR(MemO(2:2)) > MaxUpperCase) jx=j2+j1 if(IACHAR(MemO(3:3)) > MaxUpperCase) kx=k2+k1 do k=k1,k2 do j=j1,j2 do i=i1,i2 if(IO == 'write') then XField(1:di,(j-j1+1+(j2-j1+1)*((i-i1)+(k-k1)*(i2-i1+1)))) = Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) else Field(1:di,abs(ix-i),abs(jx-j),abs(kx-k)) = XField(1:di,(j-j1+1+(j2-j1+1)*((i-i1)+(k-k1)*(i2-i1+1)))) endif enddo enddo enddo return end select return end subroutine Transpose subroutine numtochar(TimeIndex,tname,Status) use wrf_phdf5_data integer, intent(in) :: TimeIndex character(len=MaxTimeSLen),intent(out)::tname integer ,intent(out)::Status integer :: i,ten_pow,temp integer :: maxtimestep maxtimestep =1 do i =1,MaxTimeSLen maxtimestep = maxtimestep * 10 enddo if(TimeIndex >= maxtimestep) then Status = WRF_HDF5_ERR_OTHERS write(msg,*) 'Cannot exceed the maximum timestep',maxtimestep,'in',__FILE__,' line',__LINE__ call wrf_debug(FATAL,msg) return endif ten_pow = 1 temp =10 do i =1,MaxTimeSLen tname(MaxTimeSLen+1-i:MaxTimeSLen+1-i) = achar(modulo(TimeIndex/ten_pow,temp)+iachar('0')) ten_pow = 10* ten_pow enddo return end subroutine numtochar