Program Listing for File mf6xmi.f90

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

module mf6xmi
  use mf6bmi
  use mf6bmiutil
  use mf6coremodule
  use kindmodule
  use bmif, only: bmi_success, bmi_failure
  use iso_c_binding, only: c_int, c_char
  implicit none


  integer(I4B), pointer :: iterationcounter => null()

  contains

  function xmi_prepare_time_step(dt) result(bmi_status) bind(C, name="prepare_time_step")
  !DEC$ ATTRIBUTES DLLEXPORT :: xmi_prepare_time_step
    double precision, intent(in) :: dt
    integer(kind=c_int) :: bmi_status

    call mf6preparetimestep()
    bmi_status = bmi_success

  end function xmi_prepare_time_step

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

    call mf6dotimestep()
    bmi_status = bmi_success

  end function xmi_do_time_step

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

    hasconverged = mf6finalizetimestep()
    if (hasconverged) then
      bmi_status = bmi_success
    else
      bmi_status = bmi_failure
    end if

  end function xmi_finalize_time_step

  function xmi_get_subcomponent_count(count) result(bmi_status) bind(C, name="get_subcomponent_count")
  !DEC$ ATTRIBUTES DLLEXPORT :: xmi_get_subcomponent_count
    use listsmodule, only: solutiongrouplist
    use simvariablesmodule, only: istdout
    integer(kind=c_int), intent(out) :: count
    integer(kind=c_int) :: bmi_status
    ! local
    class(solutiongrouptype), pointer :: sgp

    ! the following is true for all calls at this level (subcomponent)
    if (solutiongrouplist%Count() /= 1) then
      write(istdout,*) 'Error: BMI does not support the use of multiple solution groups'
      count = -1
      bmi_status = bmi_failure
      return
    end if

    sgp => getsolutiongroupfromlist(solutiongrouplist, 1)
    count = sgp%nsolutions
    bmi_status = bmi_success

  end function xmi_get_subcomponent_count

  function xmi_prepare_solve(subcomponent_idx) result(bmi_status) bind(C, name="prepare_solve")
  !DEC$ ATTRIBUTES DLLEXPORT :: xmi_prepare_solve
    use listsmodule, only: solutiongrouplist
    use numericalsolutionmodule
    use simvariablesmodule, only: istdout
    integer(kind=c_int) :: subcomponent_idx
    integer(kind=c_int) :: bmi_status
    ! local
    class(numericalsolutiontype), pointer :: ns

     ! people might not call 'xmi_get_subcomponent_count' first, so let's repeat this:
    if (solutiongrouplist%Count() /= 1) then
      write(istdout,*) 'Error: BMI does not support the use of multiple solution groups'
      bmi_status = bmi_failure
      return
    end if

    ! get the numerical solution we are running
    ns => getsolution(subcomponent_idx)

    ! *_ad (model, exg, sln)
    call ns%prepareSolve()

    ! reset counter
    allocate(iterationcounter)
    iterationcounter = 0

    bmi_status = bmi_success

  end function xmi_prepare_solve

  function xmi_solve(subcomponent_idx, has_converged) result(bmi_status) bind(C, name="solve")
  !DEC$ ATTRIBUTES DLLEXPORT :: xmi_solve
    use numericalsolutionmodule
    integer(kind=c_int), intent(in) :: subcomponent_idx
    integer(kind=c_int), intent(out) :: has_converged
    integer(kind=c_int) :: bmi_status
    ! local
    class(numericalsolutiontype), pointer :: ns

    ! get the numerical solution we are running
    ns => getsolution(subcomponent_idx)

    ! execute the nth iteration
    iterationcounter = iterationcounter + 1
    call ns%solve(iterationcounter)

    ! the following check is equivalent to that in NumericalSolution%sln_ca
    if (ns%icnvg == 1) then
      has_converged = 1
    else
      has_converged = 0
    end if

    bmi_status = bmi_success

  end function xmi_solve

  function xmi_finalize_solve(subcomponent_idx) result(bmi_status) bind(C, name="finalize_solve")
  !DEC$ ATTRIBUTES DLLEXPORT :: xmi_finalize_solve
    use numericalsolutionmodule
    integer(kind=c_int), intent(in) :: subcomponent_idx
        integer(kind=c_int) :: bmi_status
    ! local
    class(numericalsolutiontype), pointer :: ns
    integer(I4B) :: hasconverged

    ! get the numerical solution we are running
    ns => getsolution(subcomponent_idx)

    ! hasConverged is equivalent to the isgcnvg variable which is initialized to 1,
    ! see the body of the picard loop in SolutionGroupType%sgp_ca
    hasconverged = 1

    ! finish up
    call ns%finalizeSolve(iterationcounter, hasconverged, 0)

    ! check convergence on solution
    if (hasconverged == 1) then
      bmi_status = bmi_success
    else
      bmi_status = bmi_failure
    end if

    ! clear this for safety
    deallocate(iterationcounter)

  end function xmi_finalize_solve

  function get_var_address(c_component_name, c_subcomponent_name, &
                          c_var_name, c_var_address) &
                          result(bmi_status) bind(c, name="get_var_address")
    !DEC$ ATTRIBUTES DLLEXPORT :: get_var_address
    use memoryhelpermodule, only: create_mem_path, create_mem_address
    use constantsmodule, only: lencomponentname, lenvarname, lenmempath, lenmemaddress
    character(kind=c_char), intent(in) :: c_component_name(*)
    character(kind=c_char), intent(in) :: c_subcomponent_name(*)
    character(kind=c_char), intent(in) :: c_var_name(*)
    character(kind=c_char), intent(out) :: c_var_address(bmi_lenvaraddress)
    integer(kind=c_int) :: bmi_status

    ! local
    character(len=LENCOMPONENTNAME) :: component_name
    character(len=LENCOMPONENTNAME) :: subcomponent_name
    character(len=LENVARNAME) :: variable_name
    character(len=LENMEMPATH) :: mem_path
    character(len=LENMEMADDRESS) :: mem_address

    ! convert to Fortran strings
    component_name = char_array_to_string(c_component_name, strlen(c_component_name))
    subcomponent_name = char_array_to_string(c_subcomponent_name, strlen(c_subcomponent_name))
    variable_name = char_array_to_string(c_var_name, strlen(c_var_name))

    ! create memory address
    if (subcomponent_name == '') then
      mem_path = create_mem_path(component_name)
    else
      mem_path = create_mem_path(component_name, subcomponent_name)
    end if
    mem_address = create_mem_address(mem_path, variable_name)

    ! convert to c string:
    c_var_address(1:len(trim(mem_address))+1) = string_to_char_array(trim(mem_address), len(trim(mem_address)))

    bmi_status = bmi_success

  end function get_var_address

end module mf6xmi