Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/misc/netcdf/src/GridDataMapper/NetCDFGeneralUtils.f90
3206 views
1
!------------------------------------------------------------------------------
2
! Vili Forsell
3
! Created: 13.6.2011
4
! Last Modified: 4.8.2011
5
!------------------------------------------------------------------------------
6
! This module contains functions for
7
! - getting dimensions sizes and NetCDF identifiers; GetAllDimensions()
8
! - getting data from NetCDF files; GetFromNetCDF()
9
! - handling NetCDF status errors; G_Error()
10
!------------------------------------------------------------------------------
11
MODULE NetCDFGeneralUtils
12
USE DefUtils, ONLY: dp, MAX_NAME_LEN
13
USE NetCDF
14
USE Messages
15
IMPLICIT NONE
16
LOGICAL, PARAMETER :: DEBUG_UTILS = .FALSE.
17
18
!--- A type for time dimension values
19
TYPE TimeType_t
20
LOGICAL :: is_defined
21
REAL(KIND=dp) :: val ! Exact time value
22
INTEGER :: id, & ! Dimension NetCDF id
23
len, & ! Dimension length/size
24
low, & ! Nearest lower index
25
high ! Nearest higher index
26
LOGICAL :: doInterpolation ! True, if the exact value is not an integer and, hence, requires interpolation
27
END TYPE TimeType_t
28
29
CONTAINS
30
31
!------------------ CloseNetCDF() -----------------------
32
!--- Closes the given NetCDF file
33
SUBROUTINE CloseNetCDF( NCID )
34
USE NetCDF
35
IMPLICIT NONE
36
INTEGER, INTENT(IN) :: NCID
37
INTEGER :: status
38
39
status = NF90_CLOSE(NCID)
40
IF ( G_ERROR(status,'Failed to close NetCDF file.') ) THEN
41
CALL abort()
42
END IF
43
END SUBROUTINE CloseNetCDF
44
45
!------------------ GetDimension() ----------------------
46
!--- Takes the NetCDF file identifier and dimension name (as in NetCDF) and gets the id and length of the dimension, or abort otherwise
47
SUBROUTINE GetDimension( NCID, DIM_NAME, dim_id, dim_len )
48
!--------------------------------------------------------
49
USE Messages
50
IMPLICIT NONE
51
!--- Arguments
52
INTEGER, INTENT(IN) :: NCID
53
CHARACTER (*), INTENT(IN) :: DIM_NAME
54
INTEGER, INTENT(OUT) :: dim_id, dim_len
55
56
!--- Variables
57
CHARACTER :: tmp_name ! Temporary name
58
INTEGER :: status ! Results and status information from NetCDF
59
INTEGER, PARAMETER :: sentinel = -1 ! Default intial value in case of error
60
61
!--- Initializations
62
dim_id = sentinel
63
dim_len = sentinel
64
65
!--- Get dimension information and check success
66
status = NF90_INQ_DIMID(NCID,DIM_NAME,dim_id)
67
IF ( .NOT. G_Error(status, 'Dimension identifier could not be found.') ) THEN
68
status = NF90_INQUIRE_DIMENSION(NCID,dim_id,tmp_name,dim_len)
69
IF ( G_Error(status, 'Dimension could not be inquired.') ) THEN
70
dim_id = sentinel
71
dim_len = sentinel
72
CALL abort()
73
END IF
74
ELSE
75
dim_id = sentinel
76
CALL abort()
77
END IF
78
status = NF90_INQ_VARID(NCID,DIM_NAME,dim_id)
79
WRITE(Message,'(A,A,A)') 'No variable corresponding to the dimension ', DIM_NAME ,' could be found.'
80
IF ( G_Error(status, Message) ) THEN
81
dim_id = sentinel
82
CALL abort()
83
END IF
84
85
IF ( DEBUG_UTILS ) THEN ! Debug printouts
86
WRITE (Message,'(A,A10,A,I5,A,I10,A)') 'Dimension: ', DIM_NAME,' with id ', dim_id, ' and size ', dim_len, ' read correctly'
87
CALL Info('GridDataMapper',Message)
88
END IF
89
END SUBROUTINE GetDimension
90
91
92
!------------------ GetAllDimensions() ----------------------
93
!--- Takes the NetCDF file and name identifiers (as in NetCDF) and gets the ids and lengths of the dimensions, or abort otherwise
94
SUBROUTINE GetAllDimensions( NCID, NAMES, dim_ids, dim_lens )
95
!------------------------------------------------------------
96
IMPLICIT NONE
97
CHARACTER (len = MAX_NAME_LEN), INTENT(IN) :: NAMES(:)
98
INTEGER, INTENT(IN) :: NCID
99
INTEGER, ALLOCATABLE, INTENT(OUT) :: dim_ids(:),dim_lens(:)
100
INTEGER :: alloc_stat, nm
101
102
IF ( (size(NAMES,1) .NE. size(dim_ids)) .AND. (size(dim_ids) .NE. size(dim_lens)) ) THEN
103
CALL Fatal('GridDataMapper','GetAllDimensions() input dimensions do not agree!')
104
END IF
105
106
! Allocates the result vectors
107
ALLOCATE ( dim_ids(size(NAMES,1)), dim_lens(size(NAMES,1)), STAT = alloc_stat )
108
IF ( alloc_stat .NE. 0 ) THEN
109
CALL Fatal('GridDataMapper','Memory ran out')
110
END IF
111
112
! Collects the data for each name in order
113
DO nm = 1,size(NAMES,1),1
114
CALL GetDimension( NCID,NAMES(nm),dim_ids(nm),dim_lens(nm) )
115
END DO
116
! WRITE(*,*) 'End'
117
END SUBROUTINE GetAllDimensions
118
119
120
!----------------- GetFromNetCDF() --------------------
121
!--- Reads the given variable name and returns the value from NetCDF grid
122
!--- Does not require time, loc contains indexing
123
FUNCTION GetFromNetCDF( NCID, VAR_ID, LOC, LOC_TIME, TIME, DIM_LENS, accessed, OUT_SIZE ) RESULT( success )
124
!------------------------------------------------------
125
USE NetCDF
126
IMPLICIT NONE
127
128
!------------------------------------------------------------------------------
129
! ARGUMENTS
130
!------------------------------------------------------------------------------
131
INTEGER, INTENT(IN) :: DIM_LENS(:), VAR_ID
132
INTEGER, INTENT(IN) :: NCID, LOC(:), LOC_TIME
133
TYPE(TimeType_t), INTENT(IN) :: TIME
134
INTEGER, INTENT(IN) :: OUT_SIZE(:) ! The sizes of each dimension for the return type
135
LOGICAL :: success ! Output: TRUE if all ok
136
REAL (KIND=dp), ALLOCATABLE, INTENT(INOUT) :: accessed(:) ! Later reshaped to proper dimensions
137
138
!------------------------------------------------------------------------------
139
! VARIABLES
140
!------------------------------------------------------------------------------
141
INTEGER :: DIM_COUNT,TOTAL_SIZE
142
INTEGER :: alloc_stat ! alloc_stat for allocation status
143
INTEGER, ALLOCATABLE :: COUNT_VECTOR(:)
144
! COUNT_VECTOR is the amount of nodes taken
145
! starting from corresponding index vector locations (slabs of data)
146
INTEGER, ALLOCATABLE :: locs(:,:) ! First column is left limit, second column is right limit
147
INTEGER, ALLOCATABLE :: rev_perm(:) ! Reverse permuation for indices; necessary to compensate for the
148
! NetCDF Fortran access indexing being opposite to the one shown in network Common Data form Language (CDL)
149
INTEGER :: loop, status,timeBias ! timeBias is the change to array indices caused by taking time dimension into account
150
CHARACTER(len=64) :: tmpFormat
151
152
!------------------------------------------------------------------------------
153
! INITIALIZATIONS
154
!------------------------------------------------------------------------------
155
156
! Checks input size
157
IF ( (size(OUT_SIZE) .NE. size(LOC)) .OR. (size(LOC) .NE. size(DIM_LENS)) ) THEN
158
WRITE(Message, '(A,I3,A,I3,A,I3)') 'Number of dimensions differs between input coordinates: Output size ',&
159
size(OUT_SIZE), ', # of locations ', size(LOC), ', # of dimensions ', size(DIM_LENS)
160
CALL Fatal('GridDataMapper',Message)
161
END IF
162
163
! Checks if time dimension is taken into account (picks always just one point)
164
timeBias = 0
165
IF (TIME % IS_DEFINED) timeBias = 1
166
167
DIM_COUNT = size(DIM_LENS,1) + timeBias ! May take one more dimension for time
168
169
! The same calculations would be done in any case; uses a little memory to save here
170
! The product of all sizes is the size of the one dimensional version of the NetCDF return value
171
TOTAL_SIZE = 1
172
DO loop = 1,size(OUT_SIZE,1),1
173
TOTAL_SIZE = TOTAL_SIZE*OUT_SIZE(loop)
174
END DO
175
176
success = .FALSE. ! For checking if all went ok (allows later error recuperation)
177
178
ALLOCATE ( accessed(TOTAL_SIZE), COUNT_VECTOR(DIM_COUNT),locs(DIM_COUNT,2),rev_perm(DIM_COUNT), STAT = alloc_stat )
179
IF ( alloc_stat .NE. 0 ) THEN
180
CALL Fatal('GridDataMapper','Memory ran out')
181
END IF
182
183
accessed = 0
184
185
! In network Common Data form Language (CDL) notation infinite dimensions are first (f.ex. time)
186
! However, with Fortran access functions the order is required to be reversed, so it will be last
187
rev_perm = (/ (DIM_COUNT - loop + 1, loop = 1, DIM_COUNT) /)
188
189
! If has time, then the first dimension is time and is set in count vector and locs
190
! When using via the mask rev_perm, the time index will then be last
191
IF ( TIME % IS_DEFINED ) THEN
192
COUNT_VECTOR(1) = 1
193
locs(1,1) = LOC_TIME
194
END IF
195
COUNT_VECTOR(1+timeBias:size(OUT_SIZE)+timeBias) = OUT_SIZE(:)
196
locs(1+timeBias:size(LOC)+timeBias,1) = LOC(:)
197
198
locs(:,2) = locs(:,1) + COUNT_VECTOR(:) - 1 ! Covers the stencil area starting from left indices
199
200
! Checks each dimension range (and, hence, access attempt)
201
IF ( TIME % IS_DEFINED ) THEN
202
IF ( (locs(1,1) .LT. 1) .OR. (TIME % LEN .LT. locs(1,2)) ) THEN
203
WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,', size(locs,2),'(I10),/,',size(locs,2),'(I10))'
204
WRITE (*,tmpFormat) 'Locs: ', TRANSPOSE(locs)
205
WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,',size(DIM_LENS),'(I10))'
206
WRITE (*,tmpFormat) 'Dims: ', DIM_LENS
207
CALL Fatal('GridDataMapper','Indexing time out of bounds.')
208
END IF
209
END IF
210
211
DO loop = 1+timeBias,size(locs,1),1
212
IF ( (locs(loop,1) .LT. 1) .OR. (DIM_LENS(loop-timeBias) .LT. locs(loop,2)) ) THEN
213
WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,', size(locs,2),'(I10),/,',size(locs,2),'(I10))'
214
WRITE (*,tmpFormat) 'Locs: ', TRANSPOSE(locs)
215
WRITE(tmpFormat,'(A,I3,A,I3,A)') '(A,/,',size(DIM_LENS),'(I10))'
216
WRITE (*,tmpFormat) 'Dims: ', DIM_LENS
217
CALL Fatal('GridDataMapper','Indexing parameter(s) out of bounds.')
218
END IF
219
END DO
220
221
!--- The dimensions and the locations have been read and checked; NetCDF accessing info is a-ok
222
223
! Access variable and take the values
224
status = NF90_GET_VAR(NCID,var_id,accessed,locs(rev_perm(:),1),COUNT_VECTOR(rev_perm(:)))
225
WRITE(Message,*) 'NetCDF variable access failed. Variable ID: ',var_id,' and taking ',&
226
size(accessed,1), ' elements with true size ', TOTAL_SIZE,'Locs: ',locs,'Dims: ',DIM_LENS
227
IF ( G_ERROR(status,Message) ) THEN
228
accessed = 0
229
CALL abort()
230
END IF
231
232
success = .TRUE. ! Successful
233
234
END FUNCTION GetFromNetCDF
235
236
237
238
!----------------- TimeValueToIndex() ---------------
239
!--- Takes a NetCDF time value and converts it into an index
240
FUNCTION TimeValueToIndex(NCID,TIME_NAME,DIM_ID,DIM_LEN,t_val,t_eps) RESULT(t_ind)
241
IMPLICIT NONE
242
243
!--- Arguments
244
CHARACTER(len = MAX_NAME_LEN), INTENT(IN) :: TIME_NAME
245
REAL(KIND=dp), INTENT(IN) :: t_val
246
REAL(KIND=dp), INTENT(IN) :: t_eps ! The rounding for time
247
INTEGER, INTENT(IN) :: NCID, DIM_ID, DIM_LEN
248
REAL(KIND=dp) :: t_ind ! Output
249
250
!--- Variables
251
REAL(KIND=dp) :: t_min, t_max, t_tmp1(1), t_tmp2(2), t_diff
252
INTEGER :: time_id, status
253
INTEGER :: index_scalar(1), count_scalar(1)
254
255
t_ind = -1.0_dp ! Initialization to out of bounds
256
index_scalar = 1 ! Initialized to min value
257
count_scalar = 2
258
time_id = DIM_ID ! Last dimension is time
259
260
! 1) Inquire time variable's id
261
status = NF90_INQ_VARID(NCID,TIME_NAME,time_id)
262
IF ( G_Error(status,'NetCDF time variable name not found.') ) THEN
263
RETURN
264
END IF
265
266
! 2) Get the time range from NetCDF
267
status = NF90_GET_VAR(NCID,time_id,t_tmp2,index_scalar,count_scalar)
268
IF ( G_Error(status,'First NetCDF time value not found') ) THEN
269
RETURN
270
END IF
271
t_min = t_tmp2(1)
272
t_diff = t_tmp2(2) - t_tmp2(1)
273
274
count_scalar = 1
275
index_scalar = DIM_LEN ! Pick the last max value
276
277
status = NF90_GET_VAR(NCID,time_id,t_tmp1,index_scalar,count_scalar)
278
IF ( G_Error(status,'Last NetCDF time value not found') ) THEN
279
RETURN
280
END IF
281
t_max = t_tmp1(1)
282
283
! 3) Use the time range to find the index for the time value (NetCDF variables uniform)
284
IF ( t_val < t_min .OR. t_val > t_max ) THEN
285
! Sets to the nearest value if within tolerance, else error, to better compare real values
286
IF ( t_val < t_min .AND. t_val + (t_eps*t_diff) >= t_min ) THEN
287
t_ind = 1
288
ELSE IF ( t_val > t_max .AND. t_val - (t_eps*t_diff) <= t_max ) THEN
289
t_ind = DIM_LEN
290
ELSE ! If no rounding possible; a real error
291
WRITE (Message,'(A,F7.2,A,F7.2,A,F7.2,A,F7.2)') 'Input value ', t_val, &
292
' is not within range [',t_min,', ',t_max,'] with step', t_diff
293
CALL Fatal('GridDataMapper', Message)
294
END IF
295
ELSE
296
t_ind = ((t_val - t_min)/t_diff) + 1 ! Uniform grid: just remove the bias and normalize the difference out
297
END IF
298
! No rounding for it is interpolated later on
299
WRITE (Message, '(A,F7.2,A,F7.2,A,F7.2,A,F7.2,A,F7.2)') 'Time index for given value ', &
300
t_val, ' is ', t_ind, ' over range [', t_min,',',t_max,'] with step ', t_diff
301
CALL Info('GridDataMapper', Message)
302
303
END FUNCTION TimeValueToIndex
304
305
!-------------------- G_Error() ------------------------
306
!----- Checks the status and if failure, prints the error message and returns .TRUE.
307
!-------------------------------------------------------
308
FUNCTION G_Error( status, msg ) RESULT(erred)
309
IMPLICIT NONE
310
311
!----- Declarations
312
INTEGER, INTENT(IN) :: status ! Status value
313
CHARACTER (len = *), INTENT(IN) :: msg ! Error details
314
LOGICAL :: erred ! True, if all ok; False otherwise
315
316
!----- Checks for errors
317
erred = .FALSE.
318
IF ( status .NE. NF90_NOERR ) THEN ! Error encountered
319
erred = .TRUE.
320
CALL Fatal( 'GridDataMapper', msg )
321
END IF
322
323
END FUNCTION G_Error
324
325
END MODULE NetCDFGeneralUtils
326
327
328