MODULE regrid

  !-----------------------------------------------------------------------------
  !   CVS:$Id$
  !-----------------------------------------------------------------------------

  USE kinds_mod
  USE vars_mod
  USE msg_mod
  USE char_case
  USE const_mod
  USE nf_wrap
  USE nf_tools

  IMPLICIT NONE
  SAVE

  !*****************************************************************************

  INTEGER(KIND=int_kind) src_ncid, src_varid
  INTEGER(KIND=int_kind) src_imt, src_jmt, src_km, timelen

  CHARACTER(LEN=char_len) :: src_lon_name, src_lat_name, &
       src_depth_name, src_depth_edges_name, src_time_name

  REAL(KIND=real_kind), DIMENSION(:), ALLOCATABLE :: &
       src_lon, src_lat, src_depth, src_depth_edges, time
!################## debug ##################
  REAL(KIND=real_kind), DIMENSION(:), ALLOCATABLE :: &
       src_depth_neg
!################## debug ##################


  REAL(KIND=real_kind), DIMENSION(:), ALLOCATABLE :: &
       dst_depth, dst_depth_edges

  INTEGER(KIND=int_kind) dst_kmin, dst_kmax

  INTEGER(KIND=int_kind), DIMENSION(:,:), ALLOCATABLE :: &
       dst_kmt, REGION_MASK

  REAL(KIND=real_kind), DIMENSION(:,:), ALLOCATABLE :: &
       ULAT, ULONG, TLAT, TLONG, TAREA

  INTEGER(KIND=int_kind) dst_ncid, dst_varid

  LOGICAL(KIND=log_kind) :: dst_needs_time, &
       dst_needs_depth, dst_needs_depth_edges, &
       dst_needs_X, dst_needs_Y, &
       dst_needs_ULAT, dst_needs_ULONG, &
       dst_needs_TLAT, dst_needs_TLONG, &
       dst_needs_TAREA, dst_needs_REGION_MASK, &
       dst_needs_KMT

  INTEGER(KIND=int_kind) :: dst_time_dimid, &
       dst_depth_dimid, dst_depth_edges_varid, &
       dst_X_dimid, dst_Y_dimid, &
       dst_ULAT_varid, dst_ULONG_varid, &
       dst_TLAT_varid, dst_TLONG_varid, &
       dst_TAREA_varid, dst_REGION_MASK_varid, &
       dst_KMT_varid

  CHARACTER(LEN=char_len) :: dst_time_name, &
       dst_depth_name, dst_depth_edges_name, &
       dst_X_name, dst_Y_name, &
       dst_ULAT_name, dst_ULONG_name, &
       dst_TLAT_name, dst_TLONG_name, &
       dst_TAREA_name, dst_REGION_MASK_name, &
       dst_KMT_name

  REAL(KIND=real_kind), PARAMETER :: default_msv = -1.0E+34

  !*****************************************************************************

CONTAINS

  !*****************************************************************************

  SUBROUTINE regrid_driver

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'regrid_driver'
    LOGICAL(KIND=log_kind) :: lstat_ok
    INTEGER(KIND=int_kind) :: l

    CALL read_src_file_info
    CALL read_dst_grid_info
    CALL dst_depth_select(lstat_ok)
    IF (lstat_ok) THEN
       CALL create_dst_file
       CALL def_dst_file
       CALL nf_enddef_wrap(dst_ncid)
       CALL put_aux_vars
       DO l=1,MAX(1,timelen)
          CALL msg_write(sub_name, 'regridding l = ', l)
          CALL regrid_all_z_levs(l)
       END DO
       CALL nf_close_wrap(dst_ncid)
    END IF
    CALL nf_close_wrap(src_ncid)
    CALL free_vars

  END SUBROUTINE regrid_driver

  !*****************************************************************************

  SUBROUTINE read_src_file_info

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'read_src_file_info'

    INTEGER(KIND=int_kind) :: ndims
    INTEGER(KIND=int_kind), DIMENSION(4) :: dimids

    !---------------------------------------------------------------------------

    CALL nf_open_wrap(src_file, 0, src_ncid)

    CALL nf_inq_varid_wrap(src_ncid, src_var, src_varid)

    CALL nf_inq_varndims_wrap(src_ncid, src_varid, ndims)

    IF (ndims > 4 .OR. ndims < 2) THEN
       CALL msg_write(sub_name, 'illegal ndims ', ndims)
       STOP
    END IF

    CALL nf_inq_vardimid_wrap(src_ncid, src_varid, dimids)

    CALL nf_inq_dim_wrap(src_ncid, dimids(1), src_lon_name, src_imt)

    IF ((INDEX(upper(src_lon_name), 'LON') /= 1) .AND. &
         (INDEX(upper(src_lon_name), 'X') /= 1)) THEN
       CALL msg_write(sub_name, 'unexpected src_lon_name ', &
            TRIM(src_lon_name))
       STOP
    END IF

    CALL nf_inq_dim_wrap(src_ncid, dimids(2), src_lat_name, src_jmt)

    IF ((INDEX(upper(src_lat_name), 'LAT') /= 1) .AND. &
         (INDEX(upper(src_lat_name), 'Y') /= 1)) THEN
       CALL msg_write(sub_name, 'unexpected src_lat_name ', &
            TRIM(src_lat_name))
       STOP
    END IF

    src_km = 0
    timelen = 0

    IF (ndims > 2) THEN
       CALL nf_inq_dim_wrap(src_ncid, dimids(3), src_depth_name, src_km)

       IF ((INDEX(upper(src_depth_name), 'DEPTH') /= 1) .AND. &
            (INDEX(upper(src_depth_name), 'Z') /= 1)) THEN
          IF (((INDEX(upper(src_depth_name), 'TIME') == 1) .OR. &
               (INDEX(upper(src_depth_name), 'DATE') == 1) .OR. &
               (INDEX(upper(src_depth_name), 'T') == 1) .OR. &
               (INDEX(upper(src_depth_name), 'MONTH') == 1)) .AND. &
               (ndims == 3)) THEN
             src_time_name = src_depth_name
             src_depth_name = 'unknown'
             timelen = src_km
             src_km = 0
          ELSE
             CALL msg_write(sub_name, 'unexpected src_depth_name ', &
                  TRIM(src_depth_name))
             STOP
          END IF
       END IF

       IF (ndims > 3) THEN
          CALL nf_inq_dim_wrap(src_ncid, dimids(4), src_time_name, timelen)

          IF ((INDEX(upper(src_time_name), 'TIME') /= 1) .AND. &
               (INDEX(upper(src_time_name), 'DATE') /= 1) .AND. &
               (INDEX(upper(src_time_name), 'T') /= 1) .AND. &
               (INDEX(upper(src_time_name), 'MONTH') /= 1)) THEN
             CALL msg_write(sub_name, 'unexpected src_time_name ', &
                  TRIM(src_time_name))
             STOP
          END IF
       END IF

    END IF

    ALLOCATE(src_lon(src_imt), src_lat(src_jmt))

    CALL nf_read_dim(src_ncid, dimids(1), src_lon)
    CALL nf_read_dim(src_ncid, dimids(2), src_lat)

    IF (src_km > 0) THEN
       ALLOCATE(src_depth(src_km), src_depth_edges(src_km+1))
       CALL nf_read_dim(src_ncid, dimids(3), src_depth)
       CALL nf_read_dim_edges(src_ncid, dimids(3), src_depth_edges_name, &
            src_depth_edges)
