MODFLOW 6  version 6.7.0.dev0
USGS Modular Hydrologic Model
mf6xmi Module Reference

This module contains the eXtended Model Interface. More...

Functions/Subroutines

integer(kind=c_int) function xmi_prepare_time_step (dt)
 Prepare a single time step. More...
 
integer(kind=c_int) function xmi_do_time_step ()
 Perform a single time step. More...
 
integer(kind=c_int) function xmi_finalize_time_step ()
 Finalize the time step. More...
 
integer(kind=c_int) function xmi_get_subcomponent_count (count)
 This will get the number of Numerical Solutions in the simulation. More...
 
integer(kind=c_int) function xmi_prepare_solve (subcomponent_idx)
 Prepare for solving the system. More...
 
integer(kind=c_int) function xmi_solve (subcomponent_idx, has_converged)
 Build and solve the linear system. More...
 
integer(kind=c_int) function xmi_finalize_solve (subcomponent_idx)
 Finalize the solve of the system. More...
 
integer(kind=c_int) function xmi_get_version (mf_version)
 Get the version string for this component. More...
 
integer(kind=c_int) function get_var_address (c_component_name, c_subcomponent_name, c_var_name, c_var_address)
 Get the full address string for a variable. More...
 

Variables

integer(i4b), pointer iterationcounter => null()
 the counter for the outer iteration loop, initialized in xmi_prepare_iteration() More...
 

Detailed Description

In this module we expose functionality in the MODFLOW 6 shared library, that is beyond the basic model interface: https://bmi-spec.readthedocs.io/en/latest/. The main extensions are

  • Controlling the kernel at a finer granularity, isolating the call to the linear solve of the system. This way, the interface can be used to set up a non-linear coupling with, for example, an external unsaturated zone model.
  • Expose the concept of subcomponents, which in case of MODFLOW 6 are 'Numerical Solution' objects, each of which represents a separate linear system to solve. An example here would be a transport model (GWT) coupled to a groundwater model (GWF).

The common BMI control flow is

while t < t_end:
update()
subroutine initialize(this, filename, name, swf_ftype, id, m1_id, m2_id, input_mempath)
@ brief Initialize SWF GWF exchange
Definition: exg-swfgwf.f90:98

With the XMI you can now also use it as:

while(t < t_end):
prepare_time_step()
# modify some values here
do_time_step()
# and maybe something here as well
finalize_time_step()

Or, when you want to isolate the call to the linear solve, a typical application could look like this:

while t < t_end:
prepare_time_step()
for i_sol in solutions:
prepare_solve(i_sol)
while k_iter < max_iter:
# exchange coupled variables
exchange_data()
# the MODFLOW linear solve:
# maybe solve some other, external model here:
solveExternalModel()
# and exchange back
exchange_data()
# check for convergence
convergence_check()
finalize_solve(i_sol)
finalize_time_step()
subroutine solve(this, thiswork, jbelow, icell, totfluxtot, ietflag, issflag, iseepflag, hgwf, qfrommvr, ierr, reset_state, trhs, thcof, deriv, watercontent)
Formulate the unsaturated flow object, calculate terms for gwf equation.

Note that the last example can only work when there is a single Solution Group defined. This will typically not be a problem, though, as applications with multiple Solution Groups should be quite rare.

Function/Subroutine Documentation

◆ get_var_address()

integer(kind=c_int) function mf6xmi::get_var_address ( character(kind=c_char), dimension(*), intent(in)  c_component_name,
character(kind=c_char), dimension(*), intent(in)  c_subcomponent_name,
character(kind=c_char), dimension(*), intent(in)  c_var_name,
character(kind=c_char), dimension(bmi_lenvaraddress), intent(out)  c_var_address 
)

This routine constructs the full address string of a variable using the exact same logic as the internal memory manager. This routine should always be used when accessing a variable through the BMI to assure compatibility with future versions of the library.

Parameters
[in]c_component_namename of the component (a Model or Solution)
[in]c_subcomponent_namename of the subcomponent (Package), or an empty string'' when not applicable
[in]c_var_namename of the variable
[out]c_var_addressfull address of the variable
Returns
BMI status code

