text:
!> ----------------------------------------------------------------------------
!!  Model Initialization includes allocating memory for boundary and domain
!!      data structures.  It reads all of the options from the namelist
!!      file (or files).  It also reads in Lat/Lon and Terrain data.  This module
!!      also sets up geographic (and vertical) look uptables for the forcing data
!!      Finally, there is a driver routine to initialize all model physics packages
!!
!!   The module has been updated to allow arbitrary named variables
!!       this allows the use of e.g. ERAi, but still is not as flexible as it could be
!!
!!   The use of various python wrapper scripts in helpers/ makes it easy to add new
!!       datasets, and make them conform to the expectations of the current system.
!!      For now there are no plans to near term plans to substantially modify this.
!!
!!  @author
!!  Ethan Gutmann (gutmann@ucar.edu)
!!
!! ----------------------------------------------------------------------------
module initialization
    use data_structures
    use options_interface,  only : options_t
    use domain_interface,   only : domain_t
    use boundary_interface, only : boundary_t
    use microphysics,               only : mp_init
    use advection,                  only : adv_init
    use radiation,                  only : radiation_init
    use convection,                 only : init_convection
    use planetary_boundary_layer,   only : pbl_init
    use land_surface,               only : lsm_init
    use io_routines,          only : io_read, io_write
    use mod_atm_utilities,          only : init_atm_utilities
    use wind,                       only : update_winds

    ! use io_routines,                only : io_read, &
    !                                        io_write3d,io_write3di, io_write
    ! use geo,                        only : geo_LUT, geo_interp, geo_interp2d, standardize_coordinates
    ! use vertical_interpolation,     only : vLUT, vinterp
    ! use wind,                       only : init_winds
    ! use initialize_options,         only : init_options
    ! use string,                     only : str

    implicit none
    private
    public::init_model, init_physics

contains
    subroutine init_model(options,domain,boundary,add_cond)
        implicit none
        type(options_t), intent(inout) :: options
        type(domain_t),  intent(inout) :: domain
        type(boundary_t),intent(inout) :: boundary ,add_cond ! forcing and external file(s) for init conditions

        integer :: omp_get_max_threads, num_threads

#if defined(_OPENMP)
        num_threads = omp_get_max_threads()
#else
        num_threads = 1
#endif
        if (this_image()==1) call welcome_message()

        if (this_image()==1) then
            write(*,*) "  Number of coarray image:",num_images()
            write(*,*) "  Max number of OpenMP Threads:",num_threads
        endif

        ! read in options file
        if (this_image()==1) write(*,*) "Initializing Options"
        call options%init()

        if (this_image()==1) write(*,*) "Initializing Domain"
        call domain%init(options)

        if (this_image()==1) write(*,*) "Initializing boundary condition data structure"
        call boundary%init(options)

        ! if (this_image()==1) then
        !     write(*,*) "options%parameters%external_files: ", trim(options%parameters%external_files)
        !     write(*,*) "options%parameters%restart: ", options%parameters%restart
        ! endif

        if(options%parameters%external_files/="MISSING") then
            if (options%parameters%restart) then  ! Do not overwrite restart if this is specified!
                if (this_image()==1) write(*,*) "Restart requested, therefore ignoring external starting conditions! "
                if (this_image()==1) write(*,*) "Reading Initial conditions from boundary dataset"
                call domain%get_initial_conditions(boundary, options)  ! same call as above, but without the (optional) add_cond

            else ! If not restarting, and external conditions are specified, init & read in the latter.
                if (this_image()==1) write(*,*) "Initializing data structure for external starting conditions from file: ", trim(options%parameters%external_files)
                call add_cond%init_external(options)

                if (this_image()==1) write(*,*) "Reading Initial conditions from boundary dataset(s)"
                call domain%get_initial_conditions(boundary, options, add_cond)
            endif

        else ! In the (standard) case that there are no additional conditions files for the initialization
            if (this_image()==1) write(*,*) "Reading Initial conditions from boundary dataset"
            call domain%get_initial_conditions(boundary, options)  ! same call as above, but without the (optional) add_cond

        endif

        if(options%parameters%use_terrain_difference)  then

            if (this_image()==1 .AND. options%physics%windtype==kCONSERVE_MASS) then
                write(*,*) "Using the difference between hi- and lo-res terrain  for horizontal wind acceleration "
            ! elseif (this_image()==1) then
            !     write(*,*) "using the difference between hi- and lo-res terrain for u/v components of w_real "
            endif

            call domain%calculate_delta_terrain(boundary, options)

        endif

        if (this_image()==1) write(*,*) "Updating initial winds"
        call update_winds(domain, options)

        ! initialize the atmospheric helper utilities
        call init_atm_utilities(options)

        ! call init_physics(options, domain)

        ! call setup_bias_correction(options,domain)
        if (this_image()==1) write(*,'(/ A)') "Finished basic initialization"
        if (this_image()==1) write(*,'(A /)') "---------------------------------------"

    end subroutine init_model

    subroutine init_physics(options, domain)
        implicit none
        type(options_t), intent(inout) :: options
        type(domain_t),  intent(inout) :: domain

        ! initialize microphysics code (e.g. compute look up tables in Thompson et al)
        call mp_init(options) !this could easily be moved to init_model...

        call init_convection(domain,options)

        call pbl_init(domain,options)

        call radiation_init(domain,options)

        call lsm_init(domain,options)

        call adv_init(domain,options)

    end subroutine init_physics

    subroutine welcome_message()
        implicit none

        write(*,*) ""
        write(*,*) "============================================================"
        write(*,*) "|                                                          |"
        write(*,*) "|  The Intermediate Complexity Atmospheric Research Model  |"
        write(*,*) "|                          (ICAR)                          |"
        write(*,*) "|                                                          |"
        write(*,*) "|   Developed at NCAR:                                     |"
        write(*,*) "|     The National Center for Atmospheric Research         |"
        write(*,*) "|     NCAR is sponsored by the National Science Foundation |"
        write(*,*) "|                                                          |"
        write(*,*) "|   Version: ",kVERSION_STRING,"                                         |"
        write(*,*) "|                                                          |"
        write(*,*) "============================================================"
        write(*,*) ""

    end subroutine welcome_message

! ------------------------------------------------------------------------------------------
!-==== Model Domain Section ====
!
! Begining of section focused on allocating and initializing the model domain data structures
!
! ------------------------------------------------------------------------------------------

!   Allow running over a sub-domain, by removing the outer N grid cells from all sides of the domain (lat,lon,terrain)
    ! subroutine remove_edges(domain,edgesize)
    !     implicit none
    !     type(domain_t), intent(inout) :: domain
    !     integer, intent(in)::edgesize
    !     integer::nx1,ny1,nx2,ny2,nz
    !     real,allocatable,dimension(:,:)::temp_data
    !
    !     nx1 = size(domain%lat,1)
    !     ny1 = size(domain%lat,2)
    !     nx2 = nx1-(edgesize*2)
    !     ny2 = ny1-(edgesize*2)
    !
    !     allocate(temp_data(nx1,ny1))
    !
    !     temp_data = domain%lat
    !     deallocate(domain%lat)
    !     allocate(domain%lat(nx2,ny2))
    !     domain%lat = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     temp_data = domain%lon
    !     deallocate(domain%lon)
    !     allocate(domain%lon(nx2,ny2))
    !
    !     domain%lon = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     temp_data = domain%terrain
    !     deallocate(domain%terrain)
    !     allocate(domain%terrain(nx2,ny2))
    !     domain%terrain = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     temp_data = domain%landmask
    !     deallocate(domain%landmask)
    !     allocate(domain%landmask(nx2,ny2))
    !     domain%landmask = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !
    !     deallocate(temp_data)
    !
    !     nx1 = size(domain%u_geo%lat,1)
    !     ny1 = size(domain%u_geo%lat,2)
    !     nx2 = nx1-(edgesize*2)
    !     ny2 = ny1-(edgesize*2)
    !
    !     allocate(temp_data(nx1,ny1))
    !     temp_data = domain%u_geo%lat
    !     deallocate(domain%u_geo%lat)
    !     allocate(domain%u_geo%lat(nx2,ny2))
    !     domain%u_geo%lat = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     temp_data = domain%u_geo%lon
    !     deallocate(domain%u_geo%lon)
    !     allocate(domain%u_geo%lon(nx2,ny2))
    !     domain%u_geo%lon = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     deallocate(temp_data)
    !
    !     nx1 = size(domain%v_geo%lat,1)
    !     ny1 = size(domain%v_geo%lat,2)
    !     nx2 = nx1-(edgesize*2)
    !     ny2 = ny1-(edgesize*2)
    !
    !     allocate(temp_data(nx1,ny1))
    !     temp_data = domain%v_geo%lat
    !     deallocate(domain%v_geo%lat)
    !     allocate(domain%v_geo%lat(nx2,ny2))
    !     domain%v_geo%lat = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     temp_data = domain%v_geo%lon
    !     deallocate(domain%v_geo%lon)
    !     allocate(domain%v_geo%lon(nx2,ny2))
    !     domain%v_geo%lon = temp_data(1+edgesize:nx1-edgesize,1+edgesize:ny1-edgesize)
    !
    !     deallocate(temp_data)
    ! end subroutine remove_edges

