Path: blob/devel/misc/netcdf/src/GridDataMapper/NetCDFGeneralUtils.f90
3206 views
!------------------------------------------------------------------------------1! Vili Forsell2! Created: 13.6.20113! Last Modified: 4.8.20114!------------------------------------------------------------------------------5! This module contains functions for6! - getting dimensions sizes and NetCDF identifiers; GetAllDimensions()7! - getting data from NetCDF files; GetFromNetCDF()8! - handling NetCDF status errors; G_Error()9!------------------------------------------------------------------------------10MODULE NetCDFGeneralUtils11USE DefUtils, ONLY: dp, MAX_NAME_LEN12USE NetCDF13USE Messages14IMPLICIT NONE15LOGICAL, PARAMETER :: DEBUG_UTILS = .FALSE.1617!--- A type for time dimension values18TYPE TimeType_t19LOGICAL :: is_defined20REAL(KIND=dp) :: val ! Exact time value21INTEGER :: id, & ! Dimension NetCDF id22len, & ! Dimension length/size23low, & ! Nearest lower index24high ! Nearest higher index25LOGICAL :: doInterpolation ! True, if the exact value is not an integer and, hence, requires interpolation26END TYPE TimeType_t2728CONTAINS2930!------------------ CloseNetCDF() -----------------------31!--- Closes the given NetCDF file32SUBROUTINE CloseNetCDF( NCID )33USE NetCDF34IMPLICIT NONE35INTEGER, INTENT(IN) :: NCID36INTEGER :: status3738status = NF90_CLOSE(NCID)39IF ( G_ERROR(status,'Failed to close NetCDF file.') ) THEN40CALL abort()41END IF42END SUBROUTINE CloseNetCDF4344!------------------ GetDimension() ----------------------45!--- Takes the NetCDF file identifier and dimension name (as in NetCDF) and gets the id and length of the dimension, or abort otherwise46SUBROUTINE GetDimension( NCID, DIM_NAME, dim_id, dim_len )47!--------------------------------------------------------48USE Messages49IMPLICIT NONE50!--- Arguments51INTEGER, INTENT(IN) :: NCID52CHARACTER (*), INTENT(IN) :: DIM_NAME53INTEGER, INTENT(OUT) :: dim_id, dim_len5455!--- Variables56CHARACTER :: tmp_name ! Temporary name57INTEGER :: status ! Results and status information from NetCDF58INTEGER, PARAMETER :: sentinel = -1 ! Default intial value in case of error5960!--- Initializations61dim_id = sentinel62dim_len = sentinel6364!--- Get dimension information and check success65status = NF90_INQ_DIMID(NCID,DIM_NAME,dim_id)66IF ( .NOT. G_Error(status, 'Dimension identifier could not be found.') ) THEN67status = NF90_INQUIRE_DIMENSION(NCID,dim_id,tmp_name,dim_len)68IF ( G_Error(status, 'Dimension could not be inquired.') ) THEN69dim_id = sentinel70dim_len = sentinel71CALL abort()72END IF73ELSE74dim_id = sentinel75CALL abort()76END IF77status = NF90_INQ_VARID(NCID,DIM_NAME,dim_id)78WRITE(Message,'(A,A,A)') 'No variable corresponding to the dimension ', DIM_NAME ,' could be found.'79IF ( G_Error(status, Message) ) THEN80dim_id = sentinel81CALL abort()82END IF8384IF ( DEBUG_UTILS ) THEN ! Debug printouts85WRITE (Message,'(A,A10,A,I5,A,I10,A)') 'Dimension: ', DIM_NAME,' with id ', dim_id, ' and size ', dim_len, ' read correctly'86CALL Info('GridDataMapper',Message)87END IF88END SUBROUTINE GetDimension899091!------------------ GetAllDimensions() ----------------------92!--- Takes the NetCDF file and name identifiers (as in NetCDF) and gets the ids and lengths of the dimensions, or abort otherwise93SUBROUTINE GetAllDimensions( NCID, NAMES, dim_ids, dim_lens )94!------------------------------------------------------------95IMPLICIT NONE96CHARACTER (len = MAX_NAME_LEN), INTENT(IN) :: NAMES(:)97INTEGER, INTENT(IN) :: NCID98INTEGER, ALLOCATABLE, INTENT(OUT) :: dim_ids(:),dim_lens(:)99INTEGER :: alloc_stat, nm100101IF ( (size(NAMES,1) .NE. size(dim_ids)) .AND. (size(dim_ids) .NE. size(dim_lens)) ) THEN102CALL Fatal('GridDataMapper','GetAllDimensions() input dimensions do not agree!')103END IF104105! Allocates the result vectors106ALLOCATE ( dim_ids(size(NAMES,1)), dim_lens(size(NAMES,1)), STAT = alloc_stat )107IF ( alloc_stat .NE. 0 ) THEN108CALL Fatal('GridDataMapper','Memory ran out')109END IF110111! Collects the data for each name in order112DO nm = 1,size(NAMES,1),1113CALL GetDimension( NCID,NAMES(nm),dim_ids(nm),dim_lens(nm) )114END DO115! WRITE(*,*) 'End'116END SUBROUTINE GetAllDimensions117118119!----------------- GetFromNetCDF() --------------------120!--- Reads the given variable name and returns the value from NetCDF grid121!--- Does not require time, loc contains indexing122FUNCTION GetFromNetCDF( NCID, VAR_ID, LOC, LOC_TIME, TIME, DIM_LENS, accessed, OUT_SIZE ) RESULT( success )123!------------------------------------------------------124USE NetCDF125IMPLICIT NONE126127!------------------------------------------------------------------------------128! ARGUMENTS129!------------------------------------------------------------------------------130INTEGER, INTENT(IN) :: DIM_LENS(:), VAR_ID131INTEGER, INTENT(IN) :: NCID, LOC(:), LOC_TIME132TYPE(TimeType_t), INTENT(IN) :: TIME133INTEGER, INTENT(IN) :: OUT_SIZE(:) ! The sizes of each dimension for the return type134LOGICAL :: success ! Output: TRUE if all ok135REAL (KIND=dp), ALLOCATABLE, INTENT(INOUT) :: accessed(:) ! Later reshaped to proper dimensions136137!------------------------------------------------------------------------------138! VARIABLES139!------------------------------------------------------------------------------140INTEGER :: DIM_COUNT,TOTAL_SIZE141INTEGER :: alloc_stat ! alloc_stat for allocation status142INTEGER, ALLOCATABLE :: COUNT_VECTOR(:)143! COUNT_VECTOR is the amount of nodes taken144! starting from corresponding index vector locations (slabs of data)145INTEGER, ALLOCATABLE :: locs(:,:) ! First column is left limit, second column is right limit146INTEGER, ALLOCATABLE :: rev_perm(:) ! Reverse permuation for indices; necessary to compensate for the147! NetCDF Fortran access indexing being opposite to the one shown in network Common Data form Language (CDL)148INTEGER :: loop, status,timeBias ! timeBias is the change to array indices caused by taking time dimension into account149CHARACTER(len=64) :: tmpFormat150151!------------------------------------------------------------------------------152! INITIALIZATIONS153!------------------------------------------------------------------------------154155! Checks input size156IF ( (size(OUT_SIZE) .NE. size(LOC)) .OR. (size(LOC) .NE. size(DIM_LENS)) ) THEN157WRITE(Message, '(A,I3,A,I3,A,I3)') 'Number of dimensions differs between input coordinates: Output size ',&158size(OUT_SIZE), ', # of locations ', size(LOC), ', # of dimensions ', size(DIM_LENS)159CALL Fatal('GridDataMapper',Message)160END IF161162! Checks if time dimension is taken into account (picks always just one point)163timeBias = 0164IF (TIME % IS_DEFINED) timeBias = 1165166DIM_COUNT = size(DIM_LENS,1) + timeBias ! May take one more dimension for time167168! The same calculations would be done in any case; uses a little memory to save here169! The product of all sizes is the size of the one dimensional version of the NetCDF return value170TOTAL_SIZE = 1171DO loop = 1,size(OUT_SIZE,1),1172TOTAL_SIZE = TOTAL_SIZE*OUT_SIZE(loop)173END DO174175success = .FALSE. ! For checking if all went ok (allows later error recuperation)176177ALLOCATE ( accessed(TOTAL_SIZE), COUNT_VECTOR(DIM_COUNT),locs(DIM_COUNT,2),rev_perm(DIM_COUNT), STAT = alloc_stat )178IF ( alloc_stat .NE. 0 ) THEN179CALL Fatal('GridDataMapper','Memory ran out')180END IF181182accessed = 0183184! In network Common Data form Language (CDL) notation infinite dimensions are first (f.ex. time)185! However, with Fortran access functions the order is required to be reversed, so it will be last186rev_perm = (/ (DIM_COUNT - loop + 1, loop = 1, DIM_COUNT) /)187188! If has time, then the first dimension is time and is set in count vector and locs189! When using via the mask rev_perm, the time index will then be last190IF ( TIME % IS_DEFINED ) THEN191COUNT_VECTOR(1) = 1192locs(1,1) = LOC_TIME193END IF194COUNT_VECTOR(1+timeBias:size(OUT_SIZE)+timeBias) = OUT_SIZE(:)195locs(1+timeBias:size(LOC)+timeBias,1) = LOC(:)196197locs(:,2) = locs(:,1) + COUNT_VECTOR(:) - 1 ! Covers the stencil area starting from left indices198199! Checks each dimension range (and, hence, access attempt)200IF ( TIME % IS_DEFINED ) THEN201IF ( (locs(1,1) .LT. 1) .OR. (TIME % LEN .LT. locs(1,2)) ) THEN202WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,', size(locs,2),'(I10),/,',size(locs,2),'(I10))'203WRITE (*,tmpFormat) 'Locs: ', TRANSPOSE(locs)204WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,',size(DIM_LENS),'(I10))'205WRITE (*,tmpFormat) 'Dims: ', DIM_LENS206CALL Fatal('GridDataMapper','Indexing time out of bounds.')207END IF208END IF209210DO loop = 1+timeBias,size(locs,1),1211IF ( (locs(loop,1) .LT. 1) .OR. (DIM_LENS(loop-timeBias) .LT. locs(loop,2)) ) THEN212WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,', size(locs,2),'(I10),/,',size(locs,2),'(I10))'213WRITE (*,tmpFormat) 'Locs: ', TRANSPOSE(locs)214WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,',size(DIM_LENS),'(I10))'215WRITE (*,tmpFormat) 'Dims: ', DIM_LENS216CALL Fatal('GridDataMapper','Indexing parameter(s) out of bounds.')217END IF218END DO219220!--- The dimensions and the locations have been read and checked; NetCDF accessing info is a-ok221222! Access variable and take the values223status = NF90_GET_VAR(NCID,var_id,accessed,locs(rev_perm(:),1),COUNT_VECTOR(rev_perm(:)))224WRITE(Message,*) 'NetCDF variable access failed. Variable ID: ',var_id,' and taking ',&225size(accessed,1), ' elements with true size ', TOTAL_SIZE,'Locs: ',locs,'Dims: ',DIM_LENS226IF ( G_ERROR(status,Message) ) THEN227accessed = 0228CALL abort()229END IF230231success = .TRUE. ! Successful232233END FUNCTION GetFromNetCDF234235236237!----------------- TimeValueToIndex() ---------------238!--- Takes a NetCDF time value and converts it into an index239FUNCTION TimeValueToIndex(NCID,TIME_NAME,DIM_ID,DIM_LEN,t_val,t_eps) RESULT(t_ind)240IMPLICIT NONE241242!--- Arguments243CHARACTER(len = MAX_NAME_LEN), INTENT(IN) :: TIME_NAME244REAL(KIND=dp), INTENT(IN) :: t_val245REAL(KIND=dp), INTENT(IN) :: t_eps ! The rounding for time246INTEGER, INTENT(IN) :: NCID, DIM_ID, DIM_LEN247REAL(KIND=dp) :: t_ind ! Output248249!--- Variables250REAL(KIND=dp) :: t_min, t_max, t_tmp1(1), t_tmp2(2), t_diff251INTEGER :: time_id, status252INTEGER :: index_scalar(1), count_scalar(1)253254t_ind = -1.0_dp ! Initialization to out of bounds255index_scalar = 1 ! Initialized to min value256count_scalar = 2257time_id = DIM_ID ! Last dimension is time258259! 1) Inquire time variable's id260status = NF90_INQ_VARID(NCID,TIME_NAME,time_id)261IF ( G_Error(status,'NetCDF time variable name not found.') ) THEN262RETURN263END IF264265! 2) Get the time range from NetCDF266status = NF90_GET_VAR(NCID,time_id,t_tmp2,index_scalar,count_scalar)267IF ( G_Error(status,'First NetCDF time value not found') ) THEN268RETURN269END IF270t_min = t_tmp2(1)271t_diff = t_tmp2(2) - t_tmp2(1)272273count_scalar = 1274index_scalar = DIM_LEN ! Pick the last max value275276status = NF90_GET_VAR(NCID,time_id,t_tmp1,index_scalar,count_scalar)277IF ( G_Error(status,'Last NetCDF time value not found') ) THEN278RETURN279END IF280t_max = t_tmp1(1)281282! 3) Use the time range to find the index for the time value (NetCDF variables uniform)283IF ( t_val < t_min .OR. t_val > t_max ) THEN284! Sets to the nearest value if within tolerance, else error, to better compare real values285IF ( t_val < t_min .AND. t_val + (t_eps*t_diff) >= t_min ) THEN286t_ind = 1287ELSE IF ( t_val > t_max .AND. t_val - (t_eps*t_diff) <= t_max ) THEN288t_ind = DIM_LEN289ELSE ! If no rounding possible; a real error290WRITE (Message,'(A,F7.2,A,F7.2,A,F7.2,A,F7.2)') 'Input value ', t_val, &291' is not within range [',t_min,', ',t_max,'] with step', t_diff292CALL Fatal('GridDataMapper', Message)293END IF294ELSE295t_ind = ((t_val - t_min)/t_diff) + 1 ! Uniform grid: just remove the bias and normalize the difference out296END IF297! No rounding for it is interpolated later on298WRITE (Message, '(A,F7.2,A,F7.2,A,F7.2,A,F7.2,A,F7.2)') 'Time index for given value ', &299t_val, ' is ', t_ind, ' over range [', t_min,',',t_max,'] with step ', t_diff300CALL Info('GridDataMapper', Message)301302END FUNCTION TimeValueToIndex303304!-------------------- G_Error() ------------------------305!----- Checks the status and if failure, prints the error message and returns .TRUE.306!-------------------------------------------------------307FUNCTION G_Error( status, msg ) RESULT(erred)308IMPLICIT NONE309310!----- Declarations311INTEGER, INTENT(IN) :: status ! Status value312CHARACTER (len = *), INTENT(IN) :: msg ! Error details313LOGICAL :: erred ! True, if all ok; False otherwise314315!----- Checks for errors316erred = .FALSE.317IF ( status .NE. NF90_NOERR ) THEN ! Error encountered318erred = .TRUE.319CALL Fatal( 'GridDataMapper', msg )320END IF321322END FUNCTION G_Error323324END MODULE NetCDFGeneralUtils325326327328