Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/misc/netcdf/src/GridDataMapper/NetCDFGridUtils.f90
3206 views
1
!------------------------------------------------------------------------------
2
! Vili Forsell
3
! Created: 13.6.2011
4
! Last Modified: 13.7.2011
5
!------------------------------------------------------------------------------
6
! Contains tools for
7
! - getting the essential information on the uniform NetCDF grid; GetNetCDFGridParameters()
8
! - adjusting the grid parameters on basis of a mask; Focus2DNetCDFGrid()
9
!------------------------------------------------------------------------------
10
11
MODULE NetCDFGridUtils
12
USE DefUtils, ONLY: dp
13
USE NetCDF
14
USE Messages
15
USE NetCDFGeneralUtils, ONLY: G_Error
16
IMPLICIT NONE
17
18
!--- A type for defining an uniform grid (to simplify parameter passing)
19
TYPE UniformGrid_t
20
REAL(KIND=dp), ALLOCATABLE :: x0(:), & ! Lower left corner of grid
21
dx(:), & ! Uniform difference between points
22
x1(:), & ! Upper right corner of grid
23
Eps(:) ! Error tolerance for overshooting bounds
24
INTEGER, ALLOCATABLE :: nmax(:), & ! Amount of points
25
const_vals(:) ! NetCDF data values for constants
26
INTEGER :: dims ! Amount of used dimensions
27
INTEGER :: coord_count ! Amount of used coordinates from the used dimensions
28
REAL(KIND=dp), ALLOCATABLE :: scale(:), & ! Scales an Elmer point into this grid by multiplying with this...
29
move(:) ! ... and moving by this
30
LOGICAL :: is_def ! True, if size is non-zero, else not defined and false
31
INTEGER, ALLOCATABLE :: access_perm(:) ! The proper NetCDF access order, indexed by first coordinates, then constants
32
INTEGER, ALLOCATABLE :: Elmer_perm(:) ! Transforms Elmer coordinate order to the NetCDF Coordinate order
33
END TYPE UniformGrid_t
34
35
CONTAINS
36
37
!------------------ PrintGrid() ------------------------------------
38
!--- Prints the Uniform Grid contents to stdout
39
SUBROUTINE PrintGrid( GRID, ID )
40
!-------------------------------------------------------------------
41
IMPLICIT NONE
42
TYPE(UniformGrid_t), INTENT(IN) :: GRID
43
INTEGER, INTENT(IN) :: ID ! Numeric name for the grid
44
INTEGER :: loop
45
46
PRINT *, 'dims ', Grid % dims
47
PRINT *, 'coordinate count ', Grid % coord_count
48
IF ( GRID % IS_DEF ) THEN
49
PRINT *, 'x0 ', Grid % x0
50
PRINT *, 'dx ', Grid % dx
51
PRINT *, 'nmax ', Grid % nmax
52
PRINT *, 'x1 ', Grid % x1
53
PRINT *, 'eps ', Grid % eps
54
PRINT *, 'scale ', Grid % scale
55
PRINT *, 'move ', Grid % move
56
PRINT *, 'const vals ', Grid % const_vals
57
PRINT *, 'access perm ', Grid % access_perm
58
PRINT *, 'Elmer perm ', Grid % Elmer_perm
59
60
PRINT *,'NetCDF (Uniform) Grid Bounding Box ',ID,':'
61
DO loop = 1,size( GRID % x0, 1),1
62
PRINT '(A,I3,A,F20.2,F20.2)','Coordinate ', loop,':',GRID % X0(loop),GRID % X1(loop)
63
END DO
64
END IF
65
66
END SUBROUTINE PrintGrid
67
68
69
!------------------ InitGrid() -------------------------------------
70
!--- Initializes the contents of a grid
71
SUBROUTINE InitGrid( Grid, DIMS, COORDS )
72
!-------------------------------------------------------------------
73
USE Messages
74
IMPLICIT NONE
75
TYPE(UniformGrid_t), INTENT(INOUT) :: Grid
76
INTEGER, INTENT(IN) :: DIMS, COORDS
77
INTEGER :: alloc_stat
78
79
Grid % is_def = .TRUE.
80
Grid % dims = DIMS
81
Grid % coord_count = COORDS
82
IF ( DIMS .LE. 0 ) THEN
83
Grid % is_def = .FALSE.
84
RETURN
85
END IF
86
87
ALLOCATE (Grid % x0(COORDS),Grid % dx(COORDS),Grid % nmax(COORDS),Grid % x1(COORDS),&
88
Grid % eps(COORDS),Grid % scale(COORDS),Grid % move(COORDS),&
89
Grid % const_vals((DIMS-COORDS)),Grid % access_perm(DIMS),&
90
Grid % Elmer_perm(COORDS), STAT=alloc_stat)
91
IF ( alloc_stat .NE. 0 ) THEN
92
CALL Fatal('GridDataMapper','Memory ran out!')
93
END IF
94
95
Grid % x0 = 0.0_dp
96
Grid % dx = 0.0_dp
97
Grid % nmax = 0
98
Grid % x1 = 0.0_dp
99
Grid % eps = 0.0_dp
100
! With these initializations: 1*x(:) + 0 = x(:) ; i.e. doesn't modify
101
Grid % scale = 1.0_dp ! Default: no effect
102
Grid % move = 0.0_dp ! Default: no effect
103
Grid % const_vals = 0
104
Grid % access_perm = 0 ! Unusable index
105
Grid % Elmer_perm = 0 ! -"-
106
107
END SUBROUTINE InitGrid
108
109
!------------------ GetNetCDFGridParameters() ----------------------
110
!--- Takes the limits for the uniform grid of NetCDF
111
!--- (x0,y0,z0,...) is the lower left corner, (dx,dy,dz,...) contains the associated step sizes,
112
!--- and (nxmax,nymax,nzmax,...) are the amounts of steps
113
SUBROUTINE GetNetCDFGridParameters( NCID,Grid,DIM_IDS,DIM_LENS )
114
!-------------------------------------------------------------------
115
116
IMPLICIT NONE
117
INTEGER, INTENT(IN) :: NCID
118
TYPE(UniformGrid_t), INTENT(INOUT) :: Grid
119
INTEGER, INTENT(IN) :: DIM_IDS(:),DIM_LENS(:)
120
REAL(KIND=dp) :: first(1),first_two(2)
121
INTEGER :: ind, status, ind_vec(1),count_vec(1)
122
123
! Takes the first two values of all grid dimensions to determine the whole grid
124
ind_vec = 1
125
count_vec = 1
126
first = 0
127
first_two = 0
128
129
! Takes the first two values for each dimension, saves the information necessary for reconstructing the grid
130
! Assumes the NetCDF grid is uniform, the indexing of the dimensions enabled via the usual convention of variables with same names
131
DO ind = 1,GRID % COORD_COUNT,1
132
133
! If the only value on a dimension, there is no dx and only one value can be taken
134
IF (DIM_LENS(ind) .EQ. 1) THEN
135
CALL Warn('GridDataMapper','Scalar dimension encountered; No obtainable difference')
136
count_vec = 1
137
first = 0
138
Grid % dx(ind) = 0
139
status = NF90_GET_VAR(NCID,DIM_IDS(ind),first,ind_vec,count_vec)
140
ELSE
141
count_vec = 2
142
first_two = 0
143
status = NF90_GET_VAR(NCID,DIM_IDS(ind),first_two,ind_vec,count_vec)
144
END IF
145
IF ( G_ERROR(status,'NetCDF dimension values access failed.') ) THEN
146
CALL abort()
147
END IF
148
149
IF ( DIM_LENS(ind) .GT. 1 ) THEN
150
Grid % x0(ind) = first_two(1)
151
Grid % dx(ind) = first_two(2) - first_two(1)
152
ELSE
153
Grid % x0(ind) = first(1)
154
Grid % dx(ind) = 0
155
END IF
156
Grid % nmax(ind) = DIM_LENS(ind)
157
END DO
158
159
END SUBROUTINE GetNetCDFGridParameters
160
161
162
!------------------ FocusNetCDFGrid ----------------------
163
!--- Tightens the original bounding box until it touches the masked area
164
SUBROUTINE Focus2DNetCDFGrid( NCID,MASK_VAR,MASK_LIMIT,Grid,TIME,DIM_LENS )
165
!-------------------------------------------------------------------
166
IMPLICIT NONE
167
INTEGER, INTENT(IN) :: NCID,DIM_LENS(:)
168
TYPE(UniformGrid_t), INTENT(INOUT) :: Grid
169
REAL(KIND=dp) :: i_bl(2), i_br(2), i_ul(2), i_ur(2) ! Indices for the grid boundaries
170
CHARACTER(len = *), INTENT(IN) :: MASK_VAR ! The variable for catching the masking data from NetCDF
171
REAL(KIND=dp), INTENT(IN) :: MASK_LIMIT ! The limiting value which decides the mask has been reached
172
INTEGER, INTENT(IN) :: TIME ! Used time instance
173
174
REAL(KIND=dp), ALLOCATABLE :: scan_horizontal(:), scan_vertical(:) ! Scanning lines of differing dimensions
175
INTEGER :: low_limits(2,4), high_limits(2,4),i0(2),i1(2),line,mask_ind, alloc_stat ! The lowest and highest limits for each scanning line
176
INTEGER :: status, mask_id, index_vector(3), count_vert(3), count_horiz(3) ! For NetCDF access to the mask
177
LOGICAL :: is_vert(4), finished(4) ! True if the scanning line has reached the data mask
178
179
status = NF90_INQ_VARID(NCID,MASK_VAR,mask_id)
180
IF ( G_Error(status,'NetCDF mask variable name not found.') ) THEN
181
CALL abort()
182
END IF
183
184
index_vector = 1 ! Specialize later on
185
! Counts over one time value
186
count_vert = (/ 1,DIM_LENS(2),1 /) ! Vertical scanning line count
187
count_horiz = (/ DIM_LENS(1),1,1 /) ! Horizontal scanning line count
188
finished = .FALSE.
189
is_vert = .FALSE.
190
is_vert(1) = .TRUE. ! left scanning line is vertical
191
is_vert(3) = .TRUE. ! right scanning line is vertical
192
i0(:) = (/1,1/) ! Indices for lower left corner
193
i1(:) = Grid % nmax(1:Grid % COORD_COUNT) ! Indices for upper right corner
194
IF ( (i0(1) .EQ. i1(1)) .AND. (i0(2) .EQ. i1(2)) ) RETURN ! Does nothing if there is nothing to mask
195
196
! Allocates the scanning lines ; cannot avoid using at most the dimension sizes amount of memory at some point,
197
! and memory allocation/deallocation to accomodate for changing sizes would be slow, so of constant size.
198
ALLOCATE (scan_horizontal(DIM_LENS(1)), scan_vertical(DIM_LENS(2)), STAT = alloc_stat)
199
IF ( alloc_stat .NE. 0 ) THEN
200
CALL Fatal('GridDataMapper','Scanning line memory allocation failed')
201
END IF
202
203
! Collect the four corners of the grid (b - bottom, l - left, u - up, r - right)
204
i_bl(:) = i0(:)
205
i_br(1) = i1(1)
206
i_br(2) = i0(2)
207
i_ul(1) = i0(1)
208
i_ul(2) = i1(2)
209
i_ur(:) = i1(:)
210
211
! Initialize low_limits and high_limits with the input grid points
212
! ORDER of scanning lines: Left, down, right, up.
213
low_limits(:,1) = i_bl(:)
214
low_limits(:,2) = i_bl(:)
215
low_limits(:,3) = i_br(:)
216
low_limits(:,4) = i_ul(:)
217
218
high_limits(:,1) = i_ul(:)
219
high_limits(:,2) = i_br(:)
220
high_limits(:,3) = i_ur(:)
221
high_limits(:,4) = i_ur(:)
222
223
! Tunes each scanning line at a time to find the suitable rectangular grid region
224
DO line = 1,size(finished),1
225
226
! Chooses the right line type, fills it with data, finds the mask, updates the limits of the intersecting lines
227
IF ( is_vert(line) ) THEN ! 1) A vertical line
228
229
! Focuses the boundary until it's done
230
DO WHILE (.NOT. finished(line) )
231
232
! A) Gather data for scanning
233
! The x and time invariant during the search
234
index_vector = (/low_limits(1,line),1,1/)
235
236
status = NF90_GET_VAR(NCID,mask_id,scan_vertical,index_vector,count_vert)
237
IF ( G_ERROR(status,'NetCDF mask variable access failed.') ) THEN
238
CALL abort()
239
END IF
240
241
! B) The scan for the mask along y coordinate
242
DO mask_ind = 1,dim_lens(2)
243
! If reaches the mask, the current line is finished
244
IF ( scan_vertical(mask_ind) .GT. MASK_LIMIT ) THEN
245
finished(line) = .TRUE.
246
EXIT
247
END IF
248
END DO
249
250
! C) Every vertical line out of the box handled, tighten the box
251
IF ( .NOT. finished(line) ) THEN
252
IF ( line .EQ. 1 ) THEN ! Left line moves to right
253
low_limits(1,line) = low_limits(1,line) + 1
254
high_limits(1,line) = high_limits(1,line) + 1
255
ELSE ! Right line moves to left
256
low_limits(1,line) = low_limits(1,line) - 1
257
high_limits(1,line) = high_limits(1,line) - 1
258
END IF
259
END IF
260
261
! D) Finishing criteria, if no mask found (left and right vertical lines reach each other)
262
IF ( low_limits(1,1) .EQ. low_limits(1,3) ) THEN
263
finished(1) = .TRUE.
264
finished(3) = .TRUE.
265
END IF
266
END DO
267
268
! E) Vertical line's x coordinates restrain the intersecting horizontal lines
269
IF ( line .EQ. 1 ) THEN ! Left vertical scanning line
270
low_limits(1,2) = low_limits(1,line) ! Down horizontal, low
271
low_limits(1,4) = low_limits(1,line) ! Up horizontal, low
272
ELSE ! Right vertical scanning line
273
high_limits(1,2) = low_limits(1,line) ! Down horizontal, high
274
high_limits(1,4) = low_limits(1,line) ! Up horizontal, high
275
END IF
276
277
ELSE ! 2) A horizontal line
278
279
! Focuses the boundary until it's done
280
DO WHILE (.NOT. finished(line) )
281
282
! A) Gather data for scanning
283
! The y and time invariant during the search
284
index_vector = (/1,low_limits(2,line),1/)
285
286
status = NF90_GET_VAR(NCID,mask_id,scan_horizontal,index_vector,count_horiz)
287
IF ( G_ERROR(status,'NetCDF mask variable access failed.') ) THEN
288
CALL abort()
289
END IF
290
291
! B) The scan for the mask
292
DO mask_ind = 1,dim_lens(1)
293
IF ( scan_horizontal(mask_ind) .GT. MASK_LIMIT ) THEN
294
finished(line) = .TRUE.
295
EXIT
296
END IF
297
END DO
298
299
! C) Every horizontal line out of the box has been handled
300
IF ( .NOT. finished(line) ) THEN
301
IF ( line .EQ. 2 ) THEN ! Lower line moves up
302
low_limits(2,line) = low_limits(2,line) + 1
303
high_limits(2,line) = high_limits(2,line) + 1
304
ELSE ! Upper line moves down
305
low_limits(2,line) = low_limits(2,line) - 1
306
high_limits(2,line) = high_limits(2,line) - 1
307
END IF
308
END IF
309
310
! D) Finishing criteria, if no mask found (upper and lower horizontal line reach each other)
311
IF ( low_limits(2,2) .EQ. low_limits(2,4) ) THEN
312
finished(2) = .TRUE.
313
finished(4) = .TRUE.
314
END IF
315
END DO
316
317
! E) Horizontal line's y coordinates restrain the intersecting vertical lines
318
IF ( line .EQ. 2 ) THEN ! Lower horizontal scanning line
319
low_limits(2,1) = low_limits(2,line) ! Left vertical, low
320
low_limits(2,3) = low_limits(2,line) ! Right vertical, low
321
ELSE ! Upper horizontal scanning line
322
high_limits(2,1) = low_limits(2,line) ! Left vertical, high
323
high_limits(2,3) = low_limits(2,line) ! Right vertical, high
324
END IF
325
326
END IF
327
328
END DO
329
330
!---- Finally, updates the values
331
i0(:) = low_limits(:,1)
332
i1(:) = high_limits(:,4)
333
Grid % x0(1:Grid % COORD_COUNT) = Grid % x0(1:Grid % COORD_COUNT) + (i0(:) - 1)* Grid % DX(1:Grid % COORD_COUNT)
334
Grid % nmax(1:Grid % COORD_COUNT) = i1(:) - i0(:) + 1
335
336
WRITE (Message,'(A)') '2D grid focusing complete:'
337
CALL Info('GridDataMapper', Message)
338
WRITE (Message,'(A,2(I5),A,2(I5))') 'Lower left indices: ', i0, ' upper right indices: ', i1
339
CALL Info('GridDataMapper', Message)
340
WRITE (Message,'(A,2(F14.1),A,2(I5))') 'x0: ', Grid % x0, ' nmax: ', Grid % nmax
341
CALL Info('GridDataMapper', Message)
342
343
END SUBROUTINE Focus2DNetCDFGrid
344
345
END MODULE NetCDFGridUtils
346
347