!   allocate all arrays in domain

! interpolate intput%z to output%z assuming that input has one less grid cell
! in the interpolate_dim dimension
    ! subroutine copy_z(input,output,interpolate_dim)
    !     implicit none
    !     class(interpolable_type), intent(in) :: input
    !     class(interpolable_type), intent(inout) :: output
    !     integer,intent(in)::interpolate_dim
    !
    !     integer::nxi,nyi,nzi,nxo,nyo,nzo
    !
    !     ! dimensions of the input data
    !     nxi = size(input%z,1)
    !     nzi = size(input%z,2)
    !     nyi = size(input%z,3)
    !     ! dimensions of the output data
    !     nxo = size(output%lat,1)
    !     nzo = nzi
    !     nyo = size(output%lat,2)
    !
    !     if (allocated(output%z)) then
    !         deallocate(output%z)
    !     endif
    !     allocate(output%z(nxo,nzo,nyo))
    !     if (interpolate_dim==1) then
    !         if ((nxo/=(nxi+1)).or.(nyo/=nyi).or.(nzo/=nzi)) then
    !             stop "Error copying z levels from mass grid to U grid, dimensions don't match"
    !         endif
    !         output%z(2:nxo-1,:,:) = (input%z(1:nxi-1,:,:) + input%z(2:nxi,:,:))/2
    !         output%z(1,:,:) = input%z(1,:,:)
    !         output%z(nxo,:,:) = input%z(nxi,:,:)
    !     else if (interpolate_dim==3) then
    !         if ((nyo/=(nyi+1)).or.(nxo/=nxi).or.(nzo/=nzi)) then
    !             stop "Error copying z levels from mass grid to V grid, dimensions don't match"
    !         endif
    !         output%z(:,:,2:nyo-1) = (input%z(:,:,1:nyi-1) + input%z(:,:,2:nyi))/2
    !         output%z(:,:,1) = input%z(:,:,1)
    !         output%z(:,:,nyo) = input%z(:,:,nyi)
    !     else
    !         write(*,*) "Can not interpolate z data over z dimension"
    !     endif
    !
    ! end subroutine copy_z

    ! subroutine setup_bias_correction(options, domain)
    !     implicit none
    !     type(options_t), intent(in) :: options
    !     type(domain_t), intent(inout):: domain
    !
    !     if (options%use_bias_correction) then
    !         call io_read(options%bias_options%filename, options%bias_options%rain_fraction_var, domain%rain_fraction)
    !     endif
    ! end subroutine setup_bias_correction
    !
    ! subroutine init_domain_land(domain,options)
    !     implicit none
    !     type(options_t), intent(in) :: options
    !     type(domain_t), intent(inout):: domain
    !     ! these are temporary variables used for IO before storing data in the domain data structure
    !     integer,dimension(:,:),allocatable :: temp_idata
    !     real,dimension(:,:,:),allocatable :: temp_rdata
    !     real,dimension(:,:),allocatable :: temp_rdata_2d
    !
    !     integer:: buffer,nx,ny,nz, i
    !
    !     buffer = options%buffer
    !     nx = size(domain%veg_type,1)+buffer*2
    !     ny = size(domain%veg_type,2)+buffer*2
    !     nz = size(domain%soil_t,2)
    !
    !
    !     ! Veg cover fraction = 2D/3D real
    !     if (options%vegfrac_var.ne."") then
    !         ! if we are supposed to read a veg fraction for each month then it will be a 3D variable
    !         if (options%lsm_options%monthly_vegfrac) then
    !             call io_read(options%init_conditions_file,options%vegfrac_var,temp_rdata,1)
    !             domain%vegfrac = temp_rdata(1+buffer:nx-buffer,1+buffer:ny-buffer,:)  ! subset the data by buffer grid cells
    !             deallocate(temp_rdata)
    !         else
    !             ! otherwise we just read a 2D variable and store it in the first time entry in the 3D vegfrac field
    !             call io_read(options%init_conditions_file,options%vegfrac_var,temp_rdata_2d,1)
    !             domain%vegfrac(:,:,1) = temp_rdata_2d(1+buffer:nx-buffer,1+buffer:ny-buffer)  ! subset the data by buffer grid cells
    !             deallocate(temp_rdata_2d)
    !         endif
    !     endif
    !     ! if the input vegetation fraction is a fraction (0-1)
    !     ! then convert it to a percent
    !     if (maxval(domain%vegfrac)<=1) then
    !         domain%vegfrac = domain%vegfrac*100
    !     endif
    !
    !     ! Veg TYPE = 2D integer
    !     if (options%vegtype_var.ne."") then
    !         call io_read(options%init_conditions_file,options%vegtype_var,temp_idata,1)
    !         domain%veg_type = temp_idata(1+buffer:nx-buffer,1+buffer:ny-buffer)  ! subset the data by buffer grid cells
    !         deallocate(temp_idata)
    !     endif
    !
    !     ! Soil TYPE = 2D integer
    !     if (options%soiltype_var.ne."") then
    !         call io_read(options%init_conditions_file,options%soiltype_var,temp_idata,1)
    !         domain%soil_type = temp_idata(1+buffer:nx-buffer,1+buffer:ny-buffer)  ! subset the data by buffer grid cells
    !         deallocate(temp_idata)
    !     endif
    !
    !     ! Soil Volumetric Water Content = 3D real
    !     if (options%soil_vwc_var.ne."") then
    !         call io_read(options%init_conditions_file,options%soil_vwc_var,temp_rdata,1)
    !         domain%soil_vwc = reshape(temp_rdata(1+buffer:nx-buffer,1+buffer:ny-buffer,1:nz) &    ! subset the data by buffer grid cells
    !                                 ,[nx-buffer*2,nz,ny-buffer*2],order=[1,3,2])                ! and reshape to move the z axis to the middle
    !         deallocate(temp_rdata)
    !     endif
    !
    !     ! Soil Temperature = 3D real
    !     if (options%soil_t_var.ne."") then
    !         call io_read(options%init_conditions_file,options%soil_t_var,temp_rdata,1)
    !         domain%soil_t = reshape(temp_rdata(1+buffer:nx-buffer,1+buffer:ny-buffer,1:nz) &  ! subset the data by buffer grid cells
    !                                 ,[nx-buffer*2,nz,ny-buffer*2],order=[1,3,2])            ! and reshape to move the z axis to the middle
    !         deallocate(temp_rdata)
    !     endif
    !
    !     ! Deep Soil Temperature = 2D real
    !     if (options%soil_deept_var.ne."") then
    !         call io_read(options%init_conditions_file,options%soil_deept_var,temp_rdata_2d,1)
    !         domain%soil_tdeep = temp_rdata_2d(1+buffer:nx-buffer,1+buffer:ny-buffer)  ! subset the data by buffer grid cells
    !         where(domain%soil_tdeep<200) domain%soil_tdeep = 200 ! mitigates zeros that can cause problems
    !         if (options%soil_t_var=="") then
    !             write(*,*) "Missing explicit soil T, using deep soil T for all depths"
    !             do i = 1,nz
    !                 domain%soil_t(:,i,:) = domain%soil_tdeep
    !             end do
    !         endif
    !         deallocate(temp_rdata_2d)
    !     endif
    !
    ! end subroutine init_domain_land

    !> ----------------------------------------------------------------------------
    !!  Interpolate a 2D data array in the "x" (first) dimension, extrapolate out one on edges
    !!
    !!  If data_out is not allocated, it will be allocated as (nx+1,ny). Then a simple average between
    !!  grid cells computes a bilinear interpolation. Edge cells are extrapolated outwards using a
    !!  similar method.
    !!
    !!  @param[in]  data_in     real 2D array to be interpolated
    !!  @param[out] data_out    real 2D allocatable array to store the interpolated output
    !!
    !> ----------------------------------------------------------------------------
    ! subroutine interpolate_in_x(data_in, data_out)
    !     implicit none
    !     real, dimension(:,:), intent(in) :: data_in
    !     real, dimension(:,:), allocatable, intent(out) :: data_out
    !
    !     integer :: nx, ny
    !
    !     nx = size(data_in,1)
    !     ny = size(data_in,2)
    !     if (.not.allocated(data_out)) then
    !         allocate(data_out(nx+1,ny))
    !     else
    !         if ((size(data_out,1)/=nx+1).or.(size(data_out,2)/=ny)) then
    !             deallocate(data_out)
    !             allocate(data_out(nx+1,ny))
    !         endif
    !     endif
    !
    !     ! simple bilinear inteprolation (included extrapolation one grid cell out)
    !     data_out(2:nx,:) = (data_in(2:,:) + data_in(1:nx-1,:))/2
    !     data_out(1,:)    = data_in(1,:)*2 - data_in(2,:)
    !     data_out(nx+1,:) = data_in(nx,:)*2 - data_in(nx-1,:)
    !     ! result is data_out
    ! end subroutine interpolate_in_x

    !> ----------------------------------------------------------------------------
    !!  Interpolate a 2D data array in the "y" (second) dimension, extrapolate out one on edges
    !!
    !!  If data_out is not allocated, it will be allocated as (nx,ny+1). Then a simple average between
    !!  grid cells computes a bilinear interpolation. Edge cells are extrapolated outwards using a
    !!  similar method.
    !!
    !!  @param[in]  data_in     real 2D array to be interpolated
    !!  @param[out] data_out    real 2D allocatable array to store the interpolated output
    !!
    !> ----------------------------------------------------------------------------
    ! subroutine interpolate_in_y(data_in, data_out)
    !     implicit none
    !     real, dimension(:,:), intent(in) :: data_in
    !     real, dimension(:,:), allocatable, intent(out) :: data_out
    !
    !     integer :: nx, ny
    !
    !     nx = size(data_in,1)
    !     ny = size(data_in,2)
    !     if (.not.allocated(data_out)) then
    !         allocate(data_out(nx,ny+1))
    !     else
    !         if ((size(data_out,1)/=nx).or.(size(data_out,2)/=ny+1)) then
    !             deallocate(data_out)
    !             allocate(data_out(nx,ny+1))
    !         endif
    !     endif
    !
    !     ! simple bilinear inteprolation (included extrapolation one grid cell out)
    !     data_out(:,2:ny) = (data_in(:,2:) + data_in(:,1:ny-1))/2
    !     data_out(:,1)    = data_in(:,1)*2 - data_in(:,2)
    !     data_out(:,ny+1) = data_in(:,ny)*2 - data_in(:,ny-1)
    !     ! result is data_out
    ! end subroutine interpolate_in_y

