Program Listing for File mf6bmiUtil.f90

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

module mf6bmiutil
  use iso_c_binding, only: c_int, c_char, c_null_char
  use constantsmodule, only: maxcharlen, lenmempath, lenvarname, &
                             lenmodelname, linelength, lenmemtype, &
                             lenmemaddress, lencomponentname
  use kindmodule, only: dp, i4b, lgp
  use genericutilitiesmodule, only: sim_message
  use simvariablesmodule, only: istdout
  use memorymanagermodule, only: get_from_memorylist
  use memoryhelpermodule, only: split_mem_address, split_mem_path
  use memorytypemodule, only: memorytype
  implicit none

  ! the following exported parameters will trigger annoying warnings with
  ! the Intel Fortran compiler (4049,4217). We know that these can be ignored:
  !
  ! https://community.intel.com/t5/Intel-Fortran-Compiler/suppress-linker-warnings/td-p/855137
  ! https://community.intel.com/t5/Intel-Fortran-Compiler/Locally-Defined-Symbol-Imported/m-p/900805
  !
  ! and gfortran does so anyway. They have been disabled in the linker config.

  integer(I4B), parameter :: lengridtype = 16

  integer(c_int), bind(c, name="BMI_LENVARTYPE") :: bmi_lenvartype = lenmemtype + 1
  !DEC$ ATTRIBUTES DLLEXPORT :: BMI_LENVARTYPE

  integer(c_int), bind(c, name="BMI_LENGRIDTYPE") :: bmi_lengridtype = lengridtype + 1
  !DEC$ ATTRIBUTES DLLEXPORT :: BMI_LENGRIDTYPE

  integer(c_int), bind(c, name="BMI_LENVARADDRESS") :: bmi_lenvaraddress = lenmemaddress + 1
  !DEC$ ATTRIBUTES DLLEXPORT :: BMI_LENVARADDRESS

contains

  subroutine split_address(c_var_address, mem_path, var_name)
    use memoryhelpermodule, only: mempathseparator
    character (kind=c_char), intent(in) :: c_var_address(*)
    character(len=LENMEMPATH), intent(out) :: mem_path
    character(len=LENVARNAME), intent(out) :: var_name
    ! local
    character(len=LENMEMPATH) :: var_address

    ! convert to fortran string
    var_address = char_array_to_string(c_var_address, strlen(c_var_address))

    call split_mem_address(var_address, mem_path, var_name)

  end subroutine split_address

  pure function strlen(char_array) result(string_length)
    character(c_char), intent(in) :: char_array(lenmempath)
    integer(I4B)                  :: string_length
    integer(I4B) :: i

    string_length = 0
    do i = 1, size(char_array)
      if (char_array(i) .eq. c_null_char) then
        string_length = i-1
          exit
      end if
    end do

  end function strlen

  pure function char_array_to_string(char_array, length) result(f_string)
    integer(c_int), intent(in) :: length
    character(c_char),intent(in) :: char_array(length)
    character(len=length) :: f_string
    integer(I4B) :: i

    do i = 1, length
      f_string(i:i) = char_array(i)
    enddo

  end function char_array_to_string

  pure function string_to_char_array(string, length) result(c_array)
  integer(c_int),intent(in) :: length
  character(len=length), intent(in) :: string
  character(kind=c_char,len=1) :: c_array(length+1)
  integer(I4B) :: i

  do i = 1, length
    c_array(i) = string(i:i)
  enddo
  c_array(length+1) = c_null_char

  end function string_to_char_array

  function extract_model_name(var_address) result(model_name)
    character(len=*), intent(in) :: var_address
    character(len=LENMODELNAME) :: model_name
    ! local
    character(len=LENMEMPATH) :: mem_path
    character(len=LENCOMPONENTNAME) :: dummy_component
    character(len=LENVARNAME) :: var_name

    call split_mem_address(var_address, mem_path, var_name)
    call split_mem_path(mem_path, model_name, dummy_component)

  end function extract_model_name

  function get_model_name(grid_id) result(model_name)
    use listsmodule, only: basemodellist
    use basemodelmodule, only: basemodeltype, getbasemodelfromlist
    integer(kind=c_int), intent(in) :: grid_id
    character(len=LENMODELNAME) :: model_name
    ! local
    integer(I4B) :: i
    class(basemodeltype), pointer :: basemodel
    character(len=LINELENGTH) :: error_msg

    model_name = ''

    do i = 1,basemodellist%Count()
      basemodel => getbasemodelfromlist(basemodellist, i)
      if (basemodel%id == grid_id) then
        model_name = basemodel%name
        return
      end if
    end do

    write(error_msg,'(a,i0)') 'BMI error: no model for grid id ', grid_id
    call sim_message(error_msg, iunit=istdout, skipbefore=1, skipafter=1)
  end function get_model_name

  function getsolution(subcomponent_idx) result(solution)
    use solutiongroupmodule
    use numericalsolutionmodule
    use listsmodule, only: basesolutionlist, solutiongrouplist
    integer(I4B), intent(in) :: subcomponent_idx
    class(numericalsolutiontype), pointer :: solution
    ! local
    class(solutiongrouptype), pointer :: sgp
    integer(I4B) :: solutionidx

    ! this is equivalent to how it's done in sgp_ca
    sgp => getsolutiongroupfromlist(solutiongrouplist, 1)
    solutionidx = sgp%idsolutions(subcomponent_idx)
    solution => getnumericalsolutionfromlist(basesolutionlist, solutionidx)
  end function getsolution

  subroutine get_grid_type_model(model_name, grid_type_f)
    use listsmodule, only: basemodellist
    use numericalmodelmodule, only: numericalmodeltype, getnumericalmodelfromlist
    character(len=LENMODELNAME) :: model_name
    character(len=LENGRIDTYPE) :: grid_type_f
    ! local
    integer(I4B) :: i
    class(numericalmodeltype), pointer :: numericalmodel

    do i = 1,basemodellist%Count()
      numericalmodel => getnumericalmodelfromlist(basemodellist, i)
      if (numericalmodel%name == model_name) then
        call numericalmodel%dis%get_dis_type(grid_type_f)
      end if
    end do
  end subroutine get_grid_type_model

  function confirm_grid_type(grid_id, expected_type) result(is_match)
    integer(kind=c_int), intent(in) :: grid_id
    character(len=*), intent(in) :: expected_type
    logical :: is_match
    ! local
    character(len=LENMODELNAME) :: model_name
    character(len=LENGRIDTYPE) :: grid_type

    is_match = .false.

    model_name = get_model_name(grid_id)
    call get_grid_type_model(model_name, grid_type)
    ! careful comparison:
    if (expected_type == grid_type) is_match = .true.
  end function confirm_grid_type

  function get_memory_access_type(mem_path, var_name, found) result(mem_access_type)
    character(len=*), intent(in) :: mem_path
    character(len=*), intent(in) :: var_name
    logical(LGP), intent(out) :: found
    integer(I4B) :: mem_access_type
    ! local
    type(memorytype), pointer :: mt

     ! check access
    found = .false.
    mem_access_type = -1
    mt => null()
    call get_from_memorylist(var_name, mem_path, mt, found, check=.false.)
    if (found) then
      mem_access_type = mt%memaccess
      found = .true.
    end if
  end function get_memory_access_type

end module mf6bmiUtil