!----------------------------------------------------------------------
! NEMO system team, System and Interface for oceanic RElocable Nesting
!----------------------------------------------------------------------
!
! MODULE: extrap
!
! DESCRIPTION:
!> @brief
!> This module manage extrapolation.
!>
!> @details
!> Extrapolation method to be used is specify inside variable
!> strcuture, as array of string character.
!> - td_var\%c_extrap(1) string character is the interpolation name choose between:
!> - 'dist_weight'
!> - 'min_error'
!>
!> @note Extrapolation method could be specify for each variable in namelist _namvar_,
!> defining string character _cn\_varinfo_. By default _dist_weight_.
!> Example:
!> - cn_varinfo='varname1:ext=dist_weight', 'varname2:ext=min_error'
!>
!> to detect point to be extrapolated:
!> @code
!> il_detect(:,:,:)=extrap_detect(td_var)
!> @endcode
!> - il_detect(:,:,:) is 3D array of point to be extrapolated
!> - td_var is coarse grid variable to be extrapolated
!>
!> to extrapolate variable:
!> @code
!> CALL extrap_fill_value( td_var, [id_radius])
!> @endcode
!> - td_var is coarse grid variable to be extrapolated
!> - id_radius is radius of the halo used to compute extrapolation [optional]
!>
!> to add extraband to the variable (to be extrapolated):
!> @code
!> CALL extrap_add_extrabands(td_var, [id_isize,] [id_jsize] )
!> @endcode
!> - td_var is variable structure
!> - id_isize : i-direction size of extra bands [optional]
!> - id_jsize : j-direction size of extra bands [optional]
!>
!> to delete extraband of a variable:
!> @code
!> CALL extrap_del_extrabands(td_var, [id_isize,] [id_jsize] )
!> @endcode
!> - td_var is variable structure
!> - id_isize : i-direction size of extra bands [optional]
!> - id_jsize : j-direction size of extra bands [optional]
!>
!> @warning _FillValue must not be zero (use var_chg_FillValue())
!>
!> @author
!> J.Paul
! REVISION HISTORY:
!> @date November, 2013 - Initial Version
!> @date September, 2014
!> - add header
!> @date June, 2015
!> - extrapolate all land points (_FillValue)
!> - move deriv function to math module
!> @date July, 2015
!> - compute extrapolation from north west to south east,
!> and from south east to north west
!>
!> @todo
!> - create module for each extrapolation method
!> - smooth extrapolated points
!>
!> @note Software governed by the CeCILL licence (NEMOGCM/NEMO_CeCILL.txt)
!----------------------------------------------------------------------
MODULE extrap
USE netcdf ! nf90 library
USE kind ! F90 kind parameter
USE phycst ! physical constant
USE global ! global variable
USE fct ! basic useful function
USE date ! date manager
USE logger ! log file manager
USE math ! mathematical function
USE att ! attribute manager
USE dim ! dimension manager
USE var ! variable manager
IMPLICIT NONE
! NOTE_avoid_public_variables_if_possible
! type and variable
PRIVATE :: im_minext !< default minumum number of point to extrapolate
PRIVATE :: im_mincubic !< default minumum number of point to extrapolate for cubic interpolation
! function and subroutine
PUBLIC :: extrap_detect !< detected point to be extrapolated
PUBLIC :: extrap_fill_value !< extrapolate value over detected point
PUBLIC :: extrap_add_extrabands !< add extraband to the variable (to be extrapolated)
PUBLIC :: extrap_del_extrabands !< delete extraband of the variable
PRIVATE :: extrap__detect_wrapper ! detected point to be extrapolated wrapper
PRIVATE :: extrap__detect ! detected point to be extrapolated
PRIVATE :: extrap__fill_value_wrapper ! extrapolate value over detected point wrapper
PRIVATE :: extrap__fill_value ! extrapolate value over detected point
PRIVATE :: extrap__3D !
PRIVATE :: extrap__3D_min_error_coef !
PRIVATE :: extrap__3D_min_error_fill !
PRIVATE :: extrap__3D_dist_weight_coef !
PRIVATE :: extrap__3D_dist_weight_fill !
INTEGER(i4), PARAMETER :: im_minext = 2 !< default minumum number of point to extrapolate
INTEGER(i4), PARAMETER :: im_mincubic= 4 !< default minumum number of point to extrapolate for cubic interpolation
INTERFACE extrap_detect
MODULE PROCEDURE extrap__detect_wrapper !< detected point to be extrapolated
END INTERFACE extrap_detect
INTERFACE extrap_fill_value
MODULE PROCEDURE extrap__fill_value_wrapper !< detected point to be interpolated
END INTERFACE extrap_fill_value
CONTAINS
!-------------------------------------------------------------------
!> @brief
!> This function detected point to be extrapolated, given variable structure.
!>
!> @details
!> optionaly, you could sepcify fine grid level, refinment factor (default 1),
!> offset between fine and coarse grid (default compute from refinment factor
!> as offset=(rho-1)/2), number of point to be extrapolated in each direction
!> (default im_minext).
!>
!> First coarsening fine grid level, if need be, then select point near
!> grid point already inform.
!>
!> @note point to be extrapolated are selected using FillValue,
!> so to avoid mistake FillValue should not be zero (use var_chg_FillValue)
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date June, 2015
!> - do not use level to select points to be extrapolated
!
!> @param[in] td_var0 coarse grid variable to extrapolate
!> @return array of point to be extrapolated
!-------------------------------------------------------------------
FUNCTION extrap__detect( td_var0 )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(IN ) :: td_var0
! function
INTEGER(i4), DIMENSION(td_var0%t_dim(1)%i_len,&
& td_var0%t_dim(2)%i_len,&
& td_var0%t_dim(3)%i_len ) :: extrap__detect
! local variable
! loop indices
INTEGER(i4) :: ji0
INTEGER(i4) :: jj0
INTEGER(i4) :: jk0
!----------------------------------------------------------------
! force to extrapolated all points
extrap__detect(:,:,:)=1
! do not compute grid point already inform
DO jk0=1,td_var0%t_dim(3)%i_len
DO jj0=1,td_var0%t_dim(2)%i_len
DO ji0=1,td_var0%t_dim(1)%i_len
IF( td_var0%d_value(ji0,jj0,jk0,1) /= td_var0%d_fill )THEN
extrap__detect(ji0,jj0,jk0)=0
ENDIF
ENDDO
ENDDO
ENDDO
END FUNCTION extrap__detect
!-------------------------------------------------------------------
!> @brief
!> This function sort variable to be extrapolated, depending on number of
!> dimentsion, then detected point to be extrapolated.
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date June, 2015
!> - select all land points for extrapolation
!>
!> @param[in] td_var coarse grid variable to extrapolate
!> @return 3D array of point to be extrapolated
!-------------------------------------------------------------------
FUNCTION extrap__detect_wrapper( td_var )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(IN ) :: td_var
! function
INTEGER(i4), DIMENSION(td_var%t_dim(1)%i_len,&
& td_var%t_dim(2)%i_len,&
& td_var%t_dim(3)%i_len ) :: extrap__detect_wrapper
! local variable
! loop indices
!----------------------------------------------------------------
! init
extrap__detect_wrapper(:,:,:)=0
IF( .NOT. ANY(td_var%t_dim(1:3)%l_use) )THEN
! no dimension I-J-K used
CALL logger_debug(" EXTRAP DETECT: nothing done for variable"//&
& TRIM(td_var%c_name) )
ELSE IF( ALL(td_var%t_dim(1:3)%l_use) )THEN
! detect point to be extrapolated on I-J-K
CALL logger_debug(" EXTRAP DETECT: detect point "//&
& " for variable "//TRIM(td_var%c_name) )
extrap__detect_wrapper(:,:,:)=extrap__detect( td_var )
ELSE IF( ALL(td_var%t_dim(1:2)%l_use) )THEN
! detect point to be extrapolated on I-J
CALL logger_debug(" EXTRAP DETECT: detect horizontal point "//&
& " for variable "//TRIM(td_var%c_name) )
extrap__detect_wrapper(:,:,1:1)=extrap__detect( td_var )
ELSE IF( td_var%t_dim(3)%l_use )THEN
! detect point to be extrapolated on K
CALL logger_debug(" EXTRAP DETECT: detect vertical point "//&
& " for variable "//TRIM(td_var%c_name) )
extrap__detect_wrapper(1:1,1:1,:)=extrap__detect( td_var )
ENDIF
CALL logger_debug(" EXTRAP DETECT: "//&
& TRIM(fct_str(SUM(extrap__detect_wrapper(:,:,:))))//&
& " points to be extrapolated" )
END FUNCTION extrap__detect_wrapper
!-------------------------------------------------------------------
!> @brief
!> This subroutine select method to be used for extrapolation.
!> If need be, increase number of points to be extrapolated.
!> Finally launch extrap__fill_value.
!>
!> @details
!> optionaly, you could specify :
!> - refinment factor (default 1)
!> - offset between fine and coarse grid (default compute from refinment factor
!> as offset=(rho-1)/2)
!> - number of point to be extrapolated in each direction (default im_minext)
!> - radius of the halo used to compute extrapolation
!> - maximum number of iteration
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date June, 2015
!> - select all land points for extrapolation
!
!> @param[inout] td_var variable structure
!> @param[in] id_radius radius of the halo used to compute extrapolation
!-------------------------------------------------------------------
SUBROUTINE extrap__fill_value_wrapper( td_var, &
& id_radius )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(INOUT) :: td_var
INTEGER(i4), INTENT(IN ), OPTIONAL :: id_radius
! local variable
INTEGER(i4) :: il_radius
CHARACTER(LEN=lc) :: cl_method
! loop indices
!----------------------------------------------------------------
IF( .NOT. ASSOCIATED(td_var%d_value) )THEN
CALL logger_error("EXTRAP FILL VALUE: no value "//&
& "associted to variable "//TRIM(td_var%c_name) )
ELSE
SELECT CASE(TRIM(td_var%c_extrap(1)))
CASE('min_error')
cl_method='min_error'
CASE DEFAULT
cl_method='dist_weight'
!update variable structure
td_var%c_extrap(1)='dist_weight'
END SELECT
! number of point use to compute box
il_radius=1
IF( PRESENT(id_radius) ) il_radius=id_radius
IF( il_radius < 0 )THEN
CALL logger_error("EXTRAP FILL VALUE: invalid "//&
& " radius of the box used to compute extrapolation "//&
& "("//TRIM(fct_str(il_radius))//")")
ENDIF
CALL logger_info("EXTRAP FILL: extrapolate "//TRIM(td_var%c_name)//&
& " using "//TRIM(cl_method)//" method." )
CALL extrap__fill_value( td_var, cl_method, &
& il_radius )
ENDIF
END SUBROUTINE extrap__fill_value_wrapper
!-------------------------------------------------------------------
!> @brief
!> This subroutine compute point to be extrapolated, then extrapolate point.
!>
!> @details
!> optionaly, you could specify :
!> - refinment factor (default 1)
!> - offset between fine and coarse grid (default compute from refinment factor
!> as offset=(rho-1)/2)
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date June, 2015
!> - select all land points for extrapolation
!
!> @param[inout] td_var variable structure
!> @param[in] cd_method extrapolation method
!> @param[in] id_radius radius of the halo used to compute extrapolation
!-------------------------------------------------------------------
SUBROUTINE extrap__fill_value( td_var, cd_method, &
& id_radius )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(INOUT) :: td_var
CHARACTER(LEN=*), INTENT(IN ) :: cd_method
INTEGER(i4) , INTENT(IN ) :: id_radius
! local variable
CHARACTER(LEN=lc) :: cl_extrap
INTEGER(i4), DIMENSION(:,:,:) , ALLOCATABLE :: il_detect
TYPE(TATT) :: tl_att
! loop indices
!----------------------------------------------------------------
!1- detect point to be extrapolated
ALLOCATE( il_detect( td_var%t_dim(1)%i_len, &
& td_var%t_dim(2)%i_len, &
& td_var%t_dim(3)%i_len) )
il_detect(:,:,:) = extrap_detect( td_var )
!2- add attribute to variable
cl_extrap=fct_concat(td_var%c_extrap(:))
tl_att=att_init('extrapolation',cl_extrap)
CALL var_move_att(td_var, tl_att)
CALL att_clean(tl_att)
IF( ALL(il_detect(:,:,:)==1) )THEN
CALL logger_warn(" EXTRAP FILL: "//&
& " can not extrapolate "//TRIM(td_var%c_name)//&
& ". no value inform." )
ELSE
CALL logger_info(" EXTRAP FILL: "//&
& TRIM(fct_str(SUM(il_detect(:,:,:))))//&
& " point(s) to extrapolate " )
CALL logger_info(" EXTRAP FILL: method "//&
& TRIM(cd_method) )
!3- extrapolate
CALL extrap__3D(td_var%d_value(:,:,:,:), td_var%d_fill, &
& il_detect(:,:,:), &
& cd_method, id_radius )
ENDIF
DEALLOCATE(il_detect)
END SUBROUTINE extrap__fill_value
!-------------------------------------------------------------------
!> @brief
!> This subroutine compute point to be extrapolated in 3D array.
!>
!> @details
!> in case of 'min_error' method:
!> - compute derivative in i-, j- and k- direction
!> - compute minimum error coefficient (distance to center of halo)
!> - compute extrapolatd values by calculated minimum error using taylor expansion
!> in case of 'dist_weight' method:
!> - compute distance weight coefficient (inverse of distance to center of halo)
!> - compute extrapolatd values using Inverse Distance Weighting
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date July, 2015
!> - compute coef indices to be used
!
!> @param[inout] dd_value 3D array of variable to be extrapolated
!> @param[in] dd_fill FillValue of variable
!> @param[inout] id_detect array of point to extrapolate
!> @param[in] cd_method extrapolation method
!> @param[in] id_radius radius of the halo used to compute extrapolation
!-------------------------------------------------------------------
SUBROUTINE extrap__3D( dd_value, dd_fill, id_detect,&
& cd_method, id_radius )
IMPLICIT NONE
! Argument
REAL(dp) , DIMENSION(:,:,:,:), INTENT(INOUT) :: dd_value
REAL(dp) , INTENT(IN ) :: dd_fill
INTEGER(i4), DIMENSION(:,:,:) , INTENT(INOUT) :: id_detect
CHARACTER(LEN=*), INTENT(IN ) :: cd_method
INTEGER(i4), INTENT(IN ) :: id_radius
! local variable
INTEGER(i4) :: il_imin
INTEGER(i4) :: il_imax
INTEGER(i4) :: il_jmin
INTEGER(i4) :: il_jmax
INTEGER(i4) :: il_kmin
INTEGER(i4) :: il_kmax
INTEGER(i4) :: il_iter
INTEGER(i4) :: il_radius
INTEGER(i4) :: il_i1
INTEGER(i4) :: il_i2
INTEGER(i4) :: il_j1
INTEGER(i4) :: il_j2
INTEGER(i4) :: il_k1
INTEGER(i4) :: il_k2
INTEGER(i4), DIMENSION(4) :: il_shape
INTEGER(i4), DIMENSION(3) :: il_dim
INTEGER(i4), DIMENSION(:,:,:), ALLOCATABLE :: il_detect
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_dfdx
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_dfdy
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_dfdz
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_coef
LOGICAL :: ll_iter
! loop indices
INTEGER(i4) :: ji
INTEGER(i4) :: jj
INTEGER(i4) :: jk
INTEGER(i4) :: jl
!----------------------------------------------------------------
il_shape(:)=SHAPE(dd_value)
ALLOCATE( il_detect( il_shape(1), il_shape(2), il_shape(3)) )
SELECT CASE(TRIM(cd_method))
CASE('min_error')
DO jl=1,il_shape(4)
! intitialise table of poitn to be extrapolated
il_detect(:,:,:)=id_detect(:,:,:)
il_iter=1
DO WHILE( ANY(il_detect(:,:,:)==1) )
! change extend value to minimize number of iteration
il_radius=id_radius+(il_iter-1)
ll_iter=.TRUE.
ALLOCATE( dl_dfdx(il_shape(1), il_shape(2), il_shape(3)) )
ALLOCATE( dl_dfdy(il_shape(1), il_shape(2), il_shape(3)) )
ALLOCATE( dl_dfdz(il_shape(1), il_shape(2), il_shape(3)) )
! compute derivative in i-direction
dl_dfdx(:,:,:)=dd_fill
IF( il_shape(1) > 1 )THEN
dl_dfdx(:,:,:)=math_deriv_3D( dd_value(:,:,:,jl), &
& dd_fill, 'I' )
ENDIF
! compute derivative in j-direction
dl_dfdy(:,:,:)=dd_fill
IF( il_shape(2) > 1 )THEN
dl_dfdy(:,:,:)=math_deriv_3D( dd_value(:,:,:,jl), &
& dd_fill, 'J' )
ENDIF
! compute derivative in k-direction
dl_dfdz(:,:,:)=dd_fill
IF( il_shape(3) > 1 )THEN
dl_dfdz(:,:,:)=math_deriv_3D( dd_value(:,:,:,jl), &
& dd_fill, 'K' )
ENDIF
il_dim(1)=2*il_radius+1
IF( il_shape(1) < 2*il_radius+1 ) il_dim(1)=1
il_dim(2)=2*il_radius+1
IF( il_shape(2) < 2*il_radius+1 ) il_dim(2)=1
il_dim(3)=2*il_radius+1
IF( il_shape(3) < 2*il_radius+1 ) il_dim(3)=1
ALLOCATE( dl_coef(il_dim(1), il_dim(2), il_dim(3)) )
dl_coef(:,:,:)=extrap__3D_min_error_coef(dd_value( 1:il_dim(1), &
& 1:il_dim(2), &
& 1:il_dim(3), &
& jl ))
DO jk=1,il_shape(3)
! from North West(1,1) to South East(il_shape(1),il_shape(2))
IF( ALL(il_detect(:,:,jk) == 0) ) CYCLE
DO jj=1,il_shape(2)
IF( ALL(il_detect(:,jj,jk) == 0) ) CYCLE
DO ji=1,il_shape(1)
IF( il_detect(ji,jj,jk) == 1 )THEN
il_imin=MAX(ji-il_radius,1)
il_imax=MIN(ji+il_radius,il_shape(1))
! coef indices to be used
il_i1 = il_radius-(ji-il_imin)+1
il_i2 = il_radius+(il_imax-ji)+1
IF( il_dim(1) == 1 )THEN
il_imin=ji
il_imax=ji
! coef indices to be used
il_i1 = 1
il_i2 = 2
ENDIF
il_jmin=MAX(jj-il_radius,1)
il_jmax=MIN(jj+il_radius,il_shape(2))
! coef indices to be used
il_j1 = il_radius-(jj-il_jmin)+1
il_j2 = il_radius+(il_jmax-jj)+1
IF( il_dim(2) == 1 )THEN
il_jmin=jj
il_jmax=jj
! coef indices to be used
il_j1 = 1
il_j2 = 2
ENDIF
il_kmin=MAX(jk-il_radius,1)
il_kmax=MIN(jk+il_radius,il_shape(3))
! coef indices to be used
il_k1 = il_radius-(jk-il_kmin)+1
il_k2 = il_radius+(il_kmax-jk)+1
IF( il_dim(3) == 1 )THEN
il_kmin=jk
il_kmax=jk
! coef indices to be used
il_k1 = 1
il_k2 = 2
ENDIF
dd_value(ji,jj,jk,jl)=extrap__3D_min_error_fill( &
& dd_value( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax,jl ), dd_fill, il_radius, &
& dl_dfdx( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_dfdy( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_dfdz( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_coef(il_i1:il_i2, &
& il_j1:il_j2, &
& il_k1:il_k2) )
IF( dd_value(ji,jj,jk,jl) /= dd_fill )THEN
il_detect(ji,jj,jk)= 0
ll_iter=.FALSE.
ENDIF
ENDIF
ENDDO
ENDDO
! from South East(il_shape(1),il_shape(2)) to North West(1,1)
IF( ALL(il_detect(:,:,jk) == 0) ) CYCLE
DO jj=il_shape(2),1,-1
IF( ALL(il_detect(:,jj,jk) == 0) ) CYCLE
DO ji=il_shape(1),1,-1
IF( il_detect(ji,jj,jk) == 1 )THEN
il_imin=MAX(ji-il_radius,1)
il_imax=MIN(ji+il_radius,il_shape(1))
! coef indices to be used
il_i1 = il_radius-(ji-il_imin)+1
il_i2 = il_radius+(il_imax-ji)+1
IF( il_dim(1) == 1 )THEN
il_imin=ji
il_imax=ji
! coef indices to be used
il_i1 = 1
il_i2 = 2
ENDIF
il_jmin=MAX(jj-il_radius,1)
il_jmax=MIN(jj+il_radius,il_shape(2))
! coef indices to be used
il_j1 = il_radius-(jj-il_jmin)+1
il_j2 = il_radius+(il_jmax-jj)+1
IF( il_dim(2) == 1 )THEN
il_jmin=jj
il_jmax=jj
! coef indices to be used
il_j1 = 1
il_j2 = 2
ENDIF
il_kmin=MAX(jk-il_radius,1)
il_kmax=MIN(jk+il_radius,il_shape(3))
! coef indices to be used
il_k1 = il_radius-(jk-il_kmin)+1
il_k2 = il_radius+(il_kmax-jk)+1
IF( il_dim(3) == 1 )THEN
il_kmin=jk
il_kmax=jk
! coef indices to be used
il_k1 = 1
il_k2 = 2
ENDIF
dd_value(ji,jj,jk,jl)=extrap__3D_min_error_fill( &
& dd_value( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax,jl ), dd_fill, il_radius, &
& dl_dfdx( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_dfdy( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_dfdz( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax ), &
& dl_coef(il_i1:il_i2, &
& il_j1:il_j2, &
& il_k1:il_k2) )
IF( dd_value(ji,jj,jk,jl) /= dd_fill )THEN
il_detect(ji,jj,jk)= 0
ll_iter=.FALSE.
ENDIF
ENDIF
ENDDO
ENDDO
ENDDO
DEALLOCATE( dl_dfdx )
DEALLOCATE( dl_dfdy )
DEALLOCATE( dl_dfdz )
DEALLOCATE( dl_coef )
IF( ll_iter ) il_iter=il_iter+1
ENDDO
ENDDO
CASE DEFAULT ! 'dist_weight'
DO jl=1,il_shape(4)
! intitialise table of poitn to be extrapolated
il_detect(:,:,:)=id_detect(:,:,:)
il_iter=1
DO WHILE( ANY(il_detect(:,:,:)==1) )
! change extend value to minimize number of iteration
il_radius=id_radius+(il_iter-1)
ll_iter=.TRUE.
il_dim(1)=2*il_radius+1
IF( il_shape(1) < 2*il_radius+1 ) il_dim(1)=1
il_dim(2)=2*il_radius+1
IF( il_shape(2) < 2*il_radius+1 ) il_dim(2)=1
il_dim(3)=2*il_radius+1
IF( il_shape(3) < 2*il_radius+1 ) il_dim(3)=1
ALLOCATE( dl_coef(il_dim(1), il_dim(2), il_dim(3)) )
dl_coef(:,:,:)=extrap__3D_dist_weight_coef(dd_value(1:il_dim(1),&
& 1:il_dim(2),&
& 1:il_dim(3),&
& jl ) )
DO jk=1,il_shape(3)
! from North West(1,1) to South East(il_shape(1),il_shape(2))
IF( ALL(il_detect(:,:,jk) == 0) ) CYCLE
DO jj=1,il_shape(2)
IF( ALL(il_detect(:,jj,jk) == 0) ) CYCLE
DO ji=1,il_shape(1)
IF( il_detect(ji,jj,jk) == 1 )THEN
il_imin=MAX(ji-il_radius,1)
il_imax=MIN(ji+il_radius,il_shape(1))
! coef indices to be used
il_i1 = il_radius-(ji-il_imin)+1
il_i2 = il_radius+(il_imax-ji)+1
IF( il_dim(1) == 1 )THEN
il_imin=ji
il_imax=ji
! coef indices to be used
il_i1 = 1
il_i2 = 2
ENDIF
il_jmin=MAX(jj-il_radius,1)
il_jmax=MIN(jj+il_radius,il_shape(2))
! coef indices to be used
il_j1 = il_radius-(jj-il_jmin)+1
il_j2 = il_radius+(il_jmax-jj)+1
IF( il_dim(2) == 1 )THEN
il_jmin=jj
il_jmax=jj
! coef indices to be used
il_j1 = 1
il_j2 = 2
ENDIF
il_kmin=MAX(jk-il_radius,1)
il_kmax=MIN(jk+il_radius,il_shape(3))
! coef indices to be used
il_k1 = il_radius-(jk-il_kmin)+1
il_k2 = il_radius+(il_kmax-jk)+1
IF( il_dim(3) == 1 )THEN
il_kmin=jk
il_kmax=jk
! coef indices to be used
il_k1 = 1
il_k2 = 2
ENDIF
dd_value(ji,jj,jk,jl)=extrap__3D_dist_weight_fill( &
& dd_value( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax, &
& jl), dd_fill, il_radius, &
& dl_coef(il_i1:il_i2, &
& il_j1:il_j2, &
& il_k1:il_k2) )
IF( dd_value(ji,jj,jk,jl) /= dd_fill )THEN
il_detect(ji,jj,jk)= 0
ll_iter=.FALSE.
ENDIF
ENDIF
ENDDO
ENDDO
! from South East(il_shape(1),il_shape(2)) to North West(1,1)
IF( ALL(il_detect(:,:,jk) == 0) ) CYCLE
DO jj=il_shape(2),1,-1
IF( ALL(il_detect(:,jj,jk) == 0) ) CYCLE
DO ji=il_shape(1),1,-1
IF( il_detect(ji,jj,jk) == 1 )THEN
il_imin=MAX(ji-il_radius,1)
il_imax=MIN(ji+il_radius,il_shape(1))
! coef indices to be used
il_i1 = il_radius-(ji-il_imin)+1
il_i2 = il_radius+(il_imax-ji)+1
IF( il_dim(1) == 1 )THEN
il_imin=ji
il_imax=ji
! coef indices to be used
il_i1 = 1
il_i2 = 2
ENDIF
il_jmin=MAX(jj-il_radius,1)
il_jmax=MIN(jj+il_radius,il_shape(2))
! coef indices to be used
il_j1 = il_radius-(jj-il_jmin)+1
il_j2 = il_radius+(il_jmax-jj)+1
IF( il_dim(2) == 1 )THEN
il_jmin=jj
il_jmax=jj
! coef indices to be used
il_j1 = 1
il_j2 = 2
ENDIF
il_kmin=MAX(jk-il_radius,1)
il_kmax=MIN(jk+il_radius,il_shape(3))
! coef indices to be used
il_k1 = il_radius-(jk-il_kmin)+1
il_k2 = il_radius+(il_kmax-jk)+1
IF( il_dim(3) == 1 )THEN
il_kmin=jk
il_kmax=jk
! coef indices to be used
il_k1 = 1
il_k2 = 2
ENDIF
dd_value(ji,jj,jk,jl)=extrap__3D_dist_weight_fill( &
& dd_value( il_imin:il_imax, &
& il_jmin:il_jmax, &
& il_kmin:il_kmax, &
& jl), dd_fill, il_radius, &
& dl_coef(il_i1:il_i2, &
& il_j1:il_j2, &
& il_k1:il_k2) )
IF( dd_value(ji,jj,jk,jl) /= dd_fill )THEN
il_detect(ji,jj,jk)= 0
ll_iter=.FALSE.
ENDIF
ENDIF
ENDDO
ENDDO
ENDDO
CALL logger_info(" EXTRAP 3D: "//&
& TRIM(fct_str(SUM(il_detect(:,:,:))))//&
& " point(s) to extrapolate " )
DEALLOCATE( dl_coef )
IF( ll_iter ) il_iter=il_iter+1
ENDDO
ENDDO
END SELECT
DEALLOCATE( il_detect )
END SUBROUTINE extrap__3D
!-------------------------------------------------------------------
!> @brief
!> This function compute coefficient for min_error extrapolation.
!>
!> @details
!> coefficients are "grid distance" to the center of the box
!> choosed to compute extrapolation.
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date July, 2015
!> - decrease weight of third dimension
!
!> @param[in] dd_value 3D array of variable to be extrapolated
!> @return 3D array of coefficient for minimum error extrapolation
!-------------------------------------------------------------------
PURE FUNCTION extrap__3D_min_error_coef( dd_value )
IMPLICIT NONE
! Argument
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_value
! function
REAL(dp), DIMENSION(SIZE(dd_value(:,:,:),DIM=1), &
& SIZE(dd_value(:,:,:),DIM=2), &
& SIZE(dd_value(:,:,:),DIM=3) ) :: extrap__3D_min_error_coef
! local variable
INTEGER(i4), DIMENSION(3) :: il_shape
INTEGER(i4) :: il_imid
INTEGER(i4) :: il_jmid
INTEGER(i4) :: il_kmid
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_dist
! loop indices
INTEGER(i4) :: ji
INTEGER(i4) :: jj
INTEGER(i4) :: jk
!----------------------------------------------------------------
! init
extrap__3D_min_error_coef(:,:,:)=0
il_shape(:)=SHAPE(dd_value(:,:,:))
il_imid=INT(REAL(il_shape(1),sp)*0.5+1)
il_jmid=INT(REAL(il_shape(2),sp)*0.5+1)
il_kmid=INT(REAL(il_shape(3),sp)*0.5+1)
ALLOCATE( dl_dist(il_shape(1),il_shape(2),il_shape(3)) )
DO jk=1,il_shape(3)
DO jj=1,il_shape(2)
DO ji=1,il_shape(1)
! compute distance
! "vertical weight" is lower than horizontal
dl_dist(ji,jj,jk) = (ji-il_imid)**2 + &
& (jj-il_jmid)**2 + &
& 3*(jk-il_kmid)**2
IF( dl_dist(ji,jj,jk) /= 0 )THEN
dl_dist(ji,jj,jk)=SQRT( dl_dist(ji,jj,jk) )
ENDIF
ENDDO
ENDDO
ENDDO
WHERE( dl_dist(:,:,:) /= 0 )
extrap__3D_min_error_coef(:,:,:)=dl_dist(:,:,:)
END WHERE
DEALLOCATE( dl_dist )
END FUNCTION extrap__3D_min_error_coef
!-------------------------------------------------------------------
!> @brief
!> This function compute extrapolatd value by calculated minimum error using
!> taylor expansion
!>
!> @author J.Paul
!> - November, 2013- Initial Version
!>
!> @param[in] dd_value 3D array of variable to be extrapolated
!> @param[in] dd_fill FillValue of variable
!> @param[in] id_radius radius of the halo used to compute extrapolation
!> @param[in] dd_dfdx derivative of function in i-direction
!> @param[in] dd_dfdy derivative of function in j-direction
!> @param[in] dd_dfdz derivative of function in k-direction
!> @param[in] dd_coef array of coefficient for min_error extrapolation
!> @return extrapolatd value
!-------------------------------------------------------------------
PURE FUNCTION extrap__3D_min_error_fill( dd_value, dd_fill, id_radius, &
& dd_dfdx, dd_dfdy, dd_dfdz, &
& dd_coef )
IMPLICIT NONE
! Argument
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_value
REAL(dp) , INTENT(IN) :: dd_fill
INTEGER(i4), INTENT(IN) :: id_radius
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_dfdx
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_dfdy
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_dfdz
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_coef
! function
REAL(dp) :: extrap__3d_min_error_fill
! local variable
INTEGER(i4), DIMENSION(3) :: il_shape
INTEGER(i4), DIMENSION(3) :: il_ind
INTEGER(i4), DIMENSION(:,:,:), ALLOCATABLE :: il_mask
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_error
INTEGER(i4) :: il_min
! loop indices
!----------------------------------------------------------------
! init
extrap__3D_min_error_fill=dd_fill
il_min=MAX(1,(SIZE(dd_value(:,:,:)))/(1+id_radius*2))
IF( COUNT(dd_value(:,:,:) /= dd_fill) >= il_min )THEN
il_shape(:)=SHAPE(dd_value(:,:,:))
ALLOCATE( il_mask( il_shape(1),il_shape(2),il_shape(3)) )
ALLOCATE( dl_error(il_shape(1),il_shape(2),il_shape(3)) )
! compute error
dl_error(:,:,:)=0.
il_mask(:,:,:)=0
WHERE( dd_dfdx(:,:,:) /= dd_fill )
dl_error(:,:,:)=dd_coef(:,:,:)*dd_dfdx(:,:,:)
il_mask(:,:,:)=1
END WHERE
WHERE( dd_dfdy(:,:,:) /= dd_fill )
dl_error(:,:,:)=(dl_error(:,:,:)+dd_coef(:,:,:)*dd_dfdy(:,:,:))
il_mask(:,:,:)=1
END WHERE
WHERE( dd_dfdz(:,:,:) /= dd_fill )
dl_error(:,:,:)=(dl_error(:,:,:)+dd_coef(:,:,:)*dd_dfdz(:,:,:))
il_mask(:,:,:)=1
END WHERE
! get minimum error indices
il_ind(:)=MINLOC(dl_error(:,:,:),il_mask(:,:,:)==1)
! return value
IF( ALL(il_ind(:)/=0) )THEN
extrap__3D_min_error_fill=dd_value(il_ind(1),il_ind(2),il_ind(3))
ENDIF
DEALLOCATE( il_mask )
DEALLOCATE( dl_error )
ENDIF
END FUNCTION extrap__3D_min_error_fill
!-------------------------------------------------------------------
!> @brief
!> This function compute coefficient for inverse distance weighted method
!>
!> @details
!> coefficients are inverse "grid distance" to the center of the box choosed to compute
!> extrapolation.
!>
!> @author J.Paul
!> @date November, 2013 - Initial Version
!> @date July, 2015
!> - decrease weight of third dimension
!
!> @param[in] dd_value 3D array of variable to be extrapolated
!> @return 3D array of coefficient for inverse distance weighted extrapolation
!-------------------------------------------------------------------
PURE FUNCTION extrap__3D_dist_weight_coef( dd_value )
IMPLICIT NONE
! Argument
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_value
! function
REAL(dp), DIMENSION(SIZE(dd_value(:,:,:),DIM=1), &
& SIZE(dd_value(:,:,:),DIM=2), &
& SIZE(dd_value(:,:,:),DIM=3) ) :: extrap__3D_dist_weight_coef
! local variable
INTEGER(i4), DIMENSION(3) :: il_shape
INTEGER(i4) :: il_imid
INTEGER(i4) :: il_jmid
INTEGER(i4) :: il_kmid
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_dist
! loop indices
INTEGER(i4) :: ji
INTEGER(i4) :: jj
INTEGER(i4) :: jk
!----------------------------------------------------------------
! init
extrap__3D_dist_weight_coef(:,:,:)=0
il_shape(:)=SHAPE(dd_value(:,:,:))
il_imid=INT(REAL(il_shape(1),sp)*0.5+1,i4)
il_jmid=INT(REAL(il_shape(2),sp)*0.5+1,i4)
il_kmid=INT(REAL(il_shape(3),sp)*0.5+1,i4)
ALLOCATE( dl_dist(il_shape(1),il_shape(2),il_shape(3)) )
DO jk=1,il_shape(3)
DO jj=1,il_shape(2)
DO ji=1,il_shape(1)
! compute distance
! "vertical weight" is lower than horizontal
dl_dist(ji,jj,jk) = (ji-il_imid)**2 + &
& (jj-il_jmid)**2 + &
& 3*(jk-il_kmid)**2
IF( dl_dist(ji,jj,jk) /= 0 )THEN
dl_dist(ji,jj,jk)=SQRT( dl_dist(ji,jj,jk) )
ENDIF
ENDDO
ENDDO
ENDDO
WHERE( dl_dist(:,:,:) /= 0 )
extrap__3D_dist_weight_coef(:,:,:)=1./dl_dist(:,:,:)
END WHERE
DEALLOCATE( dl_dist )
END FUNCTION extrap__3D_dist_weight_coef
!-------------------------------------------------------------------
!> @brief
!> This function compute extrapolatd value using inverse distance weighted
!> method
!>
!> @details
!>
!> @author J.Paul
!> - November, 2013 - Initial Version
!
!> @param[in] dd_value 3D array of variable to be extrapolated
!> @param[in] dd_fill FillValue of variable
!> @param[in] id_radius radius of the halo used to compute extrapolation
!> @param[in] dd_coef 3D array of coefficient for inverse distance weighted extrapolation
!> @return extrapolatd value
!-------------------------------------------------------------------
FUNCTION extrap__3D_dist_weight_fill( dd_value, dd_fill, id_radius, &
& dd_coef )
IMPLICIT NONE
! Argument
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_value
REAL(dp) , INTENT(IN) :: dd_fill
INTEGER(i4), INTENT(IN) :: id_radius
REAL(dp) , DIMENSION(:,:,:), INTENT(IN) :: dd_coef
! function
REAL(dp) :: extrap__3D_dist_weight_fill
! local variable
INTEGER(i4), DIMENSION(3) :: il_shape
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_value
REAL(dp) , DIMENSION(:,:,:), ALLOCATABLE :: dl_coef
INTEGER(i4) :: il_min
! loop indices
INTEGER(i4) :: ji
INTEGER(i4) :: jj
INTEGER(i4) :: jk
!----------------------------------------------------------------
! init
extrap__3D_dist_weight_fill=dd_fill
il_min=MAX(1,(SIZE(dd_value(:,:,:)))/(1+id_radius*2))
IF( COUNT(dd_value(:,:,:)/= dd_fill) >= il_min )THEN
il_shape(:)=SHAPE(dd_value(:,:,:))
ALLOCATE( dl_value( il_shape(1),il_shape(2),il_shape(3)) )
ALLOCATE( dl_coef( il_shape(1),il_shape(2),il_shape(3)) )
dl_value(:,:,:)=0
dl_coef(:,:,:)=0
DO jk=1,il_shape(3)
DO jj=1,il_shape(2)
DO ji=1,il_shape(1)
IF( dd_value(ji,jj,jk) /= dd_fill )THEN
! compute factor
dl_value(ji,jj,jk)=dd_coef(ji,jj,jk)*dd_value(ji,jj,jk)
dl_coef(ji,jj,jk)=dd_coef(ji,jj,jk)
ENDIF
ENDDO
ENDDO
ENDDO
! return value
IF( SUM( dl_coef(:,:,:) ) /= 0 )THEN
extrap__3D_dist_weight_fill = &
& SUM( dl_value(:,:,:) )/SUM( dl_coef(:,:,:) )
ENDIF
DEALLOCATE( dl_value )
DEALLOCATE( dl_coef )
ENDIF
END FUNCTION extrap__3D_dist_weight_fill
!-------------------------------------------------------------------
!> @brief
!> This subroutine add to the variable (to be extrapolated) an
!> extraband of N points at north,south,east and west boundaries.
!>
!> @details
!> optionaly you could specify size of extra bands in i- and j-direction
!>
!> @author J.Paul
!> - November, 2013-Initial version
!
!> @param[inout] td_var variable
!> @param[in] id_isize i-direction size of extra bands (default=im_minext)
!> @param[in] id_jsize j-direction size of extra bands (default=im_minext)
!> @todo
!> - invalid special case for grid with north fold
!-------------------------------------------------------------------
SUBROUTINE extrap_add_extrabands(td_var, id_isize, id_jsize )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(INOUT) :: td_var
INTEGER(i4), INTENT(IN ), OPTIONAL :: id_isize
INTEGER(i4), INTENT(IN ), OPTIONAL :: id_jsize
! local variable
REAL(dp), DIMENSION(:,:,:,:) , ALLOCATABLE :: dl_value
INTEGER(i4) :: il_isize
INTEGER(i4) :: il_jsize
INTEGER(i4) :: il_tmp
! loop indices
INTEGER(i4) :: ji
INTEGER(i4) :: ij
!----------------------------------------------------------------
il_isize=im_minext
IF(PRESENT(id_isize)) il_isize=id_isize
IF( il_isize < im_minext .AND. &
& TRIM(td_var%c_interp(1)) == 'cubic' )THEN
CALL logger_warn("EXTRAP ADD EXTRABANDS: size of extrabands "//&
& "should be at least "//TRIM(fct_str(im_minext))//" for "//&
& " cubic interpolation ")
ENDIF
il_jsize=im_minext
IF(PRESENT(id_jsize)) il_jsize=id_jsize
IF( il_jsize < im_minext .AND. &
& TRIM(td_var%c_interp(1)) == 'cubic' )THEN
CALL logger_warn("EXTRAP ADD EXTRABANDS: size of extrabands "//&
& "should be at least "//TRIM(fct_str(im_minext))//" for "//&
& " cubic interpolation ")
ENDIF
IF( .NOT. td_var%t_dim(1)%l_use ) il_isize=0
IF( .NOT. td_var%t_dim(2)%l_use ) il_jsize=0
CALL logger_trace( "EXTRAP ADD EXTRABANDS: dimension change "//&
& "in variable "//TRIM(td_var%c_name) )
! add extrabands in variable
ALLOCATE(dl_value( td_var%t_dim(1)%i_len, &
& td_var%t_dim(2)%i_len, &
& td_var%t_dim(3)%i_len, &
& td_var%t_dim(4)%i_len ))
dl_value(:,:,:,:)=td_var%d_value(:,:,:,:)
td_var%t_dim(1)%i_len = td_var%t_dim(1)%i_len + 2*il_isize
td_var%t_dim(2)%i_len = td_var%t_dim(2)%i_len + 2*il_jsize
DEALLOCATE(td_var%d_value)
ALLOCATE( td_var%d_value(td_var%t_dim(1)%i_len, &
& td_var%t_dim(2)%i_len, &
& td_var%t_dim(3)%i_len, &
& td_var%t_dim(4)%i_len ) )
! intialise
td_var%d_value(:,:,:,:)=td_var%d_fill
! fill center
td_var%d_value( 1+il_isize:td_var%t_dim(1)%i_len-il_isize, &
& 1+il_jsize:td_var%t_dim(2)%i_len-il_jsize, &
& :,:) = dl_value(:,:,:,:)
! special case for overlap
IF( td_var%i_ew >= 0 .AND. il_isize /= 0 )THEN
DO ji=1,il_isize
! from east to west
il_tmp=td_var%t_dim(1)%i_len-td_var%i_ew+ji-2*il_isize
td_var%d_value(ji,:,:,:) = td_var%d_value(il_tmp,:,:,:)
! from west to east
ij=td_var%t_dim(1)%i_len-ji+1
il_tmp=td_var%i_ew-ji+2*il_isize+1
td_var%d_value(ij,:,:,:) = td_var%d_value(il_tmp,:,:,:)
ENDDO
ENDIF
DEALLOCATE( dl_value )
END SUBROUTINE extrap_add_extrabands
!-------------------------------------------------------------------
!> @brief
!> This subroutine remove of the variable an extraband
!> of N points at north,south,east and west boundaries.
!>
!> @details
!> optionaly you could specify size of extra bands in i- and j-direction
!>
!> @author J.Paul
!> - November, 2013 - Initial version
!>
!> @param[inout] td_var variable
!> @param[in] id_isize i-direction size of extra bands (default=im_minext)
!> @param[in] id_jsize j-direction size of extra bands (default=im_minext)
!-------------------------------------------------------------------
SUBROUTINE extrap_del_extrabands(td_var, id_isize, id_jsize )
IMPLICIT NONE
! Argument
TYPE(TVAR) , INTENT(INOUT) :: td_var
INTEGER(i4), INTENT(IN ), OPTIONAL :: id_isize
INTEGER(i4), INTENT(IN ), OPTIONAL :: id_jsize
! local variable
REAL(dp), DIMENSION(:,:,:,:) , ALLOCATABLE :: dl_value
INTEGER(i4) :: il_isize
INTEGER(i4) :: il_jsize
INTEGER(i4) :: il_imin
INTEGER(i4) :: il_imax
INTEGER(i4) :: il_jmin
INTEGER(i4) :: il_jmax
! loop indices
!----------------------------------------------------------------
il_isize=im_minext
IF(PRESENT(id_isize)) il_isize=id_isize
il_jsize=im_minext
IF(PRESENT(id_jsize)) il_jsize=id_jsize
IF( .NOT. td_var%t_dim(1)%l_use ) il_isize=0
IF( .NOT. td_var%t_dim(2)%l_use ) il_jsize=0
CALL logger_trace( "EXTRAP DEL EXTRABANDS: dimension change "//&
& "in variable "//TRIM(td_var%c_name) )
! add extrabands in variable
ALLOCATE(dl_value( td_var%t_dim(1)%i_len, &
& td_var%t_dim(2)%i_len, &
& td_var%t_dim(3)%i_len, &
& td_var%t_dim(4)%i_len ))
dl_value(:,:,:,:)=td_var%d_value(:,:,:,:)
! fill center
il_imin=1+il_isize
il_imax=td_var%t_dim(1)%i_len-il_isize
il_jmin=1+il_jsize
il_jmax=td_var%t_dim(2)%i_len-il_jsize
td_var%t_dim(1)%i_len = td_var%t_dim(1)%i_len - 2*il_isize
td_var%t_dim(2)%i_len = td_var%t_dim(2)%i_len - 2*il_jsize
DEALLOCATE(td_var%d_value)
ALLOCATE( td_var%d_value(td_var%t_dim(1)%i_len, &
& td_var%t_dim(2)%i_len, &
& td_var%t_dim(3)%i_len, &
& td_var%t_dim(4)%i_len ) )
! intialise
td_var%d_value(:,:,:,:)=td_var%d_fill
td_var%d_value(:,:,:,:)=dl_value(il_imin:il_imax,&
& il_jmin:il_jmax,&
& :,:)
DEALLOCATE( dl_value )
END SUBROUTINE extrap_del_extrabands
END MODULE extrap