!   initialize the domain e.g. lat,lon,terrain, 3D z coordinate
!     subroutine init_domain(options, domain)
!         implicit none
!         type(options_t), intent(in) :: options
!         type(domain_t), intent(inout):: domain
!         real,dimension(:,:,:),allocatable::temporary_z
!         integer:: ny,nz,nx,i,buf
!
! !       these are the only required variables on a high-res grid, lat, lon, and terrain elevation
!         call io_read(options%init_conditions_file,options%hgt_hi,domain%terrain,1)
!         call io_read(options%init_conditions_file,options%lat_hi,domain%lat,1)
!         call io_read(options%init_conditions_file,options%lon_hi,domain%lon,1)
!         call standardize_coordinates(domain)
!
!         !  because u/vlat/lon_hi are optional, we calculated them by interplating from the mass lat/lon if necessary
!         if (options%ulat_hi/="") then
!             call io_read(options%init_conditions_file,options%ulat_hi,domain%u_geo%lat,1)
!         else
!             call interpolate_in_x(domain%lat,domain%u_geo%lat)
!         endif
!         if (options%ulon_hi/="") then
!             call io_read(options%init_conditions_file,options%ulon_hi,domain%u_geo%lon,1)
!         else
!             call interpolate_in_x(domain%lon,domain%u_geo%lon)
!         endif
!
!         call standardize_coordinates(domain%u_geo)
!
!         if (options%vlat_hi/="") then
!             call io_read(options%init_conditions_file,options%vlat_hi,domain%v_geo%lat,1)
!         else
!             call interpolate_in_y(domain%lat,domain%v_geo%lat)
!         endif
!         if (options%vlon_hi/="") then
!             call io_read(options%init_conditions_file,options%vlon_hi,domain%v_geo%lon,1)
!         else
!             call interpolate_in_y(domain%lon,domain%v_geo%lon)
!         endif
!         call standardize_coordinates(domain%v_geo)
!
!         if (options%landvar/="") then
!             call io_read(options%init_conditions_file,options%landvar,domain%landmask,1)
!             where(domain%landmask==0) domain%landmask = kLC_WATER
!         else
!             nx = size(domain%lat,1)
!             ny = size(domain%lat,2)
!             allocate(domain%landmask(nx,ny))
!             domain%landmask = kLC_LAND !if we weren't supplied a landmask field, assume all is land (what we care about anyway)
!         endif
!
!         ! remove n grid cells from all sides of the domain if requested
!         if(options%buffer>0) then
!             write(*,*) "  Removing ",trim(str(options%buffer))," gridcells from edges of domain"
!             call remove_edges(domain,options%buffer)
!         endif
!
!         ! use the lat variable to define the x and y dimensions for all other variables
!         nx = size(domain%lat,1)
!         ny = size(domain%lat,2)
!         ! assumes nz is defined in the options
!         nz = options%nz
!
!         ! if a 3d grid was also specified, then read those data in
!         if ((options%readz).and.(options%ideal).and.(options%zvar.ne."")) then
!
!             stop "Reading Z from an external file is not currently supported, use a fixed dz"
!
!             call io_read(options%init_conditions_file,options%zvar, domain%z)
!             ! dz also has to be calculated from the 3d z file
!             buf = options%buffer
!             allocate(domain%dz(nx,nz,ny))
!             allocate(temporary_z(nx,nz,ny))
!             do i = 1,nz-1
!                 domain%dz(:,i,:) = domain%z(buf+1:nx+buf,buf+1:ny+buf,i+1)-domain%z(buf+1:nx+buf,buf+1:ny+buf,i)
!                 temporary_z(:,i,:) = domain%z(buf+1:nx+buf,buf+1:ny+buf,i)
!             enddo
!             temporary_z(:,nz,:) = domain%z(buf+1:nx+buf,buf+1:ny+buf,nz)
!             domain%dz(:,nz,:) = domain%dz(:,nz-1,:)
!             deallocate(domain%z)
!             allocate(domain%z(nx,nz,ny))
!             domain%z = temporary_z
!             deallocate(temporary_z)
!
!         else
!             ! otherwise, set up the z grid to be evenly spaced in z using the terrain +dz/2 for the base
!             ! and z[i-1]+(dz[i-1]+dz[i])/2 for the rest
!             allocate(domain%z(nx,nz,ny))
!             allocate(domain%z_inter(nx,nz,ny))
!             allocate(domain%dz(nx,nz,ny))
!             allocate(domain%dz_inter(nx,nz,ny))
!             allocate(domain%z_layers(nz))
!             allocate(domain%z_interface_layers(nz+1))
!             ! lowest model level is half of the lowest dz above the land surface
!             domain%z(:,1,:) = domain%terrain+options%dz_levels(1)/2
!             domain%z_inter(:,1,:) = domain%terrain
!             ! dz between the mass grid points is half of each dz
!             domain%dz(:,1,:) = (options%dz_levels(1) + options%dz_levels(2))/2
!             ! dz between the interface points = dz = thickness of mass grid cells
!             domain%dz_inter(:,1,:) = options%dz_levels(1)
!             ! create 1D arrays to store the height of each level and interface above the terrain too
!             domain%z_layers(1) = options%dz_levels(1)/2
!             domain%z_interface_layers(1) = 0
!             domain%z_interface_layers(2) = options%dz_levels(1)/2
!             do i = 2,nz
!                 domain%z_interface_layers(i+1) = domain%z_interface_layers(i) + options%dz_levels(i)
!                 domain%z_layers(i)    = domain%z_interface_layers(i) + options%dz_levels(i) / 2
!
!                 ! although these are almost identical, they have topography added to the first layer
!                 domain%z(:,i,:)       = domain%z(:,i-1,:)       + (options%dz_levels(i)+options%dz_levels(i-1))/2
!                 domain%z_inter(:,i,:) = domain%z_inter(:,i-1,:) +  options%dz_levels(i)
!
!                 ! dz between the interface points = dz = thickness of mass grid cells
!                 domain%dz_inter(:,i,:) = options%dz_levels(i)
!                 ! dz between the mass grid points is half of each dz
!                 if (i<nz) then
!                     domain%dz(:,i,:) = (options%dz_levels(i) + options%dz_levels(i+1))/2
!                 endif
!             enddo
!             domain%dz(:,nz,:) = options%dz_levels(nz)
!
!         endif
!         call copy_z(domain,domain%u_geo,interpolate_dim=1)
!         call copy_z(domain,domain%v_geo,interpolate_dim=3)
!
!         ! all other variables should be allocated and initialized to 0
!         call domain_allocation(domain,options,nx,nz,ny)
!         ! initializing land
!         call init_domain_land(domain,options)
!
!         ! store dx in domain as well as options, read as an option, but it is more appropriate in domain
!         domain%dx = options%dx
!
!         call init_winds(domain,options)
!     end subroutine init_domain

end module

max_stars_repo_name: scrasmussen/icar

max_issues_repo_name: scrasmussen/icar

max_forks_repo_name: scrasmussen/icar


text:
Henry E. Bennett, DDS is an oral surgeon with the Davis Dental Surgery Center who provides services such as extractions, implants, jaw surgeries, and is qualified to provide intravenous sedation and general anesthesia. Dr. Bennett is a diplomat of the American Board of Oral & Maxillofacial Surgery and a member of the http://www.calaoms.org/ California Association of Oral and Maxillofacial Surgeons.

