libsim Versione 8.0.0
grid_transform_class.F90
1! Copyright (C) 2010 ARPA-SIM <urpsim@smr.arpa.emr.it>
2! authors:
3! Davide Cesari <dcesari@arpa.emr.it>
4! Paolo Patruno <ppatruno@arpa.emr.it>
5
6! This program is free software; you can redistribute it and/or
7! modify it under the terms of the GNU General Public License as
8! published by the Free Software Foundation; either version 2 of
9! the License, or (at your option) any later version.
10
11! This program is distributed in the hope that it will be useful,
12! but WITHOUT ANY WARRANTY; without even the implied warranty of
13! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14! GNU General Public License for more details.
15
16! You should have received a copy of the GNU General Public License
17! along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19#include "config.h"
20
21!> Module for defining transformations between rectangular
22!! georeferenced grids and between grids and sparse points and
23!! vice-versa. The module defines two classes: \a transform_def which
24!! describes an 'abstract' transformation between grids and/or sparse
25!! points, and \a grid_transform which includes a \a transform_def
26!! object and describes a transformation between specific grids and/or
27!! sets of sparse points, with precomputed coefficients specific to
28!! the grids/sparse points involved. A single \a transform_def object
29!! can be used for defining different \a grid_transform objects which
30!! apply the same transformation to different sets of grids/sparse
31!! points pairs. In the same manner, a single \a grid_transform object
32!! can be used for interpolating any number of fields lying on the
33!! same grids/sparse points pair, thus recycling the interpolation
34!! coefficients which are computed only once at the time of defining
35!! the \a grid_transform object.
36!!
37!! This module performs tranformations at a relatively low-level, on
38!! 2d/3d sections of data, and it is meant primarily for use by higher
39!! level methods in \a volgrid6d_class, which operate on full volumes
40!! of physically determined quantities; however it is possible to use
41!! it in a stand-alone way as well.
42!!
43!! Different abstract transformations are supported, defined by the
44!! parameter \a trans_type, and its corresponding \a sub_type:
45!!
46!! - trans_type='zoom' a new grid is obtained by cutting or extending
47!! (with missing values) the input grid, adding or removing points
48!! on the four sides, without changing the geographical reference
49!! system (grid-to-grid)
50!! - sub_type='coord' the corners of the zoomed/extended area are
51!! defined by geographical coordinates
52!! - sub_type='coordbb' the bounds of the zoomed/extended area are
53!! computed so to contain all the input points which lie in the
54!! provided lon/lat bounding box
55!! - sub_type='projcoord' the corners of the zoomed/extended area
56!! are defined by projected coordinates
57!! - sub_type='index' the bounds of the zoomed/extended area are
58!! defined by grid indices.
59!!
60!! - trans_type='boxregrid' regrids the input data grid on a new grid
61!! in which the value at every point is the result of a function
62!! computed over \a npx X \a npy points of the original grid,
63!! without changing the geographical reference system
64!! (grid-to-grid)
65!! - sub_type='average' the function used is the average.
66!! - sub_type='stddev' the function used is the standard deviation.
67!! - sub_type='stddevnm1' the function used is the standard
68!! deviation computed with n-1.
69!! - sub_type='max' the function used is the maximum
70!! - sub_type='min' the function used is the minimum
71!! - sub_type='percentile' the function used is a requested
72!! percentile of the input points distribution.
73!!
74!! - trans_type='inter' interpolates the input data on a new set of
75!! specified points
76!! - sub_type='near' the interpolated value is that of the nearest
77!! input point (grid-to-grid, grid-to-sparse point)
78!! - sub_type='bilin' the interpolated value is computed as a
79!! bilinear interpolation of the 4 surrounding input points
80!! (grid-to-grid, grid-to-sparse point)
81!! - sub_type='linear' the interpolated value is computed as a
82!! linear interpolation of the 3 surrounding input points
83!! individuated by means of a triangulation procedure (sparse
84!! points-to-grid, sparse points-to-sparse points).
85!! - sub_type='shapiro_near' the interpolated value is that of sub_type=near
86!! after smoothing the input field with a shapiro filter of order 2.
87!!
88!! - trans_type='intersearch' (grid-to-grid, grid-to-sparse point)
89!! interpolates the input data on a new set of specified points, it
90!! supports sub_type values 'near', 'bilin' and 'shapiro_near' with
91!! the same meaning as trans_type='inter' but, when interpolation
92!! is not possible because of missing values in input, it sets the
93!! output value to the value of the nearest valid point in the
94!! input grid.
95!!
96!! - trans_type='boxinter' computes data on a new grid in which the
97!! value at every point is the result of a function computed over
98!! the input points lying inside the output point's grid box
99!! (grid-to-grid and sparse points-to-grid)
100!! - sub_type='average' the function used is the average
101!! - sub_type='stddev' the function used is the standard deviation
102!! - sub_type='stddevnm1' the function used is the standard
103!! deviation computed with n-1
104!! - sub_type='max' the function used is the maximum
105!! - sub_type='min' the function used is the minimum
106!! - sub_type='percentile' the function used is a requested
107!! percentile of the input points distribution
108!! - sub_type='frequency' the function used is the fraction of
109!! points in the box having value included in a specified
110!! interval.
111!!
112!! - trans_type='polyinter' output data are the result of a function
113!! computed over the input points lying inside a set of
114!! georeferenced polygons; polygons cannot overlap each other; for
115!! output on sparse points, the number of output points is equal to
116!! the number of polygons and output point coordinates are defined
117!! as the centroids of the polygons, for output on a grid, the
118!! output grid is equal to the input grid and the value of the
119!! field is repeated for all points within each polygon, while it
120!! is undefined outside all polygons (grid-to-grid, grid-to-sparse
121!! points and sparse points-to-sparse points)
122!! - sub_type='average' the function used is the average
123!! - sub_type='stddev' the function used is the standard deviation.
124!! - sub_type='max' the function used is the maximum
125!! - sub_type='min' the function used is the minimum
126!! - sub_type='percentile' the function used is a requested
127!! percentile of the input points distribution.
128!!
129!! - trans_type='stencilinter' computes data on a new set of
130!! specified points, for each of which the value is the result of a
131!! function computed over the input grid points forming a circular
132!! stencil centered on the nearest input grid point, with radius \a
133!! radius in input grid point units; stencils can overlap each
134!! other (grid-to-grid and grid-to-sparse points)
135!! - sub_type='average' the function used is the average
136!! - sub_type='stddev' the function used is the standard deviation.
137!! - sub_type='stddevnm1' the function used is the standard
138!! deviation computed with n-1.
139!! - sub_type='max' the function used is the maximum
140!! - sub_type='min' the function used is the minimum
141!! - sub_type='percentile' the function used is a requested
142!! percentile of the input points distribution.
143!!
144!! - trans_type='maskinter' takes a 2D field on the same grid as the
145!! input points, it divides it in a number of subareas according to
146!! its values and and optional list of boundaries
147!! and every subarea is used as a mask for
148!! interpolation; data are thus computed on a new set of points,
149!! the number of which is equal to the number of subareas, and for
150!! each of which the value is the result of a function computed
151!! over those input points belonging to the corresponding subarea;
152!! the output point coordinates are defined as the centroids of the
153!! subareas (grid-to-sparse points)
154!! - sub_type='average' the function used is the average
155!! - sub_type='stddev' the function used is the standard deviation.
156!! - sub_type='stddevnm1' the function used is the standard
157!! deviation computed with n-1.
158!! - sub_type='max' the function used is the maximum
159!! - sub_type='min' the function used is the minimum
160!! - sub_type='percentile' the function used is a requested
161!! percentile of the input points distribution.
162!!
163!! - trans_type='maskgen' generates a mask field, on the same grid as
164!! the input, suitable to be used for 'maskinter' interpolation
165!! (grid-to-grid)
166!! - sub_type='poly' the output mask field contains, at each point,
167!! integer values from 1 to the number of polygons provided,
168!! computed according to the polygon in which every point lies
169!! - sub_type='grid' the output mask field contains, at each point,
170!! integer values from 1 to the number of grid cells in the
171!! pseudo-output grid provided, depending on the index of the
172!! cell in which every point lies.
173!!
174!! - trans_type='metamorphosis' the values of the output points are
175!! the same as the input ones, but something external in the data
176!! may change, e.g. only a subset of input data is kept in output
177!! or the underlying data structure changes, as from grid to sparse
178!! points (grid-to-grid, grid-to-sparse points, sparse
179!! points-to-sparse points)
180!! - sub_type='all' all the input points are kept in the output
181!! (grid-to-sparse points)
182!! - sub_type='coordbb' the input points which lie in the provided
183!! lon/lat bounding box are kept in the output (grid-to-sparse
184!! points or sparse points-to-sparse points).
185!! - sub_type='poly' the input points which lie in any of the
186!! polygons provided are kept in the output; points are marked
187!! with the number of polygon they belong to (grid-to-sparse
188!! points or sparse points-to-sparse points).
189!! - sub_type='mask' the input points which belong to any valid
190!! subarea defined by a mask field, as for
191!! trans_type='maskinter', are kept in the output; points are
192!! marked with the number of the subarea they belong to
193!! (grid-to-sparse points).
194!! - sub_type='maskvalid' the input points corresponding to points
195!! having valid data and optionally having values within
196!! requested bounds in a 2-D mask field, are kept in the output;
197!! the other points are filled with missing values
198!! (grid-to-grid).
199!! - sub_type='maskinvalid' the input points corresponding to points
200!! having non valid data in a 2-D mask field, are kept in the
201!! output; the other points are filled with missing values
202!! (grid-to-grid).
203!! - sub_type='lemaskinvalid', 'ltmaskinvalid', 'gemaskinvalid', 'gtmaskinvalid'
204!! the input points having values <=, <, >=, > than values of a 2-D mask field
205!! respectively, are filled with missing values, the other points are
206!! kept as they are (grid-to-grid).
207!! - sub_type='setinvalidto' the input points having non valid data
208!! are set to a user-specified constant value (grid-to-grid or
209!! sparse points-to-sparse points).
210!! - sub_type='settoinvalid' the input points having values
211!! included in the requested bounds are set to an invalid value,
212!! the others are kept unchanged (grid-to-grid or sparse
213!! points-to-sparse points).
214!!
215!! \ingroup volgrid6d
217USE vol7d_class
218USE err_handling
219USE geo_proj_class
220USE grid_class
225USE simple_stat
226USE log4fortran
227IMPLICIT NONE
228
229CHARACTER(len=255),PARAMETER:: subcategory="grid_transform_class"
230
231! information for interpolation aver a rectangular area
232TYPE area_info
233 double precision :: boxdx ! longitudinal/x extension of the box for box interpolation, default the target x grid step
234 double precision :: boxdy ! latitudinal/y extension of the box for box interpolation, default the target y grid step
235 double precision :: radius ! radius in gridpoints for stencil interpolation
236END TYPE area_info
237
238! information for statistical processing of interpoland data
239TYPE stat_info
240 DOUBLE PRECISION :: percentile ! percentile [0,100] of the distribution of points in the box to use as interpolated value, if missing, the average is used, if 0.or 100. the MIN() and MAX() are used as a shortcut
241END TYPE stat_info
242
243! information for point interval
244TYPE interval_info
245 REAL :: gt=rmiss ! lower limit of interval, missing for -inf
246 REAL :: lt=rmiss ! upper limit of interval, missing for +inf
247 LOGICAL :: ge=.true. ! if true >= otherwise >
248 LOGICAL :: le=.true. ! if true <= otherwise <
249END TYPE interval_info
250
251! rectangle index information
252type rect_ind
253 INTEGER :: ix ! index of initial point of new grid on x
254 INTEGER :: iy ! index of initial point of new grid on y
255 INTEGER :: fx ! index of final point of new grid on x
256 INTEGER :: fy ! index of final point of new grid on y
257end type rect_ind
258
259! rectangle coord information
260type rect_coo
261 DOUBLEPRECISION ilon ! coordinate of initial point of new grid on x
262 DOUBLEPRECISION ilat ! coordinate of initial point of new grid on y
263 DOUBLEPRECISION flon ! coordinate of final point of new grid on x
264 DOUBLEPRECISION flat ! coordinate of final point of new grid on y
265end type rect_coo
266
267! box information
268type box_info
269 INTEGER :: npx ! number of points along x direction
270 INTEGER :: npy ! number of points along y direction
271end type box_info
272
273! Vertical interpolation information.
274! The input vertical coordinate can be indicated either as the value
275! of the vertical level (so that it will be the same on every point
276! at a given vertical level), or as the value of a specified variable
277! at each point in space (so that it will depend on the horizontal
278! position too).
279TYPE vertint
280! CHARACTER(len=80) :: sub_type ! subtype of transformation, can be \c 'linear'
281 TYPE(vol7d_level) :: input_levtype ! type of vertical level of input data (only type of first and second surface are used, level values are ignored)
282 TYPE(vol7d_var) :: input_coordvar ! variable that defines the vertical coordinate in the input volume, if missing, the value of the vertical level is used
283 TYPE(vol7d_level) :: output_levtype ! type of vertical level of output data (only type of first and second surface are used, level values are ignored)
284END TYPE vertint
285
286!> This object defines in an abstract way the type of transformation
287!! to be applied.
288!! It does not carry specific information about the grid to which it
289!! will be applied, so the same instance can be reused for
290!! transforming in the same way different grids.
291TYPE transform_def
292 PRIVATE
293 CHARACTER(len=80) :: trans_type ! type of transformation, can be \c 'zoom', \c 'boxregrid', \c 'inter', \c 'vertint' ...
294 CHARACTER(len=80) :: sub_type ! subtype of transformation, can be \c 'linear'
295 LOGICAL :: extrap ! enable elaboration outside data bounding box
296 TYPE(rect_ind) :: rect_ind ! rectangle information by index
297 TYPE(rect_coo) :: rect_coo ! rectangle information by coordinates
298 TYPE(area_info) :: area_info !
299 TYPE(arrayof_georef_coord_array) :: poly ! polygon information
300 TYPE(stat_info) :: stat_info !
301 TYPE(interval_info) :: interval_info !
302 TYPE(box_info) :: box_info ! boxregrid specification
303 TYPE(vertint) :: vertint ! vertical interpolation specification
304 INTEGER :: time_definition ! time definition for interpolating to sparse points
305 TYPE(l4f_handle) :: category ! category for log4fortran
306END TYPE transform_def
307
308
309!> This object fully defines a transformation between a couple of
310!! particular \a griddim_def or \a vol7d objects (any combination is
311!! possible). It carries information about the objects' mutual
312!! coordinates in order to speed up the transformation when it has to
313!! be repeated on objects having the same coordinates and grid
314!! projections.
316 PRIVATE
317 TYPE(transform_def),PUBLIC :: trans ! type of transformation required
318 INTEGER :: innx = imiss
319 INTEGER :: inny = imiss
320 INTEGER :: innz = imiss
321 INTEGER :: outnx = imiss
322 INTEGER :: outny = imiss
323 INTEGER :: outnz = imiss
324 INTEGER :: levshift = imiss
325 INTEGER :: levused = imiss
326 INTEGER :: iniox,inioy,infox,infoy,outinx,outiny,outfnx,outfny
327 INTEGER,POINTER :: inter_index_x(:,:) => null()
328 INTEGER,POINTER :: inter_index_y(:,:) => null()
329 INTEGER,POINTER :: inter_index_z(:) => null()
330 INTEGER,POINTER :: point_index(:,:) => null()
331 DOUBLE PRECISION,POINTER :: inter_x(:,:) => null()
332 DOUBLE PRECISION,POINTER :: inter_y(:,:) => null()
333 DOUBLE PRECISION,POINTER :: inter_xp(:,:) => null()
334 DOUBLE PRECISION,POINTER :: inter_yp(:,:) => null()
335 DOUBLE PRECISION,POINTER :: inter_zp(:) => null()
336 DOUBLE PRECISION,POINTER :: vcoord_in(:) => null()
337 DOUBLE PRECISION,POINTER :: vcoord_out(:) => null()
338 LOGICAL,POINTER :: point_mask(:,:) => null()
339 LOGICAL,POINTER :: stencil(:,:) => null()
340 REAL,ALLOCATABLE :: coord_3d_in(:,:,:)
341 REAL,ALLOCATABLE :: val_mask(:,:)
342 REAL :: val1 = rmiss
343 REAL :: val2 = rmiss
344 LOGICAL :: recur = .false.
345 LOGICAL :: dolog = .false. ! must compute log() of vert coord before vertint
346
347! type(volgrid6d) :: input_vertcoordvol ! volume which provides the input vertical coordinate if separated from the data volume itself (for vertint) cannot be here because of cross-use, should be an argument of compute
348! type(vol7d_level), pointer :: output_vertlevlist(:) ! list of vertical levels of output data (for vertint) can be here or an argument of compute, how to do?
349 TYPE(vol7d_level),POINTER :: output_level_auto(:) => null() ! array of auto-generated levels, stored for successive query
350 TYPE(l4f_handle) :: category ! category for log4fortran
351 LOGICAL :: valid = .false. ! the transformation has been successfully initialised
352 PROCEDURE(basic_find_index),NOPASS,POINTER :: find_index => basic_find_index ! allow a local implementation of find_index
353END TYPE grid_transform
354
355
356!> Constructors of the corresponding objects.
357INTERFACE init
358 MODULE PROCEDURE transform_init, grid_transform_levtype_levtype_init, &
359 grid_transform_init, &
360 grid_transform_grid_vol7d_init, grid_transform_vol7d_grid_init, &
361 grid_transform_vol7d_vol7d_init
362END INTERFACE
363
364!> Destructors of the corresponding objects.
365INTERFACE delete
366 MODULE PROCEDURE transform_delete, grid_transform_delete
367END INTERFACE
368
369!> Method for returning the contents of the object.
370INTERFACE get_val
371 MODULE PROCEDURE transform_get_val, grid_transform_get_val
372END INTERFACE
373
374!> Compute the output data array from input data array according to
375!! the defined transformation.
376INTERFACE compute
377 MODULE PROCEDURE grid_transform_compute, grid_transform_v7d_grid_compute
378END INTERFACE
379
380!> Returns \a .TRUE. if, after \a init , the corresponding \a grid_transform
381!! object has been correctly initialised.
382INTERFACE c_e
383 MODULE PROCEDURE grid_transform_c_e
384END INTERFACE
385
386PRIVATE
387PUBLIC init, delete, get_val, compute, c_e
389PUBLIC interval_info, interval_info_new, interval_info_valid, basic_find_index
390
391CONTAINS
392
393
394FUNCTION interval_info_new(interv_gt, interv_ge, interv_lt, interv_le) RESULT(this)
395REAL,INTENT(in),OPTIONAL :: interv_gt !< greater than condition for defining interval
396REAL,INTENT(in),OPTIONAL :: interv_ge !< greater equal condition for defining interval
397REAL,INTENT(in),OPTIONAL :: interv_lt !< less than condition for defining interval
398REAL,INTENT(in),OPTIONAL :: interv_le !< less equal condition for defining interval
399
400TYPE(interval_info) :: this
401
402IF (PRESENT(interv_gt)) THEN
403 IF (c_e(interv_gt)) THEN
404 this%gt = interv_gt
405 this%ge = .false.
406 ENDIF
407ENDIF
408IF (PRESENT(interv_ge)) THEN
409 IF (c_e(interv_ge)) THEN
410 this%gt = interv_ge
411 this%ge = .true.
412 ENDIF
413ENDIF
414IF (PRESENT(interv_lt)) THEN
415 IF (c_e(interv_lt)) THEN
416 this%lt = interv_lt
417 this%le = .false.
418 ENDIF
419ENDIF
420IF (PRESENT(interv_le)) THEN
421 IF (c_e(interv_le)) THEN
422 this%lt = interv_le
423 this%le = .true.
424 ENDIF
425ENDIF
426
427END FUNCTION interval_info_new
428
429! Private method for testing whether \a val is included in \a this
430! interval taking into account all cases, zero, one or two extremes,
431! strict or non strict inclusion, empty interval, etc, while no check
432! is made for val being missing. Returns 1.0 if val is in interval and
433! 0.0 if not.
434ELEMENTAL FUNCTION interval_info_valid(this, val)
435TYPE(interval_info),INTENT(in) :: this
436REAL,INTENT(in) :: val
437
438REAL :: interval_info_valid
439
440interval_info_valid = 1.0
441
442IF (c_e(this%gt)) THEN
443 IF (val < this%gt) interval_info_valid = 0.0
444 IF (.NOT.this%ge) THEN
445 IF (val == this%gt) interval_info_valid = 0.0
446 ENDIF
447ENDIF
448IF (c_e(this%lt)) THEN
449 IF (val > this%lt) interval_info_valid = 0.0
450 IF (.NOT.this%le) THEN
451 IF (val == this%lt) interval_info_valid = 0.0
452 ENDIF
453ENDIF
454
455END FUNCTION interval_info_valid
456
457!> Constructor for a \a transform_def object, defining an abstract
458!! transformation between gridded and/or sparse point data. The
459!! parameters \a trans_type and \a sub_type define the type of
460!! transformation, while all the other following parameters are
461!! optional, they have to be passed in keyword mode and those required
462!! by the transformation type and subtype chosen have to be present.
463SUBROUTINE transform_init(this, trans_type, sub_type, &
464 ix, iy, fx, fy, ilon, ilat, flon, flat, &
465 npx, npy, boxdx, boxdy, radius, poly, percentile, &
466 interv_gt, interv_ge, interv_lt, interv_le, &
467 extrap, time_definition, &
468 input_levtype, input_coordvar, output_levtype, categoryappend)
469TYPE(transform_def),INTENT(out) :: this !< transformation object
470CHARACTER(len=*) :: trans_type !< type of transformation, can be \c 'zoom', \c 'boxregrid', \c 'interp', \c 'vertint' ...
471CHARACTER(len=*) :: sub_type !< sub type of transformation, it depends on \a trans_type
472INTEGER,INTENT(in),OPTIONAL :: ix !< index of initial point of new grid on x (for zoom)
473INTEGER,INTENT(in),OPTIONAL :: iy !< index of initial point of new grid on y (for zoom)
474INTEGER,INTENT(in),OPTIONAL :: fx !< index of final point of new grid on x (for zoom)
475INTEGER,INTENT(in),OPTIONAL :: fy !< index of final point of new grid on y (for zoom)
476DOUBLEPRECISION,INTENT(in),OPTIONAL :: ilon !< coordinate of initial point of new grid or of bounding box on x (for zoom and metamorphosis)
477DOUBLEPRECISION,INTENT(in),OPTIONAL :: ilat !< coordinate of initial point of new grid or of bounding box on y (for zoom and metamorphosis)
478DOUBLEPRECISION,INTENT(in),OPTIONAL :: flon !< coordinate of final point of new grid or of bounding box on x (for zoom and metamorphosis)
479DOUBLEPRECISION,INTENT(in),OPTIONAL :: flat !< coordinate of final point of new grid or of bounding box on y (for zoom and metamorphosis)
480INTEGER,INTENT(IN),OPTIONAL :: npx !< number of points to average along x direction (for boxregrid)
481INTEGER,INTENT(IN),OPTIONAL :: npy !< number of points to average along y direction (for boxregrid)
482DOUBLEPRECISION,INTENT(in),OPTIONAL :: boxdx !< longitudinal/x extension of the box for box interpolation, default the target x grid step (unimplemented !)
483DOUBLEPRECISION,INTENT(in),OPTIONAL :: boxdy !< latitudinal/y extension of the box for box interpolation, default the target y grid step (unimplemented !)
484DOUBLEPRECISION,INTENT(in),OPTIONAL :: radius !< radius of stencil in grid points (also fractionary values) for stencil interpolation
485TYPE(arrayof_georef_coord_array),OPTIONAL :: poly !< array of polygons indicating areas over which to interpolate (for transformations 'polyinter' or 'metamorphosis:poly')
486DOUBLEPRECISION,INTENT(in),OPTIONAL :: percentile !< percentile [0,100.] of the distribution of points in the box to use as interpolated value for 'percentile' subtype
487REAL,INTENT(in),OPTIONAL :: interv_gt !< greater than condition for defining interval
488REAL,INTENT(in),OPTIONAL :: interv_ge !< greater equal condition for defining interval
489REAL,INTENT(in),OPTIONAL :: interv_lt !< less than condition for defining interval
490REAL,INTENT(in),OPTIONAL :: interv_le !< less equal condition for defining interval
491LOGICAL,INTENT(IN),OPTIONAL :: extrap !< activate extrapolation outside input domain (use with care!)
492INTEGER,INTENT(IN),OPTIONAL :: time_definition !< time definition for output vol7d object 0=time is reference time ; 1=time is validity time
493TYPE(vol7d_level),INTENT(IN),OPTIONAL :: input_levtype !< type of vertical level of input data to be vertically interpolated (only type of first and second surface are used, level values are ignored)
494TYPE(vol7d_var),INTENT(IN),OPTIONAL :: input_coordvar !< variable that defines the vertical coordinate in the input volume for vertical interpolation, if missing, the value of the vertical level defined with \a input_levtype is used
495TYPE(vol7d_level),INTENT(IN),OPTIONAL :: output_levtype !< type of vertical level to which data should be vertically interpolated (only type of first and second surface are used, level values are ignored)
496character(len=*),INTENT(in),OPTIONAL :: categoryappend !< suffix to append to log4fortran namespace category
497
498character(len=512) :: a_name
499
500IF (PRESENT(categoryappend)) THEN
501 CALL l4f_launcher(a_name,a_name_append=trim(subcategory)//"."// &
502 trim(categoryappend))
503ELSE
504 CALL l4f_launcher(a_name,a_name_append=trim(subcategory))
505ENDIF
506this%category=l4f_category_get_handle(a_name)
507
508this%trans_type = trans_type
509this%sub_type = sub_type
510
511CALL optio(extrap,this%extrap)
512
513call optio(ix,this%rect_ind%ix)
514call optio(iy,this%rect_ind%iy)
515call optio(fx,this%rect_ind%fx)
516call optio(fy,this%rect_ind%fy)
517
518call optio(ilon,this%rect_coo%ilon)
519call optio(ilat,this%rect_coo%ilat)
520call optio(flon,this%rect_coo%flon)
521call optio(flat,this%rect_coo%flat)
522
523CALL optio(boxdx,this%area_info%boxdx)
524CALL optio(boxdy,this%area_info%boxdy)
525CALL optio(radius,this%area_info%radius)
526IF (PRESENT(poly)) this%poly = poly
527CALL optio(percentile,this%stat_info%percentile)
528
529this%interval_info = interval_info_new(interv_gt, interv_ge, interv_lt, interv_le)
530
531CALL optio(npx,this%box_info%npx)
532CALL optio(npy,this%box_info%npy)
533
534IF (PRESENT(input_levtype)) THEN
535 this%vertint%input_levtype = input_levtype
536ELSE
537 this%vertint%input_levtype = vol7d_level_miss
538ENDIF
539IF (PRESENT(input_coordvar)) THEN
540 this%vertint%input_coordvar = input_coordvar
541ELSE
542 this%vertint%input_coordvar = vol7d_var_miss
543ENDIF
544IF (PRESENT(output_levtype)) THEN
545 this%vertint%output_levtype = output_levtype
546ELSE
547 this%vertint%output_levtype = vol7d_level_miss
548ENDIF
549
550call optio(time_definition,this%time_definition)
551if (c_e(this%time_definition) .and. &
552 (this%time_definition < 0 .OR. this%time_definition > 2))THEN
553 call l4f_category_log(this%category,l4f_error,"Error time_definition invalid: "//to_char(this%time_definition))
554 call raise_fatal_error()
555end if
556
557
558IF (this%trans_type == 'zoom') THEN
559
560 IF (this%sub_type == 'coord' .OR. this%sub_type == 'projcoord')THEN
561
562 if (c_e(this%rect_coo%ilon) .and. c_e(this%rect_coo%ilat) .and. &
563 c_e(this%rect_coo%flon) .and. c_e(this%rect_coo%flat)) then ! coordinates given
565!check
566 if ( this%rect_coo%ilon > this%rect_coo%flon .or. &
567 this%rect_coo%ilat > this%rect_coo%flat ) then
568
569 call l4f_category_log(this%category,l4f_error, &
570 "invalid zoom coordinates: ")
571 call l4f_category_log(this%category,l4f_error, &
572 trim(to_char(this%rect_coo%ilon))//'/'// &
573 trim(to_char(this%rect_coo%flon)))
574 call l4f_category_log(this%category,l4f_error, &
575 trim(to_char(this%rect_coo%ilat))//'/'// &
576 trim(to_char(this%rect_coo%flat)))
577 call raise_fatal_error()
578 end if
579
580 else
581
582 call l4f_category_log(this%category,l4f_error,"zoom: coord parameters missing")
583 call raise_fatal_error()
584
585 end if
586
587 else if (this%sub_type == 'coordbb')then
588
589 if (c_e(this%rect_coo%ilon) .and. c_e(this%rect_coo%ilat) .and. &
590 c_e(this%rect_coo%flon) .and. c_e(this%rect_coo%flat)) then ! coordinates given
591 else
592
593 call l4f_category_log(this%category,l4f_error,"zoom: coordbb parameters missing")
594 call raise_fatal_error()
595
596 end if
597
598 else if (this%sub_type == 'index')then
599
600 IF (c_e(this%rect_ind%ix) .AND. c_e(this%rect_ind%iy) .AND. &
601 c_e(this%rect_ind%fx) .AND. c_e(this%rect_ind%fy)) THEN
602
603! check
604 IF (this%rect_ind%ix > this%rect_ind%fx .OR. &
605 this%rect_ind%iy > this%rect_ind%fy) THEN
606
607 CALL l4f_category_log(this%category,l4f_error,'invalid zoom indices: ')
608 CALL l4f_category_log(this%category,l4f_error, &
609 trim(to_char(this%rect_ind%ix))//'/'// &
610 trim(to_char(this%rect_ind%fx)))
611 CALL l4f_category_log(this%category,l4f_error, &
612 trim(to_char(this%rect_ind%iy))//'/'// &
613 trim(to_char(this%rect_ind%fy)))
614
615 CALL raise_fatal_error()
616 ENDIF
617
618 ELSE
619
620 CALL l4f_category_log(this%category,l4f_error,&
621 'zoom: index parameters ix, iy, fx, fy not provided')
622 CALL raise_fatal_error()
623
624 ENDIF
625
626 ELSE
627 CALL sub_type_error()
628 RETURN
629 END IF
630
631ELSE IF (this%trans_type == 'inter' .OR. this%trans_type == 'intersearch') THEN
632
633 IF (this%sub_type == 'near' .OR. this%sub_type == 'bilin' .OR. &
634 this%sub_type == 'linear' .OR. this%sub_type == 'shapiro_near') THEN
635! nothing to do here
636 ELSE
637 CALL sub_type_error()
638 RETURN
639 ENDIF
640
641ELSE IF (this%trans_type == 'boxregrid' .OR. this%trans_type == 'boxinter' .OR. &
642 this%trans_type == 'polyinter' .OR. this%trans_type == 'maskinter' .OR. &
643 this%trans_type == 'stencilinter') THEN
644
645 IF (this%trans_type == 'boxregrid') THEN
646 IF (c_e(this%box_info%npx) .AND. c_e(this%box_info%npy)) THEN
647 IF (this%box_info%npx <= 0 .OR. this%box_info%npy <= 0 ) THEN
648 CALL l4f_category_log(this%category,l4f_error,'boxregrid: invalid parameters '//&
649 trim(to_char(this%box_info%npx))//' '//trim(to_char(this%box_info%npy)))
650 CALL raise_fatal_error()
651 ENDIF
652 ELSE
653 CALL l4f_category_log(this%category,l4f_error, &
654 'boxregrid: parameters npx, npy missing')
655 CALL raise_fatal_error()
656 ENDIF
657 ENDIF
658
659 IF (this%trans_type == 'polyinter') THEN
660 IF (this%poly%arraysize <= 0) THEN
661 CALL l4f_category_log(this%category,l4f_error, &
662 "polyinter: poly parameter missing or empty")
663 CALL raise_fatal_error()
664 ENDIF
665 ENDIF
666
667 IF (this%trans_type == 'stencilinter') THEN
668 IF (.NOT.c_e(this%area_info%radius)) THEN
669 CALL l4f_category_log(this%category,l4f_error, &
670 "stencilinter: radius parameter missing")
671 CALL raise_fatal_error()
672 ENDIF
673 ENDIF
674
675 IF (this%sub_type == 'average' .OR. this%sub_type == 'stddev' &
676 .OR. this%sub_type == 'stddevnm1') THEN
677 this%stat_info%percentile = rmiss
678 ELSE IF (this%sub_type == 'max') THEN
679 this%stat_info%percentile = 101.
680 ELSE IF (this%sub_type == 'min') THEN
681 this%stat_info%percentile = -1.
682 ELSE IF (this%sub_type == 'percentile') THEN
683 IF (.NOT.c_e(this%stat_info%percentile)) THEN
684 CALL l4f_category_log(this%category,l4f_error,trim(this%trans_type)// &
685 ':percentile: percentile value not provided')
686 CALL raise_fatal_error()
687 ELSE IF (this%stat_info%percentile >= 100.) THEN
688 this%sub_type = 'max'
689 ELSE IF (this%stat_info%percentile <= 0.) THEN
690 this%sub_type = 'min'
691 ENDIF
692 ELSE IF (this%sub_type == 'frequency') THEN
693 IF (.NOT.c_e(this%interval_info%gt) .AND. .NOT.c_e(this%interval_info%gt)) THEN
694 CALL l4f_category_log(this%category,l4f_error,trim(this%trans_type)// &
695 ':frequency: lower and/or upper limit not provided')
696 CALL raise_fatal_error()
697 ENDIF
698 ELSE
699 CALL sub_type_error()
700 RETURN
701 ENDIF
702
703ELSE IF (this%trans_type == 'maskgen')THEN
704
705 IF (this%sub_type == 'poly') THEN
706
707 IF (this%poly%arraysize <= 0) THEN
708 CALL l4f_category_log(this%category,l4f_error,"maskgen:poly poly parameter missing or empty")
709 CALL raise_fatal_error()
710 ENDIF
711
712 ELSE IF (this%sub_type == 'grid') THEN
713! nothing to do for now
714
715 ELSE
716 CALL sub_type_error()
717 RETURN
718 ENDIF
719
720ELSE IF (this%trans_type == 'vertint') THEN
721
722 IF (this%vertint%input_levtype == vol7d_level_miss) THEN
723 CALL l4f_category_log(this%category,l4f_error, &
724 'vertint parameter input_levtype not provided')
725 CALL raise_fatal_error()
726 ENDIF
727
728 IF (this%vertint%output_levtype == vol7d_level_miss) THEN
729 CALL l4f_category_log(this%category,l4f_error, &
730 'vertint parameter output_levtype not provided')
731 CALL raise_fatal_error()
732 ENDIF
733
734 IF (this%sub_type == 'linear' .OR. this%sub_type == 'linearsparse') THEN
735! nothing to do here
736 ELSE
737 CALL sub_type_error()
738 RETURN
739 ENDIF
740
741ELSE IF (this%trans_type == 'metamorphosis') THEN
742
743 IF (this%sub_type == 'all') THEN
744! nothing to do here
745 ELSE IF (this%sub_type == 'coordbb')THEN
746
747 IF (c_e(this%rect_coo%ilon) .AND. c_e(this%rect_coo%ilat) .AND. &
748 c_e(this%rect_coo%flon) .AND. c_e(this%rect_coo%flat)) THEN ! coordinates given
749 ELSE
750
751 CALL l4f_category_log(this%category,l4f_error,"metamorphosis: coordbb parameters missing")
752 CALL raise_fatal_error()
753
754 ENDIF
755
756 ELSE IF (this%sub_type == 'poly')THEN
757
758 IF (this%poly%arraysize <= 0) THEN
759 CALL l4f_category_log(this%category,l4f_error,"metamorphosis:poly: poly parameter missing or empty")
760 CALL raise_fatal_error()
761 ENDIF
762
763 ELSE IF (this%sub_type == 'mask' .OR. this%sub_type == 'maskvalid' .OR. &
764 this%sub_type == 'maskinvalid' .OR. this%sub_type == 'setinvalidto' .OR. &
765 this%sub_type == 'settoinvalid' .OR. this%sub_type == 'lemaskinvalid' .OR. &
766 this%sub_type == 'ltmaskinvalid' .OR. this%sub_type == 'gemaskinvalid' .OR. &
767 this%sub_type == 'gtmaskinvalid' .OR. this%sub_type == 'index') THEN
768! nothing to do here
769 ELSE
770 CALL sub_type_error()
771 RETURN
772 ENDIF
773
774ELSE
775 CALL trans_type_error()
776 RETURN
777ENDIF
778
779CONTAINS
780
781SUBROUTINE sub_type_error()
782
783CALL l4f_category_log(this%category, l4f_error, trim(this%trans_type) &
784 //': sub_type '//trim(this%sub_type)//' is not defined')
785CALL raise_fatal_error()
786
787END SUBROUTINE sub_type_error
788
789SUBROUTINE trans_type_error()
790
791CALL l4f_category_log(this%category, l4f_error, 'trans_type '//this%trans_type &
792 //' is not defined')
793CALL raise_fatal_error()
794
795END SUBROUTINE trans_type_error
796
797
798END SUBROUTINE transform_init
799
800
801!> Destructor of \a tranform_def object.
802!! It releases any memory and data associated to the \a transform_def
803!! object \a this, the logger category will be deleted too.
804SUBROUTINE transform_delete(this)
805TYPE(transform_def),INTENT(inout) :: this !< transformation object
806
807this%trans_type=cmiss
808this%sub_type=cmiss
809
810this%rect_ind%ix=imiss
811this%rect_ind%iy=imiss
812this%rect_ind%fx=imiss
813this%rect_ind%fy=imiss
814
815this%rect_coo%ilon=dmiss
816this%rect_coo%ilat=dmiss
817this%rect_coo%flon=dmiss
818this%rect_coo%flat=dmiss
819
820this%box_info%npx=imiss
821this%box_info%npy=imiss
822
823this%extrap=.false.
824
825!chiudo il logger
826CALL l4f_category_delete(this%category)
827
828END SUBROUTINE transform_delete
829
830
831!> Method for returning the contents of the object.
832SUBROUTINE transform_get_val(this, time_definition, trans_type, sub_type, &
833 input_levtype, output_levtype)
834type(transform_def),intent(in) :: this !< object to examine
835INTEGER,INTENT(out),OPTIONAL :: time_definition !< 0=time is reference time, 1=time is validity time
836CHARACTER(len=*),INTENT(out),OPTIONAL :: trans_type !< type of transformation
837CHARACTER(len=*),INTENT(out),OPTIONAL :: sub_type !< subtype of transformation
838TYPE(vol7d_level),INTENT(out),OPTIONAL :: input_levtype
839!< type of vertical level of input data (only type of first and second surface are used, level values are ignored)
840TYPE(vol7d_level),INTENT(out),OPTIONAL :: output_levtype
841!< type of vertical level of output data (only type of first and second surface are used, level values are ignored)
842
843IF (PRESENT(time_definition)) time_definition=this%time_definition
844IF (PRESENT(trans_type)) trans_type = this%trans_type
845IF (PRESENT(sub_type)) sub_type = this%sub_type
846IF (PRESENT(input_levtype)) input_levtype = this%vertint%input_levtype
847IF (PRESENT(output_levtype)) output_levtype = this%vertint%output_levtype
848
849
850END SUBROUTINE transform_get_val
851
852
853!> Constructor for a \a grid_transform object, defining a particular
854!! vertical transformation.
855!! It defines an object describing a transformation involving
856!! operations on the vertical direction only, thus it applies in the
857!! same way to grid-to-grid and to sparse points-to-sparse points
858!! transformations; the abstract type of the transformation is
859!! described in the transformation object \a trans (type
860!! transform_def) which must have been properly initialised. The
861!! additional information required here is the list of input and
862!! output levels and an optional 3-d field indicating the vertical
863!! coordinates of the input dataset.
864!!
865!! The input level list \a lev_in is a 1-d array of \a vol7d_level
866!! type, describing the vertical coordinate system of the whole input
867!! dataset, only the vertical levels or layers matching the level type
868!! indicated when initialising the transformation object \a trans will
869!! be used, the others will be discarded in the vertical
870!! transformation. However the relevant vertical levels must be
871!! contiguous and sorted accordingly to the default \a vol7d_level
872!! sort order. The argument \a lev_out describes the vertical
873!! coordinate system of the output dataset. A particular case to be
874!! considered is when \a SIZE(lev_out)==0, this means that the output
875!! coordinates have to be computed automatically in the current
876!! subroutine, this is supported only for hybrid levels/layers.
877!!
878!! When the input and output level types are different, the \a
879!! coord_3d_in array must be provided, indicating the vertical
880!! coordinate of every input grid point expressed in terms of the
881!! output vertical coordinate system (e.g. height if interpolating to
882!! constant height levels or pressure if interpolating to isobaric
883!! levels). This array must contain, in the 3rd vertical dimension,
884!! only the those levels/layers of \a lev_in that are actually used
885!! for interpolation. The storage space of \a coord_3d_in is "stolen"
886!! by this method, so the array will appear as unallocated and
887!! unusable to the calling procedure after return from this
888!! subroutine.
889!!
890!! Layers in the grib2 sense (i.e. layers between two surfaces) can be
891!! handled by this class only when the upper and lower surfaces are of
892!! the same type; in these cases the coordinate assigned to every
893!! layer fro interpolation is the average (or log-average in case of
894!! isobaric surfaces) between the coordinates of the corresponding
895!! upper and lower surfaces.
896SUBROUTINE grid_transform_levtype_levtype_init(this, trans, lev_in, lev_out, &
897 coord_3d_in, categoryappend)
898TYPE(grid_transform),INTENT(out) :: this !< grid_transformation object
899TYPE(transform_def),INTENT(in) :: trans !< transformation object
900TYPE(vol7d_level),INTENT(in) :: lev_in(:) !< vol7d_level from input object
901TYPE(vol7d_level),INTENT(in) :: lev_out(:) !< vol7d_level object defining target vertical grid
902REAL,INTENT(inout),OPTIONAL,ALLOCATABLE :: coord_3d_in(:,:,:) !< vertical coordinates of each input point in target reference system
903CHARACTER(len=*),INTENT(in),OPTIONAL :: categoryappend !< append this suffix to log4fortran namespace category
904
905DOUBLE PRECISION :: coord_in(SIZE(lev_in))
906DOUBLE PRECISION,ALLOCATABLE :: coord_out(:)
907LOGICAL :: mask_in(SIZE(lev_in))
908LOGICAL,ALLOCATABLE :: mask_out(:)
909LOGICAL :: dolog
910INTEGER :: i, j, icache, inused, istart, iend, ostart, oend
911
912
913CALL grid_transform_init_common(this, trans, categoryappend)
914#ifdef DEBUG
915CALL l4f_category_log(this%category, l4f_debug, "grid_transform vertint")
916#endif
917
918IF (this%trans%trans_type == 'vertint') THEN
919
920 IF (c_e(trans%vertint%input_levtype%level2) .AND. &
921 trans%vertint%input_levtype%level1 /= trans%vertint%input_levtype%level2) THEN
922 CALL l4f_category_log(this%category, l4f_error, &
923 'vertint: input upper and lower surface must be of the same type, '// &
924 t2c(trans%vertint%input_levtype%level1)//'/='// &
925 t2c(trans%vertint%input_levtype%level2))
926 CALL raise_error()
927 RETURN
928 ENDIF
929 IF (c_e(trans%vertint%output_levtype%level2) .AND. &
930 trans%vertint%output_levtype%level1 /= trans%vertint%output_levtype%level2) THEN
931 CALL l4f_category_log(this%category, l4f_error, &
932 'vertint: output upper and lower surface must be of the same type'// &
933 t2c(trans%vertint%output_levtype%level1)//'/='// &
934 t2c(trans%vertint%output_levtype%level2))
935 CALL raise_error()
936 RETURN
937 ENDIF
938
939 mask_in(:) = (lev_in(:)%level1 == trans%vertint%input_levtype%level1) .AND. &
940 (lev_in(:)%level2 == trans%vertint%input_levtype%level2)
941 CALL make_vert_coord(lev_in, mask_in, coord_in, dolog)
942 this%innz = SIZE(lev_in)
943 istart = firsttrue(mask_in)
944 iend = lasttrue(mask_in)
945 inused = iend - istart + 1
946 IF (inused /= count(mask_in)) THEN
947 CALL l4f_category_log(this%category, l4f_error, &
948 'grid_transform_levtype_levtype_init: input levels badly sorted '//&
949 t2c(inused)//'/'//t2c(count(mask_in)))
950 CALL raise_error()
951 RETURN
952 ENDIF
953 this%levshift = istart-1
954 this%levused = inused
955
956 IF (trans%vertint%input_levtype%level1 /= trans%vertint%output_levtype%level1) THEN
957#ifdef DEBUG
958 CALL l4f_category_log(this%category, l4f_debug, &
959 'vertint: different input and output level types '// &
960 t2c(trans%vertint%input_levtype%level1)//' '// &
961 t2c(trans%vertint%output_levtype%level1))
962#endif
963
964 ALLOCATE(mask_out(SIZE(lev_out)), this%vcoord_out(SIZE(lev_out)))
965 mask_out(:) = (lev_out(:)%level1 == trans%vertint%output_levtype%level1) .AND. &
966 (lev_out(:)%level2 == trans%vertint%output_levtype%level2)
967 CALL make_vert_coord(lev_out, mask_out, this%vcoord_out, dolog)
968 this%outnz = SIZE(mask_out)
969 DEALLOCATE(mask_out)
970
971 IF (.NOT.PRESENT(coord_3d_in)) THEN
972 CALL l4f_category_log(this%category, l4f_warn, &
973 'vertint: different input and output level types &
974 &and no coord_3d_in, expecting vert. coord. in volume')
975 this%dolog = dolog ! a little bit dirty, I must compute log later
976 ELSE
977 IF (SIZE(coord_3d_in,3) /= inused) THEN
978 CALL l4f_category_log(this%category, l4f_error, &
979 'vertint: vertical size of coord_3d_in (vertical coordinate) &
980 &different from number of input levels suitable for interpolation')
981 CALL l4f_category_log(this%category, l4f_error, &
982 'coord_3d_in: '//t2c(SIZE(coord_3d_in,3))// &
983 ', input levels for interpolation: '//t2c(inused))
984 CALL raise_error()
985 RETURN
986 ENDIF
987
988 CALL move_alloc(coord_3d_in, this%coord_3d_in) ! steal allocation
989 IF (dolog) THEN
990 WHERE(c_e(this%coord_3d_in) .AND. this%coord_3d_in > 0.0)
991 this%coord_3d_in = log(this%coord_3d_in)
992 ELSE WHERE
993 this%coord_3d_in = rmiss
994 END WHERE
995 ENDIF
996 ENDIF
997
998 this%valid = .true. ! warning, no check of subtype
999
1000 ELSE
1001! here we assume that valid levels are contiguous and ordered
1002
1003#ifdef DEBUG
1004 CALL l4f_category_log(this%category, l4f_debug, &
1005 'vertint: equal input and output level types '// &
1006 t2c(trans%vertint%input_levtype%level1))
1007#endif
1008
1009 IF (SIZE(lev_out) > 0) THEN ! output level list provided
1010 ALLOCATE(mask_out(SIZE(lev_out)), coord_out(SIZE(lev_out)))
1011 mask_out(:) = (lev_out(:)%level1 == trans%vertint%output_levtype%level1) .AND. &
1012 (lev_out(:)%level2 == trans%vertint%output_levtype%level2)
1013 CALL make_vert_coord(lev_out, mask_out, coord_out, dolog)
1014
1015 ELSE ! output level list not provided, try to autogenerate
1016 IF (c_e(trans%vertint%input_levtype%level2) .AND. &
1017 .NOT.c_e(trans%vertint%output_levtype%level2)) THEN ! full -> half
1018 IF (trans%vertint%output_levtype%level1 == 105 .OR. &
1019 trans%vertint%output_levtype%level1 == 150) THEN
1020 ALLOCATE(this%output_level_auto(inused-1))
1021 CALL l4f_category_log(this%category,l4f_info, &
1022 'grid_transform_levtype_levtype_init: autogenerating '//t2c(inused-1) &
1023 //'/'//t2c(iend-istart)//' output levels (f->h)')
1024 DO i = istart, iend - 1
1025 CALL init(this%output_level_auto(i-istart+1), &
1026 trans%vertint%input_levtype%level1, lev_in(i)%l2)
1027 ENDDO
1028 ELSE
1029 CALL l4f_category_log(this%category, l4f_error, &
1030 'grid_transform_levtype_levtype_init: automatic generation of output levels &
1031 &available only for hybrid levels')
1032 CALL raise_error()
1033 RETURN
1034 ENDIF
1035 ELSE IF (.NOT.c_e(trans%vertint%input_levtype%level2) .AND. &
1036 c_e(trans%vertint%output_levtype%level2)) THEN ! half -> full
1037 ALLOCATE(this%output_level_auto(inused-1))
1038 IF (trans%vertint%output_levtype%level1 == 105 .OR. &
1039 trans%vertint%output_levtype%level1 == 150) THEN
1040 CALL l4f_category_log(this%category,l4f_info, &
1041 'grid_transform_levtype_levtype_init: autogenerating '//t2c(inused-1) &
1042 //'/'//t2c(iend-istart)//' output levels (h->f)')
1043 DO i = istart, iend - 1
1044 CALL init(this%output_level_auto(i-istart+1), trans%vertint%input_levtype%level1, &
1045 lev_in(i)%l1, trans%vertint%input_levtype%level1, &
1046 lev_in(i)%l1+1)
1047 ENDDO
1048 ELSE
1049 CALL l4f_category_log(this%category, l4f_error, &
1050 'grid_transform_levtype_levtype_init: automatic generation of output levels &
1051 &available only for hybrid levels')
1052 CALL raise_error()
1053 RETURN
1054 ENDIF
1055 ELSE
1056 CALL l4f_category_log(this%category, l4f_error, &
1057 'grid_transform_levtype_levtype_init: strange situation'// &
1058 to_char(c_e(trans%vertint%input_levtype%level2))//' '// &
1059 to_char(c_e(trans%vertint%output_levtype%level2)))
1060 CALL raise_error()
1061 RETURN
1062 ENDIF
1063 ALLOCATE(coord_out(inused-1), mask_out(inused-1))
1064 mask_out(:) = .true.
1065 CALL make_vert_coord(this%output_level_auto, mask_out, coord_out, dolog)
1066 ENDIF
1067
1068 this%outnz = SIZE(mask_out)
1069 ostart = firsttrue(mask_out)
1070 oend = lasttrue(mask_out)
1071
1072! set valid = .FALSE. here?
1073 IF (istart == 0) THEN
1074 CALL l4f_category_log(this%category, l4f_warn, &
1075 'grid_transform_levtype_levtype_init: &
1076 &input contains no vertical levels of type ('// &
1077 trim(to_char(trans%vertint%input_levtype%level1))//','// &
1078 trim(to_char(trans%vertint%input_levtype%level2))// &
1079 ') suitable for interpolation')
1080 RETURN
1081! iend = -1 ! for loops
1082 ELSE IF (istart == iend) THEN
1083 CALL l4f_category_log(this%category, l4f_warn, &
1084 'grid_transform_levtype_levtype_init: &
1085 &input contains only 1 vertical level of type ('// &
1086 trim(to_char(trans%vertint%input_levtype%level1))//','// &
1087 trim(to_char(trans%vertint%input_levtype%level2))// &
1088 ') suitable for interpolation')
1089 ENDIF
1090 IF (ostart == 0) THEN
1091 CALL l4f_category_log(this%category, l4f_warn, &
1092 'grid_transform_levtype_levtype_init: &
1093 &output contains no vertical levels of type ('// &
1094 trim(to_char(trans%vertint%output_levtype%level1))//','// &
1095 trim(to_char(trans%vertint%output_levtype%level2))// &
1096 ') suitable for interpolation')
1097 RETURN
1098! oend = -1 ! for loops
1099 ENDIF
1100
1101! end of code common for all vertint subtypes
1102 IF (this%trans%sub_type == 'linear') THEN
1103
1104 ALLOCATE(this%inter_index_z(this%outnz), this%inter_zp(this%outnz))
1105 this%inter_index_z(:) = imiss
1106 this%inter_zp(:) = dmiss
1107 IF (this%trans%extrap .AND. istart > 0) THEN
1108 WHERE(mask_out)
1109! extrapolate down by default
1110 this%inter_index_z(:) = istart
1111 this%inter_zp(:) = 1.0d0
1112 ENDWHERE
1113 ENDIF
1114
1115 icache = istart + 1
1116 outlev: DO j = ostart, oend
1117 inlev: DO i = icache, iend
1118 IF (coord_in(i) >= coord_out(j)) THEN
1119 IF (coord_out(j) >= coord_in(i-1)) THEN
1120 this%inter_index_z(j) = i - 1
1121 this%inter_zp(j) = (coord_out(j)-coord_in(i-1)) / &
1122 (coord_in(i)-coord_in(i-1)) ! weight for (i)
1123 icache = i ! speedup next j iteration
1124 ENDIF
1125 cycle outlev ! found or extrapolated down
1126 ENDIF
1127 ENDDO inlev
1128! if I'm here I must extrapolate up
1129 IF (this%trans%extrap .AND. iend > 1) THEN
1130 this%inter_index_z(j) = iend - 1
1131 this%inter_zp(j) = 0.0d0
1132 ENDIF
1133 ENDDO outlev
1134
1135 DEALLOCATE(coord_out, mask_out)
1136 this%valid = .true.
1137
1138 ELSE IF (this%trans%sub_type == 'linearsparse') THEN
1139! just store vertical coordinates, dirty work is done later
1140 ALLOCATE(this%vcoord_in(this%levused), this%vcoord_out(SIZE(coord_out)))
1141 this%vcoord_in(:) = coord_in(this%levshift+1:this%levshift+this%levused)
1142 this%vcoord_out(:) = coord_out(:)
1143 DEALLOCATE(coord_out, mask_out)
1144 this%valid = .true.
1145
1146 ENDIF
1147
1148 ENDIF ! levels are different
1149
1150!ELSE IF (this%trans%trans_type == 'verttrans') THEN
1151
1152ENDIF
1153
1154END SUBROUTINE grid_transform_levtype_levtype_init
1155
1156
1157! internal subroutine for computing vertical coordinate values, for
1158! pressure-based coordinates the logarithm is computed
1159SUBROUTINE make_vert_coord(lev, mask, coord, dolog)
1160TYPE(vol7d_level),INTENT(in) :: lev(:)
1161LOGICAL,INTENT(inout) :: mask(:)
1162DOUBLE PRECISION,INTENT(out) :: coord(:)
1163LOGICAL,INTENT(out) :: dolog
1164
1165INTEGER :: k
1166DOUBLE PRECISION :: fact
1167
1168dolog = .false.
1169k = firsttrue(mask)
1170IF (k <= 0) RETURN
1171coord(:) = dmiss
1172
1173IF (any(lev(k)%level1 == height_level)) THEN ! improve with a conversion table somewhere
1174 fact = 1.0d-3
1175ELSE IF (any(lev(k)%level1 == thermo_level)) THEN
1176 fact = 1.0d-1
1177ELSE IF (any(lev(k)%level1 == sigma_level)) THEN
1178 fact = 1.0d-4
1179ELSE
1180 fact = 1.0d0
1181ENDIF
1182
1183IF (c_e(lev(k)%level2) .AND. lev(k)%level1 == lev(k)%level2) THEN ! layer between 2 levels
1184 IF (lev(k)%level1 == 100 .OR. lev(k)%level1 == 108) THEN ! pressure, compute log
1185 WHERE(mask(:) .AND. lev(:)%l1 > 0 .AND. lev(:)%l2 > 0)
1186 coord(:) = (log(dble(lev(:)%l1)*fact) + log(dble(lev(:)%l2)*fact))*0.5d0
1187 END WHERE
1188 dolog = .true.
1189 ELSE
1190 WHERE(mask(:))
1191 coord(:) = (lev(:)%l1 + lev(:)%l2)*fact*0.5d0
1192 END WHERE
1193 ENDIF
1194ELSE ! half level
1195 IF (lev(k)%level1 == 100 .OR. lev(k)%level1 == 108) THEN ! pressure, compute log
1196 WHERE(mask(:) .AND. lev(:)%l1 > 0)
1197 coord(:) = log(dble(lev(:)%l1)*fact)
1198 END WHERE
1199 dolog = .true.
1200 ELSE
1201 WHERE(mask(:))
1202 coord(:) = lev(:)%l1*fact
1203 END WHERE
1204 ENDIF
1205ENDIF
1206
1207! refine mask
1208mask(:) = mask(:) .AND. c_e(coord(:))
1209
1210END SUBROUTINE make_vert_coord
1211
1212
1213!> Constructor for a \a grid_transform object, defining a particular
1214!! grid-to-grid transformation. It defines an object describing a
1215!! transformation from one rectangular grid to another; the abstract
1216!! type of transformation is described in the transformation object \a
1217!! trans (type transform_def) which must have been properly
1218!! initialised. The additional information required here is the
1219!! description of the input grid \a in (type griddim_def), the
1220!! description of the output grid \a out (type griddim_def as
1221!! well). The description of the output grid must be initialized for
1222!! interpolating type transformations ('inter' and 'boxinter'), while
1223!! it is generated by this constructor and returned in output for
1224!! 'zoom', 'boxregrid', 'maskgen' and 'polyinter' transformations.
1225!!
1226!! The generated \a grid_transform object is specific to the input and
1227!! output grids involved. The function \a c_e can be used in order to
1228!! check whether the object has been successfully initialised, if the
1229!! result is \a .FALSE., it should not be used further on.
1230SUBROUTINE grid_transform_init(this, trans, in, out, maskgrid, maskbounds, &
1231 categoryappend)
1232TYPE(grid_transform),INTENT(out) :: this !< grid_transformation object
1233TYPE(transform_def),INTENT(in) :: trans !< transformation object
1234TYPE(griddim_def),INTENT(inout) :: in !< griddim object to transform
1235TYPE(griddim_def),INTENT(inout) :: out !< griddim object defining target grid (input or output depending on type of transformation)
1236REAL,INTENT(in),OPTIONAL :: maskgrid(:,:) !< 2D field to be used for defining valid points, it must have the same shape as the field to be interpolated (for transformation type 'metamorphosis:maskvalid')
1237REAL,INTENT(in),OPTIONAL :: maskbounds(:) !< array of boundary values for defining a subset of valid points where the values of \a maskgrid are within the first and last value of \a maskbounds (for transformation type 'metamorphosis:maskvalid/settoinvalid' and others)
1238CHARACTER(len=*),INTENT(in),OPTIONAL :: categoryappend !< append this suffix to log4fortran namespace category
1239
1240INTEGER :: nx, ny, i, j, ix, iy, n, nm, nr, cf_i, cf_o, nprev, &
1241 xnmin, xnmax, ynmin, ynmax
1242DOUBLE PRECISION :: xmin, xmax, ymin, ymax, steplon, steplat, &
1243 xmin_new, ymin_new, ellips_smaj_axis, ellips_flatt, r2
1244TYPE(geo_proj) :: proj_in, proj_out
1245TYPE(georef_coord) :: point
1246LOGICAL,ALLOCATABLE :: point_mask(:,:)
1247TYPE(griddim_def) :: lin, lout
1248
1249
1250CALL grid_transform_init_common(this, trans, categoryappend)
1251#ifdef DEBUG
1252CALL l4f_category_log(this%category, l4f_debug, "grid_transform vg6d-vg6d")
1253#endif
1254
1255! output ellipsoid has to be the same as for the input (improve)
1256CALL get_val(in, ellips_smaj_axis=ellips_smaj_axis, ellips_flatt=ellips_flatt)
1257CALL set_val(out, ellips_smaj_axis=ellips_smaj_axis, ellips_flatt=ellips_flatt)
1258
1259IF (this%trans%trans_type == 'zoom') THEN
1260
1261 IF (this%trans%sub_type == 'coord') THEN
1262
1263 CALL griddim_zoom_coord(in, &
1264 this%trans%rect_coo%ilon, this%trans%rect_coo%ilat,&
1265 this%trans%rect_coo%flon, this%trans%rect_coo%flat,&
1266 this%trans%rect_ind%ix, this%trans%rect_ind%iy, &
1267 this%trans%rect_ind%fx, this%trans%rect_ind%fy)
1268
1269 ELSE IF (this%trans%sub_type == 'projcoord') THEN
1270
1271 CALL griddim_zoom_projcoord(in, &
1272 this%trans%rect_coo%ilon, this%trans%rect_coo%ilat,&
1273 this%trans%rect_coo%flon, this%trans%rect_coo%flat,&
1274 this%trans%rect_ind%ix, this%trans%rect_ind%iy, &
1275 this%trans%rect_ind%fx, this%trans%rect_ind%fy)
1276
1277 ELSE IF (this%trans%sub_type == 'coordbb') THEN
1278
1279! compute coordinates of input grid in geo system
1280 CALL copy(in, lin)
1281 CALL unproj(lin)
1282 CALL get_val(lin, nx=nx, ny=ny)
1283
1284 ALLOCATE(point_mask(nx,ny))
1285 point_mask(:,:) = .false.
1286
1287! mark points falling into requested bounding-box
1288 DO j = 1, ny
1289 DO i = 1, nx
1290! IF (geo_coord_inside_rectang())
1291 IF (lin%dim%lon(i,j) > this%trans%rect_coo%ilon .AND. &
1292 lin%dim%lon(i,j) < this%trans%rect_coo%flon .AND. &
1293 lin%dim%lat(i,j) > this%trans%rect_coo%ilat .AND. &
1294 lin%dim%lat(i,j) < this%trans%rect_coo%flat) THEN ! improve!
1295 point_mask(i,j) = .true.
1296 ENDIF
1297 ENDDO
1298 ENDDO
1299
1300! determine cut indices keeping all points which fall inside b-b
1301 DO i = 1, nx
1302 IF (any(point_mask(i,:))) EXIT
1303 ENDDO
1304 this%trans%rect_ind%ix = i
1305 DO i = nx, this%trans%rect_ind%ix, -1
1306 IF (any(point_mask(i,:))) EXIT
1307 ENDDO
1308 this%trans%rect_ind%fx = i
1309
1310 DO j = 1, ny
1311 IF (any(point_mask(:,j))) EXIT
1312 ENDDO
1313 this%trans%rect_ind%iy = j
1314 DO j = ny, this%trans%rect_ind%iy, -1
1315 IF (any(point_mask(:,j))) EXIT
1316 ENDDO
1317 this%trans%rect_ind%fy = j
1318
1319 DEALLOCATE(point_mask)
1320
1321 IF (this%trans%rect_ind%ix > this%trans%rect_ind%fx .OR. &
1322 this%trans%rect_ind%iy > this%trans%rect_ind%fy) THEN
1323
1324 CALL l4f_category_log(this%category,l4f_error, &
1325 "zoom coordbb: no points inside bounding box "//&
1326 trim(to_char(this%trans%rect_coo%ilon))//","// &
1327 trim(to_char(this%trans%rect_coo%flon))//","// &
1328 trim(to_char(this%trans%rect_coo%ilat))//","// &
1329 trim(to_char(this%trans%rect_coo%flat)))
1330 CALL raise_error()
1331 RETURN
1332
1333 ENDIF
1334 CALL delete(lin)
1335 ENDIF
1336! to do in all zoom cases
1337
1338 CALL get_val(in, nx=nx, ny=ny, xmin=xmin, xmax=xmax, &
1339 ymin=ymin, ymax=ymax, dx=steplon, dy=steplat)
1340
1341! old indices
1342 this%iniox = min(max(this%trans%rect_ind%ix,1),nx) ! iox
1343 this%inioy = min(max(this%trans%rect_ind%iy,1),ny) ! ioy
1344 this%infox = max(min(this%trans%rect_ind%fx,nx),1) ! fox
1345 this%infoy = max(min(this%trans%rect_ind%fy,ny),1) ! foy
1346! new indices
1347 this%outinx = min(max(2-this%trans%rect_ind%ix,1),nx) ! inx
1348 this%outiny = min(max(2-this%trans%rect_ind%iy,1),ny) ! iny
1349 this%outfnx = min(this%trans%rect_ind%fx,nx)-this%trans%rect_ind%ix+1 ! fnx
1350 this%outfny = min(this%trans%rect_ind%fy,ny)-this%trans%rect_ind%iy+1 ! fny
1351
1352 xmin=xmin+steplon*(this%trans%rect_ind%ix-1)
1353 ymin=ymin+steplat*(this%trans%rect_ind%iy-1)
1354 xmax=xmax+steplon*(this%trans%rect_ind%fx-nx)
1355 ymax=ymax+steplat*(this%trans%rect_ind%fy-ny)
1356
1357 CALL copy(in, out)
1358! deallocate coordinates if allocated because they will change
1359 CALL dealloc(out%dim)
1360
1361 out%dim%nx = this%trans%rect_ind%fx - this%trans%rect_ind%ix + 1 ! newx
1362 out%dim%ny = this%trans%rect_ind%fy - this%trans%rect_ind%iy + 1 ! newy
1363 this%outnx = out%dim%nx
1364 this%outny = out%dim%ny
1365 this%innx = nx
1366 this%inny = ny
1367
1368 CALL set_val(out, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1369
1370 this%valid = .true. ! warning, no check of subtype
1371
1372ELSE IF (this%trans%trans_type == 'boxregrid') THEN
1373
1374 CALL get_val(in, nx=nx, ny=ny, xmin=xmin, xmax=xmax, &
1375 ymin=ymin, ymax=ymax, dx=steplon, dy=steplat)
1376
1377 this%innx = nx
1378 this%inny = ny
1379
1380! new grid
1381 xmin_new = xmin + (this%trans%box_info%npx - 1)*0.5d0*steplon
1382 ymin_new = ymin + (this%trans%box_info%npy - 1)*0.5d0*steplat
1383
1384 CALL copy(in, out)
1385! deallocate coordinates if allocated because they will change
1386 CALL dealloc(out%dim)
1387
1388 out%dim%nx = nx/this%trans%box_info%npx
1389 out%dim%ny = ny/this%trans%box_info%npy
1390 this%outnx = out%dim%nx
1391 this%outny = out%dim%ny
1392 steplon = steplon*this%trans%box_info%npx
1393 steplat = steplat*this%trans%box_info%npy
1394
1395 CALL set_val(out, xmin=xmin_new, ymin=ymin_new, &
1396 xmax=xmin_new + dble(out%dim%nx-1)*steplon, dx=steplon, &
1397 ymax=ymin_new + dble(out%dim%ny-1)*steplat, dy=steplat)
1398
1399 this%valid = .true. ! warning, no check of subtype
1400
1401ELSE IF (this%trans%trans_type == 'inter' .OR. this%trans%trans_type == 'intersearch') THEN
1402
1403 CALL outgrid_setup() ! common setup for grid-generating methods
1404
1405 IF (this%trans%sub_type == 'near' .OR. this%trans%sub_type == 'bilin'&
1406 .OR. this%trans%sub_type == 'shapiro_near') THEN
1407
1408 CALL get_val(in, nx=this%innx, ny=this%inny, &
1409 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1410 CALL get_val(out, nx=this%outnx, ny=this%outny)
1411
1412 ALLOCATE(this%inter_index_x(this%outnx,this%outny), &
1413 this%inter_index_y(this%outnx,this%outny))
1414 CALL copy(out, lout)
1415 CALL unproj(lout)
1416
1417 IF (this%trans%sub_type == 'bilin') THEN
1418 CALL this%find_index(in, .false., &
1419 this%innx, this%inny, xmin, xmax, ymin, ymax, &
1420 lout%dim%lon, lout%dim%lat, this%trans%extrap, &
1421 this%inter_index_x, this%inter_index_y)
1422
1423 ALLOCATE(this%inter_x(this%innx,this%inny), &
1424 this%inter_y(this%innx,this%inny))
1425 ALLOCATE(this%inter_xp(this%outnx,this%outny), &
1426 this%inter_yp(this%outnx,this%outny))
1427
1428! compute coordinates of input grid
1429 CALL griddim_gen_coord(in, this%inter_x, this%inter_y)
1430! compute coordinates of output grid in input system
1431 CALL proj(in, lout%dim%lon, lout%dim%lat, this%inter_xp, this%inter_yp)
1432
1433 ELSE ! near, shapiro_near
1434 CALL this%find_index(in, .true., &
1435 this%innx, this%inny, xmin, xmax, ymin, ymax, &
1436 lout%dim%lon, lout%dim%lat, this%trans%extrap, &
1437 this%inter_index_x, this%inter_index_y)
1438
1439 IF (this%trans%trans_type == 'intersearch') THEN ! replicate code above
1440 ALLOCATE(this%inter_x(this%innx,this%inny), &
1441 this%inter_y(this%innx,this%inny))
1442 ALLOCATE(this%inter_xp(this%outnx,this%outny), &
1443 this%inter_yp(this%outnx,this%outny))
1444
1445! compute coordinates of input grid
1446 CALL griddim_gen_coord(in, this%inter_x, this%inter_y)
1447! compute coordinates of output grid in input system
1448 CALL proj(in, lout%dim%lon, lout%dim%lat, this%inter_xp, this%inter_yp)
1449 ENDIF
1450 ENDIF
1451
1452 CALL delete(lout)
1453 this%valid = .true.
1454 ENDIF
1455
1456ELSE IF (this%trans%trans_type == 'boxinter') THEN
1457
1458 CALL outgrid_setup() ! common setup for grid-generating methods
1459 CALL get_val(in, nx=this%innx, ny=this%inny)
1460 CALL get_val(out, nx=this%outnx, ny=this%outny, &
1461 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1462! TODO now box size is ignored
1463! if box size not provided, use the actual grid step
1464 IF (.NOT.c_e(this%trans%area_info%boxdx)) &
1465 CALL get_val(out, dx=this%trans%area_info%boxdx)
1466 IF (.NOT.c_e(this%trans%area_info%boxdy)) &
1467 CALL get_val(out, dx=this%trans%area_info%boxdy)
1468! half size is actually needed
1469 this%trans%area_info%boxdx = this%trans%area_info%boxdx*0.5d0
1470 this%trans%area_info%boxdy = this%trans%area_info%boxdy*0.5d0
1471! unlike before, here index arrays must have the shape of input grid
1472 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
1473 this%inter_index_y(this%innx,this%inny))
1474
1475! compute coordinates of input grid in geo system
1476 CALL copy(in, lin)
1477 CALL unproj(lin)
1478! use find_index in the opposite way, here extrap does not make sense
1479 CALL this%find_index(out, .true., &
1480 this%outnx, this%outny, xmin, xmax, ymin, ymax, &
1481 lin%dim%lon, lin%dim%lat, .false., &
1482 this%inter_index_x, this%inter_index_y)
1483
1484 CALL delete(lin)
1485 this%valid = .true. ! warning, no check of subtype
1486
1487ELSE IF (this%trans%trans_type == 'stencilinter') THEN
1488
1489 CALL outgrid_setup() ! common setup for grid-generating methods
1490! from inter:near
1491 CALL get_val(in, nx=this%innx, ny=this%inny, &
1492 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1493 CALL get_val(out, nx=this%outnx, ny=this%outny)
1494
1495 ALLOCATE (this%inter_index_x(this%outnx,this%outny), &
1496 this%inter_index_y(this%outnx,this%outny))
1497 CALL copy(out, lout)
1498 CALL unproj(lout)
1499 CALL this%find_index(in, .true., &
1500 this%innx, this%inny, xmin, xmax, ymin, ymax, &
1501 lout%dim%lon, lout%dim%lat, this%trans%extrap, &
1502 this%inter_index_x, this%inter_index_y)
1503
1504! define the stencil mask
1505 nr = int(this%trans%area_info%radius) ! integer radius
1506 n = nr*2+1 ! n. of points
1507 nm = nr + 1 ! becomes index of center
1508 r2 = this%trans%area_info%radius**2
1509 ALLOCATE(this%stencil(n,n))
1510 this%stencil(:,:) = .true.
1511 DO iy = 1, n
1512 DO ix = 1, n
1513 IF ((ix-nm)**2+(iy-nm)**2 > r2) this%stencil(ix,iy) = .false.
1514 ENDDO
1515 ENDDO
1516
1517! set to missing the elements of inter_index too close to the domain
1518! borders
1519 xnmin = nr + 1
1520 xnmax = this%innx - nr
1521 ynmin = nr + 1
1522 ynmax = this%inny - nr
1523 DO iy = 1, this%outny
1524 DO ix = 1, this%outnx
1525 IF (this%inter_index_x(ix,iy) < xnmin .OR. &
1526 this%inter_index_x(ix,iy) > xnmax .OR. &
1527 this%inter_index_y(ix,iy) < ynmin .OR. &
1528 this%inter_index_y(ix,iy) > ynmax) THEN
1529 this%inter_index_x(ix,iy) = imiss
1530 this%inter_index_y(ix,iy) = imiss
1531 ENDIF
1532 ENDDO
1533 ENDDO
1534
1535#ifdef DEBUG
1536 CALL l4f_category_log(this%category, l4f_debug, &
1537 'stencilinter: stencil size '//t2c(n*n))
1538 CALL l4f_category_log(this%category, l4f_debug, &
1539 'stencilinter: stencil points '//t2c(count(this%stencil)))
1540#endif
1541
1542 CALL delete(lout)
1543 this%valid = .true. ! warning, no check of subtype
1544
1545ELSE IF (this%trans%trans_type == 'maskgen') THEN
1546
1547 IF (this%trans%sub_type == 'poly') THEN
1548
1549 CALL copy(in, out)
1550 CALL get_val(in, nx=this%innx, ny=this%inny)
1551 this%outnx = this%innx
1552 this%outny = this%inny
1553
1554! unlike before, here index arrays must have the shape of input grid
1555 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
1556 this%inter_index_y(this%innx,this%inny))
1557 this%inter_index_x(:,:) = imiss
1558 this%inter_index_y(:,:) = 1
1559
1560! compute coordinates of input grid in geo system
1561 CALL unproj(out) ! should be unproj(lin)
1562
1563 nprev = 1
1564!$OMP PARALLEL DEFAULT(SHARED)
1565!$OMP DO PRIVATE(j, i, n, point) FIRSTPRIVATE(nprev)
1566 DO j = 1, this%inny
1567 inside_x: DO i = 1, this%innx
1568 point = georef_coord_new(x=out%dim%lon(i,j), y=out%dim%lat(i,j))
1569
1570 DO n = nprev, this%trans%poly%arraysize ! optimize starting from last matched polygon
1571 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1572 this%inter_index_x(i,j) = n
1573 nprev = n
1574 cycle inside_x
1575 ENDIF
1576 ENDDO
1577 DO n = nprev-1, 1, -1 ! test the other polygons
1578 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1579 this%inter_index_x(i,j) = n
1580 nprev = n
1581 cycle inside_x
1582 ENDIF
1583 ENDDO
1584
1585! CALL delete(point) ! speedup
1586 ENDDO inside_x
1587 ENDDO
1588!$OMP END PARALLEL
1589
1590 ELSE IF (this%trans%sub_type == 'grid') THEN
1591! here out(put grid) is abused for indicating the box-generating grid
1592! but the real output grid is the input grid
1593 CALL copy(out, lout) ! save out for local use
1594 CALL delete(out) ! needed before copy
1595 CALL copy(in, out)
1596 CALL get_val(in, nx=this%innx, ny=this%inny)
1597 this%outnx = this%innx
1598 this%outny = this%inny
1599 CALL get_val(lout, nx=nx, ny=ny, &
1600 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1601
1602! unlike before, here index arrays must have the shape of input grid
1603 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
1604 this%inter_index_y(this%innx,this%inny))
1605
1606! compute coordinates of input/output grid in geo system
1607 CALL unproj(out)
1608
1609! use find_index in the opposite way, here extrap does not make sense
1610 CALL this%find_index(lout, .true., &
1611 nx, ny, xmin, xmax, ymin, ymax, &
1612 out%dim%lon, out%dim%lat, .false., &
1613 this%inter_index_x, this%inter_index_y)
1614! transform indices to 1-d for mask generation
1615 WHERE(c_e(this%inter_index_x(:,:)))
1616 this%inter_index_x(:,:) = this%inter_index_x(:,:) + &
1617 (this%inter_index_y(:,:)-1)*nx
1618 END WHERE
1619
1620 CALL delete(lout)
1621 ENDIF
1622
1623 this%valid = .true.
1624
1625ELSE IF (this%trans%trans_type == 'polyinter') THEN
1626
1627! this is the only difference wrt maskgen:poly
1628 this%recur = .true. ! grid-to-grid polyinter is done in two steps!
1629
1630 CALL copy(in, out)
1631 CALL get_val(in, nx=this%innx, ny=this%inny)
1632 this%outnx = this%innx
1633 this%outny = this%inny
1634
1635! unlike before, here index arrays must have the shape of input grid
1636 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
1637 this%inter_index_y(this%innx,this%inny))
1638 this%inter_index_x(:,:) = imiss
1639 this%inter_index_y(:,:) = 1
1640
1641! compute coordinates of input grid in geo system
1642 CALL unproj(out) ! should be unproj(lin)
1643
1644 nprev = 1
1645!$OMP PARALLEL DEFAULT(SHARED)
1646!$OMP DO PRIVATE(j, i, n, point) FIRSTPRIVATE(nprev)
1647 DO j = 1, this%inny
1648 inside_x_2: DO i = 1, this%innx
1649 point = georef_coord_new(x=out%dim%lon(i,j), y=out%dim%lat(i,j))
1650
1651 DO n = nprev, this%trans%poly%arraysize ! optimize starting from last matched polygon
1652 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1653 this%inter_index_x(i,j) = n
1654 nprev = n
1655 cycle inside_x_2
1656 ENDIF
1657 ENDDO
1658 DO n = nprev-1, 1, -1 ! test the other polygons
1659 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1660 this%inter_index_x(i,j) = n
1661 nprev = n
1662 cycle inside_x_2
1663 ENDIF
1664 ENDDO
1665
1666! CALL delete(point) ! speedup
1667 ENDDO inside_x_2
1668 ENDDO
1669!$OMP END PARALLEL
1670
1671 this%valid = .true. ! warning, no check of subtype
1672
1673ELSE IF (this%trans%trans_type == 'metamorphosis') THEN
1674
1675 CALL copy(in, out)
1676 CALL get_val(in, nx=this%innx, ny=this%inny)
1677 this%outnx = this%innx
1678 this%outny = this%inny
1679
1680 IF (this%trans%sub_type == 'maskvalid' .OR. this%trans%sub_type == 'maskinvalid') THEN
1681
1682 IF (.NOT.PRESENT(maskgrid)) THEN
1683 CALL l4f_category_log(this%category,l4f_error, &
1684 'grid_transform_init maskgrid argument missing for metamorphosis:'// &
1685 trim(this%trans%sub_type)//' transformation')
1686 CALL raise_error()
1687 RETURN
1688 ENDIF
1689
1690 IF (this%innx /= SIZE(maskgrid,1) .OR. this%inny /= SIZE(maskgrid,2)) THEN
1691 CALL l4f_category_log(this%category,l4f_error, &
1692 'grid_transform_init mask non conformal with input field')
1693 CALL l4f_category_log(this%category,l4f_error, &
1694 'mask: '//t2c(SIZE(maskgrid,1))//'x'//t2c(SIZE(maskgrid,2))// &
1695 ' input field:'//t2c(this%innx)//'x'//t2c(this%inny))
1696 CALL raise_error()
1697 RETURN
1698 ENDIF
1699
1700 ALLOCATE(this%point_mask(this%innx,this%inny))
1701
1702 IF (this%trans%sub_type == 'maskvalid') THEN
1703! behavior depends on the presence/usability of maskbounds,
1704! simplified wrt its use in metamorphosis:mask
1705 IF (.NOT.PRESENT(maskbounds)) THEN
1706 this%point_mask(:,:) = c_e(maskgrid(:,:))
1707 ELSE IF (SIZE(maskbounds) < 2) THEN
1708 this%point_mask(:,:) = c_e(maskgrid(:,:))
1709 ELSE
1710 this%point_mask(:,:) = c_e(maskgrid(:,:)) .AND. &
1711 maskgrid(:,:) > maskbounds(1) .AND. &
1712 maskgrid(:,:) <= maskbounds(SIZE(maskbounds))
1713 ENDIF
1714 ELSE ! reverse condition
1715 this%point_mask(:,:) = .NOT.c_e(maskgrid(:,:))
1716 ENDIF
1717
1718 this%valid = .true.
1719
1720 ELSE IF (this%trans%sub_type == 'lemaskinvalid' .OR. &
1721 this%trans%sub_type == 'ltmaskinvalid' .OR. &
1722 this%trans%sub_type == 'gemaskinvalid' .OR. &
1723 this%trans%sub_type == 'gtmaskinvalid') THEN
1724! here i can only store field for computing mask runtime
1725
1726 this%val_mask = maskgrid
1727 this%valid = .true.
1728
1729 ELSE IF (this%trans%sub_type == 'setinvalidto') THEN
1730
1731 IF (.NOT.PRESENT(maskbounds)) THEN
1732 CALL l4f_category_log(this%category,l4f_error, &
1733 'grid_transform_init maskbounds missing for metamorphosis:'// &
1734 trim(this%trans%sub_type)//' transformation')
1735 RETURN
1736 ELSE IF (SIZE(maskbounds) < 1) THEN
1737 CALL l4f_category_log(this%category,l4f_error, &
1738 'grid_transform_init maskbounds empty for metamorphosis:'// &
1739 trim(this%trans%sub_type)//' transformation')
1740 RETURN
1741 ELSE
1742 this%val1 = maskbounds(1)
1743#ifdef DEBUG
1744 CALL l4f_category_log(this%category, l4f_debug, &
1745 "grid_transform_init setting invalid data to "//t2c(this%val1))
1746#endif
1747 ENDIF
1748
1749 this%valid = .true.
1750
1751 ELSE IF (this%trans%sub_type == 'settoinvalid') THEN
1752
1753 IF (.NOT.PRESENT(maskbounds)) THEN
1754 CALL l4f_category_log(this%category,l4f_error, &
1755 'grid_transform_init maskbounds missing for metamorphosis:'// &
1756 trim(this%trans%sub_type)//' transformation')
1757 CALL raise_error()
1758 RETURN
1759 ELSE IF (SIZE(maskbounds) < 2) THEN
1760 CALL l4f_category_log(this%category,l4f_error, &
1761 'grid_transform_init maskbounds must have at least 2 elements for metamorphosis:'// &
1762 trim(this%trans%sub_type)//' transformation')
1763 CALL raise_error()
1764 RETURN
1765 ELSE
1766 this%val1 = maskbounds(1)
1767 this%val2 = maskbounds(SIZE(maskbounds))
1768#ifdef DEBUG
1769 CALL l4f_category_log(this%category, l4f_debug, &
1770 "grid_transform_init setting to invalid interval ]"//t2c(this%val1)//','// &
1771 t2c(this%val2)//']')
1772#endif
1773 ENDIF
1774
1775 this%valid = .true.
1776
1777 ENDIF
1778
1779ENDIF
1780
1781CONTAINS
1782
1783! local subroutine to be called by all methods interpolating to a new
1784! grid, no parameters passed, used as a macro to avoid repeating code
1785SUBROUTINE outgrid_setup()
1786
1787! set increments in new grid in order for all the baraque to work
1788CALL griddim_setsteps(out)
1789! check component flag
1790CALL get_val(in, proj=proj_in, component_flag=cf_i)
1791CALL get_val(out, proj=proj_out, component_flag=cf_o)
1792IF (proj_in == proj_out) THEN
1793! same projection: set output component flag equal to input regardless
1794! of its value
1795 CALL set_val(out, component_flag=cf_i)
1796ELSE
1797! different projection, interpolation possible only with vector data
1798! referred to geograpical axes
1799 IF (cf_i == 1) THEN
1800 CALL l4f_category_log(this%category,l4f_warn, &
1801 'trying to interpolate a grid with component flag 1 to a grid on a different projection')
1802 CALL l4f_category_log(this%category,l4f_warn, &
1803 'vector fields will probably be wrong')
1804 ELSE
1805 CALL set_val(out, component_flag=cf_i)
1806 ENDIF
1807ENDIF
1808! rotate the input grid by n*360 degrees to bring it closer to the output grid
1809CALL griddim_set_central_lon(in, griddim_central_lon(out))
1810
1811END SUBROUTINE outgrid_setup
1812
1813END SUBROUTINE grid_transform_init
1814
1815
1816!> Constructor for a \a grid_transform object, defining a particular
1817!! grid-to-sparse points transformation.
1818!! It defines an object describing a transformation from a rectangular
1819!! grid to a set of sparse points; the abstract type of transformation
1820!! is described in the transformation object \a trans (type
1821!! transform_def) which must have been properly initialised. The
1822!! additional information required here is the description of the
1823!! input grid \a in (type griddim_def), and, if required by the
1824!! transformation type, the information about the target sparse points
1825!! over which the transformation should take place:
1826!!
1827!! - for 'inter' transformation, this is provided in the form of a
1828!! vol7d object (\a v7d_out argument, input), which must have been
1829!! initialized with the coordinates of desired sparse points
1830!!
1831!! - for 'polyinter' transformation, no target point information has
1832!! to be provided in input (it is calculated on the basis of input
1833!! grid and \a trans object), and the coordinates of the target
1834!! points (polygons' centroids) are returned in output in \a
1835!! v7d_out argument
1836!!
1837!! - for 'maskinter' transformation, this is a two dimensional real
1838!! field (\a maskgrid argument), which, together with the \a
1839!! maskbounds argument (optional with default), divides the input
1840!! grid in a number of subareas according to the values of \a
1841!! maskinter, and, in this case, \a v7d_out is an output argument
1842!! which returns the coordinates of the target points (subareas'
1843!! centroids)
1844!!
1845!! - for 'metamorphosis' transformation, no target point information
1846!! has to be provided in input (it is calculated on the basis of
1847!! input grid and \a trans object), except for 'mask' subtype, for
1848!! which the same information as for 'maskinter' transformation has
1849!! to be provided; in all the cases, as for 'polyinter', the
1850!! information about target points is returned in output in \a
1851!! v7d_out argument.
1852!!
1853!! The generated \a grid_transform object is specific to the grid and
1854!! sparse point list provided or computed. The function \a c_e can be
1855!! used in order to check whether the object has been successfully
1856!! initialised, if the result is \a .FALSE., it should not be used
1857!! further on.
1858SUBROUTINE grid_transform_grid_vol7d_init(this, trans, in, v7d_out, &
1859 maskgrid, maskbounds, find_index, categoryappend)
1860TYPE(grid_transform),INTENT(out) :: this !< grid_transformation object
1861TYPE(transform_def),INTENT(in) :: trans !< transformation object
1862TYPE(griddim_def),INTENT(in) :: in !< griddim object to transform
1863TYPE(vol7d),INTENT(inout) :: v7d_out !< vol7d object with the coordinates of the sparse points to be used as transformation target (input or output depending on type of transformation)
1864REAL,INTENT(in),OPTIONAL :: maskgrid(:,:) !< 2D field to be used for defining subareas according to its values, it must have the same shape as the field to be interpolated (for transformation type 'maskinter' and 'metamorphosis:mask')
1865REAL,INTENT(in),OPTIONAL :: maskbounds(:) !< array of boundary values for defining subareas from the values of \a maskgrid, the number of subareas is SIZE(maskbounds) - 1, if not provided a default based on extreme values of \a maskgrid is used
1866PROCEDURE(basic_find_index),POINTER,OPTIONAL :: find_index
1867CHARACTER(len=*),INTENT(in),OPTIONAL :: categoryappend !< append this suffix to log4fortran namespace category
1868
1869INTEGER :: ix, iy, n, nm, nr, nprev, nmaskarea, xnmin, xnmax, ynmin, ynmax, &
1870 time_definition
1871DOUBLE PRECISION :: xmin, xmax, ymin, ymax, r2, lonref
1872DOUBLE PRECISION,ALLOCATABLE :: lon1(:), lat1(:), lon(:,:), lat(:,:)
1873REAL,ALLOCATABLE :: lmaskbounds(:)
1874TYPE(georef_coord) :: point
1875TYPE(griddim_def) :: lin
1876!$ INTEGER :: outnx
1877
1878IF (PRESENT(find_index)) THEN ! move in init_common?
1879 IF (ASSOCIATED(find_index)) THEN
1880 this%find_index => find_index
1881 ENDIF
1882ENDIF
1883CALL grid_transform_init_common(this, trans, categoryappend)
1884#ifdef DEBUG
1885CALL l4f_category_log(this%category, l4f_debug, "grid_transform vg6d-v7d")
1886#endif
1887
1888! used after in some transformations
1889CALL get_val(trans, time_definition=time_definition)
1890IF (.NOT. c_e(time_definition)) THEN
1891 time_definition=1 ! default to validity time
1892ENDIF
1893
1894IF (this%trans%trans_type == 'inter') THEN
1895
1896 IF (this%trans%sub_type == 'near' .OR. this%trans%sub_type == 'bilin' &
1897 .OR. this%trans%sub_type == 'shapiro_near') THEN
1898
1899 CALL copy(in, lin)
1900 CALL get_val(lin, nx=this%innx, ny=this%inny)
1901 this%outnx = SIZE(v7d_out%ana)
1902 this%outny = 1
1903
1904 ALLOCATE (this%inter_index_x(this%outnx,this%outny),&
1905 this%inter_index_y(this%outnx,this%outny))
1906 ALLOCATE(lon(this%outnx,1),lat(this%outnx,1))
1907
1908 CALL getval(v7d_out%ana(:)%coord,lon=lon(:,1),lat=lat(:,1))
1909! equalize in/out coordinates
1910 lonref = 0.5d0*(maxval(lon(:,1), mask=c_e(lon(:,1))) + minval(lon(:,1), mask=c_e(lon(:,1))))
1911 CALL griddim_set_central_lon(lin, lonref)
1912 CALL get_val(lin, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
1913
1914 IF (this%trans%sub_type == 'bilin') THEN
1915 CALL this%find_index(lin, .false., &
1916 this%innx, this%inny, xmin, xmax, ymin, ymax, &
1917 lon, lat, this%trans%extrap, &
1918 this%inter_index_x, this%inter_index_y)
1919
1920 ALLOCATE(this%inter_x(this%innx,this%inny),this%inter_y(this%innx,this%inny))
1921 ALLOCATE(this%inter_xp(this%outnx,this%outny),this%inter_yp(this%outnx,this%outny))
1922
1923 CALL griddim_gen_coord(lin, this%inter_x, this%inter_y)
1924 CALL proj(lin, lon, lat, this%inter_xp, this%inter_yp)
1925
1926 ELSE ! near shapiro_near
1927 CALL this%find_index(lin, .true., &
1928 this%innx, this%inny, xmin, xmax, ymin, ymax, &
1929 lon, lat, this%trans%extrap, &
1930 this%inter_index_x, this%inter_index_y)
1931
1932 IF (this%trans%trans_type == 'intersearch') THEN ! replicate code above
1933 ALLOCATE(this%inter_x(this%innx,this%inny),this%inter_y(this%innx,this%inny))
1934 ALLOCATE(this%inter_xp(this%outnx,this%outny),this%inter_yp(this%outnx,this%outny))
1935
1936 CALL griddim_gen_coord(lin, this%inter_x, this%inter_y)
1937 CALL proj(lin, lon, lat, this%inter_xp, this%inter_yp)
1938 ENDIF
1939 ENDIF
1940
1941 DEALLOCATE(lon,lat)
1942 CALL delete(lin)
1943
1944 this%valid = .true.
1945
1946 ENDIF
1947
1948ELSE IF (this%trans%trans_type == 'polyinter') THEN
1949
1950 CALL get_val(in, nx=this%innx, ny=this%inny)
1951! unlike before, here index arrays must have the shape of input grid
1952 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
1953 this%inter_index_y(this%innx,this%inny))
1954 this%inter_index_x(:,:) = imiss
1955 this%inter_index_y(:,:) = 1
1956
1957! compute coordinates of input grid in geo system
1958 CALL copy(in, lin)
1959 CALL unproj(lin)
1960
1961 this%outnx = this%trans%poly%arraysize
1962 this%outny = 1
1963 CALL delete(v7d_out) ! required to avoid leaks because intent(inout), dangerous
1964 CALL init(v7d_out, time_definition=time_definition)
1965 CALL vol7d_alloc(v7d_out, nana=this%outnx)
1966
1967! equalize in/out coordinates
1968 ALLOCATE(lon(this%outnx,1))
1969 CALL getval(v7d_out%ana(:)%coord,lon=lon(:,1))
1970 lonref = 0.5d0*(maxval(lon(:,1), mask=c_e(lon(:,1))) + minval(lon(:,1), mask=c_e(lon(:,1))))
1971 CALL griddim_set_central_lon(lin, lonref)
1972 DEALLOCATE(lon)
1973
1974! setup output point list, equal to average of polygon points
1975! warning, in case of concave areas points may coincide!
1976 CALL poly_to_coordinates(this%trans%poly, v7d_out)
1977
1978 nprev = 1
1979!$OMP PARALLEL DEFAULT(SHARED)
1980!$OMP DO PRIVATE(iy, ix, n, point) FIRSTPRIVATE(nprev)
1981 DO iy = 1, this%inny
1982 inside_x: DO ix = 1, this%innx
1983 point = georef_coord_new(x=lin%dim%lon(ix,iy), y=lin%dim%lat(ix,iy))
1984
1985 DO n = nprev, this%trans%poly%arraysize ! optimize starting from last matched polygon
1986 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1987 this%inter_index_x(ix,iy) = n
1988 nprev = n
1989 cycle inside_x
1990 ENDIF
1991 ENDDO
1992 DO n = nprev-1, 1, -1 ! test the other polygons
1993 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
1994 this%inter_index_x(ix,iy) = n
1995 nprev = n
1996 cycle inside_x
1997 ENDIF
1998 ENDDO
1999
2000! CALL delete(point) ! speedup
2001 ENDDO inside_x
2002 ENDDO
2003!$OMP END PARALLEL
2004
2005#ifdef DEBUG
2006 DO n = 1, this%trans%poly%arraysize
2007 CALL l4f_category_log(this%category, l4f_debug, &
2008 'Polygon: '//t2c(n)//' grid points: '// &
2009 t2c(count(this%inter_index_x(:,:) == n)))
2010 ENDDO
2011#endif
2012
2013 CALL delete(lin)
2014 this%valid = .true. ! warning, no check of subtype
2015
2016ELSE IF (this%trans%trans_type == 'stencilinter') THEN
2017
2018! from inter:near
2019 CALL copy(in, lin)
2020 CALL get_val(lin, nx=this%innx, ny=this%inny)
2021 this%outnx = SIZE(v7d_out%ana)
2022 this%outny = 1
2023
2024 ALLOCATE (this%inter_index_x(this%outnx,this%outny),&
2025 this%inter_index_y(this%outnx,this%outny))
2026 ALLOCATE(lon(this%outnx,1),lat(this%outnx,1))
2027
2028 CALL getval(v7d_out%ana(:)%coord,lon=lon(:,1),lat=lat(:,1))
2029! equalize in/out coordinates
2030 lonref = 0.5d0*(maxval(lon(:,1), mask=c_e(lon(:,1))) + minval(lon(:,1), mask=c_e(lon(:,1))))
2031 CALL griddim_set_central_lon(lin, lonref)
2032
2033 CALL get_val(lin, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
2034
2035 CALL this%find_index(lin, .true., &
2036 this%innx, this%inny, xmin, xmax, ymin, ymax, &
2037 lon, lat, this%trans%extrap, &
2038 this%inter_index_x, this%inter_index_y)
2039
2040! define the stencil mask
2041 nr = int(this%trans%area_info%radius) ! integer radius
2042 n = nr*2+1 ! n. of points
2043 nm = nr + 1 ! becomes index of center
2044 r2 = this%trans%area_info%radius**2
2045 ALLOCATE(this%stencil(n,n))
2046 this%stencil(:,:) = .true.
2047 DO iy = 1, n
2048 DO ix = 1, n
2049 IF ((ix-nm)**2+(iy-nm)**2 > r2) this%stencil(ix,iy) = .false.
2050 ENDDO
2051 ENDDO
2052
2053! set to missing the elements of inter_index too close to the domain
2054! borders
2055 xnmin = nr + 1
2056 xnmax = this%innx - nr
2057 ynmin = nr + 1
2058 ynmax = this%inny - nr
2059 DO iy = 1, this%outny
2060 DO ix = 1, this%outnx
2061 IF (this%inter_index_x(ix,iy) < xnmin .OR. &
2062 this%inter_index_x(ix,iy) > xnmax .OR. &
2063 this%inter_index_y(ix,iy) < ynmin .OR. &
2064 this%inter_index_y(ix,iy) > ynmax) THEN
2065 this%inter_index_x(ix,iy) = imiss
2066 this%inter_index_y(ix,iy) = imiss
2067 ENDIF
2068 ENDDO
2069 ENDDO
2070
2071#ifdef DEBUG
2072 CALL l4f_category_log(this%category, l4f_debug, &
2073 'stencilinter: stencil size '//t2c(n*n))
2074 CALL l4f_category_log(this%category, l4f_debug, &
2075 'stencilinter: stencil points '//t2c(count(this%stencil)))
2076#endif
2077
2078 DEALLOCATE(lon,lat)
2079 CALL delete(lin)
2080
2081 this%valid = .true. ! warning, no check of subtype
2082
2083ELSE IF (this%trans%trans_type == 'maskinter') THEN
2084
2085 IF (.NOT.PRESENT(maskgrid)) THEN
2086 CALL l4f_category_log(this%category,l4f_error, &
2087 'grid_transform_init maskgrid argument missing for maskinter transformation')
2088 CALL raise_fatal_error()
2089 ENDIF
2090
2091 CALL get_val(in, nx=this%innx, ny=this%inny)
2092 IF (this%innx /= SIZE(maskgrid,1) .OR. this%inny /= SIZE(maskgrid,2)) THEN
2093 CALL l4f_category_log(this%category,l4f_error, &
2094 'grid_transform_init mask non conformal with input field')
2095 CALL l4f_category_log(this%category,l4f_error, &
2096 'mask: '//t2c(SIZE(maskgrid,1))//'x'//t2c(SIZE(maskgrid,2))// &
2097 ' input field:'//t2c(this%innx)//'x'//t2c(this%inny))
2098 CALL raise_fatal_error()
2099 ENDIF
2100! unlike before, here index arrays must have the shape of input grid
2101 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
2102 this%inter_index_y(this%innx,this%inny))
2103 this%inter_index_x(:,:) = imiss
2104 this%inter_index_y(:,:) = 1
2105
2106! generate the subarea boundaries according to maskgrid and maskbounds
2107 CALL gen_mask_class()
2108
2109! compute coordinates of input grid in geo system
2110 CALL copy(in, lin)
2111 CALL unproj(lin)
2112
2113!$OMP PARALLEL DEFAULT(SHARED)
2114!$OMP DO PRIVATE(iy, ix, n)
2115 DO iy = 1, this%inny
2116 DO ix = 1, this%innx
2117 IF (c_e(maskgrid(ix,iy))) THEN
2118 IF (maskgrid(ix,iy) <= lmaskbounds(nmaskarea+1)) THEN
2119 DO n = nmaskarea, 1, -1
2120 IF (maskgrid(ix,iy) > lmaskbounds(n)) THEN
2121 this%inter_index_x(ix,iy) = n
2122 EXIT
2123 ENDIF
2124 ENDDO
2125 ENDIF
2126 ENDIF
2127 ENDDO
2128 ENDDO
2129!$OMP END PARALLEL
2130
2131 this%outnx = nmaskarea
2132 this%outny = 1
2133 CALL delete(v7d_out) ! required to avoid leaks because intent(inout), dangerous
2134 CALL init(v7d_out, time_definition=time_definition)
2135 CALL vol7d_alloc(v7d_out, nana=nmaskarea)
2136
2137! setup output point list, equal to average of polygon points
2138! warning, in case of concave areas points may coincide!
2139 DO n = 1, nmaskarea
2140 CALL init(v7d_out%ana(n), &
2141 lon=stat_average(pack(lin%dim%lon(:,:), &
2142 mask=(this%inter_index_x(:,:) == n))), &
2143 lat=stat_average(pack(lin%dim%lat(:,:), &
2144 mask=(this%inter_index_x(:,:) == n))))
2145 ENDDO
2146
2147 CALL delete(lin)
2148 this%valid = .true. ! warning, no check of subtype
2149
2150ELSE IF (this%trans%trans_type == 'metamorphosis') THEN
2151
2152! common to all metamorphosis subtypes
2153! compute coordinates of input grid in geo system
2154 CALL copy(in, lin)
2155 CALL unproj(lin)
2156
2157 CALL get_val(in, nx=this%innx, ny=this%inny)
2158! allocate index array
2159 ALLOCATE(this%point_index(this%innx,this%inny))
2160 this%point_index(:,:) = imiss
2161! setup output coordinates
2162 IF (this%trans%sub_type /= 'index') THEN ! improve!
2163 CALL delete(v7d_out) ! required to avoid leaks because intent(inout), dangerous
2164 CALL init(v7d_out, time_definition=time_definition)
2165 ENDIF
2166
2167 IF (this%trans%sub_type == 'all' ) THEN
2168
2169 this%outnx = this%innx*this%inny
2170 this%outny = 1
2171 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2172
2173 n = 0
2174 DO iy=1,this%inny
2175 DO ix=1,this%innx
2176 CALL init(v7d_out%ana((iy-1)*this%innx+ix), &
2177 lon=lin%dim%lon(ix,iy),lat=lin%dim%lat(ix,iy))
2178 n = n + 1
2179 this%point_index(ix,iy) = n
2180 ENDDO
2181 ENDDO
2182
2183 this%valid = .true.
2184
2185 ELSE IF (this%trans%sub_type == 'coordbb' ) THEN
2186
2187! count and mark points falling into requested bounding-box
2188 this%outnx = 0
2189 this%outny = 1
2190 DO iy = 1, this%inny
2191 DO ix = 1, this%innx
2192! IF (geo_coord_inside_rectang()
2193 IF (lin%dim%lon(ix,iy) > this%trans%rect_coo%ilon .AND. &
2194 lin%dim%lon(ix,iy) < this%trans%rect_coo%flon .AND. &
2195 lin%dim%lat(ix,iy) > this%trans%rect_coo%ilat .AND. &
2196 lin%dim%lat(ix,iy) < this%trans%rect_coo%flat) THEN ! improve!
2197 this%outnx = this%outnx + 1
2198 this%point_index(ix,iy) = this%outnx
2199 ENDIF
2200 ENDDO
2201 ENDDO
2202
2203 IF (this%outnx <= 0) THEN
2204 CALL l4f_category_log(this%category,l4f_warn, &
2205 "metamorphosis:coordbb: no points inside bounding box "//&
2206 trim(to_char(this%trans%rect_coo%ilon))//","// &
2207 trim(to_char(this%trans%rect_coo%flon))//","// &
2208 trim(to_char(this%trans%rect_coo%ilat))//","// &
2209 trim(to_char(this%trans%rect_coo%flat)))
2210 ENDIF
2211
2212 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2213
2214! collect coordinates of points falling into requested bounding-box
2215 n = 0
2216 DO iy = 1, this%inny
2217 DO ix = 1, this%innx
2218 IF (c_e(this%point_index(ix,iy))) THEN
2219 n = n + 1
2220 CALL init(v7d_out%ana(n), &
2221 lon=lin%dim%lon(ix,iy), lat=lin%dim%lat(ix,iy))
2222 ENDIF
2223 ENDDO
2224 ENDDO
2225
2226 this%valid = .true.
2227
2228 ELSE IF (this%trans%sub_type == 'poly' ) THEN
2229
2230! count and mark points falling into requested polygon
2231#ifdef _OPENMP
2232 outnx = 0
2233#else
2234 this%outnx = 0
2235#endif
2236 this%outny = 1
2237
2238! this OMP block has to be checked
2239!$OMP PARALLEL DEFAULT(SHARED)
2240!$OMP DO PRIVATE(iy, ix, point, n), REDUCTION(+:outnx)
2241 DO iy = 1, this%inny
2242 DO ix = 1, this%innx
2243 point = georef_coord_new(x=lin%dim%lon(ix,iy), y=lin%dim%lat(ix,iy))
2244 DO n = 1, this%trans%poly%arraysize
2245 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
2246#ifdef _OPENMP
2247 outnx = outnx + 1
2248#else
2249 this%outnx = this%outnx + 1
2250#endif
2251 this%point_index(ix,iy) = n
2252 EXIT
2253 ENDIF
2254 ENDDO
2255! CALL delete(point) ! speedup
2256 ENDDO
2257 ENDDO
2258!$OMP END PARALLEL
2259!$ this%outnx = outnx
2260 IF (this%outnx <= 0) THEN
2261 CALL l4f_category_log(this%category,l4f_warn, &
2262 "metamorphosis:poly: no points inside polygons")
2263 ENDIF
2264
2265 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2266! collect coordinates of points falling into requested polygon
2267 n = 0
2268 DO iy = 1, this%inny
2269 DO ix = 1, this%innx
2270 IF (c_e(this%point_index(ix,iy))) THEN
2271 n = n + 1
2272 CALL init(v7d_out%ana(n), &
2273 lon=lin%dim%lon(ix,iy), lat=lin%dim%lat(ix,iy))
2274 ENDIF
2275 ENDDO
2276 ENDDO
2277
2278 this%valid = .true.
2279
2280 ELSE IF (this%trans%sub_type == 'mask' ) THEN
2281
2282 IF (.NOT.PRESENT(maskgrid)) THEN
2283 CALL l4f_category_log(this%category,l4f_error, &
2284 'grid_transform_init maskgrid argument missing for metamorphosis:mask transformation')
2285 CALL raise_error()
2286 RETURN
2287 ENDIF
2288
2289 IF (this%innx /= SIZE(maskgrid,1) .OR. this%inny /= SIZE(maskgrid,2)) THEN
2290 CALL l4f_category_log(this%category,l4f_error, &
2291 'grid_transform_init mask non conformal with input field')
2292 CALL l4f_category_log(this%category,l4f_error, &
2293 'mask: '//t2c(SIZE(maskgrid,1))//'x'//t2c(SIZE(maskgrid,2))// &
2294 ' input field:'//t2c(this%innx)//'x'//t2c(this%inny))
2295 CALL raise_error()
2296 RETURN
2297 ENDIF
2298
2299! generate the subarea boundaries according to maskgrid and maskbounds
2300 CALL gen_mask_class()
2301
2302#ifdef _OPENMP
2303 outnx = 0
2304#else
2305 this%outnx = 0
2306#endif
2307 this%outny = 1
2308
2309! this OMP block has to be checked
2310!$OMP PARALLEL DEFAULT(SHARED)
2311!$OMP DO PRIVATE(iy, ix), REDUCTION(+:outnx)
2312 DO iy = 1, this%inny
2313 DO ix = 1, this%innx
2314 IF (c_e(maskgrid(ix,iy))) THEN
2315 IF (maskgrid(ix,iy) <= lmaskbounds(nmaskarea+1)) THEN
2316 DO n = nmaskarea, 1, -1
2317 IF (maskgrid(ix,iy) > lmaskbounds(n)) THEN
2318#ifdef _OPENMP
2319 outnx = outnx + 1
2320#else
2321 this%outnx = this%outnx + 1
2322#endif
2323 this%point_index(ix,iy) = n
2324 EXIT
2325 ENDIF
2326 ENDDO
2327 ENDIF
2328 ENDIF
2329 ENDDO
2330 ENDDO
2331!$OMP END PARALLEL
2332!$ this%outnx = outnx
2333
2334 IF (this%outnx <= 0) THEN
2335 CALL l4f_category_log(this%category,l4f_warn, &
2336 "grid_transform_init no points inside mask for metamorphosis:mask transformation")
2337 ENDIF
2338#ifdef DEBUG
2339 DO n = 1, nmaskarea
2340 CALL l4f_category_log(this%category,l4f_info, &
2341 "points in subarea "//t2c(n)//": "// &
2342 t2c(count(this%point_index(:,:) == n)))
2343 ENDDO
2344#endif
2345 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2346! collect coordinates of points falling into requested polygon
2347 n = 0
2348 DO iy = 1, this%inny
2349 DO ix = 1, this%innx
2350 IF (c_e(this%point_index(ix,iy))) THEN
2351 n = n + 1
2352 CALL init(v7d_out%ana(n),lon=lin%dim%lon(ix,iy),lat=lin%dim%lat(ix,iy))
2353 ENDIF
2354 ENDDO
2355 ENDDO
2356
2357 this%valid = .true.
2358
2359 ELSE IF (this%trans%sub_type == 'index') THEN
2360 this%outnx = SIZE(v7d_out%ana)
2361 this%outny = 1
2362
2363! collect points having requested index
2364! check that v7d_out has just 1 variable B01192 (index)
2365 IF (any(c_e(lin%dim%lon))) THEN ! here we set also coordinates, find a more efficient way to check
2366 DO n = 1, SIZE(v7d_out%ana)
2367 iy = (v7d_out%volanai(n,1,1) - 1)/this%innx + 1
2368 ix = v7d_out%volanai(n,1,1) - (iy-1)*this%innx
2369 this%point_index(ix,iy) = v7d_out%volanai(n,1,1)
2370 CALL init(v7d_out%ana(n), &
2371 lon=lin%dim%lon(ix,iy), lat=lin%dim%lat(ix,iy)) ! (re)set coordinates from truth
2372 ENDDO
2373 ELSE ! here we trust coordinates from input
2374 DO n = 1, SIZE(v7d_out%ana)
2375 iy = (v7d_out%volanai(n,1,1) - 1)/this%innx + 1
2376 ix = v7d_out%volanai(n,1,1) - (iy-1)*this%innx
2377 this%point_index(ix,iy) = v7d_out%volanai(n,1,1)
2378 ENDDO
2379 ENDIF
2380 this%valid = .true.
2381 ENDIF
2382 CALL delete(lin)
2383ENDIF
2384
2385CONTAINS
2386
2387SUBROUTINE gen_mask_class()
2388REAL :: startmaskclass, mmin, mmax
2389INTEGER :: i
2390
2391IF (PRESENT(maskbounds)) THEN
2392 nmaskarea = SIZE(maskbounds) - 1
2393 IF (nmaskarea > 0) THEN
2394 lmaskbounds = maskbounds ! automatic f2003 allocation
2395 RETURN
2396 ELSE IF (nmaskarea == 0) THEN
2397 CALL l4f_category_log(this%category,l4f_warn, &
2398 'only one value provided for maskbounds, '//t2c(maskbounds(1)) &
2399 //', it will be ignored')
2400 CALL l4f_category_log(this%category,l4f_warn, &
2401 'at least 2 values are required for maskbounds')
2402 ENDIF
2403ENDIF
2404
2405mmin = minval(maskgrid, mask=c_e(maskgrid))
2406mmax = maxval(maskgrid, mask=c_e(maskgrid))
2407
2408nmaskarea = int(mmax - mmin + 1.5)
2409startmaskclass = mmin - 0.5
2410! assign limits for each class
2411ALLOCATE(lmaskbounds(nmaskarea+1))
2412lmaskbounds(:) = (/(startmaskclass+real(i),i=0,nmaskarea)/)
2413#ifdef DEBUG
2414CALL l4f_category_log(this%category,l4f_debug, &
2415 'maskinter, '//t2c(nmaskarea)//' subareas defined, with boundaries:')
2416DO i = 1, SIZE(lmaskbounds)
2417 CALL l4f_category_log(this%category,l4f_debug, &
2418 'maskinter '//t2c(i)//' '//t2c(lmaskbounds(i)))
2419ENDDO
2420#endif
2421
2422END SUBROUTINE gen_mask_class
2423
2424END SUBROUTINE grid_transform_grid_vol7d_init
2425
2426
2427!> Constructor for a \a grid_transform object, defining a particular
2428!! sparse points-to-grid transformation.
2429!! It defines an object describing a transformation from a set of
2430!! sparse points to a rectangular grid; the abstract type of
2431!! transformation is described in the transformation object \a trans
2432!! (type transform_def) which must have been properly initialised. The
2433!! additional information required here is the list of the input
2434!! sparse points in the form of a \a vol7d object (parameter \a v7d_in),
2435!! which can be the same volume that will be successively used for
2436!! interpolation, or a volume with just the same coordinate data, and
2437!! the description of the output grid \a griddim (a \a griddim_def
2438!! object).
2439!!
2440!! The generated \a grid_transform object is specific to the sparse
2441!! point list and grid provided. The function \a c_e can be used in
2442!! order to check whether the object has been successfully
2443!! initialised, if the result is \a .FALSE., it should not be used
2444!! further on.
2445SUBROUTINE grid_transform_vol7d_grid_init(this, trans, v7d_in, out, categoryappend)
2446TYPE(grid_transform),INTENT(out) :: this !< grid transformation object
2447TYPE(transform_def),INTENT(in) :: trans !< transformation object
2448TYPE(vol7d),INTENT(in) :: v7d_in !< vol7d object with the coordinates of the sparse point to be used as input (only information about coordinates is used)
2449TYPE(griddim_def),INTENT(in) :: out !< griddim object defining target grid
2450character(len=*),INTENT(in),OPTIONAL :: categoryappend !< append this suffix to log4fortran namespace category
2451
2452INTEGER :: nx, ny
2453DOUBLE PRECISION :: xmin, xmax, ymin, ymax, lonref
2454DOUBLE PRECISION,ALLOCATABLE :: lon(:,:),lat(:,:)
2455TYPE(griddim_def) :: lout
2456
2457
2458CALL grid_transform_init_common(this, trans, categoryappend)
2459#ifdef DEBUG
2460CALL l4f_category_log(this%category, l4f_debug, "grid_transform v7d-vg6d")
2461#endif
2462
2463IF (this%trans%trans_type == 'inter') THEN
2464
2465 IF ( this%trans%sub_type == 'linear' ) THEN
2466
2467 this%innx=SIZE(v7d_in%ana)
2468 this%inny=1
2469 ALLOCATE(lon(this%innx,1),lat(this%innx,1))
2470 ALLOCATE(this%inter_xp(this%innx,this%inny),this%inter_yp(this%innx,this%inny))
2471 CALL getval(v7d_in%ana(:)%coord,lon=lon(:,1),lat=lat(:,1))
2472
2473 CALL copy (out, lout)
2474! equalize in/out coordinates
2475 lonref = 0.5d0*(maxval(lon(:,1), mask=c_e(lon(:,1))) + minval(lon(:,1), mask=c_e(lon(:,1))))
2476 CALL griddim_set_central_lon(lout, lonref)
2477
2478 CALL get_val(lout, nx=nx, ny=ny)
2479 this%outnx=nx
2480 this%outny=ny
2481 ALLOCATE(this%inter_x(this%outnx,this%outny),this%inter_y(this%outnx,this%outny))
2482
2483 CALL get_val(lout, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
2484 CALL proj(lout, lon, lat, this%inter_xp, this%inter_yp)
2485 CALL griddim_gen_coord(lout, this%inter_x, this%inter_y)
2486
2487 DEALLOCATE(lon,lat)
2488 CALL delete(lout)
2489
2490 this%valid = .true.
2491
2492 ENDIF
2493
2494ELSE IF (this%trans%trans_type == 'boxinter') THEN
2495
2496 this%innx=SIZE(v7d_in%ana)
2497 this%inny=1
2498! index arrays must have the shape of input grid
2499 ALLOCATE(lon(this%innx,1),lat(this%innx,1))
2500 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
2501 this%inter_index_y(this%innx,this%inny))
2502! get coordinates of input grid in geo system
2503 CALL getval(v7d_in%ana(:)%coord,lon=lon(:,1),lat=lat(:,1))
2504
2505 CALL copy (out, lout)
2506! equalize in/out coordinates
2507 lonref = 0.5d0*(maxval(lon(:,1), mask=c_e(lon(:,1))) + minval(lon(:,1), mask=c_e(lon(:,1))))
2508 CALL griddim_set_central_lon(lout, lonref)
2509
2510 CALL get_val(lout, nx=this%outnx, ny=this%outny, &
2511 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
2512! TODO now box size is ignored
2513! if box size not provided, use the actual grid step
2514 IF (.NOT.c_e(this%trans%area_info%boxdx)) &
2515 CALL get_val(out, dx=this%trans%area_info%boxdx)
2516 IF (.NOT.c_e(this%trans%area_info%boxdy)) &
2517 CALL get_val(out, dx=this%trans%area_info%boxdy)
2518! half size is actually needed
2519 this%trans%area_info%boxdx = this%trans%area_info%boxdx*0.5d0
2520 this%trans%area_info%boxdy = this%trans%area_info%boxdy*0.5d0
2521
2522! use find_index in the opposite way, here extrap does not make sense
2523 CALL this%find_index(lout, .true., &
2524 this%outnx, this%outny, xmin, xmax, ymin, ymax, &
2525 lon, lat, .false., &
2526 this%inter_index_x, this%inter_index_y)
2527
2528 DEALLOCATE(lon,lat)
2529 CALL delete(lout)
2530
2531 this%valid = .true. ! warning, no check of subtype
2532
2533ENDIF
2534
2535END SUBROUTINE grid_transform_vol7d_grid_init
2536
2537
2538!> Constructor for a \a grid_transform object, defining a particular
2539!! sparse points-to-sparse points transformation.
2540!! It defines an object describing a transformation from a set of
2541!! sparse points to a set of sparse points; the abstract type of
2542!! transformation is described in the transformation object \a trans
2543!! (type transform_def) which must have been properly initialised. The
2544!! additional information required here is the list of the input
2545!! sparse points in the form of a \a vol7d object (parameter \a
2546!! v7d_in), which can be the same volume that will be successively
2547!! used for interpolation, or a volume with just the same coordinate
2548!! data, and, if required by the transformation type, the information
2549!! about the target sparse points over which the transformation should
2550!! take place:
2551!!
2552!! - for 'inter' transformation, this is provided in the form of a
2553!! vol7d object (\a v7d_out argument, input), which must have been
2554!! initialized with the coordinates of desired sparse points
2555!!
2556!! - for 'polyinter' transformation, no target point information has
2557!! to be provided in input (it is calculated on the basis of input
2558!! grid and \a trans object), and the coordinates of the target
2559!! points (polygons' centroids) are returned in output in \a
2560!! v7d_out argument
2561!!
2562!! - for 'metamorphosis' transformation, no target point information
2563!! has to be provided in input (it is calculated on the basis of
2564!! input grid and \a trans object), and, as for 'polyinter', this
2565!! information is returned in output in \a v7d_out argument.
2566!!
2567!! The generated \a grid_transform object is specific to the input and
2568!! output sparse point lists provided or computed. The function \a c_e
2569!! can be used in order to check whether the object has been
2570!! successfully initialised, if the result is \a .FALSE., it should
2571!! not be used further on.
2572SUBROUTINE grid_transform_vol7d_vol7d_init(this, trans, v7d_in, v7d_out, &
2573 maskbounds, categoryappend)
2574TYPE(grid_transform),INTENT(out) :: this !< grid_transformation object
2575TYPE(transform_def),INTENT(in) :: trans !< transformation object
2576TYPE(vol7d),INTENT(in) :: v7d_in !< vol7d object with the coordinates of the sparse point to be used as input (only information about coordinates is used)
2577TYPE(vol7d),INTENT(inout) :: v7d_out !< vol7d object with the coordinates of the sparse points to be used as transformation target (input or output depending on type of transformation, when output, it must have been initialised anyway)
2578REAL,INTENT(in),OPTIONAL :: maskbounds(:) !< array of boundary values for defining a subset of valid points where the values of \a maskgrid are within the first and last value of \a maskbounds (for transformation type 'metamorphosis:maskvalid/settoinvalid' and others)
2579CHARACTER(len=*),INTENT(in),OPTIONAL :: categoryappend !< append this suffix to log4fortran namespace category
2580
2581INTEGER :: i, n
2582DOUBLE PRECISION,ALLOCATABLE :: lon(:), lat(:)
2583! temporary, improve!!!!
2584DOUBLE PRECISION :: lon1, lat1
2585TYPE(georef_coord) :: point
2586
2587
2588CALL grid_transform_init_common(this, trans, categoryappend)
2589#ifdef DEBUG
2590CALL l4f_category_log(this%category, l4f_debug, "grid_transform v7d-v7d")
2591#endif
2592
2593IF (this%trans%trans_type == 'inter') THEN
2594
2595 IF ( this%trans%sub_type == 'linear' ) THEN
2596
2597 CALL vol7d_alloc_vol(v7d_out) ! be safe
2598 this%outnx=SIZE(v7d_out%ana)
2599 this%outny=1
2600
2601 this%innx=SIZE(v7d_in%ana)
2602 this%inny=1
2603
2604 ALLOCATE(this%inter_xp(this%innx,this%inny),this%inter_yp(this%innx,this%inny))
2605 ALLOCATE(this%inter_x(this%outnx,this%outny),this%inter_y(this%outnx,this%outny))
2606
2607 CALL getval(v7d_in%ana(:)%coord,lon=this%inter_xp(:,1),lat=this%inter_yp(:,1))
2608 CALL getval(v7d_out%ana(:)%coord,lon=this%inter_x(:,1),lat=this%inter_y(:,1))
2609
2610 this%valid = .true.
2611
2612 ENDIF
2613
2614ELSE IF (this%trans%trans_type == 'polyinter') THEN
2615
2616 this%innx=SIZE(v7d_in%ana)
2617 this%inny=1
2618! unlike before, here index arrays must have the shape of input grid
2619 ALLOCATE(this%inter_index_x(this%innx,this%inny), &
2620 this%inter_index_y(this%innx,this%inny))
2621 this%inter_index_x(:,:) = imiss
2622 this%inter_index_y(:,:) = 1
2623
2624 DO i = 1, SIZE(v7d_in%ana)
2625! temporary, improve!!!!
2626 CALL getval(v7d_in%ana(i)%coord,lon=lon1,lat=lat1)
2627 point = georef_coord_new(x=lon1, y=lat1)
2628
2629 DO n = 1, this%trans%poly%arraysize
2630 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
2631 this%inter_index_x(i,1) = n
2632 EXIT
2633 ENDIF
2634 ENDDO
2635 ENDDO
2636
2637 this%outnx=this%trans%poly%arraysize
2638 this%outny=1
2639 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2640
2641! setup output point list, equal to average of polygon points
2642! warning, in case of concave areas points may coincide!
2643 CALL poly_to_coordinates(this%trans%poly, v7d_out)
2644
2645 this%valid = .true. ! warning, no check of subtype
2646
2647ELSE IF (this%trans%trans_type == 'metamorphosis') THEN
2648
2649! common to all metamorphosis subtypes
2650 this%innx = SIZE(v7d_in%ana)
2651 this%inny = 1
2652! allocate index array
2653 ALLOCATE(this%point_index(this%innx,this%inny))
2654 this%point_index(:,:) = imiss
2655
2656 IF (this%trans%sub_type == 'all' ) THEN
2657
2658 CALL metamorphosis_all_setup()
2659
2660 ELSE IF (this%trans%sub_type == 'coordbb' ) THEN
2661
2662 ALLOCATE(lon(this%innx),lat(this%innx))
2663
2664! count and mark points falling into requested bounding-box
2665 this%outnx = 0
2666 this%outny = 1
2667 CALL getval(v7d_in%ana(:)%coord,lon=lon,lat=lat)
2668 DO i = 1, this%innx
2669! IF (geo_coord_inside_rectang()
2670 IF (lon(i) > this%trans%rect_coo%ilon .AND. &
2671 lon(i) < this%trans%rect_coo%flon .AND. &
2672 lat(i) > this%trans%rect_coo%ilat .AND. &
2673 lat(i) < this%trans%rect_coo%flat) THEN ! improve!
2674 this%outnx = this%outnx + 1
2675 this%point_index(i,1) = this%outnx
2676 ENDIF
2677 ENDDO
2678
2679 IF (this%outnx <= 0) THEN
2680 CALL l4f_category_log(this%category,l4f_warn, &
2681 "metamorphosis:coordbb: no points inside bounding box "//&
2682 trim(to_char(this%trans%rect_coo%ilon))//","// &
2683 trim(to_char(this%trans%rect_coo%flon))//","// &
2684 trim(to_char(this%trans%rect_coo%ilat))//","// &
2685 trim(to_char(this%trans%rect_coo%flat)))
2686 ENDIF
2687
2688 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2689
2690! collect coordinates of points falling into requested bounding-box
2691 n = 0
2692 DO i = 1, this%innx
2693 IF (c_e(this%point_index(i,1))) THEN
2694 n = n + 1
2695 CALL init(v7d_out%ana(n),lon=lon(i),lat=lat(i))
2696 ENDIF
2697 ENDDO
2698 DEALLOCATE(lon, lat)
2699
2700 this%valid = .true.
2701
2702 ELSE IF (this%trans%sub_type == 'poly' ) THEN
2703
2704! count and mark points falling into requested polygon
2705 this%outnx = 0
2706 this%outny = 1
2707 DO i = 1, this%innx
2708! temporary, improve!!!!
2709 CALL getval(v7d_in%ana(i)%coord,lon=lon1,lat=lat1)
2710 point = georef_coord_new(x=lon1, y=lat1)
2711 DO n = 1, this%trans%poly%arraysize
2712 IF (inside(point, this%trans%poly%array(n))) THEN ! stop at the first matching polygon
2713 this%outnx = this%outnx + 1
2714 this%point_index(i,1) = n
2715 EXIT
2716 ENDIF
2717 ENDDO
2718! CALL delete(point) ! speedup
2719 ENDDO
2720
2721 IF (this%outnx <= 0) THEN
2722 CALL l4f_category_log(this%category,l4f_warn, &
2723 "metamorphosis:poly: no points inside polygons")
2724 ENDIF
2725
2726 CALL vol7d_alloc(v7d_out, nana=this%outnx)
2727
2728! collect coordinates of points falling into requested polygon
2729 n = 0
2730 DO i = 1, this%innx
2731 IF (c_e(this%point_index(i,1))) THEN
2732 n = n + 1
2733! temporary, improve!!!!
2734 CALL getval(v7d_in%ana(i)%coord,lon=lon1,lat=lat1)
2735 CALL init(v7d_out%ana(n),lon=lon1,lat=lat1)
2736 ENDIF
2737 ENDDO
2738
2739 this%valid = .true.
2740
2741 ELSE IF (this%trans%sub_type == 'setinvalidto' ) THEN
2742
2743 IF (.NOT.PRESENT(maskbounds)) THEN
2744 CALL l4f_category_log(this%category,l4f_error, &
2745 'grid_transform_init maskbounds missing for metamorphosis:'// &
2746 trim(this%trans%sub_type)//' transformation')
2747 RETURN
2748 ELSE IF (SIZE(maskbounds) < 1) THEN
2749 CALL l4f_category_log(this%category,l4f_error, &
2750 'grid_transform_init maskbounds empty for metamorphosis:'// &
2751 trim(this%trans%sub_type)//' transformation')
2752 RETURN
2753 ELSE
2754 this%val1 = maskbounds(1)
2755#ifdef DEBUG
2756 CALL l4f_category_log(this%category, l4f_debug, &
2757 "grid_transform_init setting invalid data to "//t2c(this%val1))
2758#endif
2759 ENDIF
2760
2761 CALL metamorphosis_all_setup()
2762
2763 ELSE IF (this%trans%sub_type == 'settoinvalid' ) THEN
2764
2765 IF (.NOT.PRESENT(maskbounds)) THEN
2766 CALL l4f_category_log(this%category,l4f_error, &
2767 'grid_transform_init maskbounds missing for metamorphosis:'// &
2768 trim(this%trans%sub_type)//' transformation')
2769 CALL raise_error()
2770 RETURN
2771 ELSE IF (SIZE(maskbounds) < 2) THEN
2772 CALL l4f_category_log(this%category,l4f_error, &
2773 'grid_transform_init maskbounds must have at least 2 elements for metamorphosis:'// &
2774 trim(this%trans%sub_type)//' transformation')
2775 CALL raise_error()
2776 RETURN
2777 ELSE
2778 this%val1 = maskbounds(1)
2779 this%val2 = maskbounds(SIZE(maskbounds))
2780#ifdef DEBUG
2781 CALL l4f_category_log(this%category, l4f_debug, &
2782 "grid_transform_init setting to invalid interval ]"//t2c(this%val1)//','// &
2783 t2c(this%val2)//']')
2784#endif
2785 ENDIF
2786
2787 CALL metamorphosis_all_setup()
2788
2789 ENDIF
2790ENDIF
2791
2792CONTAINS
2793
2794! common code to metamorphosis transformations conserving the number
2795! of points
2796SUBROUTINE metamorphosis_all_setup()
2797
2798this%outnx = SIZE(v7d_in%ana)
2799this%outny = 1
2800this%point_index(:,1) = (/(i,i=1,this%innx)/)
2801CALL vol7d_alloc(v7d_out, nana=SIZE(v7d_in%ana))
2802v7d_out%ana = v7d_in%ana
2803
2804this%valid = .true.
2805
2806END SUBROUTINE metamorphosis_all_setup
2807
2808END SUBROUTINE grid_transform_vol7d_vol7d_init
2809
2810
2811! Private subroutine for performing operations common to all constructors
2812SUBROUTINE grid_transform_init_common(this, trans, categoryappend)
2813TYPE(grid_transform),INTENT(inout) :: this
2814TYPE(transform_def),INTENT(in) :: trans
2815CHARACTER(len=*),INTENT(in),OPTIONAL :: categoryappend
2816
2817CHARACTER(len=512) :: a_name
2818
2819IF (PRESENT(categoryappend)) THEN
2820 CALL l4f_launcher(a_name,a_name_append=trim(subcategory)//"."// &
2821 trim(categoryappend))
2822ELSE
2823 CALL l4f_launcher(a_name,a_name_append=trim(subcategory))
2824ENDIF
2825this%category=l4f_category_get_handle(a_name)
2826
2827#ifdef DEBUG
2828CALL l4f_category_log(this%category,l4f_debug,"start init_grid_transform")
2829#endif
2830
2831this%trans=trans
2832
2833END SUBROUTINE grid_transform_init_common
2834
2835! internal subroutine to correctly initialise the output coordinates
2836! with polygon centroid coordinates
2837SUBROUTINE poly_to_coordinates(poly, v7d_out)
2838TYPE(arrayof_georef_coord_array),intent(in) :: poly
2839TYPE(vol7d),INTENT(inout) :: v7d_out
2840
2841INTEGER :: n, sz
2842DOUBLE PRECISION,ALLOCATABLE :: lon(:), lat(:)
2843
2844DO n = 1, poly%arraysize
2845 CALL getval(poly%array(n), x=lon, y=lat)
2846 sz = min(SIZE(lon), SIZE(lat))
2847 IF (lon(1) == lon(sz) .AND. lat(1) == lat(sz)) THEN ! closed polygon
2848 sz = sz - 1
2849 ENDIF
2850 CALL init(v7d_out%ana(n), lon=stat_average(lon(1:sz)), lat=stat_average(lat(1:sz)))
2851ENDDO
2852
2853END SUBROUTINE poly_to_coordinates
2854
2855!> Destructor of \a grid_tranform object.
2856!! It releases any memory and data associated to
2857!! \a grid_transform object \a this, the logger category will be deleted too.
2858SUBROUTINE grid_transform_delete(this)
2859TYPE(grid_transform),INTENT(inout) :: this !< grid_transform object
2860
2861CALL delete(this%trans)
2862
2863this%innx=imiss
2864this%inny=imiss
2865this%outnx=imiss
2866this%outny=imiss
2867this%iniox=imiss
2868this%inioy=imiss
2869this%infox=imiss
2870this%infoy=imiss
2871this%outinx=imiss
2872this%outiny=imiss
2873this%outfnx=imiss
2874this%outfny=imiss
2875
2876if (associated(this%inter_index_x)) deallocate (this%inter_index_x)
2877if (associated(this%inter_index_y)) deallocate (this%inter_index_y)
2878if (associated(this%inter_index_z)) deallocate (this%inter_index_z)
2879if (associated(this%point_index)) deallocate (this%point_index)
2880
2881if (associated(this%inter_x)) deallocate (this%inter_x)
2882if (associated(this%inter_y)) deallocate (this%inter_y)
2883
2884if (associated(this%inter_xp)) deallocate (this%inter_xp)
2885if (associated(this%inter_yp)) deallocate (this%inter_yp)
2886if (associated(this%inter_zp)) deallocate (this%inter_zp)
2887if (associated(this%vcoord_in)) deallocate (this%vcoord_in)
2888if (associated(this%vcoord_out)) deallocate (this%vcoord_out)
2889if (associated(this%point_mask)) deallocate (this%point_mask)
2890if (associated(this%stencil)) deallocate (this%stencil)
2891if (associated(this%output_level_auto)) deallocate (this%output_level_auto)
2892IF (ALLOCATED(this%coord_3d_in)) DEALLOCATE(this%coord_3d_in)
2893this%valid = .false.
2894
2895! close the logger
2896call l4f_category_delete(this%category)
2897
2898END SUBROUTINE grid_transform_delete
2899
2900
2901!> Method for returning the contents of the object.
2902!! Only a few selected memebrs of \a grid_transform object can be
2903!! queried, this is mainly for use by \a volgrid6d_class, rather than
2904!! for public use.
2905SUBROUTINE grid_transform_get_val(this, output_level_auto, point_mask, &
2906 point_index, output_point_index, levshift, levused)
2907TYPE(grid_transform),INTENT(in) :: this !< object to examine
2908TYPE(vol7d_level),POINTER,OPTIONAL :: output_level_auto(:) !< array of auto-generated output levels
2909LOGICAL,INTENT(out),ALLOCATABLE,OPTIONAL :: point_mask(:) !< mask array indicating the input points that are kept in the output, for metamorphosis transformations
2910INTEGER,INTENT(out),ALLOCATABLE,OPTIONAL :: point_index(:) !< array of indices indicating the polygon to which every input point has been assigned, if applicable
2911INTEGER,INTENT(out),ALLOCATABLE,OPTIONAL :: output_point_index(:) !< array of indices indicating the polygon to which every output point has been assigned, if applicable
2912INTEGER,INTENT(out),OPTIONAL :: levshift !< shift between input and output levels for vertint
2913INTEGER,INTENT(out),OPTIONAL :: levused !< number of input levels used for vertint
2914
2915INTEGER :: i
2916
2917IF (PRESENT(output_level_auto)) output_level_auto => this%output_level_auto
2918IF (PRESENT(point_mask)) THEN
2919 IF (ASSOCIATED(this%point_index)) THEN
2920 point_mask = c_e(reshape(this%point_index, (/SIZE(this%point_index)/)))
2921 ENDIF
2922ENDIF
2923IF (PRESENT(point_index)) THEN
2924 IF (ASSOCIATED(this%point_index)) THEN
2925 point_index = reshape(this%point_index, (/SIZE(this%point_index)/))
2926 ENDIF
2927ENDIF
2928IF (PRESENT(output_point_index)) THEN
2929 IF (ASSOCIATED(this%point_index)) THEN
2930! metamorphosis, index is computed from input origin of output point
2931 output_point_index = pack(this%point_index(:,:), c_e(this%point_index))
2932 ELSE IF (this%trans%trans_type == 'polyinter' .OR. &
2933 this%trans%trans_type == 'maskinter') THEN
2934! other cases, index is order of output point
2935 output_point_index = (/(i,i=1,this%outnx)/)
2936 ENDIF
2937ENDIF
2938IF (PRESENT(levshift)) levshift = this%levshift
2939IF (PRESENT(levused)) levused = this%levused
2940
2941END SUBROUTINE grid_transform_get_val
2942
2943
2944!> Returns \a .TRUE. if, after \a init , the corresponding \a grid_transform
2945!! object has been correctly initilised.
2946FUNCTION grid_transform_c_e(this)
2947TYPE(grid_transform),INTENT(in) :: this !< grid_transform object
2948LOGICAL :: grid_transform_c_e
2949
2950grid_transform_c_e = this%valid
2951
2952END FUNCTION grid_transform_c_e
2953
2954
2955!> Compute the output data array from input data array according to
2956!! the defined transformation. The \a grid_transform object \a this
2957!! must have been properly initialised, so that it contains all the
2958!! information needed for computing the transformation. This is the
2959!! grid-to-grid and grid-to-sparse points version. In the case of
2960!! grid-to-sparse points transformation, the output argument is still
2961!! a 2-d array with shape \a (np,1), which may have to be reshaped and
2962!! assigned to the target 1-d array after the subroutine call by means
2963!! of the \a RESHAPE() intrinsic function.
2964RECURSIVE SUBROUTINE grid_transform_compute(this, field_in, field_out, var, &
2965 coord_3d_in, zlist)
2966TYPE(grid_transform),INTENT(in),TARGET :: this !< grid_transformation object
2967REAL,INTENT(in) :: field_in(:,:,:) !< input array
2968REAL,INTENT(out) :: field_out(:,:,:) !< output array
2969TYPE(vol7d_var),INTENT(in),OPTIONAL :: var !< physical variable to be interpolated, if provided, some ad-hoc algorithms may be used where possible
2970REAL,INTENT(in),OPTIONAL,TARGET :: coord_3d_in(:,:,:) !< input vertical coordinate for vertical interpolation, if not provided by other means
2971LOGICAL,INTENT(in),OPTIONAL :: zlist(:) !< list of levels actually present in input volume, if provided, levels marked as \a .FALSE. will not be interpolated regardless of their content (optimisation to avoid interpolating missing data)
2972
2973INTEGER :: i, j, k, l, m, s, ii, jj, ie, je, n, navg, kk, kkcache, kkup, kkdown, &
2974 kfound, kfoundin, inused, i1, i2, j1, j2, np, ns, ix, iy
2975INTEGER,ALLOCATABLE :: nval(:,:)
2976REAL :: z1,z2,z3,z4,z(4)
2977DOUBLE PRECISION :: x1,x3,y1,y3,xp,yp, disttmp, dist
2978INTEGER :: innx, inny, innz, outnx, outny, outnz, vartype, nearcount
2979REAL,ALLOCATABLE :: coord_in(:)
2980LOGICAL,ALLOCATABLE :: mask_in(:)
2981REAL,ALLOCATABLE :: val_in(:), field_tmp(:,:,:)
2982REAL,POINTER :: coord_3d_in_act(:,:,:)
2983TYPE(grid_transform) :: likethis
2984LOGICAL :: alloc_coord_3d_in_act, nm1, optsearch, farenough
2985CHARACTER(len=4) :: env_var
2986LOGICAL,ALLOCATABLE :: lzlist(:)
2987
2988#ifdef DEBUG
2989CALL l4f_category_log(this%category,l4f_debug,"start grid_transform_compute")
2990#endif
2991
2992field_out(:,:,:) = rmiss
2993
2994IF (.NOT.this%valid) THEN
2995 CALL l4f_category_log(this%category,l4f_error, &
2996 "refusing to perform a non valid transformation")
2997 RETURN
2998ENDIF
2999
3000IF (this%recur) THEN ! if recursive transformation, recur here and exit
3001#ifdef DEBUG
3002 CALL l4f_category_log(this%category,l4f_debug, &
3003 "recursive transformation in grid_transform_compute")
3004#endif
3005
3006 IF (this%trans%trans_type == 'polyinter') THEN
3007 likethis = this
3008 likethis%recur = .false.
3009 likethis%outnx = this%trans%poly%arraysize
3010 likethis%outny = 1
3011 ALLOCATE(field_tmp(this%trans%poly%arraysize,1,SIZE(field_out,3)))
3012 CALL grid_transform_compute(likethis, field_in, field_tmp, var, coord_3d_in, zlist)
3013
3014 DO k = 1, SIZE(field_out,3)
3015 DO j = 1, this%inny
3016 DO i = 1, this%innx
3017 IF (c_e(this%inter_index_x(i,j))) THEN
3018 field_out(i,j,k) = field_tmp(this%inter_index_x(i,j),this%inter_index_y(i,j),k)
3019 ENDIF
3020 ENDDO
3021 ENDDO
3022 ENDDO
3023 DEALLOCATE(field_tmp)
3024 ENDIF
3025
3026 RETURN
3027ENDIF
3028
3029vartype = var_ord
3030IF (PRESENT(var)) THEN
3031 vartype = vol7d_vartype(var)
3032ENDIF
3033
3034innx = SIZE(field_in,1); inny = SIZE(field_in,2); innz = SIZE(field_in,3)
3035outnx = SIZE(field_out,1); outny = SIZE(field_out,2); outnz = SIZE(field_out,3)
3036ALLOCATE(lzlist(innz))
3037IF (PRESENT(zlist)) THEN
3038 lzlist(:) = zlist(:)
3039ELSE
3040 lzlist(:) = .true.
3041ENDIF
3042
3043! check size of field_in, field_out
3044IF (this%trans%trans_type == 'vertint') THEN ! vertical interpolation
3045
3046 IF (innz /= this%innz) THEN
3047 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
3048 CALL l4f_category_log(this%category,l4f_error,"inconsistent input shape: "//&
3049 t2c(this%innz)//" /= "//t2c(innz))
3050 CALL raise_error()
3051 RETURN
3052 ENDIF
3053
3054 IF (outnz /= this%outnz) THEN
3055 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
3056 CALL l4f_category_log(this%category,l4f_error,"inconsistent output shape: "//&
3057 t2c(this%outnz)//" /= "//t2c(outnz))
3058 CALL raise_error()
3059 RETURN
3060 ENDIF
3061
3062 IF (innx /= outnx .OR. inny /= outny) THEN
3063 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
3064 CALL l4f_category_log(this%category,l4f_error,"inconsistent hor. sizes: "//&
3065 t2c(innx)//","//t2c(inny)//" /= "//&
3066 t2c(outnx)//","//t2c(outny))
3067 CALL raise_error()
3068 RETURN
3069 ENDIF
3070
3071ELSE ! horizontal interpolation
3072
3073 IF (innx /= this%innx .OR. inny /= this%inny) THEN
3074 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
3075 CALL l4f_category_log(this%category,l4f_error,"inconsistent input shape: "//&
3076 t2c(this%innx)//","//t2c(this%inny)//" /= "//&
3077 t2c(innx)//","//t2c(inny))
3078 CALL raise_error()
3079 RETURN
3080 ENDIF
3081
3082 IF (outnx /= this%outnx .OR. outny /= this%outny) THEN
3083 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
3084 CALL l4f_category_log(this%category,l4f_error,"inconsistent output shape: "//&
3085 t2c(this%outnx)//","//t2c(this%outny)//" /= "//&
3086 t2c(outnx)//","//t2c(outny))
3087 CALL raise_error()
3088 RETURN
3089 ENDIF
3090
3091 IF (innz /= outnz) THEN
3092 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
3093 CALL l4f_category_log(this%category,l4f_error,"inconsistent vert. sizes: "//&
3094 t2c(innz)//" /= "//t2c(outnz))
3095 CALL raise_error()
3096 RETURN
3097 ENDIF
3098
3099ENDIF
3100
3101#ifdef DEBUG
3102call l4f_category_log(this%category,l4f_debug, &
3103 "start grid_transform_compute "//trim(this%trans%trans_type)//':'// &
3104 trim(this%trans%sub_type))
3105#endif
3106
3107IF (this%trans%trans_type == 'zoom') THEN
3108
3109 field_out(this%outinx:this%outfnx, &
3110 this%outiny:this%outfny,:) = &
3111 field_in(this%iniox:this%infox, &
3112 this%inioy:this%infoy,:)
3113
3114ELSE IF (this%trans%trans_type == 'boxregrid') THEN
3115
3116 IF (this%trans%sub_type == 'average') THEN
3117 IF (vartype == var_dir360) THEN
3118 DO k = 1, innz
3119 IF (.NOT.lzlist(k)) cycle
3120 jj = 0
3121 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3122 je = j+this%trans%box_info%npy-1
3123 jj = jj+1
3124 ii = 0
3125 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3126 ie = i+this%trans%box_info%npx-1
3127 ii = ii+1
3128 navg = count(field_in(i:ie,j:je,k) /= rmiss)
3129 IF (navg > 0) THEN
3130 field_out(ii,jj,k) = find_prevailing_direction(field_in(i:ie,j:je,k), &
3131 0.0, 360.0, 5.0)
3132 ENDIF
3133 ENDDO
3134 ENDDO
3135 ENDDO
3136
3137 ELSE
3138 DO k = 1, innz
3139 IF (.NOT.lzlist(k)) cycle
3140 jj = 0
3141 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3142 je = j+this%trans%box_info%npy-1
3143 jj = jj+1
3144 ii = 0
3145 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3146 ie = i+this%trans%box_info%npx-1
3147 ii = ii+1
3148 navg = count(field_in(i:ie,j:je,k) /= rmiss)
3149 IF (navg > 0) THEN
3150 field_out(ii,jj,k) = sum(field_in(i:ie,j:je,k), &
3151 mask=(field_in(i:ie,j:je,k) /= rmiss))/navg
3152 ENDIF
3153 ENDDO
3154 ENDDO
3155 ENDDO
3156
3157 ENDIF
3158
3159 ELSE IF (this%trans%sub_type == 'stddev' .OR. &
3160 this%trans%sub_type == 'stddevnm1') THEN
3161
3162 IF (this%trans%sub_type == 'stddev') THEN
3163 nm1 = .false.
3164 ELSE
3165 nm1 = .true.
3166 ENDIF
3167
3168 navg = this%trans%box_info%npx*this%trans%box_info%npy
3169 DO k = 1, innz
3170 IF (.NOT.lzlist(k)) cycle
3171 jj = 0
3172 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3173 je = j+this%trans%box_info%npy-1
3174 jj = jj+1
3175 ii = 0
3176 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3177 ie = i+this%trans%box_info%npx-1
3178 ii = ii+1
3179 field_out(ii,jj,k) = stat_stddev( &
3180 reshape(field_in(i:ie,j:je,k),(/navg/)), nm1=nm1)
3181 ENDDO
3182 ENDDO
3183 ENDDO
3184
3185 ELSE IF (this%trans%sub_type == 'max') THEN
3186 DO k = 1, innz
3187 IF (.NOT.lzlist(k)) cycle
3188 jj = 0
3189 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3190 je = j+this%trans%box_info%npy-1
3191 jj = jj+1
3192 ii = 0
3193 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3194 ie = i+this%trans%box_info%npx-1
3195 ii = ii+1
3196 navg = count(field_in(i:ie,j:je,k) /= rmiss)
3197 IF (navg > 0) THEN
3198 field_out(ii,jj,k) = maxval(field_in(i:ie,j:je,k), &
3199 mask=(field_in(i:ie,j:je,k) /= rmiss))
3200 ENDIF
3201 ENDDO
3202 ENDDO
3203 ENDDO
3204
3205 ELSE IF (this%trans%sub_type == 'min') THEN
3206 DO k = 1, innz
3207 IF (.NOT.lzlist(k)) cycle
3208 jj = 0
3209 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3210 je = j+this%trans%box_info%npy-1
3211 jj = jj+1
3212 ii = 0
3213 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3214 ie = i+this%trans%box_info%npx-1
3215 ii = ii+1
3216 navg = count(field_in(i:ie,j:je,k) /= rmiss)
3217 IF (navg > 0) THEN
3218 field_out(ii,jj,k) = minval(field_in(i:ie,j:je,k), &
3219 mask=(field_in(i:ie,j:je,k) /= rmiss))
3220 ENDIF
3221 ENDDO
3222 ENDDO
3223 ENDDO
3224
3225 ELSE IF (this%trans%sub_type == 'percentile') THEN
3226
3227 navg = this%trans%box_info%npx*this%trans%box_info%npy
3228 DO k = 1, innz
3229 IF (.NOT.lzlist(k)) cycle
3230 jj = 0
3231 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3232 je = j+this%trans%box_info%npy-1
3233 jj = jj+1
3234 ii = 0
3235 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3236 ie = i+this%trans%box_info%npx-1
3237 ii = ii+1
3238 field_out(ii:ii,jj,k) = stat_percentile( &
3239 reshape(field_in(i:ie,j:je,k),(/navg/)), &
3240 (/real(this%trans%stat_info%percentile)/))
3241 ENDDO
3242 ENDDO
3243 ENDDO
3244
3245 ELSE IF (this%trans%sub_type == 'frequency') THEN
3246
3247 DO k = 1, innz
3248 IF (.NOT.lzlist(k)) cycle
3249 jj = 0
3250 DO j = 1, this%inny - this%trans%box_info%npy + 1, this%trans%box_info%npy
3251 je = j+this%trans%box_info%npy-1
3252 jj = jj+1
3253 ii = 0
3254 DO i = 1, this%innx - this%trans%box_info%npx + 1, this%trans%box_info%npx
3255 ie = i+this%trans%box_info%npx-1
3256 ii = ii+1
3257 navg = count(field_in(i:ie,j:je,k) /= rmiss)
3258 IF (navg > 0) THEN
3259 field_out(ii,jj,k) = sum(interval_info_valid( &
3260 this%trans%interval_info, field_in(i:ie,j:je,k)), &
3261 mask=(field_in(i:ie,j:je,k) /= rmiss))/navg
3262 ENDIF
3263 ENDDO
3264 ENDDO
3265 ENDDO
3266
3267 ENDIF
3268
3269ELSE IF (this%trans%trans_type == 'inter') THEN
3270
3271 IF (this%trans%sub_type == 'near') THEN
3272
3273 DO k = 1, innz
3274 IF (.NOT.lzlist(k)) cycle
3275 DO j = 1, this%outny
3276 DO i = 1, this%outnx
3277
3278 IF (c_e(this%inter_index_x(i,j))) field_out(i,j,k) = &
3279 field_in(this%inter_index_x(i,j),this%inter_index_y(i,j),k)
3280
3281 ENDDO
3282 ENDDO
3283 ENDDO
3284
3285 ELSE IF (this%trans%sub_type == 'bilin') THEN
3286
3287 DO k = 1, innz
3288 IF (.NOT.lzlist(k)) cycle
3289 DO j = 1, this%outny
3290 DO i = 1, this%outnx
3291
3292 IF (c_e(this%inter_index_x(i,j))) THEN
3293
3294 z1=field_in(this%inter_index_x(i,j),this%inter_index_y(i,j),k)
3295 z2=field_in(this%inter_index_x(i,j)+1,this%inter_index_y(i,j),k)
3296 z3=field_in(this%inter_index_x(i,j)+1,this%inter_index_y(i,j)+1,k)
3297 z4=field_in(this%inter_index_x(i,j),this%inter_index_y(i,j)+1,k)
3298
3299 IF (c_e(z1) .AND. c_e(z2) .AND. c_e(z3) .AND. c_e(z4)) THEN
3300
3301 x1=this%inter_x(this%inter_index_x(i,j),this%inter_index_y(i,j))
3302 y1=this%inter_y(this%inter_index_x(i,j),this%inter_index_y(i,j))
3303 x3=this%inter_x(this%inter_index_x(i,j)+1,this%inter_index_y(i,j)+1)
3304 y3=this%inter_y(this%inter_index_x(i,j)+1,this%inter_index_y(i,j)+1)
3305
3306 xp=this%inter_xp(i,j)
3307 yp=this%inter_yp(i,j)
3308
3309 field_out(i,j,k) = hbilin(z1,z2,z3,z4,x1,y1,x3,y3,xp,yp)
3310
3311 ENDIF
3312 ENDIF
3313
3314 ENDDO
3315 ENDDO
3316 ENDDO
3317 ELSE IF (this%trans%sub_type == 'shapiro_near') THEN
3318 DO k = 1, innz
3319 IF (.NOT.lzlist(k)) cycle
3320 DO j = 1, this%outny
3321 DO i = 1, this%outnx
3322
3323 IF (c_e(this%inter_index_x(i,j))) THEN
3324
3325 IF(this%inter_index_x(i,j)-1>0)THEN
3326 z(1) = field_in(this%inter_index_x(i,j)-1,this%inter_index_y(i,j) ,k)
3327 ELSE
3328 z(1) = rmiss
3329 END IF
3330 IF(this%inter_index_x(i,j)+1<=this%outnx)THEN
3331 z(3)=field_in(this%inter_index_x(i,j)+1,this%inter_index_y(i,j) ,k)
3332 ELSE
3333 z(3) = rmiss
3334 END IF
3335 IF(this%inter_index_y(i,j)+1<=this%outny)THEN
3336 z(2)=field_in(this%inter_index_x(i,j) ,this%inter_index_y(i,j)+1,k)
3337 ELSE
3338 z(2) = rmiss
3339 END IF
3340 IF(this%inter_index_y(i,j)-1>0)THEN
3341 z(4)=field_in(this%inter_index_x(i,j), this%inter_index_y(i,j)-1,k)
3342 ELSE
3343 z(4) = rmiss
3344 END IF
3345 field_out(i,j,k) = shapiro(z,field_in(this%inter_index_x(i,j),this%inter_index_y(i,j),k))
3346
3347 ENDIF
3348
3349 ENDDO
3350 ENDDO
3351 ENDDO
3352
3353 ENDIF
3354ELSE IF (this%trans%trans_type == 'intersearch') THEN
3355
3356 likethis = this
3357 likethis%trans%trans_type = 'inter' ! fake type and make a recursive call to compute base field
3358 CALL grid_transform_compute(likethis, field_in, field_out, var, coord_3d_in, zlist)
3359 CALL getenv('LIBSIM_DISABLEOPTSEARCH', env_var)
3360 optsearch = len_trim(env_var) == 0
3361
3362 DO k = 1, innz
3363 IF (.NOT.lzlist(k)) cycle
3364 IF ((.NOT.all(c_e(field_out(:,:,k)))) .AND. (any(c_e(field_in(:,:,k))))) THEN ! must fill some values
3365 DO j = 1, this%outny
3366 DO i = 1, this%outnx
3367 IF (.NOT.c_e(field_out(i,j,k))) THEN
3368 dist = huge(dist)
3369 nearcount = 0
3370 IF (optsearch) THEN ! optimized, error-prone algorithm
3371 ix = this%inter_index_x(i,j)
3372 iy = this%inter_index_y(i,j)
3373 DO s = 0, max(this%innx, this%inny)
3374 farenough = .true.
3375 DO m = iy-s, iy+s, max(2*s, 1) ! y loop on upper and lower frames
3376 IF (m < 1 .OR. m > this%inny) cycle
3377 DO l = max(1, ix-s), min(this%innx, ix+s) ! x loop on upper and lower frames
3378 disttmp = (this%inter_xp(i,j) - this%inter_x(l,m))**2 + (this%inter_yp(i,j) - this%inter_y(l,m))**2
3379 IF (c_e(field_in(l,m,k))) THEN
3380 IF (disttmp < dist) THEN
3381 dist = disttmp
3382 field_out(i,j,k) = field_in(l,m,k)
3383 nearcount = 1
3384 ELSE IF (disttmp == dist) THEN
3385 field_out(i,j,k) = field_out(i,j,k) + field_in(l,m,k)
3386 nearcount = nearcount + 1
3387 ENDIF
3388 ENDIF
3389 IF (disttmp < dist) farenough = .false.
3390 ENDDO
3391 ENDDO
3392 DO m = max(1, iy-s+1), min(this%inny, iy+s-1) ! y loop on left and right frames (avoid corners)
3393 DO l = ix-s, ix+s, 2*s ! x loop on left and right frames (exchange loops?)
3394 IF (l < 1 .OR. l > this%innx) cycle
3395 disttmp = (this%inter_xp(i,j) - this%inter_x(l,m))**2 + (this%inter_yp(i,j) - this%inter_y(l,m))**2
3396 IF (c_e(field_in(l,m,k))) THEN
3397 IF (disttmp < dist) THEN
3398 dist = disttmp
3399 field_out(i,j,k) = field_in(l,m,k)
3400 nearcount = 1
3401 ELSE IF (disttmp == dist) THEN
3402 field_out(i,j,k) = field_out(i,j,k) + field_in(l,m,k)
3403 nearcount = nearcount + 1
3404 ENDIF
3405 ENDIF
3406 IF (disttmp < dist) farenough = .false.
3407 ENDDO
3408 ENDDO
3409 IF (s > 0 .AND. farenough) EXIT ! nearest point found, do not trust the same point, in case of bilin it could be not the nearest
3410 ENDDO
3411 ELSE ! linear, simple, slow algorithm
3412 DO m = 1, this%inny
3413 DO l = 1, this%innx
3414 IF (c_e(field_in(l,m,k))) THEN
3415 disttmp = (this%inter_xp(i,j) - this%inter_x(l,m))**2 + (this%inter_yp(i,j) - this%inter_y(l,m))**2
3416 IF (disttmp < dist) THEN
3417 dist = disttmp
3418 field_out(i,j,k) = field_in(l,m,k)
3419 nearcount = 1
3420 ELSE IF (disttmp == dist) THEN
3421 field_out(i,j,k) = field_out(i,j,k) + field_in(l,m,k)
3422 nearcount = nearcount + 1
3423 ENDIF
3424 ENDIF
3425 ENDDO
3426 ENDDO
3427 ENDIF
3428! average points with same minimum distance
3429 IF (nearcount > 1) field_out(i,j,k) = field_out(i,j,k)/nearcount
3430 ENDIF
3431 ENDDO
3432 ENDDO
3433 ENDIF
3434 ENDDO
3435
3436ELSE IF (this%trans%trans_type == 'boxinter' &
3437 .OR. this%trans%trans_type == 'polyinter' &
3438 .OR. this%trans%trans_type == 'maskinter') THEN
3439
3440 IF (this%trans%sub_type == 'average') THEN
3441
3442 IF (vartype == var_dir360) THEN
3443 DO k = 1, innz
3444 IF (.NOT.lzlist(k)) cycle
3445 DO j = 1, this%outny
3446 DO i = 1, this%outnx
3447 field_out(i,j,k) = find_prevailing_direction(field_in(:,:,k), &
3448 0.0, 360.0, 5.0, &
3449 mask=this%inter_index_x(:,:) == i .AND. this%inter_index_y(:,:) == j)
3450 ENDDO
3451 ENDDO
3452 ENDDO
3453
3454 ELSE
3455 ALLOCATE(nval(this%outnx, this%outny))
3456 field_out(:,:,:) = 0.0
3457 DO k = 1, innz
3458 IF (.NOT.lzlist(k)) cycle
3459 nval(:,:) = 0
3460 DO j = 1, this%inny
3461 DO i = 1, this%innx
3462 IF (c_e(this%inter_index_x(i,j)) .AND. c_e(field_in(i,j,k))) THEN
3463 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3464 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) + &
3465 field_in(i,j,k)
3466 nval(this%inter_index_x(i,j),this%inter_index_y(i,j)) = &
3467 nval(this%inter_index_x(i,j),this%inter_index_y(i,j)) + 1
3468 ENDIF
3469 ENDDO
3470 ENDDO
3471 WHERE (nval(:,:) /= 0)
3472 field_out(:,:,k) = field_out(:,:,k)/nval(:,:)
3473 ELSEWHERE
3474 field_out(:,:,k) = rmiss
3475 END WHERE
3476 ENDDO
3477 DEALLOCATE(nval)
3478 ENDIF
3479
3480 ELSE IF (this%trans%sub_type == 'stddev' .OR. &
3481 this%trans%sub_type == 'stddevnm1') THEN
3482
3483 IF (this%trans%sub_type == 'stddev') THEN
3484 nm1 = .false.
3485 ELSE
3486 nm1 = .true.
3487 ENDIF
3488 DO k = 1, innz
3489 IF (.NOT.lzlist(k)) cycle
3490 DO j = 1, this%outny
3491 DO i = 1, this%outnx
3492! da paura
3493 field_out(i:i,j,k) = stat_stddev( &
3494 reshape(field_in(:,:,k), (/SIZE(field_in(:,:,k))/)), &
3495 mask=reshape((this%inter_index_x == i .AND. &
3496 this%inter_index_y == j), (/SIZE(field_in(:,:,k))/)), nm1=nm1)
3497 ENDDO
3498 ENDDO
3499 ENDDO
3500
3501 ELSE IF (this%trans%sub_type == 'max') THEN
3502
3503 DO k = 1, innz
3504 IF (.NOT.lzlist(k)) cycle
3505 DO j = 1, this%inny
3506 DO i = 1, this%innx
3507 IF (c_e(this%inter_index_x(i,j)) .AND. c_e(field_in(i,j,k))) THEN
3508 IF (c_e(field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k))) THEN
3509 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3510 max(field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k), &
3511 field_in(i,j,k))
3512 ELSE
3513 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3514 field_in(i,j,k)
3515 ENDIF
3516 ENDIF
3517 ENDDO
3518 ENDDO
3519 ENDDO
3520
3521
3522 ELSE IF (this%trans%sub_type == 'min') THEN
3523
3524 DO k = 1, innz
3525 IF (.NOT.lzlist(k)) cycle
3526 DO j = 1, this%inny
3527 DO i = 1, this%innx
3528 IF (c_e(this%inter_index_x(i,j)) .AND. c_e(field_in(i,j,k))) THEN
3529 IF (c_e(field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k))) THEN
3530 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3531 min(field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k), &
3532 field_in(i,j,k))
3533 ELSE
3534 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3535 field_in(i,j,k)
3536 ENDIF
3537 ENDIF
3538 ENDDO
3539 ENDDO
3540 ENDDO
3541
3542 ELSE IF (this%trans%sub_type == 'percentile') THEN
3543
3544 DO k = 1, innz
3545 IF (.NOT.lzlist(k)) cycle
3546 DO j = 1, this%outny
3547 DO i = 1, this%outnx
3548! da paura
3549 field_out(i:i,j,k) = stat_percentile( &
3550 reshape(field_in(:,:,k), (/SIZE(field_in(:,:,k))/)), &
3551 (/real(this%trans%stat_info%percentile)/), &
3552 mask=reshape((this%inter_index_x == i .AND. &
3553 this%inter_index_y == j), (/SIZE(field_in(:,:,k))/)))
3554 ENDDO
3555 ENDDO
3556 ENDDO
3557
3558 ELSE IF (this%trans%sub_type == 'frequency') THEN
3559
3560 ALLOCATE(nval(this%outnx, this%outny))
3561 DO k = 1, innz
3562 IF (.NOT.lzlist(k)) cycle
3563 field_out(:,:,k) = 0.0
3564 nval(:,:) = 0
3565 DO j = 1, this%inny
3566 DO i = 1, this%innx
3567 IF (c_e(this%inter_index_x(i,j)) .AND. c_e(field_in(i,j,k))) THEN
3568 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) = &
3569 field_out(this%inter_index_x(i,j),this%inter_index_y(i,j),k) + &
3570 interval_info_valid(this%trans%interval_info, field_in(i,j,k))
3571 nval(this%inter_index_x(i,j),this%inter_index_y(i,j)) = &
3572 nval(this%inter_index_x(i,j),this%inter_index_y(i,j)) + 1
3573 ENDIF
3574 ENDDO
3575 ENDDO
3576 WHERE (nval(:,:) /= 0)
3577 field_out(:,:,k) = field_out(:,:,k)/nval(:,:)
3578 ELSEWHERE
3579 field_out(:,:,k) = rmiss
3580 END WHERE
3581 ENDDO
3582 DEALLOCATE(nval)
3583
3584 ENDIF
3585
3586ELSE IF (this%trans%trans_type == 'stencilinter') THEN
3587 np = SIZE(this%stencil,1)/2
3588 ns = SIZE(this%stencil)
3589
3590 IF (this%trans%sub_type == 'average') THEN
3591
3592 IF (vartype == var_dir360) THEN
3593 DO k = 1, innz
3594 IF (.NOT.lzlist(k)) cycle
3595 DO j = 1, this%outny
3596 DO i = 1, this%outnx
3597 IF (c_e(this%inter_index_x(i,j))) THEN
3598 i1 = this%inter_index_x(i,j) - np
3599 i2 = this%inter_index_x(i,j) + np
3600 j1 = this%inter_index_y(i,j) - np
3601 j2 = this%inter_index_y(i,j) + np
3602 field_out(i,j,k) = find_prevailing_direction(field_in(i1:i2,j1:j2,k), &
3603 0.0, 360.0, 5.0, &
3604 mask=this%stencil(:,:)) ! simpler and equivalent form
3605! mask=field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3606 ENDIF
3607 ENDDO
3608 ENDDO
3609 ENDDO
3610
3611 ELSE
3612!$OMP PARALLEL DEFAULT(SHARED)
3613!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2, n)
3614 DO k = 1, innz
3615 IF (.NOT.lzlist(k)) cycle
3616 DO j = 1, this%outny
3617 DO i = 1, this%outnx
3618 IF (c_e(this%inter_index_x(i,j))) THEN
3619 i1 = this%inter_index_x(i,j) - np
3620 i2 = this%inter_index_x(i,j) + np
3621 j1 = this%inter_index_y(i,j) - np
3622 j2 = this%inter_index_y(i,j) + np
3623 n = count(field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3624 IF (n > 0) THEN
3625 field_out(i,j,k) = sum(field_in(i1:i2,j1:j2,k), &
3626 mask=field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))/n
3627 ENDIF
3628 ENDIF
3629 ENDDO
3630 ENDDO
3631 ENDDO
3632!$OMP END PARALLEL
3633 ENDIF
3634
3635 ELSE IF (this%trans%sub_type == 'stddev' .OR. &
3636 this%trans%sub_type == 'stddevnm1') THEN
3637
3638 IF (this%trans%sub_type == 'stddev') THEN
3639 nm1 = .false.
3640 ELSE
3641 nm1 = .true.
3642 ENDIF
3643
3644!$OMP PARALLEL DEFAULT(SHARED)
3645!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2)
3646 DO k = 1, innz
3647 IF (.NOT.lzlist(k)) cycle
3648 DO j = 1, this%outny
3649 DO i = 1, this%outnx
3650 IF (c_e(this%inter_index_x(i,j))) THEN
3651 i1 = this%inter_index_x(i,j) - np
3652 i2 = this%inter_index_x(i,j) + np
3653 j1 = this%inter_index_y(i,j) - np
3654 j2 = this%inter_index_y(i,j) + np
3655! da paura
3656 field_out(i:i,j,k) = stat_stddev( &
3657 reshape(field_in(i1:i2,j1:j2,k), (/ns/)), &
3658 mask=reshape(field_in(i1:i2,j1:j2,k) /= rmiss .AND. &
3659 this%stencil(:,:), (/ns/)), nm1=nm1)
3660 ENDIF
3661 ENDDO
3662 ENDDO
3663 ENDDO
3664!$OMP END PARALLEL
3665
3666 ELSE IF (this%trans%sub_type == 'max') THEN
3667
3668!$OMP PARALLEL DEFAULT(SHARED)
3669!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2, n)
3670 DO k = 1, innz
3671 IF (.NOT.lzlist(k)) cycle
3672 DO j = 1, this%outny
3673 DO i = 1, this%outnx
3674 IF (c_e(this%inter_index_x(i,j))) THEN
3675 i1 = this%inter_index_x(i,j) - np
3676 i2 = this%inter_index_x(i,j) + np
3677 j1 = this%inter_index_y(i,j) - np
3678 j2 = this%inter_index_y(i,j) + np
3679 n = count(field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3680 IF (n > 0) THEN
3681 field_out(i,j,k) = maxval(field_in(i1:i2,j1:j2,k), &
3682 mask=field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3683 ENDIF
3684 ENDIF
3685 ENDDO
3686 ENDDO
3687 ENDDO
3688!$OMP END PARALLEL
3689
3690 ELSE IF (this%trans%sub_type == 'min') THEN
3691
3692!$OMP PARALLEL DEFAULT(SHARED)
3693!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2, n)
3694 DO k = 1, innz
3695 IF (.NOT.lzlist(k)) cycle
3696 DO j = 1, this%outny
3697 DO i = 1, this%outnx
3698 IF (c_e(this%inter_index_x(i,j))) THEN
3699 i1 = this%inter_index_x(i,j) - np
3700 i2 = this%inter_index_x(i,j) + np
3701 j1 = this%inter_index_y(i,j) - np
3702 j2 = this%inter_index_y(i,j) + np
3703 n = count(field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3704 IF (n > 0) THEN
3705 field_out(i,j,k) = minval(field_in(i1:i2,j1:j2,k), &
3706 mask=field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3707 ENDIF
3708 ENDIF
3709 ENDDO
3710 ENDDO
3711 ENDDO
3712!$OMP END PARALLEL
3713
3714 ELSE IF (this%trans%sub_type == 'percentile') THEN
3715
3716!$OMP PARALLEL DEFAULT(SHARED)
3717!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2)
3718 DO k = 1, innz
3719 IF (.NOT.lzlist(k)) cycle
3720 DO j = 1, this%outny
3721 DO i = 1, this%outnx
3722 IF (c_e(this%inter_index_x(i,j))) THEN
3723 i1 = this%inter_index_x(i,j) - np
3724 i2 = this%inter_index_x(i,j) + np
3725 j1 = this%inter_index_y(i,j) - np
3726 j2 = this%inter_index_y(i,j) + np
3727! da paura
3728 field_out(i:i,j,k) = stat_percentile( &
3729 reshape(field_in(i1:i2,j1:j2,k), (/ns/)), &
3730 (/real(this%trans%stat_info%percentile)/), &
3731 mask=reshape(field_in(i1:i2,j1:j2,k) /= rmiss .AND. &
3732 this%stencil(:,:), (/ns/)))
3733 ENDIF
3734 ENDDO
3735 ENDDO
3736 ENDDO
3737!$OMP END PARALLEL
3738
3739 ELSE IF (this%trans%sub_type == 'frequency') THEN
3740
3741!$OMP PARALLEL DEFAULT(SHARED)
3742!$OMP DO PRIVATE(i, j, k, i1, i2, j1, j2, n)
3743 DO k = 1, innz
3744 IF (.NOT.lzlist(k)) cycle
3745 DO j = 1, this%outny
3746 DO i = 1, this%outnx
3747 IF (c_e(this%inter_index_x(i,j))) THEN
3748 i1 = this%inter_index_x(i,j) - np
3749 i2 = this%inter_index_x(i,j) + np
3750 j1 = this%inter_index_y(i,j) - np
3751 j2 = this%inter_index_y(i,j) + np
3752 n = count(field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))
3753 IF (n > 0) THEN
3754 field_out(i,j,k) = sum(interval_info_valid( &
3755 this%trans%interval_info, field_in(i1:i2,j1:j2,k)), &
3756 mask=field_in(i1:i2,j1:j2,k) /= rmiss .AND. this%stencil(:,:))/n
3757 ENDIF
3758 ENDIF
3759 ENDDO
3760 ENDDO
3761 ENDDO
3762!$OMP END PARALLEL
3763
3764 ENDIF
3765
3766ELSE IF (this%trans%trans_type == 'maskgen') THEN
3767
3768 DO k = 1, innz
3769 WHERE(c_e(this%inter_index_x(:,:)))
3770 field_out(:,:,k) = real(this%inter_index_x(:,:))
3771 END WHERE
3772 ENDDO
3773
3774ELSE IF (this%trans%trans_type == 'metamorphosis') THEN
3775
3776 IF (this%trans%sub_type == 'all') THEN
3777
3778 field_out(:,:,:) = reshape(field_in(:,:,:), (/this%outnx,this%outny,innz/))
3779
3780 ELSE IF (this%trans%sub_type == 'coordbb' .OR. this%trans%sub_type == 'poly' &
3781 .OR. this%trans%sub_type == 'mask' .OR. this%trans%sub_type == 'index') THEN
3782
3783 DO k = 1, innz
3784 IF (.NOT.lzlist(k)) cycle
3785! this is to sparse-points only, so field_out(:,1,k) is acceptable
3786 field_out(:,1,k) = pack(field_in(:,:,k), c_e(this%point_index(:,:)))
3787 ENDDO
3788
3789 ELSE IF (this%trans%sub_type == 'maskvalid' .OR. &
3790 this%trans%sub_type == 'maskinvalid') THEN
3791
3792 DO k = 1, innz
3793 IF (.NOT.lzlist(k)) cycle
3794 WHERE (this%point_mask(:,:)) ! this is almost the same as the previous syntax
3795 field_out(:,:,k) = field_in(:,:,k) ! check which is more efficient
3796 END WHERE
3797 ENDDO
3798
3799 ELSE IF (this%trans%sub_type == 'lemaskinvalid') THEN
3800
3801 DO k = 1, innz
3802 IF (.NOT.lzlist(k)) cycle
3803 WHERE (c_e(field_in(:,:,k)) .AND. field_in(:,:,k) > this%val_mask(:,:))
3804 field_out(:,:,k) = field_in(:,:,k)
3805 ELSEWHERE
3806 field_out(:,:,k) = rmiss
3807 END WHERE
3808 ENDDO
3809
3810 ELSE IF (this%trans%sub_type == 'ltmaskinvalid') THEN
3811
3812 DO k = 1, innz
3813 IF (.NOT.lzlist(k)) cycle
3814 WHERE (c_e(field_in(:,:,k)) .AND. field_in(:,:,k) >= this%val_mask(:,:))
3815 field_out(:,:,k) = field_in(:,:,k)
3816 ELSEWHERE
3817 field_out(:,:,k) = rmiss
3818 END WHERE
3819 ENDDO
3820
3821 ELSE IF (this%trans%sub_type == 'gemaskinvalid') THEN
3822
3823 DO k = 1, innz
3824 IF (.NOT.lzlist(k)) cycle
3825 WHERE (c_e(field_in(:,:,k)) .AND. field_in(:,:,k) < this%val_mask(:,:))
3826 field_out(:,:,k) = field_in(:,:,k)
3827 ELSEWHERE
3828 field_out(:,:,k) = rmiss
3829 END WHERE
3830 ENDDO
3831
3832 ELSE IF (this%trans%sub_type == 'gtmaskinvalid') THEN
3833
3834 DO k = 1, innz
3835 IF (.NOT.lzlist(k)) cycle
3836 WHERE (c_e(field_in(:,:,k)) .AND. field_in(:,:,k) <= this%val_mask(:,:))
3837 field_out(:,:,k) = field_in(:,:,k)
3838 ELSEWHERE
3839 field_out(:,:,k) = rmiss
3840 END WHERE
3841 ENDDO
3842
3843 ELSE IF (this%trans%sub_type == 'setinvalidto') THEN
3844
3845 DO k = 1, innz
3846 IF (.NOT.lzlist(k)) cycle ! should i skip levels here?
3847 WHERE (c_e(field_in(:,:,k)))
3848 field_out(:,:,k) = field_in(:,:,k)
3849 ELSE WHERE
3850 field_out(:,:,k) = this%val1
3851 END WHERE
3852 ENDDO
3853
3854 ELSE IF (this%trans%sub_type == 'settoinvalid') THEN
3855 IF (c_e(this%val1) .AND. c_e(this%val2)) THEN
3856 WHERE (c_e(field_in(:,:,:)) .AND. field_in(:,:,:) > this%val1 &
3857 .AND. field_in(:,:,:) <= this%val2)
3858 field_out(:,:,:) = rmiss
3859 ELSE WHERE
3860 field_out(:,:,:) = field_in(:,:,:)
3861 END WHERE
3862 ELSE IF (c_e(this%val1)) THEN
3863 WHERE (c_e(field_in(:,:,:)) .AND. field_in(:,:,:) > this%val1)
3864 field_out(:,:,:) = rmiss
3865 ELSE WHERE
3866 field_out(:,:,:) = field_in(:,:,:)
3867 END WHERE
3868 ELSE IF (c_e(this%val2)) THEN
3869 WHERE (c_e(field_in(:,:,:)) .AND. field_in(:,:,:) <= this%val2)
3870 field_out(:,:,:) = rmiss
3871 ELSE WHERE
3872 field_out(:,:,:) = field_in(:,:,:)
3873 END WHERE
3874 ENDIF
3875 ENDIF
3876
3877ELSE IF (this%trans%trans_type == 'vertint') THEN
3878
3879 IF (this%trans%sub_type == 'linear') THEN
3880
3881 alloc_coord_3d_in_act = .false.
3882 IF (ASSOCIATED(this%inter_index_z)) THEN
3883
3884 DO k = 1, outnz
3885 IF (c_e(this%inter_index_z(k))) THEN
3886 z1 = real(this%inter_zp(k)) ! weight for k+1
3887 z2 = real(1.0d0 - this%inter_zp(k)) ! weight for k
3888 SELECT CASE(vartype)
3889
3890 CASE(var_dir360)
3891 DO j = 1, inny
3892 DO i = 1, innx
3893 IF (c_e(field_in(i,j,this%inter_index_z(k))) .AND. &
3894 c_e(field_in(i,j,this%inter_index_z(k)+1))) THEN
3895 field_out(i,j,k) = &
3896 interp_var_360(field_in(i,j,this%inter_index_z(k)), &
3897 field_in(i,j,this%inter_index_z(k)+1), z1, z2)
3898 ENDIF
3899 ENDDO
3900 ENDDO
3901
3902 CASE(var_press)
3903 DO j = 1, inny
3904 DO i = 1, innx
3905 IF (c_e(field_in(i,j,this%inter_index_z(k))) .AND. &
3906 c_e(field_in(i,j,this%inter_index_z(k)+1)) .AND. &
3907 field_in(i,j,this%inter_index_z(k)) > 0. .AND. &
3908 field_in(i,j,this%inter_index_z(k)+1) > 0.) THEN
3909 field_out(i,j,k) = exp( &
3910 log(field_in(i,j,this%inter_index_z(k)))*z2 + &
3911 log(field_in(i,j,this%inter_index_z(k)+1))*z1)
3912 ENDIF
3913 ENDDO
3914 ENDDO
3915
3916 CASE default
3917 DO j = 1, inny
3918 DO i = 1, innx
3919 IF (c_e(field_in(i,j,this%inter_index_z(k))) .AND. &
3920 c_e(field_in(i,j,this%inter_index_z(k)+1))) THEN
3921 field_out(i,j,k) = &
3922 field_in(i,j,this%inter_index_z(k))*z2 + &
3923 field_in(i,j,this%inter_index_z(k)+1)*z1
3924 ENDIF
3925 ENDDO
3926 ENDDO
3927
3928 END SELECT
3929
3930 ENDIF
3931 ENDDO
3932
3933 ELSE ! use coord_3d_in
3934
3935 IF (ALLOCATED(this%coord_3d_in)) THEN
3936 coord_3d_in_act => this%coord_3d_in
3937#ifdef DEBUG
3938 CALL l4f_category_log(this%category,l4f_debug, &
3939 "Using external vertical coordinate for vertint")
3940#endif
3941 ELSE
3942 IF (PRESENT(coord_3d_in)) THEN
3943#ifdef DEBUG
3944 CALL l4f_category_log(this%category,l4f_debug, &
3945 "Using internal vertical coordinate for vertint")
3946#endif
3947 IF (this%dolog) THEN
3948 ALLOCATE(coord_3d_in_act(SIZE(coord_3d_in,1), &
3949 SIZE(coord_3d_in,2), SIZE(coord_3d_in,3)))
3950 alloc_coord_3d_in_act = .true.
3951 WHERE (c_e(coord_3d_in) .AND. coord_3d_in > 0.0)
3952 coord_3d_in_act = log(coord_3d_in)
3953 ELSEWHERE
3954 coord_3d_in_act = rmiss
3955 END WHERE
3956 ELSE
3957 coord_3d_in_act => coord_3d_in
3958 ENDIF
3959 ELSE
3960 CALL l4f_category_log(this%category,l4f_error, &
3961 "Missing internal and external vertical coordinate for vertint")
3962 CALL raise_error()
3963 RETURN
3964 ENDIF
3965 ENDIF
3966
3967 inused = SIZE(coord_3d_in_act,3)
3968 IF (inused < 2) RETURN ! to avoid algorithm failure
3969 kkcache = 1
3970
3971 DO k = 1, outnz
3972 IF (.NOT.c_e(this%vcoord_out(k))) cycle
3973 DO j = 1, inny
3974 DO i = 1, innx
3975 kfound = imiss
3976 DO kk = 1, max(inused-kkcache-1, kkcache) ! +-1
3977 kkup = kkcache + kk
3978 kkdown = kkcache - kk + 1
3979
3980 IF (kkdown >= 1) THEN ! search down
3981 IF (this%vcoord_out(k) >= &
3982 min(coord_3d_in_act(i,j,kkdown), coord_3d_in_act(i,j,kkdown+1)) .AND. &
3983 this%vcoord_out(k) < &
3984 max(coord_3d_in_act(i,j,kkdown), coord_3d_in_act(i,j,kkdown+1))) THEN
3985 kkcache = kkdown
3986 kfoundin = kkcache
3987 kfound = kkcache + this%levshift
3988 EXIT ! kk
3989 ENDIF
3990 ENDIF
3991 IF (kkup < inused) THEN ! search up
3992 IF (this%vcoord_out(k) >= &
3993 min(coord_3d_in_act(i,j,kkup), coord_3d_in_act(i,j,kkup+1)) .AND. &
3994 this%vcoord_out(k) < &
3995 max(coord_3d_in_act(i,j,kkup), coord_3d_in_act(i,j,kkup+1))) THEN
3996 kkcache = kkup
3997 kfoundin = kkcache
3998 kfound = kkcache + this%levshift
3999 EXIT ! kk
4000 ENDIF
4001 ENDIF
4002
4003 ENDDO
4004
4005 IF (c_e(kfound)) THEN
4006 IF (c_e(field_in(i,j,kfound)) .AND. c_e(field_in(i,j,kfound+1))) THEN
4007 z1 = real((this%vcoord_out(k) - coord_3d_in_act(i,j,kfoundin))/ &
4008 (coord_3d_in_act(i,j,kfoundin+1) - coord_3d_in_act(i,j,kfoundin)))
4009 z2 = 1.0 - z1
4010 SELECT CASE(vartype)
4011
4012 CASE(var_dir360)
4013 field_out(i,j,k) = &
4014 interp_var_360(field_in(i,j,kfound), field_in(i,j,kfound+1), z1, z2)
4015 CASE(var_press)
4016 field_out(i,j,k) = exp(log(field_in(i,j,kfound))*z2 + &
4017 log(field_in(i,j,kfound+1))*z1)
4018
4019 CASE default
4020 field_out(i,j,k) = field_in(i,j,kfound)*z2 + field_in(i,j,kfound+1)*z1
4021 END SELECT
4022
4023 ENDIF
4024 ELSE
4025 ENDIF
4026 ENDDO ! i
4027 ENDDO ! j
4028 ENDDO ! k
4029 IF (alloc_coord_3d_in_act) DEALLOCATE(coord_3d_in_act)
4030 ENDIF
4031
4032 ELSE IF (this%trans%sub_type == 'linearsparse') THEN
4033
4034
4035 IF (.NOT.ASSOCIATED(this%vcoord_in) .AND. .NOT.PRESENT(coord_3d_in)) THEN
4036 CALL l4f_category_log(this%category,l4f_error, &
4037 "linearsparse interpolation, no input vert coord available")
4038 RETURN
4039 ENDIF
4040
4041 ALLOCATE(coord_in(innz),val_in(innz),mask_in(innz))
4042 DO j = 1, inny
4043 DO i = 1, innx
4044
4045 IF (ASSOCIATED(this%vcoord_in)) THEN
4046 mask_in = c_e(field_in(i,j,this%levshift+1:this%levshift+this%levused)) &
4047 .AND. c_e(this%vcoord_in(:))
4048 ELSE
4049 mask_in = c_e(field_in(i,j,this%levshift+1:this%levshift+this%levused)) &
4050 .AND. c_e(coord_3d_in(i,j,:))
4051 ENDIF
4052
4053 IF (vartype == var_press) THEN
4054 mask_in(:) = mask_in(:) .AND. &
4055 (field_in(i,j,this%levshift+1:this%levshift+this%levused) > 0.0d0)
4056 ENDIF
4057 inused = count(mask_in)
4058 IF (inused > 1) THEN
4059 IF (ASSOCIATED(this%vcoord_in)) THEN
4060 coord_in(1:inused) = pack(this%vcoord_in(:), mask=mask_in)
4061 ELSE
4062 coord_in(1:inused) = pack(coord_3d_in(i,j,:), mask=mask_in)
4063 ENDIF
4064 IF (vartype == var_press) THEN
4065 val_in(1:inused) = log(pack( &
4066 field_in(i,j,this%levshift+1:this%levshift+this%levused), &
4067 mask=mask_in))
4068 ELSE
4069 val_in(1:inused) = pack( &
4070 field_in(i,j,this%levshift+1:this%levshift+this%levused), &
4071 mask=mask_in)
4072 ENDIF
4073 kkcache = 1
4074 DO k = 1, outnz
4075
4076 kfound = imiss
4077 DO kk = 1, max(inused-kkcache-1, kkcache) ! +-1
4078 kkup = kkcache + kk
4079 kkdown = kkcache - kk + 1
4080
4081 IF (kkdown >= 1) THEN ! search down
4082 IF (this%vcoord_out(k) >= &
4083 min(coord_in(kkdown), coord_in(kkdown+1)) .AND. &
4084 this%vcoord_out(k) < &
4085 max(coord_in(kkdown), coord_in(kkdown+1))) THEN
4086 kkcache = kkdown
4087 kfoundin = kkcache
4088 kfound = kkcache
4089 EXIT ! kk
4090 ENDIF
4091 ENDIF
4092 IF (kkup < inused) THEN ! search up
4093 IF (this%vcoord_out(k) >= &
4094 min(coord_in(kkup), coord_in(kkup+1)) .AND. &
4095 this%vcoord_out(k) < &
4096 max(coord_in(kkup), coord_in(kkup+1))) THEN
4097 kkcache = kkup
4098 kfoundin = kkcache
4099 kfound = kkcache
4100 EXIT ! kk
4101 ENDIF
4102 ENDIF
4103
4104 ENDDO
4105
4106 IF (c_e(kfound)) THEN
4107 z1 = real((this%vcoord_out(k) - coord_in(kfound-1))/ &
4108 (coord_in(kfound) - coord_in(kfound-1)))
4109 z2 = 1.0 - z1
4110 IF (vartype == var_dir360) THEN
4111 field_out(i,j,k) = &
4112 interp_var_360(val_in(kfound-1), val_in(kfound), z1, z2)
4113 ELSE IF (vartype == var_press) THEN
4114 field_out(i,j,k) = exp(val_in(kfound-1)*z2 + val_in(kfound)*z1)
4115 ELSE
4116 field_out(i,j,k) = val_in(kfound-1)*z2 + val_in(kfound)*z1
4117 ENDIF
4118 ENDIF
4119
4120 ENDDO
4121
4122 ENDIF
4123
4124 ENDDO
4125 ENDDO
4126 DEALLOCATE(coord_in,val_in)
4127
4128
4129 ENDIF
4130
4131ELSE IF (this%trans%trans_type == '' .OR. this%trans%trans_type == 'none') THEN
4132
4133 field_out(:,:,:) = field_in(:,:,:)
4134
4135ENDIF
4136
4137
4138CONTAINS
4139
4140
4141! internal function for interpolating directions from 0 to 360 degree
4142! hope it is inlined by the compiler
4143FUNCTION interp_var_360(v1, v2, w1, w2)
4144REAL,INTENT(in) :: v1, v2, w1, w2
4145REAL :: interp_var_360
4146
4147REAL :: lv1, lv2
4148
4149IF (abs(v1 - v2) > 180.) THEN
4150 IF (v1 > v2) THEN
4151 lv1 = v1 - 360.
4152 lv2 = v2
4153 ELSE
4154 lv1 = v1
4155 lv2 = v2 - 360.
4156 ENDIF
4157 interp_var_360 = modulo(lv1*w2 + lv2*w1, 360.)
4158ELSE
4159 interp_var_360 = v1*w2 + v2*w1
4160ENDIF
4161
4162END FUNCTION interp_var_360
4163
4164
4165RECURSIVE FUNCTION find_prevailing_direction(v1, l, h, res, mask) RESULT(prev)
4166REAL,INTENT(in) :: v1(:,:)
4167REAL,INTENT(in) :: l, h, res
4168LOGICAL,INTENT(in),OPTIONAL :: mask(:,:)
4169REAL :: prev
4170
4171REAL :: m
4172INTEGER :: nh, nl
4173!REAL,PARAMETER :: res = 1.0
4174
4175m = (l + h)/2.
4176IF ((h - l) <= res) THEN
4177 prev = m
4178 RETURN
4179ENDIF
4180
4181IF (PRESENT(mask)) THEN
4182 nl = count(v1 >= l .AND. v1 < m .AND. mask)
4183 nh = count(v1 >= m .AND. v1 < h .AND. mask)
4184ELSE
4185 nl = count(v1 >= l .AND. v1 < m)
4186 nh = count(v1 >= m .AND. v1 < h)
4187ENDIF
4188IF (nh > nl) THEN
4189 prev = find_prevailing_direction(v1, m, h, res)
4190ELSE IF (nl > nh) THEN
4191 prev = find_prevailing_direction(v1, l, m, res)
4192ELSE IF (nl == 0 .AND. nh == 0) THEN
4193 prev = rmiss
4194ELSE
4195 prev = m
4196ENDIF
4197
4198END FUNCTION find_prevailing_direction
4199
4200
4201END SUBROUTINE grid_transform_compute
4202
4203
4204!> Compute the output data array from input data array according to
4205!! the defined transformation. The \a grid_transform object \a this
4206!! must have been properly initialised, so that it contains all the
4207!! information needed for computing the transformation. This is the
4208!! sparse points-to-grid and sparse points-to-sparse points version.
4209SUBROUTINE grid_transform_v7d_grid_compute(this, field_in, field_out, var, &
4210 coord_3d_in)
4211TYPE(grid_transform),INTENT(in) :: this !< grid_tranform object
4212REAL, INTENT(in) :: field_in(:,:) !< input array
4213REAL, INTENT(out):: field_out(:,:,:) !< output array
4214TYPE(vol7d_var),INTENT(in),OPTIONAL :: var !< physical variable to be interpolated, if provided, some ad-hoc algorithms may be used where possible
4215REAL,INTENT(in),OPTIONAL,TARGET :: coord_3d_in(:,:,:) !< input vertical coordinate for vertical interpolation, if not provided by other means
4216
4217real,allocatable :: field_in_p(:),x_in_p(:),y_in_p(:)
4218INTEGER :: inn_p, ier, k
4219INTEGER :: innx, inny, innz, outnx, outny, outnz
4220
4221#ifdef DEBUG
4222call l4f_category_log(this%category,l4f_debug,"start v7d_grid_transform_compute")
4223#endif
4224
4225field_out(:,:,:) = rmiss
4226
4227IF (.NOT.this%valid) THEN
4228 call l4f_category_log(this%category,l4f_error, &
4229 "refusing to perform a non valid transformation")
4230 call raise_error()
4231 RETURN
4232ENDIF
4233
4234innx = SIZE(field_in,1); inny = 1; innz = SIZE(field_in,2)
4235outnx = SIZE(field_out,1); outny = SIZE(field_out,2); outnz = SIZE(field_out,3)
4236
4237! check size of field_in, field_out
4238IF (this%trans%trans_type == 'vertint') THEN ! vertical interpolation
4239
4240 IF (innz /= this%innz) THEN
4241 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
4242 CALL l4f_category_log(this%category,l4f_error,"inconsistent input shape: "//&
4243 t2c(this%innz)//" /= "//t2c(innz))
4244 CALL raise_error()
4245 RETURN
4246 ENDIF
4247
4248 IF (outnz /= this%outnz) THEN
4249 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
4250 CALL l4f_category_log(this%category,l4f_error,"inconsistent output shape: "//&
4251 t2c(this%outnz)//" /= "//t2c(outnz))
4252 CALL raise_error()
4253 RETURN
4254 ENDIF
4255
4256 IF (innx /= outnx .OR. inny /= outny) THEN
4257 CALL l4f_category_log(this%category,l4f_error,"vertical interpolation")
4258 CALL l4f_category_log(this%category,l4f_error,"inconsistent hor. sizes: "//&
4259 t2c(innx)//","//t2c(inny)//" /= "//&
4260 t2c(outnx)//","//t2c(outny))
4261 CALL raise_error()
4262 RETURN
4263 ENDIF
4264
4265ELSE ! horizontal interpolation
4266
4267 IF (innx /= this%innx .OR. inny /= this%inny) THEN
4268 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
4269 CALL l4f_category_log(this%category,l4f_error,"inconsistent input shape: "//&
4270 t2c(this%innx)//","//t2c(this%inny)//" /= "//&
4271 t2c(innx)//","//t2c(inny))
4272 CALL raise_error()
4273 RETURN
4274 ENDIF
4275
4276 IF (outnx /= this%outnx .OR. outny /= this%outny) THEN
4277 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
4278 CALL l4f_category_log(this%category,l4f_error,"inconsistent output shape: "//&
4279 t2c(this%outnx)//","//t2c(this%outny)//" /= "//&
4280 t2c(outnx)//","//t2c(outny))
4281 CALL raise_error()
4282 RETURN
4283 ENDIF
4284
4285 IF (innz /= outnz) THEN
4286 CALL l4f_category_log(this%category,l4f_error,"horizontal interpolation")
4287 CALL l4f_category_log(this%category,l4f_error,"inconsistent vert. sizes: "//&
4288 t2c(innz)//" /= "//t2c(outnz))
4289 CALL raise_error()
4290 RETURN
4291 ENDIF
4292
4293ENDIF
4294
4295#ifdef DEBUG
4296 call l4f_category_log(this%category,l4f_debug, &
4297 "start grid_transform_v7d_grid_compute "//trim(this%trans%trans_type)//':'// &
4298 trim(this%trans%sub_type))
4299#endif
4300
4301IF (this%trans%trans_type == 'inter') THEN
4302
4303 IF (this%trans%sub_type == 'linear') THEN
4304
4305#ifdef HAVE_LIBNGMATH
4306! optimization, allocate only once with a safe size
4307 ALLOCATE(field_in_p(innx*inny), x_in_p(innx*inny), y_in_p(innx*inny))
4308 DO k = 1, innz
4309 inn_p = count(c_e(field_in(:,k)))
4310
4311 CALL l4f_category_log(this%category,l4f_info, &
4312 "Number of sparse data points: "//t2c(inn_p)//','//t2c(SIZE(field_in(:,k))))
4313
4314 IF (inn_p > 2) THEN
4315
4316 field_in_p(1:inn_p) = pack(field_in(:,k), c_e(field_in(:,k)))
4317 x_in_p(1:inn_p) = pack(this%inter_xp(:,1), c_e(field_in(:,k)))
4318 y_in_p(1:inn_p) = pack(this%inter_yp(:,1), c_e(field_in(:,k)))
4319
4320 IF (.NOT.this%trans%extrap) THEN
4321 CALL nnseti('ext', 0) ! 0 = no extrapolation
4322 CALL nnsetr('nul', rmiss)
4323 ENDIF
4324
4325 CALL natgrids(inn_p, x_in_p, y_in_p, field_in_p, & ! (1:inn_p) omitted
4326 this%outnx, this%outny, real(this%inter_x(:,1)), & ! no f90 interface
4327 REAL(this%inter_y(1,:)), field_out(1,1,k), ier)
4328
4329 IF (ier /= 0) THEN
4330 CALL l4f_category_log(this%category,l4f_error, &
4331 "Error code from NCAR natgrids: "//t2c(ier))
4332 CALL raise_error()
4333 EXIT
4334 ENDIF ! exit loop to deallocate
4335 ELSE
4336
4337 CALL l4f_category_log(this%category,l4f_info, &
4338 "insufficient data in gridded region to triangulate")
4339
4340 ENDIF
4341 ENDDO
4342 DEALLOCATE(field_in_p, x_in_p, y_in_p)
4343
4344#else
4345 CALL l4f_category_log(this%category,l4f_error, &
4346 "libsim compiled without NATGRIDD (ngmath ncarg library)")
4347 CALL raise_error()
4348 RETURN
4349#endif
4350
4351 ENDIF
4352
4353ELSE IF (this%trans%trans_type == 'boxinter' .OR. &
4354 this%trans%trans_type == 'polyinter' .OR. &
4355 this%trans%trans_type == 'vertint' .OR. &
4356 this%trans%trans_type == 'metamorphosis') THEN ! use the grid-to-grid method
4357
4358 CALL compute(this, &
4359 reshape(field_in, (/SIZE(field_in,1), 1, SIZE(field_in,2)/)), field_out, var, &
4360 coord_3d_in)
4361
4362ENDIF
4363
4364END SUBROUTINE grid_transform_v7d_grid_compute
4365
4366
4367! Bilinear interpolation
4368! Effettua interpolazione bilineare dati i valori nei punti 1,2,3,4 e
4369! le coordinate dei punti 1 e 3 oltre a quelle del punto p dove viene
4370! valutato il campo.
4371!_____________________________________________________________
4372! disposizione punti
4373! 4 3
4374!
4375! p
4376!
4377! 1 2
4378! _____________________________________________________________
4379ELEMENTAL FUNCTION hbilin (z1,z2,z3,z4,x1,y1,x3,y3,xp,yp) RESULT(zp)
4380REAL,INTENT(in) :: z1,z2,z3,z4 ! Z values on the four points
4381DOUBLE PRECISION,INTENT(in):: x1,y1 ! coordinate of the lower left point
4382DOUBLE PRECISION,INTENT(in):: x3,y3 ! coordinate of the upper right point
4383DOUBLE PRECISION,INTENT(in):: xp,yp ! coordinate of point where interpolate
4384REAL :: zp
4385
4386REAL :: p1,p2
4387REAL :: z5,z6
4388
4389
4390p2 = real((yp-y1)/(y3-y1))
4391p1 = real((xp-x1)/(x3-x1))
4392
4393z5 = (z4-z1)*p2+z1
4394z6 = (z3-z2)*p2+z2
4395
4396zp = (z6-z5)*(p1)+z5
4397
4398END FUNCTION hbilin
4399
4400
4401! Shapiro filter of order 2
4402FUNCTION shapiro (z,zp) RESULT(zs)
4403!! z_smoothed(i,j) = z(i,j) * (1-S) + S * sum(z_vicini)/N
4404!! = z(i,j) - S/N (N*z(i,j) - sum(z_vicini))
4405REAL,INTENT(in) :: z(:) ! Z values on the four surrounding points
4406REAL,INTENT(in) :: zp ! Z values on the central point
4407REAL :: zs ! Z smoothed value on the central point
4408INTEGER:: N
4409
4410IF(c_e(zp))THEN
4411 n = count(c_e(z))
4412 zs = zp - 0.5* ( n*zp - sum(z, c_e(z)) )/n
4413ELSE
4414 zs = rmiss
4415END IF
4416
4417END FUNCTION shapiro
4418
4419
4420! Locate index of requested point
4421SUBROUTINE basic_find_index(this, near, nx, ny, xmin, xmax, ymin, ymax, &
4422 lon, lat, extrap, index_x, index_y)
4423TYPE(griddim_def),INTENT(in) :: this ! griddim object (from grid)
4424LOGICAL,INTENT(in) :: near ! near or bilin interpolation (determine wich point is requested)
4425INTEGER,INTENT(in) :: nx,ny ! dimension (to grid)
4426DOUBLE PRECISION,INTENT(in) :: xmin, xmax, ymin, ymax ! extreme coordinate (to grid)
4427DOUBLE PRECISION,INTENT(in) :: lon(:,:),lat(:,:) ! target coordinate
4428LOGICAL,INTENT(in) :: extrap ! extrapolate
4429INTEGER,INTENT(out) :: index_x(:,:),index_y(:,:) ! index of point requested
4430
4431INTEGER :: lnx, lny
4432DOUBLE PRECISION :: x(SIZE(lon,1),SIZE(lon,2)),y(SIZE(lon,1),SIZE(lon,2))
4433
4434IF (near) THEN
4435 CALL proj(this,lon,lat,x,y)
4436 index_x = nint((x-xmin)/((xmax-xmin)/dble(nx-1)))+1
4437 index_y = nint((y-ymin)/((ymax-ymin)/dble(ny-1)))+1
4438 lnx = nx
4439 lny = ny
4440ELSE
4441 CALL proj(this,lon,lat,x,y)
4442 index_x = floor((x-xmin)/((xmax-xmin)/dble(nx-1)))+1
4443 index_y = floor((y-ymin)/((ymax-ymin)/dble(ny-1)))+1
4444 lnx = nx-1
4445 lny = ny-1
4446ENDIF
4447
4448IF (extrap) THEN ! trim indices outside grid for extrapolation
4449 index_x = max(index_x, 1)
4450 index_y = max(index_y, 1)
4451 index_x = min(index_x, lnx)
4452 index_y = min(index_y, lny)
4453ELSE ! nullify indices outside grid
4454 WHERE(index_x < 1 .OR. index_x > lnx .OR. index_y < 1 .OR. index_y > lny)
4455 index_x = imiss
4456 index_y = imiss
4457 END WHERE
4458ENDIF
4459
4460END SUBROUTINE basic_find_index
4461
4462END MODULE grid_transform_class
Copy an object, creating a fully new instance.
Compute forward coordinate transformation from geographical system to projected system.
Method for setting the contents of the object.
Compute backward coordinate transformation from projected system to geographical system.
Compute the distance in m between two points.
Methods for returning the value of object members.
Determine whether a point lies inside a polygon or a rectangle.
Compute the output data array from input data array according to the defined transformation.
Destructors of the corresponding objects.
Method for returning the contents of the object.
Constructors of the corresponding objects.
Emit log message for a category with specific priority.
Generic subroutine for checking OPTIONAL parameters.
Compute the average of the random variable provided, taking into account missing data.
Compute a set of percentiles for a random variable.
Compute the standard deviation of the random variable provided, taking into account missing data.
Represent data in a pretty string.
This module defines usefull general purpose function and subroutine.
Gestione degli errori.
This module defines objects describing georeferenced sparse points possibly with topology and project...
Module for describing geographically referenced regular grids.
Module for defining the extension and coordinates of a rectangular georeferenced grid.
Module for defining transformations between rectangular georeferenced grids and between grids and spa...
classe per la gestione del logging
Module for quickly interpreting the OPTIONAL parameters passed to a subprogram.
Module for basic statistical computations taking into account missing data.
Classe per la gestione di un volume completo di dati osservati.
Derived type defining a dynamically extensible array of TYPE(georef_coord_array) elements.
Derive type defining a single georeferenced point, either in geodetic or in projected coordinates.
This object completely describes a grid on a geographic projection.
This object fully defines a transformation between a couple of particular griddim_def or vol7d object...
This object defines in an abstract way the type of transformation to be applied.
Definisce un oggetto contenente i volumi anagrafica e dati e tutti i descrittori delle loro dimension...

Generated with Doxygen.