Program Listing for File mf6bmi.f90

Return to documentation for file (srcbmi/mf6bmi.f90)

module mf6bmi
  use mf6bmiutil
  use bmif, only: bmi_failure, bmi_success
  use mf6coremodule
  use tdismodule, only: kper, kstp
  use iso_c_binding, only: c_int, c_char, c_double, c_null_char, c_loc, c_ptr,   &
                           c_f_pointer
  use kindmodule, only: dp, i4b, lgp
  use constantsmodule, only: lenmempath, lenvarname, memreadwrite, memreadonly
  use memorymanagermodule, only: mem_setptr, get_mem_elem_size, get_isize,       &
                                 get_mem_rank, get_mem_shape, get_mem_type,      &
                                 memorylist, get_from_memorylist
  use memorytypemodule, only: memorytype
  use memoryhelpermodule, only: create_mem_address
  use simvariablesmodule, only: simstdout, istdout
  use inputoutputmodule, only: getunit
  implicit none

  integer(c_int), bind(c, name="ISTDOUTTOFILE") :: istdout_to_file = 1
  !DEC$ ATTRIBUTES DLLEXPORT :: istdout_to_file

  contains

  function bmi_initialize() result(bmi_status) bind(C, name="initialize")
  !DEC$ ATTRIBUTES DLLEXPORT :: bmi_initialize
    integer(kind=c_int) :: bmi_status
    ! local

    if (istdout_to_file > 0) then
      ! -- open stdout file mfsim.stdout
      istdout = getunit()
      !
      ! -- set STDOUT to a physical file unit
      open(unit=istdout, file=simstdout)
    end if
    !
    ! -- initialize MODFLOW 6
    call mf6initialize()

    bmi_status = bmi_success

  end function bmi_initialize


  function bmi_update() result(bmi_status) bind(C, name="update")
  !DEC$ ATTRIBUTES DLLEXPORT :: bmi_update
    integer(kind=c_int) :: bmi_status
    ! local
    logical :: hasconverged

    hasconverged = mf6update()

    bmi_status = bmi_success
  end function bmi_update


  function bmi_finalize() result(bmi_status) bind(C, name="finalize")
  !DEC$ ATTRIBUTES DLLEXPORT :: bmi_finalize
    use simvariablesmodule, only: iforcestop
    integer(kind=c_int) :: bmi_status

    ! we don't want a full stop() here, this disables it:
    iforcestop = 0
    call mf6finalize()

    bmi_status = bmi_success

  end function bmi_finalize


  function get_start_time(start_time) result(bmi_status) bind(C, name="get_start_time")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_start_time
    double precision, intent(out) :: start_time
    integer(kind=c_int) :: bmi_status

    start_time = 0.0_dp
    bmi_status = bmi_success

  end function get_start_time


  function get_end_time(end_time) result(bmi_status) bind(C, name="get_end_time")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_end_time
    use tdismodule, only: totalsimtime
    double precision, intent(out) :: end_time
    integer(kind=c_int) :: bmi_status

    end_time = totalsimtime
    bmi_status = bmi_success

  end function get_end_time

  function get_current_time(current_time) result(bmi_status) bind(C, name="get_current_time")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_current_time
    use tdismodule, only: totim
    double precision, intent(out) :: current_time
    integer(kind=c_int) :: bmi_status

    current_time = totim
    bmi_status = bmi_success

  end function get_current_time


  function get_time_step(time_step) result(bmi_status) bind(C, name="get_time_step")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_time_step
    use tdismodule, only: delt
    double precision, intent(out) :: time_step
    integer(kind=c_int) :: bmi_status

    time_step = delt
    bmi_status = bmi_success

  end function get_time_step

  function get_input_item_count(count) result(bmi_status) bind(C, name="get_input_item_count")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_input_item_count
    integer(kind=c_int), intent(out) :: count
    integer(kind=c_int) :: bmi_status
    ! local
    integer(I4B) :: ipos
    type(memorytype), pointer :: mt => null()

    count = 0
    do ipos = 1, memorylist%count()
      mt => memorylist%Get(ipos)
      if (mt%memaccess == memreadwrite) then
        count = count + 1
      end if
    end do

    bmi_status = bmi_success

  end function get_input_item_count

  function get_output_item_count(count) result(bmi_status) bind(C, name="get_output_item_count")
    !DEC$ ATTRIBUTES DLLEXPORT :: get_output_item_count
      integer(kind=c_int), intent(out) :: count
      integer(kind=c_int) :: bmi_status
      ! local
      integer(I4B) :: ipos
      type(memorytype), pointer :: mt => null()

      count = 0
      do ipos = 1, memorylist%count()
        mt => memorylist%Get(ipos)
        if (mt%memaccess == memreadonly .or. mt%memaccess == memreadwrite) then
          count = count + 1
        end if
      end do

      bmi_status = bmi_success

    end function get_output_item_count

  function get_input_var_names(c_names) result(bmi_status) bind(C, name="get_input_var_names")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_input_var_names
    character(kind=c_char,len=1), intent(inout) :: c_names(*)
    integer(kind=c_int) :: bmi_status
    ! local
    integer(I4B) :: imem, start, i
    type(memorytype), pointer :: mt => null()
    character(len=LENMEMADDRESS) :: var_address

    start = 1
    do imem = 1, memorylist%count()
      mt => memorylist%Get(imem)
      if (mt%memaccess == memreadwrite) then
        var_address = create_mem_address(mt%path, mt%name)
        do i = 1, len(trim(var_address))
          c_names(start + i - 1) = var_address(i:i)
        end do
        c_names(start + i) = c_null_char
        start = start + bmi_lenvaraddress
      end if
    end do

    bmi_status = bmi_success

  end function get_input_var_names

  function get_output_var_names(c_names) result(bmi_status) bind(C, name="get_output_var_names")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_output_var_names
    character(kind=c_char,len=1), intent(inout) :: c_names(*)
    integer(kind=c_int) :: bmi_status
    ! local
    integer(I4B) :: imem, start, i
    type(memorytype), pointer :: mt => null()
    character(len=LENMEMADDRESS) :: var_address

    start = 1
    do imem = 1, memorylist%count()
      mt => memorylist%Get(imem)
      if (mt%memaccess == memreadonly .or. mt%memaccess == memreadwrite) then
        var_address = create_mem_address(mt%path, mt%name)
        do i = 1, len(trim(var_address))
          c_names(start + i - 1) = var_address(i:i)
        end do
        c_names(start + i) = c_null_char
        start = start + bmi_lenvaraddress
      end if
    end do

    bmi_status = bmi_success

  end function get_output_var_names


  function get_var_itemsize(c_var_address, var_size) result(bmi_status) bind(C, name="get_var_itemsize")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_var_itemsize
    character (kind=c_char), intent(in) :: c_var_address(*)
    integer, intent(out) :: var_size
    integer(kind=c_int) :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name_only

    call split_address(c_var_address, mem_path, var_name_only)

    bmi_status = bmi_success
    call get_mem_elem_size(var_name_only, mem_path, var_size)
    if (var_size == -1) bmi_status = bmi_failure

  end function get_var_itemsize

  function get_var_nbytes(c_var_address, var_nbytes) result(bmi_status) bind(C, name="get_var_nbytes")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_var_nbytes
    character (kind=c_char), intent(in) :: c_var_address(*)
    integer, intent(out) :: var_nbytes
    integer(kind=c_int) :: bmi_status
    ! local
    integer(I4B) :: var_size, isize
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name

    call split_address(c_var_address, mem_path, var_name)

    bmi_status = bmi_success
    call get_mem_elem_size(var_name, mem_path, var_size)
    if (var_size == -1) bmi_status = bmi_failure
    call get_isize(var_name, mem_path, isize)
    if (isize == -1) bmi_status = bmi_failure

    var_nbytes = var_size*isize

  end function get_var_nbytes

  function get_value_double(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="get_value_double")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_value_double
    use memorysethandlermodule, only: on_memory_set
    character (kind=c_char), intent(in) :: c_var_address(*)
    type(c_ptr), intent(in) :: c_arr_ptr
    integer :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    integer(I4B) :: access_type
    logical(LGP) :: found
    integer(I4B) :: rank
    real(DP), pointer :: src_ptr, tgt_ptr
    real(DP), dimension(:), pointer, contiguous :: src1d_ptr, tgt1d_ptr
    real(DP), dimension(:,:), pointer, contiguous :: src2d_ptr, tgt2d_ptr
    integer(I4B) :: i, j

    bmi_status = bmi_failure

    call split_address(c_var_address, mem_path, var_name)

    ! check access
    access_type = get_memory_access_type(mem_path, var_name, found)
    if (.not. found) then
      write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
      return
    end if

    if (access_type /= memreadwrite .and. access_type /= memreadonly) then
      write(istdout,*) 'BMI Error: no read access for variable '//var_name
      return
    end if

    ! convert pointer and copy data from memory manager into
    ! the passed array, using loops to avoid stack overflow
    rank = -1
    call get_mem_rank(var_name, mem_path, rank)

    if (rank == 0) then
      call mem_setptr(src_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt_ptr)
      tgt_ptr = src_ptr
    else if (rank == 1) then
      call mem_setptr(src1d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt1d_ptr, shape(src1d_ptr))
      do i = 1, size(tgt1d_ptr)
        tgt1d_ptr(i) = src1d_ptr(i)
      end do
    else if (rank == 2) then
      call mem_setptr(src2d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt2d_ptr, shape(src2d_ptr))
      do j = 1, size(tgt2d_ptr,2)
        do i = 1, size(tgt2d_ptr,1)
          tgt2d_ptr(i,j) = src2d_ptr(i,j)
        end do
      end do
    else
      write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
      return
    end if

    bmi_status = bmi_success

  end function get_value_double

  function get_value_int(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="get_value_int")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_value_int
    use memorysethandlermodule, only: on_memory_set
    character (kind=c_char), intent(in) :: c_var_address(*)
    type(c_ptr), intent(in) :: c_arr_ptr
    integer :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    integer(I4B) :: access_type
    logical(LGP) :: found
    integer(I4B) :: rank
    integer(I4B), pointer :: src_ptr, tgt_ptr
    integer(I4B), dimension(:), pointer, contiguous :: src1d_ptr, tgt1d_ptr
    integer(I4B), dimension(:,:), pointer, contiguous :: src2d_ptr, tgt2d_ptr
    integer(I4B) :: i, j

    bmi_status = bmi_failure

    call split_address(c_var_address, mem_path, var_name)

    ! check access
    access_type = get_memory_access_type(mem_path, var_name, found)
    if (.not. found) then
      write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
      return
    end if

    if (access_type /= memreadwrite .and. access_type /= memreadonly) then
      write(istdout,*) 'BMI Error: no read access for variable '//var_name
      return
    end if

    ! convert pointer and copy data from memory manager into
    ! the passed array, using loops to avoid stack overflow
    rank = -1
    call get_mem_rank(var_name, mem_path, rank)

    if (rank == 0) then
      call mem_setptr(src_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt_ptr)
      tgt_ptr = src_ptr
    else if (rank == 1) then
      call mem_setptr(src1d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt1d_ptr, shape(src1d_ptr))
      do i = 1, size(tgt1d_ptr)
        tgt1d_ptr(i) = src1d_ptr(i)
      end do
    else if (rank == 2) then
      call mem_setptr(src2d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, tgt2d_ptr, shape(src2d_ptr))
      do j = 1, size(tgt2d_ptr,2)
        do i = 1, size(tgt2d_ptr,1)
          tgt2d_ptr(i,j) = src2d_ptr(i,j)
        end do
      end do
    else
      write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
      return
    end if

    bmi_status = bmi_success

  end function get_value_int

  function get_value_ptr_double(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="get_value_ptr_double")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_value_ptr_double
    character (kind=c_char), intent(in) :: c_var_address(*)
    type(c_ptr), intent(inout) :: c_arr_ptr
    integer(kind=c_int) :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    integer(I4B) :: access_type
    logical(LGP) :: found
    real(DP), pointer :: scalar_ptr
    real(DP), dimension(:), pointer, contiguous :: array_ptr
    real(DP), dimension(:,:), pointer, contiguous :: array2d_ptr
    integer(I4B) :: rank

    bmi_status = bmi_failure

    call split_address(c_var_address, mem_path, var_name)

    ! check access
    access_type = get_memory_access_type(mem_path, var_name, found)
    if (.not. found) then
      write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
      return
    end if

    if (access_type /= memreadwrite .and. access_type /= memreadonly) then
      write(istdout,*) 'BMI Error: no read access for variable '//var_name
      return
    end if

    rank = -1
    call get_mem_rank(var_name, mem_path, rank)
    if (rank == 0) then
      call mem_setptr(scalar_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(scalar_ptr)
    else if (rank == 1) then
      call mem_setptr(array_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(array_ptr)
    else if (rank == 2) then
      call mem_setptr(array2d_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(array2d_ptr)
    else
      write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
      return
    end if

    bmi_status = bmi_success

  end function get_value_ptr_double


  function get_value_ptr_int(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="get_value_ptr_int")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_value_ptr_int
    character (kind=c_char), intent(in) :: c_var_address(*)
    type(c_ptr), intent(inout) :: c_arr_ptr
    integer(kind=c_int) :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    integer(I4B) :: access_type
    logical(LGP) :: found
    integer(I4B) :: rank
    integer(I4B), pointer :: scalar_ptr
    integer(I4B), dimension(:), pointer, contiguous :: array_ptr
    integer(I4B), dimension(:,:), pointer, contiguous :: array2d_ptr

    bmi_status = bmi_failure

    call split_address(c_var_address, mem_path, var_name)

    ! check access
    access_type = get_memory_access_type(mem_path, var_name, found)
    if (.not. found) then
      write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
      return
    end if

    if (access_type /= memreadwrite .and. access_type /= memreadonly) then
      write(istdout,*) 'BMI Error: no read access for variable '//var_name
      return
    end if

    rank = -1
    call get_mem_rank(var_name, mem_path, rank)

    if (rank == 0) then
      call mem_setptr(scalar_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(scalar_ptr)
    else if (rank == 1) then
      call mem_setptr(array_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(array_ptr)
    else if (rank == 2) then
      call mem_setptr(array_ptr, var_name, mem_path)
      c_arr_ptr = c_loc(array2d_ptr)
    else
      write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
      return
    end if

    bmi_status = bmi_success

  end function get_value_ptr_int

  function set_value_double(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="set_value_double")
  !DEC$ ATTRIBUTES DLLEXPORT :: set_value_double
    use memorysethandlermodule, only: on_memory_set
    character (kind=c_char), intent(in) :: c_var_address(*)
    type(c_ptr), intent(in) :: c_arr_ptr
    integer :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    integer(I4B) :: access_type
    logical(LGP) :: found
    integer(I4B) :: rank
    real(DP), pointer :: src_ptr, tgt_ptr
    real(DP), dimension(:), pointer, contiguous :: src1d_ptr, tgt1d_ptr
    real(DP), dimension(:,:), pointer, contiguous :: src2d_ptr, tgt2d_ptr
    integer(I4B) :: i, j
    integer(I4B) :: status

    bmi_status = bmi_failure

    call split_address(c_var_address, mem_path, var_name)

    ! check access
    access_type = get_memory_access_type(mem_path, var_name, found)
    if (.not. found) then
      write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
      return
    end if

    if (access_type /= memreadwrite) then
      write(istdout,*) 'BMI Error: cannot write to variable '//var_name
      return
    end if

    ! convert pointer and copy, using loops to avoid stack overflow
    rank = -1
    call get_mem_rank(var_name, mem_path, rank)

    if (rank == 0) then
      call mem_setptr(tgt_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, src_ptr)
      tgt_ptr = src_ptr
    else if (rank == 1) then
      call mem_setptr(tgt1d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, src1d_ptr, shape(tgt1d_ptr))
      do i = 1, size(tgt1d_ptr)
        tgt1d_ptr(i) = src1d_ptr(i)
      end do
    else if (rank == 2) then
      call mem_setptr(tgt2d_ptr, var_name, mem_path)
      call c_f_pointer(c_arr_ptr, src2d_ptr, shape(tgt2d_ptr))
      do j = 1, size(tgt2d_ptr,2)
        do i = 1, size(tgt2d_ptr,1)
          tgt2d_ptr(i,j) = src2d_ptr(i,j)
        end do
      end do
    else
      write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
      return
    end if

    ! trigger event:
    call on_memory_set(var_name, mem_path, status)
    if (status /= 0) then
      ! something went terribly wrong here, aborting
      write(istdout,*) 'Fatal BMI Error: invalid writing of memory for variable '//var_name
      return
    end if

    bmi_status = bmi_success

  end function set_value_double

  function set_value_int(c_var_address, c_arr_ptr) result(bmi_status) bind(C, name="set_value_int")
    !DEC$ ATTRIBUTES DLLEXPORT :: set_value_int
      use memorysethandlermodule, only: on_memory_set
      character (kind=c_char), intent(in) :: c_var_address(*)
      type(c_ptr), intent(in) :: c_arr_ptr
      integer :: bmi_status
      ! local
      character(len=LENMEMPATH) :: mem_path
      character(len=LENVARNAME) :: var_name
      integer(I4B) :: access_type
      logical(LGP) :: found
      integer(I4B) :: rank
      integer(I4B), pointer :: src_ptr, tgt_ptr
      integer(I4B), dimension(:), pointer, contiguous :: src1d_ptr, tgt1d_ptr
      integer(I4B), dimension(:,:), pointer, contiguous :: src2d_ptr, tgt2d_ptr
      integer(I4B) :: i, j
      integer(I4B) :: status

      bmi_status = bmi_failure

      call split_address(c_var_address, mem_path, var_name)

      ! check access
      access_type = get_memory_access_type(mem_path, var_name, found)
      if (.not. found) then
        write(istdout,*) 'BMI Error: unknown variable '//var_name//' at '//mem_path
        return
      end if

      if (access_type /= memreadwrite) then
        write(istdout,*) 'BMI Error: cannot write to variable '//var_name
        return
      end if

      ! convert pointer and copy, using loops to avoid stack overflow
      rank = -1
      call get_mem_rank(var_name, mem_path, rank)

      if (rank == 0) then
        call mem_setptr(tgt_ptr, var_name, mem_path)
        call c_f_pointer(c_arr_ptr, src_ptr)
        tgt_ptr = src_ptr
      else if (rank == 1) then
        call mem_setptr(tgt1d_ptr, var_name, mem_path)
        call c_f_pointer(c_arr_ptr, src1d_ptr, shape(tgt1d_ptr))
        do i = 1, size(tgt1d_ptr)
          tgt1d_ptr(i) = src1d_ptr(i)
        end do
      else if (rank == 2) then
        call mem_setptr(tgt2d_ptr, var_name, mem_path)
        call c_f_pointer(c_arr_ptr, src2d_ptr, shape(tgt2d_ptr))
        do j = 1, size(tgt2d_ptr,2)
          do i = 1, size(tgt2d_ptr,1)
            tgt2d_ptr(i,j) = src2d_ptr(i,j)
          end do
        end do
      else
        write(istdout,*) 'BMI Error: unsupported rank for variable '//var_name
        return
      end if

      ! trigger event:
      call on_memory_set(var_name, mem_path, status)
      if (status /= 0) then
        ! something went terribly wrong here, aborting
        write(istdout,*) 'Fatal BMI Error: invalid writing of memory for variable '//var_name
        return
      end if

      bmi_status = bmi_success

    end function set_value_int

  function get_var_type(c_var_address, c_var_type) result(bmi_status) bind(C, name="get_var_type")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_var_type
    use constantsmodule, only: lenmemtype
    character (kind=c_char), intent(in) :: c_var_address(*)
    character (kind=c_char), intent(out) :: c_var_type(bmi_lenvartype)
    integer(kind=c_int) :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name
    character(len=LENMEMTYPE) :: mem_type

    call split_address(c_var_address, mem_path, var_name)

    bmi_status = bmi_success
    call get_mem_type(var_name, mem_path, mem_type)
    c_var_type(1:len(trim(mem_type))+1) = string_to_char_array(trim(mem_type), len(trim(mem_type)))

    if (mem_type == 'UNKNOWN') then
      bmi_status = bmi_failure
    end if

  end function get_var_type

  function get_var_rank(c_var_address, c_var_rank) result(bmi_status) bind(C, name="get_var_rank")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_var_rank
    character (kind=c_char), intent(in) :: c_var_address(*)
    integer(kind=c_int), intent(out) :: c_var_rank
    integer(kind=c_int) :: bmi_status
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name

    call split_address(c_var_address, mem_path, var_name)

    call get_mem_rank(var_name, mem_path, c_var_rank)
    if (c_var_rank == -1) then
        bmi_status = bmi_failure
        return
    end if

    bmi_status = bmi_success

  end function get_var_rank


  function get_var_shape(c_var_address, c_var_shape) result(bmi_status) bind(C, name="get_var_shape")
  !DEC$ ATTRIBUTES DLLEXPORT :: get_var_shape
    use constantsmodule, only: maxmemrank
    character (kind=c_char), intent(in) :: c_var_address(*)
    integer(c_int), intent(inout) :: c_var_shape(*)
    integer(kind=c_int) :: bmi_status
    ! local
    integer(I4B), dimension(MAXMEMRANK) :: var_shape
    integer(I4B) :: var_rank
    character(len=LENMEMPATH) :: mem_path
    character(len=LENVARNAME) :: var_name

    call split_address(c_var_address, mem_path, var_name)

    var_shape = 0
    var_rank = 0
    call get_mem_rank(var_name, mem_path, var_rank)
    call get_mem_shape(var_name, mem_path, var_shape)
    if (var_shape(1) == -1 .or. var_rank == -1) then
      bmi_status = bmi_failure
      return
    end if

    ! External calls to this BMI are assumed C style, so if the internal shape
    ! is (100,1) we get (100,1,undef) from the call get_mem_shape
    ! This we need to convert to C-style which should be (1,100).
    ! Hence, we reverse the array and drop undef:
    c_var_shape(1:var_rank) = var_shape(var_rank:1:-1)

    bmi_status = bmi_success

  end function get_var_shape

end module mf6bmi