To explore other dentists in the Davis area, please visit our Dentists page.

20071231 12:11:21 nbsp My son had a very good experience with Dr. Bennett and his staff during and after his wisdom teeth extractions. A relatively wholistic approach to care, including a postsurgical regimen of vitamin/mineral supplementation. Users/nls

20080816 01:10:35 nbsp i got my wisdoms pulled yesterday...lovely staff and serviice Users/davisrox

20090619 12:02:51 nbsp I got my wisdom teeth pulled yesterday. I dont think I could have asked for a better doctor to preform the surgery. He set me up with a very natural post surgical regimen that involves enzyme lotion, special mouthwash, and a variety of vitamins and minerals to speed the healing process. His staff is very nice and knows you by name. Since my mom brought me home after the surgery and I live a few hours away, he set up for a doctor in the area in case there were any complications and called me last night to make sure everything was going smoothly and to see if I had any questions and concerns. Very nice doctor, very knowledgeable. I would definitely recommend him to anyone looking for a dental surgeon.  Users/AppleBlossom

20090810 08:21:56 nbsp I dont know how the doctor is, because I havent had my surgery, but both receptionists I spoke to at this office were surprisingly rude and condescending to me every time I called, from the initial contact onwards.  One even hung up on me when I was trying to discuss payment options.  Very unprofessional and the last thing a patient wants to deal with on top of serious pain. Users/egs1981

20090810 09:17:29 nbsp Based on what appeared to be a culture of disrespect for patients at this office, I called to cancel my appointment and was hung up on a second time.  Dr. Bennett may be a great doctor but I really dont recommend dealing with his staff. Users/egs1981

20100812 20:40:54 nbsp I too am not particularly impressed with the staff on the phone. The woman I spoke with is neither polite nor accurate.  Users/trutybetold

20100911 13:46:41 nbsp I cant believe anybody would say that the front office staff is rude at Dr. Bennetts! I had an amazing experience there. I am one of those people who are intensely scared of the dentist and any dental procedure. I went to Dr. Bennett so that I could be sedated and have my widsom teeth and one badly damaged tooth extracted. He spent the consultation time really getting to know me and make me feel incredibly comfortable. As for the front desk staff they knew I was nervous and got me water, asked me about my recent marraige and my job. I couldnt have asked for a better experience and would highly reccommend this office for anyone needing oral surgery. I also had a great conversation with the Dr. about history, overall health and wellness and natural ways to heal. He is my favorite kind of dr!  Users/LFHR

20100911 21:09:10 nbsp I have recommended Dr. Bennett to others looking for oral surgery, based on my excellent experience over a decade ago. I had a growth removed, had it become infected on a Sunday afternoon, and called the number Dr. Bennett had given me. He quickly called me back and arranged for antibiotics to be available (there were no Davis pharmacies open at that time on a Sunday). I felt that the procedure went well  mouths just can get infected. Caring, integrative in his approach, and very knowledgeable: a professional I can recommend. I certainly had no problems with his office staff, but you would guess theyve changed since my experience :^) Users/DougWalter

20110524 12:08:24 nbsp I would absolutely recommend him!  He is kind, and will provide sedation so that YOU FEEL NO PAIN.  This is the absolute truth.  I have had an unfortunate dental history and have had to see Dr. Bennett many times (sadly)  since 1978 (!!!!)  Horrifying procedures...NO PAIN.  And he will give you lots of information about holistic approaches to your issues if they are relevant.  He is as bright as a new penny and charming too.  I have not had any problem with the office staff.   Users/FredaBrown

20110913 11:24:27 nbsp I just called their office to get information about wisdom teeth, and the staff person spent ten minutes talking me through both the procedure and my payment options.  She was extremely clear, patient and helpful. Users/mcenzer

ext: f

max_stars_repo_name: voflo/Search

max_issues_repo_name: voflo/Search

max_forks_repo_name: voflo/Search


text:
Davisville Travel proudly displays zebra butt in their window. Go check it out.

They frequently participate in ArtAbout with receptions for the artwork (mostly paintings) displayed within.

20060322 14:23:15 nbsp I had to book a trip to england/scotland recently, they got me out the day I wanted, into the city we wanted (Manchester) out of the city we wanted (London), with the layover we wanted (NYC),  for about 300 dollars less than the travel sites.  This took aproximately 30 minutes. Hightly recommended. Users/CharlesGeorge

20120628 19:11:53 nbsp I came into Davisville to book a lastminute vacation to Hawaii not too long ago.  I had never been before and didnt really know what I wanted to do.  Nancy was extremely knowledgeable and helpful.  She knew the right questions to ask to sort out the perfect destination for me and my wife (Maui).  Doing it lastminute wasnt a problem; she was able to set us up with a flight and a great place to stay.  She offered several pricing options to make sure we were able to get something we wanted within our budget.  We wound up with a package consisting of flights at relatively good times of day, an extremely nice ocean view condo, and a rental car for a price comparable to what we wouldve paid through Orbitz or Expedia or whatever.  Two days later, we got a packet with an itinerary and our travel documents and everything in the mail.

This was the first time I used a travel agent, and Im glad I did.  I wouldnt have had any idea where to go in Hawaii, but we wound up with an absolutely fantastic vacation.  It took all of about 45 minutes at their office, compared to the many hours I wouldve no doubt spent trying to research destinations and hotels and whatnot from home at the last minute.

I only have one negative comment, and its about one of their partner agencies.  Nancy used Pleasant Vacations, the largest travel agency in Hawaii, to put together my travel package.  As I said, for the most part it was excellent.  The packet of documents I got included vouchers for an island orientation breakfast the day after our arrival, though.  We were naive enough to go.  It turned out to have nothing whatsoever to do with orienting you to the island; instead, it was a 2hour sales pitch by Pleasant Vacations for various tours and whatnot on the island.  The lady said at the start of her talk that it would be 30 minutes, but that was a load.  At the end, reps for various tours that were pitched came around to talk to everyone, along with a couple of extraordinarily pushy timeshare salesmen.  We actually did end up booking two things through them: a luau (which we were going to do anyway) and a bus tour to Hana, which we planned to visit and decided it would be nice not to have to make the 5hour drive ourselves.  The bus tour was nice.  The luau, we discovered soon after leaving, has a reputation for being the worst luau and the worst food on the island.  We promptly cancelled it and booked a different one through our hotels concierge.

Anyway, long story short: Davisville Travel is great.  Nancy is great.  Just dont buy into the sales pitch gimmicks that some of the partner companies might try and pull at your destination. Users/TomGarberson