Definition at line 365 of file mf6xmi.F90.

368  !DIR$ ATTRIBUTES DLLEXPORT :: get_var_address
369  ! -- modules
373  ! -- dummy variables
374  character(kind=c_char), intent(in) :: c_component_name(*) !< name of the component (a Model or Solution)
375  character(kind=c_char), intent(in) :: c_subcomponent_name(*) !< name of the subcomponent (Package), or an empty
376  !! string'' when not applicable
377  character(kind=c_char), intent(in) :: c_var_name(*) !< name of the variable
378  character(kind=c_char), intent(out) :: c_var_address(BMI_LENVARADDRESS) !< full address of the variable
379  integer(kind=c_int) :: bmi_status !< BMI status code
380  ! -- local variables
381  character(len=LENCOMPONENTNAME) :: component_name
382  character(len=LENCOMPONENTNAME) :: subcomponent_name
383  character(len=LENVARNAME) :: variable_name
384  character(len=LENMEMPATH) :: mem_path
385  character(len=LENMEMADDRESS) :: mem_address
386 
387  ! convert to Fortran strings
388  component_name = char_array_to_string(c_component_name, &
389  strlen(c_component_name, &
390  lencomponentname + 1))
391  subcomponent_name = char_array_to_string(c_subcomponent_name, &
392  strlen(c_subcomponent_name, &
393  lencomponentname + 1))
394  variable_name = char_array_to_string(c_var_name, &
395  strlen(c_var_name, &
396  lenvarname + 1))
397 
398  ! create memory address
399  if (subcomponent_name == '') then
400  mem_path = create_mem_path(component_name)
401  else
402  mem_path = create_mem_path(component_name, subcomponent_name)
403  end if
404  mem_address = create_mem_address(mem_path, variable_name)
405 
406  ! convert to c string:
407  c_var_address(1:len(trim(mem_address)) + 1) = &
408  string_to_char_array(trim(mem_address), len(trim(mem_address)))
409 
410  bmi_status = bmi_success
411 
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter lencomponentname
maximum length of a component name
Definition: Constants.f90:18
integer(i4b), parameter lenmemaddress
maximum length of the full memory address, including variable name
Definition: Constants.f90:31
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
character(len=lenmemaddress) function create_mem_address(mem_path, var_name)
returns the address string of the memory object
character(len=lenmempath) function create_mem_path(component, subcomponent, context)
returns the path to the memory object
Here is the call graph for this function:

◆ xmi_do_time_step()

integer(kind=c_int) function mf6xmi::xmi_do_time_step

It does so by looping over all solution groups, and calling the calculate function on all solutions in there.

Returns
BMI status code

Definition at line 146 of file mf6xmi.F90.

147  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_do_time_step
148  ! -- dummy variables
149  integer(kind=c_int) :: bmi_status !< BMI status code
150 
151  call mf6dotimestep()
152  bmi_status = bmi_success
153 
Here is the call graph for this function:

◆ xmi_finalize_solve()

integer(kind=c_int) function mf6xmi::xmi_finalize_solve ( integer(kind=c_int), intent(in)  subcomponent_idx)

This will determine convergence, reports, calculate flows and budgets, and more... It should always follow after a call to xmi_prepare_solve() and xmi_solve().

Parameters
[in]subcomponent_idxthe index of the subcomponent (Numerical Solution)
Returns
bmi_status the BMI status code
Parameters
[in]subcomponent_idxindex of the subcomponent (i.e. Numerical Solution)
Returns
BMI status code

Definition at line 306 of file mf6xmi.F90.