!################## debug ##################
       write(6,*) ' src_depth_edges_name = ', src_depth_edges_name
       if (src_depth(1) < 0) then
          ALLOCATE(src_depth_neg(src_km))
          src_depth = src_depth_neg
          src_depth = -src_depth_neg
       endif
!################## debug ##################

    END IF

    IF (timelen > 0) THEN
       ALLOCATE(time(timelen))
       IF (src_km > 0) THEN
          CALL nf_read_dim(src_ncid, dimids(4), time)
       ELSE
          CALL nf_read_dim(src_ncid, dimids(3), time)
       END IF
    END IF

  END SUBROUTINE read_src_file_info

  !*****************************************************************************

  SUBROUTINE nf_read_dim(ncid, dimid, dim_vals)

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind), INTENT(IN) :: ncid, dimid
    REAL(KIND=real_kind), DIMENSION(:), INTENT(OUT) :: dim_vals

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'nf_read_dim'

    INTEGER(KIND=int_kind) :: varid, ndims
    INTEGER(KIND=int_kind), DIMENSION(1) :: vardims
    CHARACTER(LEN=char_len) :: var_name

    !---------------------------------------------------------------------------

    CALL nf_inq_dimname_wrap(ncid, dimid, var_name)
    CALL nf_inq_varid_wrap(ncid, var_name, varid)

    CALL nf_inq_varndims_wrap(ncid, varid, ndims)
    IF (ndims /= 1) THEN
       CALL msg_write(sub_name, 'illegal ndims for ', var_name, ndims)
       STOP
    END IF

    CALL nf_inq_vardimid_wrap(ncid, varid, vardims)
    IF (vardims(1) /= dimid) THEN
       CALL msg_write(sub_name, 'illegal dimid for ', var_name, vardims(1))
       STOP
    END IF

    CALL nf_get_var_wrap(ncid, varid, dim_vals)

  END SUBROUTINE nf_read_dim

  !*****************************************************************************

  SUBROUTINE nf_read_dim_edges(ncid, dimid, dim_edge_name, dim_edge_vals)

    include 'netcdf.inc'

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind), INTENT(IN) :: ncid, dimid
    REAL(KIND=real_kind), DIMENSION(:), INTENT(OUT) :: dim_edge_vals
    CHARACTER(len=*), INTENT(OUT) :: dim_edge_name

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'nf_read_dim_edges'

    INTEGER(KIND=int_kind) :: stat, varid
    INTEGER(KIND=int_kind) :: ndims
    INTEGER(KIND=int_kind), DIMENSION(1) :: vardims
    INTEGER(KIND=int_kind) :: dimlen, dimlenp1, i
    REAL(KIND=real_kind), DIMENSION(:), ALLOCATABLE :: dim_vals
    REAL(KIND=real_kind) :: delta

    CHARACTER(LEN=char_len) :: var_name

    !---------------------------------------------------------------------------

    CALL nf_inq_dim_wrap(ncid, dimid, var_name, dimlen)
    CALL nf_inq_varid_wrap(ncid, var_name, varid)

    CALL nf_inq_varndims_wrap(ncid, varid, ndims)
    IF (ndims /= 1) THEN
       CALL msg_write(sub_name, 'illegal ndims for ', var_name, ndims)
       STOP
    END IF

    CALL nf_inq_vardimid_wrap(ncid, varid, vardims)
    IF (vardims(1) /= dimid) THEN
       CALL msg_write(sub_name, 'illegal dimid for ', var_name, vardims(1))
       STOP
    END IF

    dim_edge_name = ''
    CALL nf_get_att_wrap(ncid, varid, 'edges', dim_edge_name, &
         ALLOW=NF_ENOTATT, stat_out=stat)

    !---------------------------------------------------------------------------
    !   If no edge attribute exists for the variable corresponding to
    !   the dimension, generate edges.
    !---------------------------------------------------------------------------

    IF (stat == NF_ENOTATT) THEN
       dim_edge_name = TRIM(var_name) // '_edges'

       ALLOCATE(dim_vals(1:dimlen))
       CALL nf_get_var_wrap(ncid, varid, dim_vals)

       IF (dimlen == 1) THEN
          dim_edge_vals(1) = dim_vals(1)
          dim_edge_vals(2) = dim_vals(1)
       ELSE
          delta = dim_vals(2) - dim_vals(1)
          dim_edge_vals(1) = dim_vals(1) - p5 * delta
          DO i = 1,dimlen
             delta = dim_vals(i) - dim_edge_vals(i)
             dim_edge_vals(i+1) = dim_edge_vals(i) + c2 * delta
          END DO
       END IF
       DEALLOCATE(dim_vals)
    ELSE
       CALL nf_inq_varid_wrap(ncid, dim_edge_name, varid)

       CALL nf_inq_varndims_wrap(ncid, varid, ndims)
       IF (ndims /= 1) THEN
          CALL msg_write(sub_name, 'illegal ndims for ', dim_edge_name, ndims)
          STOP
       END IF

       CALL nf_inq_vardimid_wrap(ncid, varid, vardims)
       CALL nf_inq_dimlen_wrap(ncid, vardims(1), dimlenp1)
       IF (dimlenp1 /= dimlen + 1) THEN
          CALL msg_write(sub_name, 'invalid size for dimension ', &
               dim_edge_name, dimlenp1)
          STOP
       END IF

       CALL nf_get_var_wrap(ncid, varid, dim_edge_vals)
    END IF

  END SUBROUTINE nf_read_dim_edges

  !*****************************************************************************

  SUBROUTINE read_dst_grid_info

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind), PARAMETER :: unit_in  = 21
    INTEGER(KIND=int_kind) :: recl_dbl, recl_int

    REAL(KIND=dbl_kind) :: WORK(dst_imt,dst_jmt)
    REAL(KIND=real_kind) :: dz, ztmp1, ztmp2, dlat, dlon, min_lat
    INTEGER(KIND=int_kind) :: k, i, j, im1, jm1, n

    REAL(KIND=dbl_kind) :: recl_dbl_tmp
    INTEGER(KIND=int_kind) :: recl_int_tmp

    REAL(KIND=real_kind), DIMENSION(4,dst_imt,dst_jmt) :: &
         TLAT_corner, TLONG_corner

    REAL(KIND=dbl_kind), DIMENSION(dst_imt,dst_jmt) :: &
         HTN, HTE, DXT, DYT

    !---------------------------------------------------------------------------
    !   generate recl_dbl, recl_int
    !---------------------------------------------------------------------------

    INQUIRE(IOLENGTH=recl_dbl) recl_dbl_tmp
    INQUIRE(IOLENGTH=recl_int) recl_int_tmp

    !---------------------------------------------------------------------------
    !   if necessary, read depth grid info
    !---------------------------------------------------------------------------

    IF (src_km > 0) THEN
       ALLOCATE(dst_depth(dst_km), dst_depth_edges(dst_km+1))

       dst_depth_edges(1) = c0
       OPEN(unit_in, file=dst_vert_grid_file, status='old', form='formatted')
       DO k = 1,dst_km
          READ(unit_in,*) dz, ztmp1, ztmp2
          dz = dz * p01
          dst_depth_edges(k+1) = dst_depth_edges(k) + dz
          dst_depth(k) = dst_depth_edges(k) + &
               p5*(dst_depth_edges(k+1)-dst_depth_edges(k))
       END DO
       CLOSE(unit_in)
    END IF

    ALLOCATE(dst_kmt(dst_imt,dst_jmt))

    OPEN(unit_in, file=dst_topography_file, status='old', &
         form='unformatted', access='direct', &
         recl=dst_imt*dst_jmt*recl_int)
    READ(unit_in,rec=1) dst_kmt
    CLOSE(unit_in)

    ALLOCATE(ULAT(dst_imt,dst_jmt), ULONG(dst_imt,dst_jmt), &
         TLAT(dst_imt,dst_jmt), TLONG(dst_imt,dst_jmt), &
         TAREA(dst_imt,dst_jmt))

    OPEN(unit_in, file=dst_horiz_grid_file, status='old', &
         form='unformatted', access='direct', &
         recl=dst_imt*dst_jmt*recl_dbl)
    READ(unit_in,rec=1) WORK
    ULAT = WORK
    READ(unit_in,rec=2) WORK
    ULONG = WORK
    READ(unit_in,rec=3) WORK
    HTN = WORK
    READ(unit_in,rec=4) WORK
    HTE = WORK
    CLOSE(unit_in)

    DO j = 1,dst_jmt
       DO i = 1,dst_imt
          IF (i /= 1) THEN
             im1 = i - 1
          ELSE
             im1 = dst_imt
          END IF

          TLAT_corner(3,i,j) = ULAT(i,j)
          TLONG_corner(3,i,j) = ULONG(i,j)

          TLAT_corner(4,i,j) = ULAT(im1,j)
          TLONG_corner(4,i,j) = ULONG(im1,j)
       END DO
    END DO

    DO j = 2,dst_jmt
       DO i = 1,dst_imt
          jm1 = j - 1

          TLAT_corner(2,i,j) = TLAT_corner(3,i,jm1)
          TLAT_corner(1,i,j) = TLAT_corner(4,i,jm1)

          TLONG_corner(2,i,j) = TLONG_corner(3,i,jm1)
          TLONG_corner(1,i,j) = TLONG_corner(4,i,jm1)
       END DO
    END DO

    min_lat = -pih + tiny

    DO i = 1,dst_imt
       dlat = TLAT_corner(1,i,3) - TLAT_corner(1,i,2)
       TLAT_corner(1,i,1) = MAX(TLAT_corner(1,i,2) - dlat, min_lat)

       dlat = TLAT_corner(2,i,3) - TLAT_corner(2,i,2)
       TLAT_corner(2,i,1) = MAX(TLAT_corner(2,i,2) - dlat, min_lat)

       TLONG_corner(1,i,1) = TLONG_corner(4,i,1)
       TLONG_corner(2,i,1) = TLONG_corner(3,i,1)
    END DO

    DO j = 1,dst_jmt
       DO i = 1,dst_imt
          IF (TLONG_corner(1,i,j) > pi2) &
               TLONG_corner(1,i,j) = TLONG_corner(1,i,j) - pi2
          IF (TLONG_corner(1,i,j) < c0) &
               TLONG_corner(1,i,j) = TLONG_corner(1,i,j) + pi2
          DO n = 2,4
             dlon = TLONG_corner(n,i,j) - TLONG_corner(n-1,i,j)
             IF (dlon < -c3*pih) &
                  TLONG_corner(n,i,j) = TLONG_corner(n,i,j) + pi2
             IF (dlon >  c3*pih) &
                  TLONG_corner(n,i,j) = TLONG_corner(n,i,j) - pi2
          END DO
       END DO
    END DO

    DO j = 1,dst_jmt
       DO i = 1,dst_imt
          TLAT(i,j) = (TLAT_corner(1,i,j) + TLAT_corner(2,i,j) + &
               TLAT_corner(3,i,j) + TLAT_corner(4,i,j)) / c4
          TLAT(i,j) = TLAT(i,j) * c90 / pih

          TLONG(i,j) = (TLONG_corner(1,i,j) + TLONG_corner(2,i,j) + &
               TLONG_corner(3,i,j) + TLONG_corner(4,i,j)) / c4
          IF (TLONG(i,j) > pi2)  TLONG(i,j) = TLONG(i,j) - pi2
          IF (TLONG(i,j) < c0) TLONG(i,j) = TLONG(i,j) + pi2
          TLONG(i,j) = TLONG(i,j) * c90 / pih
       END DO
    END DO

    ULAT = ULAT * c90 / pih
    ULONG = ULONG * c90 / pih

    !---------------------------------------------------------------------------
    !   compute TAREA the same way that POP does
    !---------------------------------------------------------------------------

    ! call s_shift(WORK, HTN)
    DO j = 1,dst_jmt
       IF (j /= 1) THEN
          jm1 = j - 1
       ELSE
          jm1 = dst_jmt
       END IF
       WORK(:,j) = HTN(:,jm1)
    END DO
    DXT = p5 * (HTN + WORK)

    ! call w_shift(WORK, HTE)
    DO i = 1,dst_imt
       IF (i /= 1) THEN
          im1 = i - 1
       ELSE
          im1 = dst_imt
       END IF
       WORK(i,:) = HTE(im1,:)
    END DO
    DYT = p5 * (HTE + WORK)

    TAREA = DXT * DYT

    !---------------------------------------------------------------------------
    !   read in REGION_MASK
    !---------------------------------------------------------------------------

    IF (dst_region_mask_filename /= 'unknown') THEN
       ALLOCATE(REGION_MASK(dst_imt,dst_jmt))

       OPEN(unit_in, file=dst_region_mask_filename, status='old', &
            form='unformatted', access='direct', &
            recl=dst_imt*dst_jmt*recl_int)
       READ(unit_in,rec=1) REGION_MASK
       CLOSE(unit_in)
    END IF

  END SUBROUTINE read_dst_grid_info

  !*****************************************************************************

  SUBROUTINE dst_depth_select(lstat_ok)

    !---------------------------------------------------------------------------
    !   argument
    !---------------------------------------------------------------------------

    LOGICAL(KIND=log_kind), INTENT(OUT) :: lstat_ok

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'dst_depth_select'
    REAL(KIND=real_kind) :: ztmp

    !---------------------------------------------------------------------------
    !   If there is no depth information in src, do not put any in dst.
    !---------------------------------------------------------------------------

    IF (src_km == 0) THEN
       dst_kmin = 0
       dst_kmax = 0
       lstat_ok = .TRUE.
       RETURN
    END IF

    !---------------------------------------------------------------------------
    !   Non-overlapping vertical grids is an error.
    !---------------------------------------------------------------------------

    IF (MAXVAL(dst_depth) < MINVAL(src_depth)) THEN
       CALL msg_write(sub_name, 'dst_depth < src_depth')
       lstat_ok = .FALSE.
       RETURN
    END IF

    IF (MINVAL(dst_depth) > MAXVAL(src_depth)) THEN
       CALL msg_write(sub_name, 'dst_depth > src_depth')
       lstat_ok = .FALSE.
       RETURN
    END IF

    !---------------------------------------------------------------------------
    !   If src_depth is a single z surface, use closest dst_depth, otherwise
    !   use dst_depth values that are bracketed by src_depth.
    !---------------------------------------------------------------------------

    IF (src_km == 1) THEN
       dst_kmin = MINLOC(ABS(dst_depth-src_depth(1)), DIM=1)
       dst_kmax = dst_kmin
    ELSE