text:
module zFORp_module

  use, intrinsic :: iso_c_binding, only: c_int, c_int64_t, c_size_t, c_double, c_ptr, c_null_ptr, c_loc
  implicit none
  private

  ! bind(c) on types, enums because tests written in C need to reference them
  type, bind(c) :: zFORp_bitstream
    private
    type(c_ptr) :: object = c_null_ptr
  end type zFORp_bitstream

  type, bind(c) :: zFORp_stream
    private
    type(c_ptr) :: object = c_null_ptr
  end type zFORp_stream

  type, bind(c) :: zFORp_field
    private
    type(c_ptr) :: object = c_null_ptr
  end type zFORp_field

  enum, bind(c)
    enumerator :: zFORp_type_none = 0, &
                  zFORp_type_int32 = 1, &
                  zFORp_type_int64 = 2, &
                  zFORp_type_float = 3, &
                  zFORp_type_double = 4
  end enum

  enum, bind(c)
    enumerator :: zFORp_mode_null = 0, &
                  zFORp_mode_expert = 1, &
                  zFORp_mode_fixed_rate = 2, &
                  zFORp_mode_fixed_precision = 3, &
                  zFORp_mode_fixed_accuracy = 4, &
                  zFORp_mode_reversible = 5
  end enum

  enum, bind(c)
    enumerator :: zFORp_exec_serial = 0, &
                  zFORp_exec_omp = 1, &
                  zFORp_exec_cuda = 2
  end enum

  ! constants are hardcoded
  ! const_xyz holds value, but xyz is the public constant

  integer, parameter :: const_zFORp_version_major = 0
  integer, parameter :: const_zFORp_version_minor = 5
  integer, parameter :: const_zFORp_version_patch = 5
  integer, protected, bind(c, name="zFORp_version_major") :: zFORp_version_major
  integer, protected, bind(c, name="zFORp_version_minor") :: zFORp_version_minor
  integer, protected, bind(c, name="zFORp_version_patch") :: zFORp_version_patch
  data zFORp_version_major/const_zFORp_version_major/, &
       zFORp_version_minor/const_zFORp_version_minor/, &
       zFORp_version_patch/const_zFORp_version_patch/

  integer, parameter :: const_zFORp_codec_version = 5
  integer, protected, bind(c, name="zFORp_codec_version") :: zFORp_codec_version
  data zFORp_codec_version/const_zFORp_codec_version/

  integer, parameter :: const_zFORp_library_version = 85 ! 0x55
  integer, protected, bind(c, name="zFORp_library_version") :: zFORp_library_version
  data zFORp_library_version/const_zFORp_library_version/

  character(len = 36), parameter :: zFORp_version_string = 'zfp version 0.5.5 (May 5, 2019)'

  integer, parameter :: const_zFORp_min_bits = 1
  integer, parameter :: const_zFORp_max_bits = 16657
  integer, parameter :: const_zFORp_max_prec = 64
  integer, parameter :: const_zFORp_min_exp = -1074
  integer, protected, bind(c, name="zFORp_min_bits") :: zFORp_min_bits
  integer, protected, bind(c, name="zFORp_max_bits") :: zFORp_max_bits
  integer, protected, bind(c, name="zFORp_max_prec") :: zFORp_max_prec
  integer, protected, bind(c, name="zFORp_min_exp") :: zFORp_min_exp
  data zFORp_min_bits/const_zFORp_min_bits/, &
       zFORp_max_bits/const_zFORp_max_bits/, &
       zFORp_max_prec/const_zFORp_max_prec/, &
       zFORp_min_exp/const_zFORp_min_exp/

  integer, parameter :: const_zFORp_header_magic = 1
  integer, parameter :: const_zFORp_header_meta = 2
  integer, parameter :: const_zFORp_header_mode = 4
  integer, parameter :: const_zFORp_header_full = 7
  integer, protected, bind(c, name="zFORp_header_magic") :: zFORp_header_magic
  integer, protected, bind(c, name="zFORp_header_meta") :: zFORp_header_meta
  integer, protected, bind(c, name="zFORp_header_mode") :: zFORp_header_mode
  integer, protected, bind(c, name="zFORp_header_full") :: zFORp_header_full
  data zFORp_header_magic/const_zFORp_header_magic/, &
       zFORp_header_meta/const_zFORp_header_meta/, &
       zFORp_header_mode/const_zFORp_header_mode/, &
       zFORp_header_full/const_zFORp_header_full/

  integer (kind=8), parameter :: const_zFORp_meta_null = -1
  integer (kind=8), protected, bind(c, name="zFORp_meta_null") :: zFORp_meta_null
  data zFORp_meta_null/const_zFORp_meta_null/

  integer, parameter :: const_zFORp_magic_bits = 32
  integer, parameter :: const_zFORp_meta_bits = 52
  integer, parameter :: const_zFORp_mode_short_bits = 12
  integer, parameter :: const_zFORp_mode_long_bits = 64
  integer, parameter :: const_zFORp_header_max_bits = 148
  integer, parameter :: const_zFORp_mode_short_max = 4094
  integer, protected, bind(c, name="zFORp_magic_bits") :: zFORp_magic_bits
  integer, protected, bind(c, name="zFORp_meta_bits") :: zFORp_meta_bits
  integer, protected, bind(c, name="zFORp_mode_short_bits") :: zFORp_mode_short_bits
  integer, protected, bind(c, name="zFORp_mode_long_bits") :: zFORp_mode_long_bits
  integer, protected, bind(c, name="zFORp_header_max_bits") :: zFORp_header_max_bits
  integer, protected, bind(c, name="zFORp_mode_short_max") :: zFORp_mode_short_max
  data zFORp_magic_bits/const_zFORp_magic_bits/, &
       zFORp_meta_bits/const_zFORp_meta_bits/, &
       zFORp_mode_short_bits/const_zFORp_mode_short_bits/, &
       zFORp_mode_long_bits/const_zFORp_mode_long_bits/, &
       zFORp_header_max_bits/const_zFORp_header_max_bits/, &
       zFORp_mode_short_max/const_zFORp_mode_short_max/

  interface

    ! minimal bitstream API

    function zfp_bitstream_stream_open(buffer, bytes) result(bs) bind(c, name="stream_open")
      import
      type(c_ptr), value :: buffer
      integer(c_size_t), value :: bytes
      type(c_ptr) :: bs
    end function zfp_bitstream_stream_open

    subroutine zfp_bitstream_stream_close(bs) bind(c, name="stream_close")
      import
      type(c_ptr), value :: bs
    end subroutine

    ! high-level API: utility functions

    function zfp_type_size(scalar_type) result(type_size) bind(c, name="zfp_type_size")
      import
      integer(c_int) scalar_type
      integer(c_size_t) type_size
    end function

    ! high-level API: zfp_stream functions

    function zfp_stream_open(bs) result(stream) bind(c, name="zfp_stream_open")
      import
      type(c_ptr), value :: bs
      type(c_ptr) :: stream
    end function zfp_stream_open

    subroutine zfp_stream_close(stream) bind(c, name="zfp_stream_close")
      import
      type(c_ptr), value :: stream
    end subroutine

    function zfp_stream_bit_stream(stream) result(bs) bind(c, name="zfp_stream_bit_stream")
      import
      type(c_ptr), value :: stream
      type(c_ptr) :: bs
    end function

    function zfp_stream_compression_mode(stream) result(zfp_mode) bind(c, name="zfp_stream_compression_mode")
      import
      type(c_ptr), value :: stream
      integer(c_int) :: zfp_mode
    end function

    function zfp_stream_mode(stream) result(encoded_mode) bind(c, name="zfp_stream_mode")
      import
      type(c_ptr), value :: stream
      integer(c_int64_t) encoded_mode
    end function

    subroutine zfp_stream_params(stream, minbits, maxbits, maxprec, minexp) bind(c, name="zfp_stream_params")
      import
      type(c_ptr), value :: stream
      integer(c_int) :: minbits, maxbits, maxprec, minexp
    end subroutine

    function zfp_stream_compressed_size(stream) result(compressed_size) bind(c, name="zfp_stream_compressed_size")
      import
      type(c_ptr), value :: stream
      integer(c_size_t) compressed_size
    end function

    function zfp_stream_maximum_size(stream, field) result(max_size) bind(c, name="zfp_stream_maximum_size")
      import
      type(c_ptr), value :: stream, field
      integer(c_size_t) max_size
    end function

    subroutine zfp_stream_set_bit_stream(stream, bs) bind(c, name="zfp_stream_set_bit_stream")
      import
      type(c_ptr), value :: stream, bs
    end subroutine

    subroutine zfp_stream_set_reversible(stream) bind(c, name="zfp_stream_set_reversible")
      import
      type(c_ptr), value :: stream
    end subroutine

    function zfp_stream_set_rate(stream, rate, scalar_type, dims, wra) result(rate_result) bind(c, name="zfp_stream_set_rate")
      import
      type(c_ptr), value :: stream
      real(c_double), value :: rate
      integer(c_int), value :: scalar_type
      ! no unsigned int in Fortran
      integer(c_int), value :: dims, wra
      real(c_double) :: rate_result
    end function

    function zfp_stream_set_precision(stream, prec) result(prec_result) bind(c, name="zfp_stream_set_precision")
      import
      type(c_ptr), value :: stream
      integer(c_int), value :: prec
      integer(c_int) prec_result
    end function

    function zfp_stream_set_accuracy(stream, acc) result(acc_result) bind(c, name="zfp_stream_set_accuracy")
      import
      type(c_ptr), value :: stream
      real(c_double), value :: acc
      real(c_double) acc_result
    end function

    function zfp_stream_set_mode(stream, encoded_mode) result(mode_result) bind(c, name="zfp_stream_set_mode")
      import
      type(c_ptr), value :: stream
      integer(c_int64_t), value :: encoded_mode
      integer(c_int) mode_result
    end function

    function zfp_stream_set_params(stream, minbits, maxbits, maxprec, minexp) &
        result(is_success) bind(c, name="zfp_stream_set_params")
      import
      type(c_ptr), value :: stream
      integer(c_int), value :: minbits, maxbits, maxprec, minexp
      integer(c_int) is_success
    end function

    ! high-level API: execution policy functions

    function zfp_stream_execution(stream) result(execution_policy) bind(c, name="zfp_stream_execution")
      import
      type(c_ptr), value :: stream
      integer(c_int) execution_policy
    end function

    function zfp_stream_omp_threads(stream) result(num_threads) bind(c, name="zfp_stream_omp_threads")
      import
      type(c_ptr), value :: stream
      integer(c_int) num_threads
    end function

    function zfp_stream_omp_chunk_size(stream) result(chunk_size_blocks) bind(c, name="zfp_stream_omp_chunk_size")
      import
      type(c_ptr), value :: stream
      integer(c_int) chunk_size_blocks
    end function

    function zfp_stream_set_execution(stream, execution_policy) result(is_success) bind(c, name="zfp_stream_set_execution")
      import
      type(c_ptr), value :: stream
      integer(c_int) execution_policy, is_success
    end function

    function zfp_stream_set_omp_threads(stream, threads) result(is_success) bind(c, name="zfp_stream_set_omp_threads")
      import
      type(c_ptr), value :: stream
      integer(c_int) threads, is_success
    end function

    function zfp_stream_set_omp_chunk_size(stream, chunk_size) result(is_success) bind(c, name="zfp_stream_set_omp_chunk_size")
      import
      type(c_ptr), value :: stream
      integer(c_int) chunk_size, is_success
    end function

    ! high-level API: zfp_field functions

    function zfp_field_alloc() result(field) bind(c, name="zfp_field_alloc")
      import
      type(c_ptr) :: field
    end function

    function zfp_field_1d(uncompressed_ptr, scalar_type, nx) result(field) bind(c, name="zfp_field_1d")
      import
      type(c_ptr), value :: uncompressed_ptr
      type(c_ptr) :: field
      integer(c_int), value :: scalar_type, nx
    end function

    function zfp_field_2d(uncompressed_ptr, scalar_type, nx, ny) result(field) bind(c, name="zfp_field_2d")
      import
      type(c_ptr), value :: uncompressed_ptr
      type(c_ptr) :: field
      integer(c_int), value :: scalar_type, nx, ny
    end function

    function zfp_field_3d(uncompressed_ptr, scalar_type, nx, ny, nz) result(field) bind(c, name="zfp_field_3d")
      import
      type(c_ptr), value :: uncompressed_ptr
      type(c_ptr) :: field
      integer(c_int), value :: scalar_type, nx, ny, nz
    end function

    function zfp_field_4d(uncompressed_ptr, scalar_type, nx, ny, nz, nw) result(field) bind(c, name="zfp_field_4d")
      import
      type(c_ptr), value :: uncompressed_ptr
      type(c_ptr) :: field
      integer(c_int), value :: scalar_type, nx, ny, nz, nw
    end function

    subroutine zfp_field_free(field) bind(c, name="zfp_field_free")
      import
      type(c_ptr), value :: field
    end subroutine

    function zfp_field_pointer(field) result(arr_ptr) bind(c, name="zfp_field_pointer")
      import
      type(c_ptr), value :: field
      type(c_ptr) :: arr_ptr
    end function

    function zfp_field_type(field) result(scalar_type) bind(c, name="zfp_field_type")
      import
      type(c_ptr), value :: field
      integer(c_int) scalar_type
    end function

    function zfp_field_precision(field) result(prec) bind(c, name="zfp_field_precision")
      import
      type(c_ptr), value :: field
      integer(c_int) prec
    end function

    function zfp_field_dimensionality(field) result(dims) bind(c, name="zfp_field_dimensionality")
      import
      type(c_ptr), value :: field
      integer(c_int) dims
    end function

    function zfp_field_size(field, size_arr) result(total_size) bind(c, name="zfp_field_size")
      import
      type(c_ptr), value :: field, size_arr
      integer(c_size_t) total_size
    end function

    function zfp_field_stride(field, stride_arr) result(is_strided) bind(c, name="zfp_field_stride")
      import
      type(c_ptr), value :: field, stride_arr
      integer(c_int) is_strided
    end function

    function zfp_field_metadata(field) result(encoded_metadata) bind(c, name="zfp_field_metadata")
      import
      type(c_ptr), value :: field
      integer(c_int64_t) encoded_metadata
    end function

    subroutine zfp_field_set_pointer(field, arr_ptr) bind(c, name="zfp_field_set_pointer")
      import
      type(c_ptr), value :: field, arr_ptr
    end subroutine

    function zfp_field_set_type(field, scalar_type) result(scalar_type_result) bind(c, name="zfp_field_set_type")
      import
      type(c_ptr), value :: field
      integer(c_int) scalar_type, scalar_type_result
    end function

    subroutine zfp_field_set_size_1d(field, nx) bind(c, name="zfp_field_set_size_1d")
      import
      type(c_ptr), value :: field
      integer(c_int) nx
    end subroutine

    subroutine zfp_field_set_size_2d(field, nx, ny) bind(c, name="zfp_field_set_size_2d")
      import
      type(c_ptr), value :: field
      integer(c_int) nx, ny
    end subroutine

    subroutine zfp_field_set_size_3d(field, nx, ny, nz) bind(c, name="zfp_field_set_size_3d")
      import
      type(c_ptr), value :: field
      integer(c_int) nx, ny, nz
    end subroutine

    subroutine zfp_field_set_size_4d(field, nx, ny, nz, nw) bind(c, name="zfp_field_set_size_4d")
      import
      type(c_ptr), value :: field
      integer(c_int) nx, ny, nz, nw
    end subroutine

    subroutine zfp_field_set_stride_1d(field, sx) bind(c, name="zfp_field_set_stride_1d")
      import
      type(c_ptr), value :: field
      integer(c_int) sx
    end subroutine

    subroutine zfp_field_set_stride_2d(field, sx, sy) bind(c, name="zfp_field_set_stride_2d")
      import
      type(c_ptr), value :: field
      integer(c_int) sx, sy
    end subroutine

    subroutine zfp_field_set_stride_3d(field, sx, sy, sz) bind(c, name="zfp_field_set_stride_3d")
      import
      type(c_ptr), value :: field
      integer(c_int) sx, sy, sz
    end subroutine

    subroutine zfp_field_set_stride_4d(field, sx, sy, sz, sw) bind(c, name="zfp_field_set_stride_4d")
      import
      type(c_ptr), value :: field
      integer(c_int) sx, sy, sz, sw
    end subroutine

    function zfp_field_set_metadata(field, encoded_metadata) result(is_success) bind(c, name="zfp_field_set_metadata")
      import
      type(c_ptr), value :: field
      integer(c_int64_t) :: encoded_metadata
      integer(c_int) is_success
    end function

    ! high-level API: compression and decompression

    function zfp_compress(stream, field) result(bitstream_offset_bytes) bind(c, name="zfp_compress")
      import
      type(c_ptr), value :: stream, field
      integer(c_size_t) :: bitstream_offset_bytes
    end function

    function zfp_decompress(stream, field) result(bitstream_offset_bytes) bind(c, name="zfp_decompress")
      import
      type(c_ptr), value :: stream, field
      integer(c_size_t) :: bitstream_offset_bytes
    end function

    function zfp_write_header(stream, field, mask) result(num_bits_written) bind(c, name="zfp_write_header")
      import
      type(c_ptr), value :: stream, field
      integer(c_int) mask
      integer(c_size_t) num_bits_written
    end function

    function zfp_read_header(stream, field, mask) result(num_bits_read) bind(c, name="zfp_read_header")
      import
      type(c_ptr), value :: stream, field
      integer(c_int) mask
      integer(c_size_t) num_bits_read
    end function

    ! low-level API: stream manipulation
    subroutine zfp_stream_rewind(stream) bind(c, name="zfp_stream_rewind")
      import
      type(c_ptr), value :: stream
    end subroutine

  end interface

  ! types

  public :: zFORp_bitstream, &
            zFORp_stream, &
            zFORp_field

  ! enums

  public :: zFORp_type_none, &
            zFORp_type_int32, &
            zFORp_type_int64, &
            zFORp_type_float, &
            zFORp_type_double

  public :: zFORp_mode_null, &
            zFORp_mode_expert, &
            zFORp_mode_fixed_rate, &
            zFORp_mode_fixed_precision, &
            zFORp_mode_fixed_accuracy

  public :: zFORp_exec_serial, &
            zFORp_exec_omp, &
            zFORp_exec_cuda

  ! C macros -> constants
  public :: zFORp_version_major, &
            zFORp_version_minor, &
            zFORp_version_patch

  public :: zFORp_codec_version, &
            zFORp_library_version, &
            zFORp_version_string

  public :: zFORp_min_bits, &
            zFORp_max_bits, &
            zFORp_max_prec, &
            zFORp_min_exp

  public :: zFORp_header_magic, &
            zFORp_header_meta, &
            zFORp_header_mode, &
            zFORp_header_full

  public :: zFORp_meta_null

  public :: zFORp_magic_bits, &
            zFORp_meta_bits, &
            zFORp_mode_short_bits, &
            zFORp_mode_long_bits, &
            zFORp_header_max_bits, &
            zFORp_mode_short_max

  ! minimal bitstream API

  public :: zFORp_type_size

  public :: zFORp_bitstream_stream_open, &
            zFORp_bitstream_stream_close

  ! high-level API: zfp_stream functions

  public :: zFORp_stream_open, &
            zFORp_stream_close, &
            zFORp_stream_bit_stream, &
            zFORp_stream_compression_mode, &
            zFORp_stream_mode, &
            zFORp_stream_params, &
            zFORp_stream_compressed_size, &
            zFORp_stream_maximum_size, &
            zFORp_stream_set_bit_stream, &
            zFORp_stream_set_reversible, &
            zFORp_stream_set_rate, &
            zFORp_stream_set_precision, &
            zFORp_stream_set_accuracy, &
            zFORp_stream_set_mode, &
            zFORp_stream_set_params

  ! high-level API: execution policy functions
  public :: zFORp_stream_execution, &
            zFORp_stream_omp_threads, &
            zFORp_stream_omp_chunk_size, &
            zFORp_stream_set_execution, &
            zFORp_stream_set_omp_threads, &
            zFORp_stream_set_omp_chunk_size

  ! high-level API: zfp_field functions

  public :: zFORp_field_alloc, &
            zFORp_field_1d, &
            zFORp_field_2d, &
            zFORp_field_3d, &
            zFORp_field_4d, &
            zFORp_field_free, &
            zFORp_field_pointer, &
            zFORp_field_type, &
            zFORp_field_precision, &
            zFORp_field_dimensionality, &
            zFORp_field_size, &
            zFORp_field_stride, &
            zFORp_field_metadata, &
            zFORp_field_set_pointer, &
            zFORp_field_set_type, &
            zFORp_field_set_size_1d, &
            zFORp_field_set_size_2d, &
            zFORp_field_set_size_3d, &
            zFORp_field_set_size_4d, &
            zFORp_field_set_stride_1d, &
            zFORp_field_set_stride_2d, &
            zFORp_field_set_stride_3d, &
            zFORp_field_set_stride_4d, &
            zFORp_field_set_metadata

  ! high-level API: compression and decompression

  public :: zFORp_compress, &
            zFORp_decompress, &
            zFORp_write_header, &
            zFORp_read_header

  ! low-level API: stream manipulation

  public :: zFORp_stream_rewind