308  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_finalize_solve
309  ! -- modules
311  ! -- dummy variables
312  integer(kind=c_int), intent(in) :: subcomponent_idx !< index of the subcomponent (i.e. Numerical Solution)
313  integer(kind=c_int) :: bmi_status !< BMI status code
314  ! -- local variables
315  class(BaseSolutionType), pointer :: bs
316  integer(I4B) :: hasConverged
317 
318  ! get the numerical solution we are running
319  bs => getsolution(subcomponent_idx)
320 
321  ! hasConverged is equivalent to the isgcnvg variable which is initialized to 1,
322  ! see the body of the picard loop in SolutionGroupType%sgp_ca
323  hasconverged = 1
324 
325  ! finish up
326  call bs%finalizeSolve(iterationcounter, hasconverged, 0)
327 
328  ! check convergence on solution
329  if (.not. hasconverged == 1) then
330  write (bmi_last_error, fmt_fail_cvg_sol) subcomponent_idx
331  call report_bmi_error(bmi_last_error)
332  end if
333 
334  ! non-convergence is no reason to crash the API:
335  bmi_status = bmi_success
336 
337  ! clear this for safety
338  deallocate (iterationcounter)
339 
Here is the call graph for this function:

◆ xmi_finalize_time_step()

integer(kind=c_int) function mf6xmi::xmi_finalize_time_step

This will mostly write output and messages. It is essential to call this to finish the time step.

Returns
BMI status code

Definition at line 161 of file mf6xmi.F90.

163  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_finalize_time_step
164  ! -- dummy variables
165  integer(kind=c_int) :: bmi_status !< BMI status code
166  ! -- local variables
167  logical :: hasConverged
168 
169  hasconverged = mf6finalizetimestep()
170  if (hasconverged) then
171  bmi_status = bmi_success
172  else
173  write (bmi_last_error, fmt_general_err) 'simulation failed to converge'
174  call report_bmi_error(bmi_last_error)
175  bmi_status = bmi_failure
176  end if
177 
Here is the call graph for this function:

◆ xmi_get_subcomponent_count()

integer(kind=c_int) function mf6xmi::xmi_get_subcomponent_count ( integer(kind=c_int), intent(out)  count)