!!!    ztmp = MINVAL(src_depth)
!!!    dst_kmin = MINLOC(dst_depth, DIM=1, MASK=(dst_depth >= ztmp))
       dst_kmin = 1

!!!    ztmp = MAXVAL(src_depth)
!!!    dst_kmax = MAXLOC(dst_depth, DIM=1, MASK=(dst_depth <= ztmp))
       dst_kmax = dst_km

       IF (dst_kmax < dst_kmin) THEN
          CALL msg_write(sub_name, 'internal error : dst_kmax < dst_kmin')
          lstat_ok = .FALSE.
          RETURN
       END IF
    END IF

    lstat_ok = .TRUE.

  END SUBROUTINE dst_depth_select

  !*****************************************************************************

  SUBROUTINE create_dst_file

    include 'netcdf.inc'

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    LOGICAL(KIND=log_kind) :: dst_exists, lcreate
    REAL(KIND=real_kind), DIMENSION(dst_imt) :: X
    REAL(KIND=real_kind), DIMENSION(dst_jmt) :: Y
    INTEGER(KIND=int_kind) :: i, j

    !---------------------------------------------------------------------------

    dst_needs_time        = timelen > 0
    dst_needs_depth       = dst_kmin > 0
    dst_needs_depth_edges = dst_kmin > 0
    dst_needs_X           = .true.
    dst_needs_Y           = .true.
    dst_needs_ULAT        = .true.
    dst_needs_ULONG       = .true.
    dst_needs_TLAT        = .true.
    dst_needs_TLONG       = .true.
    dst_needs_TAREA       = .true.
    dst_needs_REGION_MASK = dst_region_mask_filename /= 'unknown'
    dst_needs_KMT         = .true.

    inquire(file=dst_file, exist=dst_exists)
    lcreate = dst_lclobber .OR. .NOT. dst_exists

    IF (lcreate) THEN
       CALL nf_create_wrap(dst_file, 0, dst_ncid)
    ELSE
       CALL nf_open_wrap(dst_file, NF_WRITE, dst_ncid)

       !------------------------------------------------------------------------
       !   Determine which dimensions & variables are already in the file.
       !------------------------------------------------------------------------

       IF (dst_needs_time) THEN
          CALL nf_dimlookup(dst_ncid, time, dst_time_name, dst_time_dimid)
          IF (dst_time_dimid > 0) dst_needs_time = .FALSE.
       END IF

       IF (dst_needs_depth) THEN
          CALL nf_dimlookup(dst_ncid, dst_depth(dst_kmin:dst_kmax), &
               dst_depth_name, dst_depth_dimid)
          IF (dst_depth_dimid > 0) dst_needs_depth = .FALSE.
       END IF

       IF (dst_needs_depth_edges) THEN
          CALL nf_varlookup(dst_ncid, dst_depth_edges(dst_kmin:dst_kmax+1), &
               dst_depth_edges_name, dst_depth_edges_varid)
          IF (dst_depth_edges_varid > 0) dst_needs_depth_edges = .FALSE.
       END IF

       DO i = 1,dst_imt
          X(i) = REAL(i,real_kind)
       END DO
       CALL nf_dimlookup(dst_ncid, X, dst_X_name, dst_X_dimid)
       IF (dst_X_dimid > 0) dst_needs_X = .FALSE.

       DO j = 1,dst_jmt
          Y(j) = REAL(j,real_kind)
       END DO
       CALL nf_dimlookup(dst_ncid, Y, dst_Y_name, dst_Y_dimid)
       IF (dst_Y_dimid > 0) dst_needs_Y = .FALSE.

       CALL nf_varlookup(dst_ncid, ULAT, dst_ULAT_name, dst_ULAT_varid)
       IF (dst_ULAT_varid > 0) dst_needs_ULAT = .FALSE.

       CALL nf_varlookup(dst_ncid, ULONG, dst_ULONG_name, dst_ULONG_varid)
       IF (dst_ULONG_varid > 0) dst_needs_ULONG = .FALSE.

       CALL nf_varlookup(dst_ncid, TLAT, dst_TLAT_name, dst_TLAT_varid)
       IF (dst_TLAT_varid > 0) dst_needs_TLAT = .FALSE.

       CALL nf_varlookup(dst_ncid, TLONG, dst_TLONG_name, dst_TLONG_varid)
       IF (dst_TLONG_varid > 0) dst_needs_TLONG = .FALSE.

       CALL nf_varlookup(dst_ncid, TAREA, dst_TAREA_name, dst_TAREA_varid)
       IF (dst_TAREA_varid > 0) dst_needs_TAREA = .FALSE.

       IF (dst_needs_REGION_MASK) THEN
          CALL nf_varlookup(dst_ncid, REAL(REGION_MASK, real_kind), &
               dst_REGION_MASK_name, dst_REGION_MASK_varid)
          IF (dst_REGION_MASK_varid > 0) dst_needs_REGION_MASK = .FALSE.
       END IF

       CALL nf_varlookup(dst_ncid, REAL(dst_kmt, real_kind), &
            dst_KMT_name, dst_KMT_varid)
       IF (dst_KMT_varid > 0) dst_needs_KMT = .FALSE.

       CALL nf_redef_wrap(dst_ncid)
    END IF

  END SUBROUTINE create_dst_file

  !*****************************************************************************

  SUBROUTINE def_dst_file

    !---------------------------------------------------------------------------
    !   define dimensions, variables and attributes
    !---------------------------------------------------------------------------

    INCLUDE 'netcdf.inc'

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind) :: tmp_varid, src_timeid, natts, attnum, tmp_dimid
    CHARACTER(LEN=char_len) :: att_name, att_string
    INTEGER(KIND=int_kind) :: stat
    REAL(KIND=real_kind) :: msv

    !---------------------------------------------------------------------------

    IF (dst_needs_time) THEN
       dst_time_name = src_time_name
       CALL nf_def_dim_wrap(dst_ncid, dst_time_name, timelen, dst_time_dimid)

       CALL nf_def_var_wrap(dst_ncid, dst_time_name, NF_FLOAT, 1, &
            (/ dst_time_dimid /), tmp_varid)

       !------------------------------------------------------------------------
       !   copy attributes for src_time
       !------------------------------------------------------------------------

       CALL nf_inq_varid_wrap(src_ncid, src_time_name, src_timeid)
       CALL nf_inq_varnatts_wrap(src_ncid, src_timeid, natts)
       DO attnum = 1,natts
          CALL nf_inq_attname_wrap(src_ncid, src_timeid, attnum, att_name)
          CALL nf_copy_att_wrap(src_ncid, src_timeid, att_name, dst_ncid, &
               tmp_varid)
       END DO
    END IF

    IF (dst_needs_depth) THEN
       dst_depth_name = src_depth_name
       CALL nf_def_dim_wrap(dst_ncid, dst_depth_name, dst_kmax-dst_kmin+1, &
            dst_depth_dimid)

       CALL nf_def_var_wrap(dst_ncid, dst_depth_name, NF_FLOAT, 1, &
            (/ dst_depth_dimid /), tmp_varid)

       att_name = 'units'
       att_string = 'meters'
       CALL nf_put_att_wrap(dst_ncid, tmp_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'positive'
       att_string = 'down'
       CALL nf_put_att_wrap(dst_ncid, tmp_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'edges'
       att_string = src_depth_edges_name
       CALL nf_put_att_wrap(dst_ncid, tmp_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_depth_edges) THEN
       dst_depth_edges_name = src_depth_edges_name
       CALL nf_def_dim_wrap(dst_ncid, dst_depth_edges_name, &
            dst_kmax-dst_kmin+2, tmp_dimid)

       CALL nf_def_var_wrap(dst_ncid, dst_depth_edges_name, NF_FLOAT, 1, &
            (/ tmp_dimid /), tmp_varid)
    END IF

    IF (dst_needs_X) THEN
       dst_X_name = 'X'
       CALL nf_def_dim_wrap(dst_ncid, dst_X_name, dst_imt, dst_X_dimid)

       CALL nf_def_var_wrap(dst_ncid, dst_X_name, NF_FLOAT, 1, &
            (/ dst_X_dimid /), tmp_varid)
    END IF

    IF (dst_needs_Y) THEN
       dst_Y_name = 'Y'
       CALL nf_def_dim_wrap(dst_ncid, dst_Y_name, dst_jmt, dst_Y_dimid)

       CALL nf_def_var_wrap(dst_ncid, dst_Y_name, NF_FLOAT, 1, &
            (/ dst_Y_dimid /), tmp_varid)
    END IF

    IF (dst_needs_ULAT) THEN
       dst_ULAT_name = 'ULAT'
       CALL nf_def_var_wrap(dst_ncid, dst_ULAT_name, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_ULAT_varid)

       att_name = 'units'
       att_string = 'degrees_north'
       CALL nf_put_att_wrap(dst_ncid, dst_ULAT_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'Latitude (U grid)'
       CALL nf_put_att_wrap(dst_ncid, dst_ULAT_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_ULONG) THEN
       dst_ULONG_name = 'ULONG'
       CALL nf_def_var_wrap(dst_ncid, dst_ULONG_name, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_ULONG_varid)

       att_name = 'units'
       att_string = 'degrees_east'
       CALL nf_put_att_wrap(dst_ncid, dst_ULONG_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'Longitude (U grid)'
       CALL nf_put_att_wrap(dst_ncid, dst_ULONG_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_TLAT) THEN
       dst_TLAT_name = 'TLAT'
       CALL nf_def_var_wrap(dst_ncid, dst_TLAT_name, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_TLAT_varid)

       att_name = 'units'
       att_string = 'degrees_north'
       CALL nf_put_att_wrap(dst_ncid, dst_TLAT_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'Latitude (T grid)'
       CALL nf_put_att_wrap(dst_ncid, dst_TLAT_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_TLONG) THEN
       dst_TLONG_name = 'TLONG'
       CALL nf_def_var_wrap(dst_ncid, dst_TLONG_name, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_TLONG_varid)

       att_name = 'units'
       att_string = 'degrees_east'
       CALL nf_put_att_wrap(dst_ncid, dst_TLONG_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'Longitude (T grid)'
       CALL nf_put_att_wrap(dst_ncid, dst_TLONG_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_TAREA) THEN
       dst_TAREA_name = 'TAREA'
       CALL nf_def_var_wrap(dst_ncid, dst_TAREA_name, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_TAREA_varid)

       att_name = 'units'
       att_string = 'centimeter^2'
       CALL nf_put_att_wrap(dst_ncid, dst_TAREA_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'area of T cells'
       CALL nf_put_att_wrap(dst_ncid, dst_TAREA_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'coordinates'
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name)
       CALL nf_put_att_wrap(dst_ncid, dst_TAREA_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_REGION_MASK) THEN
       dst_REGION_MASK_name = 'REGION_MASK'
       CALL nf_def_var_wrap(dst_ncid, dst_REGION_MASK_name, NF_INT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_REGION_MASK_varid)

       att_name = 'units'
       att_string = 'Basin Index'
       CALL nf_put_att_wrap(dst_ncid, dst_REGION_MASK_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'basin index number (signed integers)'
       CALL nf_put_att_wrap(dst_ncid, dst_REGION_MASK_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'coordinates'
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name)
       CALL nf_put_att_wrap(dst_ncid, dst_REGION_MASK_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    IF (dst_needs_KMT) THEN
       dst_KMT_name = 'KMT'
       CALL nf_def_var_wrap(dst_ncid, dst_KMT_name, NF_INT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_KMT_varid)

       att_name = 'units'
       att_string = 'unitless'
       CALL nf_put_att_wrap(dst_ncid, dst_KMT_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'long_name'
       att_string = 'k Index of Deepest Grid Cell on T Grid'
       CALL nf_put_att_wrap(dst_ncid, dst_KMT_varid, att_name, &
            LEN_TRIM(att_string), att_string)

       att_name = 'coordinates'
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name)
       CALL nf_put_att_wrap(dst_ncid, dst_KMT_varid, att_name, &
            LEN_TRIM(att_string), att_string)
    END IF

    !------------------------------------------------------------------------
    !   define dst_var
    !------------------------------------------------------------------------

    IF (dst_kmin == 0 .AND. timelen == 0) THEN
       CALL nf_def_var_wrap(dst_ncid, dst_var, NF_FLOAT, 2, &
            (/ dst_X_dimid, dst_Y_dimid /), dst_varid)
    ELSE IF (dst_kmin > 0 .AND. timelen == 0) THEN
       CALL nf_def_var_wrap(dst_ncid, dst_var, NF_FLOAT, 3, &
            (/ dst_X_dimid, dst_Y_dimid, dst_depth_dimid /), dst_varid)
    ELSE IF (dst_kmin == 0 .AND. timelen > 0) THEN
       CALL nf_def_var_wrap(dst_ncid, dst_var, NF_FLOAT, 3, &
            (/ dst_X_dimid, dst_Y_dimid, dst_time_dimid /), dst_varid)
    ELSE
       CALL nf_def_var_wrap(dst_ncid, dst_var, NF_FLOAT, 4, &
            (/ dst_X_dimid, dst_Y_dimid, dst_depth_dimid, dst_time_dimid /),&
            dst_varid)
    END IF

    !------------------------------------------------------------------------
    !   copy attributes for src_varid
    !------------------------------------------------------------------------

    CALL nf_inq_varnatts_wrap(src_ncid, src_varid, natts)
    DO attnum = 1,natts
       CALL nf_inq_attname_wrap(src_ncid, src_varid, attnum, att_name)
       CALL nf_copy_att_wrap(src_ncid, src_varid, att_name, dst_ncid, dst_varid)
    END DO

    att_name = 'coordinates'
    IF (dst_kmin == 0 .AND. timelen == 0) THEN
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name)
    ELSE IF (dst_kmin > 0 .AND. timelen == 0) THEN
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name) &
            // ' ' // TRIM(dst_depth_name)
    ELSE IF (dst_kmin == 0 .AND. timelen > 0) THEN
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name) &
            // ' ' // TRIM(dst_time_name)
    ELSE
       att_string = TRIM(dst_TLONG_name) // ' ' // TRIM(dst_TLAT_name) &
            // ' ' // TRIM(dst_depth_name) // ' ' // TRIM(dst_time_name)
    END IF
    CALL nf_put_att_wrap(dst_ncid, dst_varid, att_name, &
         LEN_TRIM(att_string), att_string)

    CALL nf_get_att_wrap(dst_ncid, dst_varid, 'missing_value', msv, &
         ALLOW=NF_ENOTATT, stat_out=stat)
    IF (stat == NF_ENOTATT) THEN
       att_name = 'missing_value'
       CALL nf_put_att_wrap(dst_ncid, dst_varid, att_name, NF_FLOAT, 1, &
            default_msv)
    END IF

  END SUBROUTINE def_dst_file

  !*****************************************************************************

  SUBROUTINE put_aux_vars

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    REAL(KIND=real_kind), DIMENSION(dst_imt) :: X
    REAL(KIND=real_kind), DIMENSION(dst_jmt) ::Y
    INTEGER(KIND=int_kind) :: varid, i, j

    !---------------------------------------------------------------------------

    IF (timelen > 0) THEN
       CALL nf_inq_varid_wrap(dst_ncid, dst_time_name, varid)
       CALL nf_put_var_wrap(dst_ncid, varid, time)
    END IF

    IF (dst_kmin > 0 .AND. dst_needs_depth) THEN
       CALL nf_inq_varid_wrap(dst_ncid, dst_depth_name, varid)
       CALL nf_put_var_wrap(dst_ncid, varid, dst_depth(dst_kmin:dst_kmax))
    END IF

    IF (dst_kmin > 0 .AND. dst_needs_depth_edges) THEN
       CALL nf_inq_varid_wrap(dst_ncid, dst_depth_edges_name, varid)
       CALL nf_put_var_wrap(dst_ncid, varid, &
            dst_depth_edges(dst_kmin:dst_kmax+1))
    END IF

    IF (dst_needs_X) THEN
       DO i = 1,dst_imt
          X(i) = REAL(i,real_kind)
       END DO
       CALL nf_inq_varid_wrap(dst_ncid, dst_X_name, varid)
       CALL nf_put_var_wrap(dst_ncid, varid, X)
    END IF

    IF (dst_needs_Y) THEN
       DO j = 1,dst_jmt
          Y(j) = REAL(j,real_kind)
       END DO
       CALL nf_inq_varid_wrap(dst_ncid, dst_Y_name, varid)
       CALL nf_put_var_wrap(dst_ncid, varid, Y)
    END IF

    IF (dst_needs_ULAT) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_ULAT_varid, ULAT)
    END IF

    IF (dst_needs_ULONG) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_ULONG_varid, ULONG)
    END IF

    IF (dst_needs_TLAT) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_TLAT_varid, TLAT)
    END IF

    IF (dst_needs_TLONG) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_TLONG_varid, TLONG)
    END IF

    IF (dst_needs_TAREA) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_TAREA_varid, TAREA)
    END IF

    IF (dst_needs_REGION_MASK) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_REGION_MASK_varid, REGION_MASK)
    END IF

    IF (dst_needs_KMT) THEN
       CALL nf_put_var_wrap(dst_ncid, dst_KMT_varid, dst_kmt)
    END IF

  END SUBROUTINE put_aux_vars

  !*****************************************************************************

  SUBROUTINE regrid_all_z_levs(l)

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind), INTENT(IN) :: l ! time level

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    CHARACTER(LEN=*), PARAMETER :: sub_name = 'regrid_all_z_levs'
    INTEGER(KIND=int_kind) :: stat
    REAL(KIND=real_kind) :: msv
    REAL(KIND=real_kind), DIMENSION(src_imt, src_jmt) :: src_data
    REAL(KIND=real_kind), DIMENSION(dst_imt, dst_jmt) :: &
       src_data_above_on_dst, src_data_below_on_dst, dst_data, dst_data_prev
    INTEGER(KIND=int_kind) :: dst_lev, src_lev, i, j
    REAL(KIND=real_kind) :: dst_z, src_z, src_z_above, src_z_below, wz, &
       num, denom
    LOGICAL(KIND=log_kind) :: needs_fill

    !---------------------------------------------------------------------------
    !   use unlikely value if variable has no missing_value attribute
    !---------------------------------------------------------------------------

    CALL nf_get_att_wrap(src_ncid, src_varid, 'missing_value', msv, &
         ALLOW=NF_ENOTATT, stat_out=stat)
    IF (stat == NF_ENOTATT) msv = default_msv

    !---------------------------------------------------------------------------
    !   handle case where there is no vertical axis, use surface mask
    !---------------------------------------------------------------------------

    IF (dst_kmin == 0) THEN
       CALL nf_get_vara_wrap(src_ncid, src_varid, (/ 1, 1, l /), &
            (/ src_imt, src_jmt, 1 /), src_data)
       CALL regrid_level(msv, src_data, dst_data, dst_kmt > 0)
       CALL fill_level(msv, dst_data, dst_kmt > 0, needs_fill)
       IF (needs_fill) CALL msg_write(sub_name, 'some ocean points not filled')
       CALL nf_put_vara_wrap(dst_ncid, dst_varid, (/ 1, 1, l /), &
            (/ dst_imt, dst_jmt, 1 /), dst_data)
       RETURN
    END IF

    !---------------------------------------------------------------------------
    !   handle case where src vertical axis has only 1 level
    !---------------------------------------------------------------------------

    IF (src_km == 1) THEN
       CALL nf_get_vara_wrap(src_ncid, src_varid, (/ 1, 1, 1, l /), &
            (/ src_imt, src_jmt, 1, 1 /), src_data)
       CALL regrid_level(msv, src_data, dst_data, dst_kmt >= dst_kmin)
       CALL fill_level(msv, dst_data, dst_kmt >= dst_kmin, needs_fill)
       IF (needs_fill) CALL msg_write(sub_name, 'some ocean points not filled')
       CALL nf_put_vara_wrap(dst_ncid, dst_varid, (/ 1, 1, 1, l /), &
            (/ dst_imt, dst_jmt, 1, 1 /), dst_data)
       RETURN
    END IF

    !---------------------------------------------------------------------------
    !   handle the general case w/ vertical interpolation
    !---------------------------------------------------------------------------

    DO dst_lev = dst_kmin,dst_kmax
       CALL msg_write(sub_name, 'regridding k = ', dst_lev)
       dst_z = dst_depth(dst_lev)

       !------------------------------------------------------------------------
       !   if a level exists whose depth is nearly identical to dst_z,
       !   use that level with no vertical interpolation
       !   also do this if dst_z is less than least src_depth or
       !                            greater than largest src_depth
       !------------------------------------------------------------------------

       src_lev = MINLOC(ABS(src_depth-dst_z), DIM=1)
       src_z = src_depth(src_lev)

       IF (equal(src_z, dst_z, RELTOL=1.0e-4) &
               .or. (dst_z .le. MINVAL(src_depth)) &
               .or. (dst_z .ge. MAXVAL(src_depth))) THEN
          CALL nf_get_vara_wrap(src_ncid, src_varid, (/ 1, 1, src_lev, l /), &
               (/ src_imt, src_jmt, 1, 1 /), src_data)
          CALL regrid_level(msv, src_data, src_data_above_on_dst, &
               dst_kmt >= dst_lev)
          CALL fill_level(msv, src_data_above_on_dst, dst_kmt >= dst_lev, &
               needs_fill)
          src_data_below_on_dst = src_data_above_on_dst
          wz = c0
       ELSE
          src_lev = MAXLOC(src_depth, DIM=1, MASK=(src_depth<=dst_z))
          src_z_above = src_depth(src_lev)

          CALL nf_get_vara_wrap(src_ncid, src_varid, (/ 1, 1, src_lev, l /), &
               (/ src_imt, src_jmt, 1, 1 /), src_data)
          CALL regrid_level(msv, src_data, src_data_above_on_dst, &
               dst_kmt >= dst_lev)
          CALL fill_level(msv, src_data_above_on_dst, dst_kmt >= dst_lev, &
               needs_fill)

          src_lev = MINLOC(src_depth, DIM=1, MASK=(src_depth>dst_z))
          src_z_below = src_depth(src_lev)

          CALL nf_get_vara_wrap(src_ncid, src_varid, (/ 1, 1, src_lev, l /), &
               (/ src_imt, src_jmt, 1, 1 /), src_data)
          CALL regrid_level(msv, src_data, src_data_below_on_dst, &
               dst_kmt >= dst_lev)
          CALL fill_level(msv, src_data_below_on_dst, dst_kmt >= dst_lev, &
               needs_fill)

          wz = (dst_z - src_z_above) / (src_z_below - src_z_above)
       END IF

       DO j = 1,dst_jmt
          DO i = 1,dst_imt
             IF (dst_kmt(i,j) >= dst_lev) THEN
                num = 0.0
                denom = 0.0

                IF (src_data_above_on_dst(i,j) /= msv) THEN
                   num = num + (1.0 - wz) * src_data_above_on_dst(i,j)
                   denom = denom + (1.0 - wz)
                END IF

                IF (src_data_below_on_dst(i,j) /= msv) THEN
                   num = num + wz * src_data_below_on_dst(i,j)
                   denom = denom + wz
                END IF

                IF (denom > 0) THEN
                   dst_data(i,j) = num / denom
                ELSE
                   dst_data(i,j) = msv
                END IF
             ELSE
                dst_data(i,j) = msv
             END IF
          END DO
       END DO

       CALL fill_level(msv, dst_data, dst_kmt >= dst_lev, needs_fill)

       !------------------------------------------------------------------------
       !   if point are still unfilled, attempt to copy from previous level
       !------------------------------------------------------------------------

       IF (needs_fill) THEN
          If (dst_lev > dst_kmin) THEN
             CALL msg_write(sub_name, 'filling points from previous level ', &
                  TRIM(dst_var), ' dst_lev=', dst_lev)
             needs_fill = .FALSE.
             DO j = 1,dst_jmt
                DO i = 1,dst_imt
                   IF (dst_kmt(i,j) >= dst_lev .AND. dst_data(i,j) == msv) THEN
                      IF (dst_data_prev(i,j) == msv) THEN
                         needs_fill = .TRUE.
                      ELSE
                         dst_data(i,j) = dst_data_prev(i,j)
                      END IF
                   END IF
                END DO
             END DO
          END IF
          IF (needs_fill) CALL msg_write(sub_name, &
               'some ocean points not filled ', TRIM(dst_var), &
               ' dst_lev=', dst_lev)
       END IF

       CALL nf_put_vara_wrap(dst_ncid, dst_varid, &
            (/ 1, 1, dst_lev-dst_kmin+1, l /), (/ dst_imt, dst_jmt, 1, 1 /), &
            dst_data)

       dst_data_prev = dst_data
    END DO

  END SUBROUTINE regrid_all_z_levs

  !*****************************************************************************

  SUBROUTINE regrid_level(msv, src_data, dst_data, dst_mask)

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    REAL(KIND=real_kind), INTENT(IN) :: msv
    REAL(KIND=real_kind), DIMENSION(src_imt, src_jmt), INTENT(IN) :: src_data
    REAL(KIND=real_kind), DIMENSION(dst_imt, dst_jmt), INTENT(OUT) :: dst_data
    LOGICAL(KIND=log_kind), DIMENSION(dst_imt, dst_jmt), INTENT(IN) :: dst_mask

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind) :: src_i, src_ip1, src_j, src_jp1, dst_i, dst_j
    REAL(KIND=real_kind) :: dst_lat, dst_lon, src_dlon, src_dlat, wx, wy
    REAL(KIND=real_kind), DIMENSION(2,2) :: weight, vals
    LOGICAL(KIND=log_kind), DIMENSION(2,2) :: interp_mask

    DO dst_j = 1,dst_jmt
       DO dst_i = 1,dst_imt
          IF (.NOT. dst_mask(dst_i, dst_j)) THEN
             dst_data(dst_i, dst_j) = msv
             CYCLE
          END IF

          dst_lat = TLAT(dst_i, dst_j)
          dst_lon = TLONG(dst_i, dst_j)
          IF (dst_lon > src_lon(src_imt)) dst_lon = dst_lon - 360.0
          IF (dst_lon < src_lon(1)) dst_lon = dst_lon + 360.0

          src_i = find_index(dst_lon, src_lon)
          IF (src_i < src_imt) THEN
             src_ip1 = src_i + 1
             src_dlon = src_lon(src_ip1) - src_lon(src_i)
          ELSE
             src_ip1 = 1
             src_dlon = 360.0 + src_lon(src_ip1) - src_lon(src_i)
          END IF

          wx = (dst_lon - src_lon(src_i)) / src_dlon

          src_j = find_index(dst_lat, src_lat)
          IF (src_j < src_jmt) THEN
             src_jp1 = src_j + 1
             src_dlat = src_lat(src_jp1) - src_lat(src_j)
             wy = (dst_lat - src_lat(src_j)) / src_dlat
          ELSE
             src_jp1 = src_j
             wy = 0.0
          END IF

          weight = RESHAPE( (/ (1.0-wx)*(1.0-wy), wx*(1.0-wy), &
               (1.0-wx)*wy, wx*wy /), (/ 2, 2 /) )

          vals = src_data( (/ src_i, src_ip1 /), (/ src_j, src_jp1 /) )

          interp_mask = (vals /= msv) .AND. (weight > 0.0)

          !---------------------------------------------------------------------
          !   no valid data to interpolate from
          !---------------------------------------------------------------------

          IF (COUNT(interp_mask) == 0) THEN
             dst_data(dst_i, dst_j) = msv
             CYCLE
          END IF

          !---------------------------------------------------------------------
          !   do not interpolate if valid data is only at opposite corners
          !   copy value from appropriate quadrant
          !---------------------------------------------------------------------

          IF ((COUNT(interp_mask) == 2) .AND. &
               (interp_mask(1,2) .EQV. interp_mask(2,1))) THEN

             IF (wx <= 0.5) THEN
                IF (wy <= 0.5) THEN
                   dst_data(dst_i, dst_j) = src_data(src_i,src_j)
                ELSE
                   dst_data(dst_i, dst_j) = src_data(src_i,src_jp1)
                END IF
             ELSE
                IF (wy <= 0.5) THEN
                   dst_data(dst_i, dst_j) = src_data(src_ip1,src_j)
                ELSE
                   dst_data(dst_i, dst_j) = src_data(src_ip1,src_jp1)
                END IF
             END IF

             CYCLE
          END IF

          !---------------------------------------------------------------------
          !   compute masked weighted average
          !---------------------------------------------------------------------

          dst_data(dst_i, dst_j) = SUM(weight * vals, MASK=interp_mask) / &
               SUM(weight, MASK=interp_mask)

       END DO
    END DO

  END SUBROUTINE regrid_level

  !*****************************************************************************

  SUBROUTINE fill_level(msv, data, mask, needs_fill)

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    REAL(KIND=real_kind), INTENT(IN) :: msv
    REAL(KIND=real_kind), DIMENSION(:,:), INTENT(INOUT) :: data
    LOGICAL(KIND=log_kind), DIMENSION(:,:), INTENT(IN) :: mask
    LOGICAL(KIND=log_kind), INTENT(OUT) :: needs_fill

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind) :: imt, jmt, i, j, ip1, im1, jp1, jm1
    REAL(KIND=real_kind), DIMENSION(SIZE(data,1), SIZE(data,2)) :: work
    LOGICAL(KIND=log_kind) :: filled_a_cell

    REAL(KIND=real_kind), DIMENSION(4) :: vals
    LOGICAL(KIND=log_kind), DIMENSION(4) :: interp_mask

    imt = SIZE(data,1)
    jmt = SIZE(data,2)

    filled_a_cell = .TRUE.

    DO WHILE (filled_a_cell)
       filled_a_cell = .FALSE.
       needs_fill = .FALSE.
       work = data
       DO j = 1,jmt

          jm1 = MAX(j - 1, 1)
          jp1 = MIN(j + 1, jmt)

          DO i = 1,imt

             im1 = i - 1
             IF (i == 1) im1 = imt
             ip1 = i + 1
             IF (i == imt) ip1 = 1

             IF (mask(i,j) .AND. data(i,j) == msv) THEN

                vals = (/ work(ip1,j), work(i,jp1), work(im1,j), work(i,jm1) /)

                !---------------------------------------------------------------
                !   fill only from unmasked points w/ valid data
                !---------------------------------------------------------------

                interp_mask = &
                     (/ mask(ip1,j), mask(i,jp1), mask(im1,j), mask(i,jm1) /)
                interp_mask = interp_mask .AND. (vals /= msv)

                IF (ANY(interp_mask)) THEN
                   filled_a_cell = .TRUE.
                   data(i,j) = SUM(vals, MASK=interp_mask) / COUNT(interp_mask)
                ELSE
                   needs_fill = .TRUE.
                END IF

             END IF
          END DO
       END DO
    END DO

  END SUBROUTINE fill_level

  !*****************************************************************************

  INTEGER FUNCTION find_index(x, sorted_array)

    !---------------------------------------------------------------------------
    !   arguments
    !---------------------------------------------------------------------------

    REAL(KIND=real_kind) :: x
    REAL(KIND=real_kind), DIMENSION(:) :: sorted_array

    !---------------------------------------------------------------------------
    !   local variables
    !---------------------------------------------------------------------------

    INTEGER(KIND=int_kind) :: len, i

    len = SIZE(sorted_array)

    IF (x < sorted_array(1)) THEN
       find_index = 0
       RETURN
    END IF

    DO i = 1,len-1
       IF (x < sorted_array(i+1)) THEN
          find_index = i
          RETURN
       END IF
    END DO

    find_index = len

  END FUNCTION find_index

  !*****************************************************************************

  SUBROUTINE free_vars

    IF (ALLOCATED(src_lon))         DEALLOCATE(src_lon)
    IF (ALLOCATED(src_lat))         DEALLOCATE(src_lat)
    IF (ALLOCATED(src_depth))       DEALLOCATE(src_depth)
    IF (ALLOCATED(src_depth_edges)) DEALLOCATE(src_depth_edges)
    IF (ALLOCATED(dst_depth))       DEALLOCATE(dst_depth)
    IF (ALLOCATED(dst_depth_edges)) DEALLOCATE(dst_depth_edges)
    IF (ALLOCATED(dst_kmt))         DEALLOCATE(dst_kmt)
    IF (ALLOCATED(ULAT))            DEALLOCATE(ULAT)
    IF (ALLOCATED(ULONG))           DEALLOCATE(ULONG)
    IF (ALLOCATED(TLAT))            DEALLOCATE(TLAT)
    IF (ALLOCATED(TLONG))           DEALLOCATE(TLONG)
    IF (ALLOCATED(TAREA))           DEALLOCATE(TAREA)
    IF (ALLOCATED(REGION_MASK))     DEALLOCATE(REGION_MASK)
    IF (ALLOCATED(time))            DEALLOCATE(time)

  END SUBROUTINE free_vars

  !*****************************************************************************

END MODULE regrid