contains

  ! minimal bitstream API

  function zFORp_bitstream_stream_open(buffer, bytes) result(bs) bind(c, name="zforp_bitstream_stream_open")
    implicit none
    type(zFORp_bitstream) :: bs
    type(c_ptr), intent(in) :: buffer
    integer (kind=8), intent(in) :: bytes
    bs%object = zfp_bitstream_stream_open(buffer, int(bytes, c_size_t))
  end function zFORp_bitstream_stream_open

  subroutine zFORp_bitstream_stream_close(bs) bind(c, name="zforp_bitstream_stream_close")
    type(zFORp_bitstream), intent(inout) :: bs
    call zfp_bitstream_stream_close(bs%object)
    bs%object = c_null_ptr
  end subroutine zFORp_bitstream_stream_close

  ! high-level API: utility functions

  function zFORp_type_size(scalar_type) result(type_size) bind(c, name="zforp_type_size")
    implicit none
    integer, intent(in) :: scalar_type
    integer (kind=8) type_size
    type_size = zfp_type_size(int(scalar_type, c_int))
  end function zFORp_type_size

  ! high-level API: zfp_stream functions

  function zFORp_stream_open(bs) result(stream) bind(c, name="zforp_stream_open")
    implicit none
    type(zFORp_bitstream), intent(in) :: bs
    type(zFORp_stream) :: stream
    stream%object = zfp_stream_open(bs%object)
  end function zFORp_stream_open

  subroutine zFORp_stream_close(stream) bind(c, name="zforp_stream_close")
    type(zFORp_stream), intent(inout) :: stream
    call zfp_stream_close(stream%object)
    stream%object = c_null_ptr
  end subroutine zFORp_stream_close

  function zFORp_stream_bit_stream(stream) result(bs) bind(c, name="zforp_stream_bit_stream")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_bitstream) :: bs
    bs%object = zfp_stream_bit_stream(stream%object)
  end function zFORp_stream_bit_stream

  function zFORp_stream_compression_mode(stream) result(zfp_mode) bind(c, name="zforp_stream_compression_mode")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer zfp_mode
    zfp_mode = zfp_stream_compression_mode(stream%object)
  end function zFORp_stream_compression_mode

  function zFORp_stream_mode(stream) result(encoded_mode) bind(c, name="zforp_stream_mode")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer (kind=8) encoded_mode
    encoded_mode = zfp_stream_mode(stream%object)
  end function zFORp_stream_mode

  subroutine zFORp_stream_params(stream, minbits, maxbits, maxprec, minexp) bind(c, name="zforp_stream_params")
    type(zFORp_stream), intent(in) :: stream
    integer, intent(inout) :: minbits, maxbits, maxprec, minexp
    call zfp_stream_params(stream%object, &
                           int(minbits, c_int), &
                           int(maxbits, c_int), &
                           int(maxprec, c_int), &
                           int(minexp, c_int))
  end subroutine zFORp_stream_params

  function zFORp_stream_compressed_size(stream) result(compressed_size) bind(c, name="zforp_stream_compressed_size")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer (kind=8) compressed_size
    compressed_size = zfp_stream_compressed_size(stream%object)
  end function zFORp_stream_compressed_size

  function zFORp_stream_maximum_size(stream, field) result(max_size) bind(c, name="zforp_stream_maximum_size")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_field), intent(in) :: field
    integer (kind=8) max_size
    max_size = zfp_stream_maximum_size(stream%object, field%object)
  end function zFORp_stream_maximum_size

  subroutine zFORp_stream_set_bit_stream(stream, bs) bind(c, name="zforp_stream_set_bit_stream")
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_bitstream), intent(in) :: bs
    call zfp_stream_set_bit_stream(stream%object, bs%object)
  end subroutine zFORp_stream_set_bit_stream

  subroutine zFORp_stream_set_reversible(stream) bind(c, name="zforp_stream_set_reversible")
    type(zFORp_stream), intent(in) :: stream
    call zfp_stream_set_reversible(stream%object)
  end subroutine zFORp_stream_set_reversible

  function zFORp_stream_set_rate(stream, rate, scalar_type, dims, wra) result(rate_result) bind(c, name="zforp_stream_set_rate")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    real (kind=8), intent(in) :: rate
    integer, intent(in) :: scalar_type
    integer, intent(in) :: dims, wra
    real (kind=8) :: rate_result
    rate_result = zfp_stream_set_rate(stream%object, real(rate, c_double), &
      int(scalar_type, c_int), int(dims, c_int), int(wra, c_int))
  end function zFORp_stream_set_rate

  function zFORp_stream_set_precision(stream, prec) result(prec_result) bind(c, name="zforp_stream_set_precision")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer, intent(in) :: prec
    integer prec_result
    prec_result = zfp_stream_set_precision(stream%object, int(prec, c_int))
  end function zFORp_stream_set_precision

  function zFORp_stream_set_accuracy(stream, acc) result(acc_result) bind(c, name="zforp_stream_set_accuracy")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    real (kind=8), intent(in) :: acc
    real (kind=8) acc_result
    acc_result = zfp_stream_set_accuracy(stream%object, real(acc, c_double))
  end function zFORp_stream_set_accuracy

  function zFORp_stream_set_mode(stream, encoded_mode) result(mode_result) bind(c, name="zforp_stream_set_mode")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer (kind=8), intent(in) :: encoded_mode
    integer mode_result
    mode_result = zfp_stream_set_mode(stream%object, int(encoded_mode, c_int64_t))
  end function zFORp_stream_set_mode

  function zFORp_stream_set_params(stream, minbits, maxbits, maxprec, minexp) result(is_success) &
      bind(c, name="zforp_stream_set_params")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer, intent(in) :: minbits, maxbits, maxprec, minexp
    integer is_success
    is_success = zfp_stream_set_params(stream%object, &
                                       int(minbits, c_int), &
                                       int(maxbits, c_int), &
                                       int(maxprec, c_int), &
                                       int(minexp, c_int))
  end function zFORp_stream_set_params

  ! high-level API: execution policy functions

  function zFORp_stream_execution(stream) result(execution_policy) bind(c, name="zforp_stream_execution")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer execution_policy
    execution_policy = zfp_stream_execution(stream%object)
  end function zFORp_stream_execution

  function zFORp_stream_omp_threads(stream) result(thread_count) bind(c, name="zforp_stream_omp_threads")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer thread_count
    thread_count = zfp_stream_omp_threads(stream%object)
  end function zFORp_stream_omp_threads

  function zFORp_stream_omp_chunk_size(stream) result(chunk_size_blocks) bind(c, name="zforp_stream_omp_chunk_size")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer (kind=8) chunk_size_blocks
    chunk_size_blocks = zfp_stream_omp_chunk_size(stream%object)
  end function zFORp_stream_omp_chunk_size

  function zFORp_stream_set_execution(stream, execution_policy) result(is_success) bind(c, name="zforp_stream_set_execution")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer, intent(in) :: execution_policy
    integer is_success
    is_success = zfp_stream_set_execution(stream%object, int(execution_policy, c_int))
  end function zFORp_stream_set_execution

  function zFORp_stream_set_omp_threads(stream, thread_count) result(is_success) bind(c, name="zforp_stream_set_omp_threads")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer, intent(in) :: thread_count
    integer is_success
    is_success = zfp_stream_set_omp_threads(stream%object, int(thread_count, c_int))
  end function zFORp_stream_set_omp_threads

  function zFORp_stream_set_omp_chunk_size(stream, chunk_size) result(is_success) &
      bind(c, name="zforp_stream_set_omp_chunk_size")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    integer, intent(in) :: chunk_size
    integer is_success
    is_success = zfp_stream_set_omp_chunk_size(stream%object, int(chunk_size, c_int))
  end function zFORp_stream_set_omp_chunk_size

  ! high-level API: zfp_field functions

  function zFORp_field_alloc() result(field) bind(c, name="zforp_field_alloc")
    implicit none
    type(zFORp_field) field
    field%object = zfp_field_alloc()
  end function zFORp_field_alloc

  function zFORp_field_1d(uncompressed_ptr, scalar_type, nx) result(field) bind(c, name="zforp_field_1d")
    implicit none
    type(c_ptr), intent(in) :: uncompressed_ptr
    integer, intent(in) :: scalar_type, nx
    type(zFORp_field) field
    field%object = zfp_field_1d(uncompressed_ptr, int(scalar_type, c_int), &
                                    int(nx, c_int))
  end function zFORp_field_1d

  function zFORp_field_2d(uncompressed_ptr, scalar_type, nx, ny) result(field) bind(c, name="zforp_field_2d")
    implicit none
    type(c_ptr), intent(in) :: uncompressed_ptr
    integer, intent(in) :: scalar_type, nx, ny
    type(zFORp_field) field
    field%object = zfp_field_2d(uncompressed_ptr, int(scalar_type, c_int), &
                                    int(nx, c_int), int(ny, c_int))
  end function zFORp_field_2d

  function zFORp_field_3d(uncompressed_ptr, scalar_type, nx, ny, nz) result(field) bind(c, name="zforp_field_3d")
    implicit none
    type(c_ptr), intent(in) :: uncompressed_ptr
    integer, intent(in) :: scalar_type, nx, ny, nz
    type(zFORp_field) field
    field%object = zfp_field_3d(uncompressed_ptr, int(scalar_type, c_int), &
                                    int(nx, c_int), int(ny, c_int), &
                                    int(nz, c_int))
  end function zFORp_field_3d

  function zFORp_field_4d(uncompressed_ptr, scalar_type, nx, ny, nz, nw) result(field) bind(c, name="zforp_field_4d")
    implicit none
    type(c_ptr), intent(in) :: uncompressed_ptr
    integer, intent(in) :: scalar_type, nx, ny, nz, nw
    type(zFORp_field) field
    field%object = zfp_field_4d(uncompressed_ptr, int(scalar_type, c_int), &
                                    int(nx, c_int), int(ny, c_int), &
                                    int(nz, c_int), int(nw, c_int))
  end function zFORp_field_4d

  subroutine zFORp_field_free(field) bind(c, name="zforp_field_free")
    type(zFORp_field), intent(inout) :: field
    call zfp_field_free(field%object)
    field%object = c_null_ptr
  end subroutine zFORp_field_free

  function zFORp_field_pointer(field) result(arr_ptr) bind(c, name="zforp_field_pointer")
    implicit none
    type(zFORp_field), intent(in) :: field
    type(c_ptr) arr_ptr
    arr_ptr = zfp_field_pointer(field%object)
  end function zFORp_field_pointer

  function zFORp_field_type(field) result(scalar_type) bind(c, name="zforp_field_type")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer scalar_type
    scalar_type = zfp_field_type(field%object)
  end function zFORp_field_type

  function zFORp_field_precision(field) result(prec) bind(c, name="zforp_field_precision")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer prec
    prec = zfp_field_precision(field%object)
  end function zFORp_field_precision

  function zFORp_field_dimensionality(field) result(dims) bind(c, name="zforp_field_dimensionality")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer dims
    dims = zfp_field_dimensionality(field%object)
  end function zFORp_field_dimensionality

  function zFORp_field_size(field, size_arr) result(total_size) bind(c, name="zforp_field_size")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer, dimension(4), target, intent(inout) :: size_arr
    integer (kind=8) total_size
    total_size = zfp_field_size(field%object, c_loc(size_arr))
  end function zFORp_field_size

  function zFORp_field_stride(field, stride_arr) result(is_strided) bind(c, name="zforp_field_stride")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer, dimension(4), target, intent(inout) :: stride_arr
    integer is_strided
    is_strided = zfp_field_stride(field%object, c_loc(stride_arr))
  end function zFORp_field_stride

  function zFORp_field_metadata(field) result(encoded_metadata) bind(c, name="zforp_field_metadata")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer (kind=8) encoded_metadata
    encoded_metadata = zfp_field_metadata(field%object)
  end function zFORp_field_metadata

  subroutine zFORp_field_set_pointer(field, arr_ptr) bind(c, name="zforp_field_set_pointer")
    type(zFORp_field), intent(in) :: field
    type(c_ptr), intent(in) :: arr_ptr
    call zfp_field_set_pointer(field%object, arr_ptr)
  end subroutine zFORp_field_set_pointer

  function zFORp_field_set_type(field, scalar_type) result(scalar_type_result) bind(c, name="zforp_field_set_type")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: scalar_type
    integer scalar_type_result
    scalar_type_result = zfp_field_set_type(field%object, int(scalar_type, c_int))
  end function zFORp_field_set_type

  subroutine zFORp_field_set_size_1d(field, nx) bind(c, name="zforp_field_set_size_1d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: nx
    call zfp_field_set_size_1d(field%object, int(nx, c_int))
  end subroutine zFORp_field_set_size_1d

  subroutine zFORp_field_set_size_2d(field, nx, ny) bind(c, name="zforp_field_set_size_2d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: nx, ny
    call zfp_field_set_size_2d(field%object, int(nx, c_int), int(ny, c_int))
  end subroutine zFORp_field_set_size_2d

  subroutine zFORp_field_set_size_3d(field, nx, ny, nz) bind(c, name="zforp_field_set_size_3d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: nx, ny, nz
    call zfp_field_set_size_3d(field%object, int(nx, c_int), int(ny, c_int), int(nz, c_int))
  end subroutine zFORp_field_set_size_3d

  subroutine zFORp_field_set_size_4d(field, nx, ny, nz, nw) bind(c, name="zforp_field_set_size_4d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: nx, ny, nz, nw
    call zfp_field_set_size_4d(field%object, int(nx, c_int), int(ny, c_int), int(nz, c_int), int(nw, c_int))
  end subroutine zFORp_field_set_size_4d

  subroutine zFORp_field_set_stride_1d(field, sx) bind(c, name="zforp_field_set_stride_1d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: sx
    call zfp_field_set_stride_1d(field%object, int(sx, c_int))
  end subroutine zFORp_field_set_stride_1d

  subroutine zFORp_field_set_stride_2d(field, sx, sy) bind(c, name="zforp_field_set_stride_2d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: sx, sy
    call zfp_field_set_stride_2d(field%object, int(sx, c_int), int(sy, c_int))
  end subroutine zFORp_field_set_stride_2d

  subroutine zFORp_field_set_stride_3d(field, sx, sy, sz) bind(c, name="zforp_field_set_stride_3d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: sx, sy, sz
    call zfp_field_set_stride_3d(field%object, int(sx, c_int), int(sy, c_int), int(sz, c_int))
  end subroutine zFORp_field_set_stride_3d

  subroutine zFORp_field_set_stride_4d(field, sx, sy, sz, sw) bind(c, name="zforp_field_set_stride_4d")
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: sx, sy, sz, sw
    call zfp_field_set_stride_4d(field%object, int(sx, c_int), int(sy, c_int), int(sz, c_int), int(sw, c_int))
  end subroutine zFORp_field_set_stride_4d

  function zFORp_field_set_metadata(field, encoded_metadata) result(is_success) bind(c, name="zforp_field_set_metadata")
    implicit none
    type(zFORp_field), intent(in) :: field
    integer (kind=8), intent(in) :: encoded_metadata
    integer is_success
    is_success = zfp_field_set_metadata(field%object, int(encoded_metadata, c_int64_t))
  end function zFORp_field_set_metadata

  ! high-level API: compression and decompression

  function zFORp_compress(stream, field) result(bitstream_offset_bytes) bind(c, name="zforp_compress")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_field), intent(in) :: field
    integer (kind=8) bitstream_offset_bytes
    bitstream_offset_bytes = zfp_compress(stream%object, field%object)
  end function zFORp_compress

  function zFORp_decompress(stream, field) result(bitstream_offset_bytes) bind(c, name="zforp_decompress")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_field), intent(in) :: field
    integer (kind=8) bitstream_offset_bytes
    bitstream_offset_bytes = zfp_decompress(stream%object, field%object)
  end function zFORp_decompress

  function zFORp_write_header(stream, field, mask) result(num_bits_written) bind(c, name="zforp_write_header")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: mask
    integer (kind=8) num_bits_written
    num_bits_written = zfp_write_header(stream%object, field%object, int(mask, c_int))
  end function zFORp_write_header

  function zFORp_read_header(stream, field, mask) result(num_bits_read) bind(c, name="zforp_read_header")
    implicit none
    type(zFORp_stream), intent(in) :: stream
    type(zFORp_field), intent(in) :: field
    integer, intent(in) :: mask
    integer (kind=8) num_bits_read
    num_bits_read = zfp_read_header(stream%object, field%object, int(mask, c_int))
  end function zFORp_read_header

  ! low-level API: stream manipulation

  subroutine zFORp_stream_rewind(stream) bind(c, name="zforp_stream_rewind")
    type(zFORp_stream), intent(in) :: stream
    call zfp_stream_rewind(stream%object)
  end subroutine zFORp_stream_rewind

end module zFORp_module