For most applications, this number will be equal to 1. Note that this part of the XMI only works when the simulation is defined with a single Solution Group. (If you don't know what a Solution Group is, then you are most likely not using more than one...)

Parameters
[out]countnumber of solutions
Returns
BMI status code

Definition at line 187 of file mf6xmi.F90.

189  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_get_subcomponent_count
190  ! -- modules
191  use listsmodule, only: solutiongrouplist
192  use simvariablesmodule, only: istdout
193  ! -- dummy variables
194  integer(kind=c_int), intent(out) :: count !< number of solutions
195  integer(kind=c_int) :: bmi_status !< BMI status code
196  ! -- local variables
197  class(SolutionGroupType), pointer :: sgp
198 
199  ! the following is true for all calls at this level (subcomponent)
200  if (solutiongrouplist%Count() /= 1) then
201  write (istdout, *) &
202  'Error: BMI does not support the use of multiple solution groups'
203  count = -1
204  bmi_status = bmi_failure
205  return
206  end if
207 
208  sgp => getsolutiongroupfromlist(solutiongrouplist, 1)
209  count = sgp%nsolutions
210  bmi_status = bmi_success
211 
type(listtype), public solutiongrouplist
Definition: mf6lists.f90:22
This module contains simulation variables.
Definition: SimVariables.f90:9
integer(i4b) istdout
unit number for stdout

◆ xmi_get_version()

integer(kind=c_int) function mf6xmi::xmi_get_version ( character(kind=c_char), dimension(bmi_lenversion), intent(inout)  mf_version)
Returns
BMI status code

Definition at line 344 of file mf6xmi.F90.

346  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_get_version
347  ! -- modules
349  ! -- dummy variables
350  character(kind=c_char), intent(inout) :: mf_version(BMI_LENVERSION)
351  integer(kind=c_int) :: bmi_status !< BMI status code
352 
353  mf_version = string_to_char_array(versionnumber, len_trim(versionnumber))
354  bmi_status = bmi_success
355 
This module contains version information.
Definition: version.f90:7
integer(i4b), parameter idevelopmode
Definition: version.f90:19
character(len= *), parameter versionnumber
Definition: version.f90:20
Here is the call graph for this function:

◆ xmi_prepare_solve()

integer(kind=c_int) function mf6xmi::xmi_prepare_solve ( integer(kind=c_int)  subcomponent_idx)

This preparation mostly consists of advancing the solutions, models, and exchanges in the simulation. The index subcomponent_idx runs from 1 to the value returned by xmi_get_subcomponent_count().

Parameters
subcomponent_idxindex of the subcomponent (i.e. Numerical Solution)
Returns
BMI status code

Definition at line 220 of file mf6xmi.F90.

222  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_prepare_solve
223  ! -- modules
224  use listsmodule, only: solutiongrouplist
226  use simvariablesmodule, only: istdout
227  ! -- dummy variables
228  integer(kind=c_int) :: subcomponent_idx !< index of the subcomponent (i.e. Numerical Solution)
229  integer(kind=c_int) :: bmi_status !< BMI status code
230  ! -- local variables
231  class(BaseSolutionType), pointer :: bs
232 
233  ! people might not call 'xmi_get_subcomponent_count' first, so let's repeat this:
234  if (solutiongrouplist%Count() /= 1) then
235  write (istdout, *) &
236  'Error: BMI does not support the use of multiple solution groups'
237  bmi_status = bmi_failure
238  return
239  end if
240 
241  ! get the solution we are running
242  bs => getsolution(subcomponent_idx)
243 
244  ! *_ad (model, exg, sln)
245  call bs%prepareSolve()
246 
247  ! reset counter
248  allocate (iterationcounter)
249  iterationcounter = 0
250 
251  bmi_status = bmi_success
252 
Here is the call graph for this function:

◆ xmi_prepare_time_step()

integer(kind=c_int) function mf6xmi::xmi_prepare_time_step ( real(kind=c_double), intent(in)  dt)

The routine takes the time step dt as an argument. However, MODFLOW (currently) does not allow to alter this value after initialization, so it is ignored here.

Parameters
[in]dttime step
Returns
BMI status code

Definition at line 129 of file mf6xmi.F90.

131  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_prepare_time_step
132  ! -- dummy variables
133  real(kind=c_double), intent(in) :: dt !< time step
134  integer(kind=c_int) :: bmi_status !< BMI status code
135 
136  call mf6preparetimestep()
137  bmi_status = bmi_success
138 
Here is the call graph for this function:

◆ xmi_solve()

integer(kind=c_int) function mf6xmi::xmi_solve ( integer(kind=c_int), intent(in)  subcomponent_idx,
integer(kind=c_int), intent(out)  has_converged 
)

The solve is called on the Numerical Solution indicated by the value of subcomponent_idx, which runs from 1 to the value returned by xmi_get_subcomponent_count(). Before calling this, a matching call to xmi_prepare_solve() should be done.

Parameters
[in]subcomponent_idxindex of the subcomponent (i.e. Numerical Solution)
[out]has_convergedequal to 1 for convergence, 0 otherwise
Returns
BMI status code

Definition at line 261 of file mf6xmi.F90.

263  !DIR$ ATTRIBUTES DLLEXPORT :: xmi_solve
264  ! -- modules
268  ! -- dummy variables
269  integer(kind=c_int), intent(in) :: subcomponent_idx !< index of the subcomponent (i.e. Numerical Solution)
270  integer(kind=c_int), intent(out) :: has_converged !< equal to 1 for convergence, 0 otherwise
271  integer(kind=c_int) :: bmi_status !< BMI status code
272  ! -- local variables
273  class(BaseSolutionType), pointer :: bs
274 
275  ! get the numerical solution we are running
276  bs => getsolution(subcomponent_idx)
277 
278  ! execute the nth iteration
279  iterationcounter = iterationcounter + 1
280  call bs%solve(iterationcounter)
281 
282  ! the following check is equivalent to that in NumericalSolution%sln_ca
283  select type (bs)
284  class is (numericalsolutiontype)
285  if (bs%icnvg == 1) then
286  has_converged = 1
287  else
288  has_converged = 0
289  end if
290  class is (explicitsolutiontype)
291  has_converged = 1
292  end select
293 
294  bmi_status = bmi_success
295 
Explicit Solution Module.
Manages and solves explicit models.
Here is the call graph for this function:

Variable Documentation

◆ iterationcounter

integer(i4b), pointer mf6xmi::iterationcounter => null()

Definition at line 96 of file mf6xmi.F90.

96  integer(I4B), pointer :: iterationCounter => null() !< the counter for the outer iteration loop, initialized in xmi_prepare_iteration()