MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
mf6bmiGrid.f90
Go to the documentation of this file.
1 !> @brief This module contains BMI routines to expose the MODFLOW 6 discretization
2 !!
3 !! NB: this module is experimental and still under development:
4 !! - add error handling
5 !! - add var address checks
6 !! - ...
7 !<
8 module mf6bmigrid
9  use mf6bmiutil
10  use mf6bmierror
11  use iso_c_binding, only: c_double, c_ptr, c_loc
13  use kindmodule, only: dp, i4b
16  implicit none
17 
18 contains
19 
20  ! Get the grid identifier for the given variable.
21  function get_var_grid(c_var_address, var_grid) result(bmi_status) &
22  bind(C, name="get_var_grid")
23  !DIR$ ATTRIBUTES DLLEXPORT :: get_var_grid
24  ! -- modules
25  use listsmodule, only: basemodellist
27  ! -- dummy variables
28  character(kind=c_char), intent(in) :: c_var_address(*)
29  integer(kind=c_int), intent(out) :: var_grid
30  integer(kind=c_int) :: bmi_status
31  ! -- local variables
32  character(len=LENMODELNAME) :: model_name
33  character(len=LENMEMPATH) :: var_address
34  integer(I4B) :: i
35  logical(LGP) :: success
36  class(basemodeltype), pointer :: basemodel
37 
38  var_grid = -1
39 
40  bmi_status = bmi_failure
41  var_address = char_array_to_string(c_var_address, &
42  strlen(c_var_address, lenmemaddress + 1))
43  model_name = extract_model_name(var_address, success)
44  if (.not. success) then
45  ! we failed
46  return
47  end if
48 
49  do i = 1, basemodellist%Count()
50  basemodel => getbasemodelfromlist(basemodellist, i)
51  if (basemodel%name == model_name) then
52  var_grid = basemodel%id
53  bmi_status = bmi_success
54  return
55  end if
56  end do
57  end function get_var_grid
58 
59  ! Get the grid type as a string.
60  function get_grid_type(grid_id, grid_type) result(bmi_status) &
61  bind(C, name="get_grid_type")
62  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_type
63  ! -- dummy variables
64  integer(kind=c_int), intent(in) :: grid_id
65  character(kind=c_char), intent(inout) :: grid_type(bmi_lengridtype)
66  integer(kind=c_int) :: bmi_status
67  ! -- local variables
68  character(len=LENGRIDTYPE) :: grid_type_f
69  character(len=LENMODELNAME) :: model_name
70 
71  bmi_status = bmi_failure
72  model_name = get_model_name(grid_id)
73  if (model_name == '') return
74 
75  call get_grid_type_model(model_name, grid_type_f)
76 
77  if (grid_type_f == "DIS") then
78  grid_type_f = "rectilinear"
79  else if ((grid_type_f == "DISV") .or. (grid_type_f == "DISU")) then
80  grid_type_f = "unstructured"
81  else
82  return
83  end if
84  grid_type(1:len_trim(grid_type_f) + 1) = &
85  string_to_char_array(trim(grid_type_f), len_trim(grid_type_f))
86  bmi_status = bmi_success
87  end function get_grid_type
88 
89  ! Get number of dimensions of the computational grid.
90  function get_grid_rank(grid_id, grid_rank) result(bmi_status) &
91  bind(C, name="get_grid_rank")
92  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_rank
93  ! -- dummy variables
94  integer(kind=c_int), intent(in) :: grid_id
95  integer(kind=c_int), intent(out) :: grid_rank
96  integer(kind=c_int) :: bmi_status
97  ! -- local variables
98  character(len=LENMODELNAME) :: model_name
99  integer(I4B), dimension(:), pointer, contiguous :: grid_shape
100 
101  bmi_status = bmi_failure
102  ! TODO: It is currently only implemented for DIS grids
103  if (.not. confirm_grid_type(grid_id, "DIS")) return
104 
105  ! get shape array
106  model_name = get_model_name(grid_id)
107  call mem_setptr(grid_shape, "MSHAPE", create_mem_path(model_name, 'DIS'))
108 
109  if (grid_shape(1) == 1) then
110  grid_rank = 2
111  else
112  grid_rank = 3
113  end if
114  bmi_status = bmi_success
115  end function get_grid_rank
116 
117  ! Get the total number of elements in the computational grid.
118  function get_grid_size(grid_id, grid_size) result(bmi_status) &
119  bind(C, name="get_grid_size")
120  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_size
121  ! -- dummy variables
122  integer(kind=c_int), intent(in) :: grid_id
123  integer(kind=c_int), intent(out) :: grid_size
124  integer(kind=c_int) :: bmi_status
125  ! -- local variables
126  character(len=LENMODELNAME) :: model_name
127  integer(I4B), dimension(:), pointer, contiguous :: grid_shape
128  character(kind=c_char) :: grid_type(bmi_lengridtype)
129  character(len=LENGRIDTYPE) :: grid_type_f
130  integer(I4B) :: status
131 
132  bmi_status = bmi_failure
133 
134  if (get_grid_type(grid_id, grid_type) /= bmi_success) return
135  grid_type_f = char_array_to_string(grid_type, &
136  strlen(grid_type, lengridtype + 1))
137  model_name = get_model_name(grid_id)
138 
139  if (grid_type_f == "rectilinear") then
140  call mem_setptr(grid_shape, "MSHAPE", create_mem_path(model_name, 'DIS'))
141  grid_size = grid_shape(1) * grid_shape(2) * grid_shape(3)
142  bmi_status = bmi_success
143  return
144  else if (grid_type_f == "unstructured") then
145  status = get_grid_node_count(grid_id, grid_size)
146  bmi_status = bmi_success
147  return
148  end if
149  end function get_grid_size
150 
151  ! Get the dimensions of the computational grid.
152  function get_grid_shape(grid_id, grid_shape) result(bmi_status) &
153  bind(C, name="get_grid_shape")
154  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_shape
155  ! -- dummy variables
156  integer(kind=c_int), intent(in) :: grid_id
157  integer(kind=c_int), intent(out) :: grid_shape(*)
158  integer(kind=c_int) :: bmi_status
159  ! -- local variables
160  integer, dimension(:), pointer, contiguous :: grid_shape_ptr
161  character(len=LENMODELNAME) :: model_name
162  character(kind=c_char) :: grid_type(bmi_lengridtype)
163 
164  bmi_status = bmi_failure
165  ! make sure function is only used for implemented grid_types
166  if (get_grid_type(grid_id, grid_type) /= bmi_success) return
167 
168  ! get shape array
169  model_name = get_model_name(grid_id)
170  call mem_setptr(grid_shape_ptr, "MSHAPE", create_mem_path(model_name, 'DIS'))
171 
172  if (grid_shape_ptr(1) == 1) then
173  grid_shape(1:2) = grid_shape_ptr(2:3) ! 2D
174  else
175  grid_shape(1:3) = grid_shape_ptr ! 3D
176  end if
177  bmi_status = bmi_success
178  end function get_grid_shape
179 
180  ! Provides an array (whose length is the number of rows) that gives the x-coordinate for each row.
181  function get_grid_x(grid_id, grid_x) result(bmi_status) &
182  bind(C, name="get_grid_x")
183  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_x
184  ! -- dummy variables
185  integer(kind=c_int), intent(in) :: grid_id
186  real(kind=c_double), intent(out) :: grid_x(*)
187  integer(kind=c_int) :: bmi_status
188  ! -- local variables
189  integer(I4B) :: i
190  integer, dimension(:), pointer, contiguous :: grid_shape_ptr
191  character(len=LENMODELNAME) :: model_name
192  character(kind=c_char) :: grid_type(bmi_lengridtype)
193  real(dp), dimension(:, :), pointer, contiguous :: vertices_ptr
194  character(len=LENGRIDTYPE) :: grid_type_f
195  integer(I4B) :: x_size
196 
197  bmi_status = bmi_failure
198  ! make sure function is only used for implemented grid_types
199  if (get_grid_type(grid_id, grid_type) /= bmi_success) return
200  grid_type_f = char_array_to_string(grid_type, &
201  strlen(grid_type, lengridtype + 1))
202 
203  model_name = get_model_name(grid_id)
204  if (grid_type_f == "rectilinear") then
205  call mem_setptr(grid_shape_ptr, "MSHAPE", &
206  create_mem_path(model_name, 'DIS'))
207  ! The dimension of x is in the last element of the shape array.
208  ! + 1 because we count corners, not centers.
209  x_size = grid_shape_ptr(size(grid_shape_ptr)) + 1
210  grid_x(1:x_size) = [(i, i=0, x_size - 1)]
211  else if (grid_type_f == "unstructured") then
212  call mem_setptr(vertices_ptr, "VERTICES", &
213  create_mem_path(model_name, 'DIS'))
214  ! x-coordinates are in the 1st column
215  x_size = size(vertices_ptr(1, :))
216  grid_x(1:x_size) = vertices_ptr(1, :)
217  else
218  bmi_status = bmi_failure
219  return
220  end if
221  bmi_status = bmi_success
222  end function get_grid_x
223 
224  ! Provides an array (whose length is the number of rows) that gives the y-coordinate for each row.
225  function get_grid_y(grid_id, grid_y) result(bmi_status) &
226  bind(C, name="get_grid_y")
227  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_y
228  ! -- dummy variables
229  integer(kind=c_int), intent(in) :: grid_id
230  real(kind=c_double), intent(out) :: grid_y(*)
231  integer(kind=c_int) :: bmi_status
232  ! -- local variables
233  integer(I4B) :: i
234  integer, dimension(:), pointer, contiguous :: grid_shape_ptr
235  character(len=LENMODELNAME) :: model_name
236  character(kind=c_char) :: grid_type(bmi_lengridtype)
237  real(dp), dimension(:, :), pointer, contiguous :: vertices_ptr
238  character(len=LENGRIDTYPE) :: grid_type_f
239  integer(I4B) :: y_size
240 
241  bmi_status = bmi_failure
242  if (get_grid_type(grid_id, grid_type) /= bmi_success) return
243  grid_type_f = char_array_to_string(grid_type, &
244  strlen(grid_type, lengridtype + 1))
245 
246  model_name = get_model_name(grid_id)
247  if (grid_type_f == "rectilinear") then
248  call mem_setptr(grid_shape_ptr, "MSHAPE", &
249  create_mem_path(model_name, 'DIS'))
250  ! The dimension of y is in the second last element of the shape array.
251  ! + 1 because we count corners, not centers.
252  y_size = grid_shape_ptr(size(grid_shape_ptr - 1)) + 1
253  grid_y(1:y_size) = [(i, i=y_size - 1, 0, -1)]
254  else if (grid_type_f == "unstructured") then
255  call mem_setptr(vertices_ptr, "VERTICES", &
256  create_mem_path(model_name, 'DIS'))
257  ! y-coordinates are in the 2nd column
258  y_size = size(vertices_ptr(2, :))
259  grid_y(1:y_size) = vertices_ptr(2, :)
260  else
261  bmi_status = bmi_failure
262  return
263  end if
264  bmi_status = bmi_success
265  end function get_grid_y
266 
267  ! NOTE: node in BMI-terms is a vertex in Modflow terms
268  ! Get the number of nodes in an unstructured grid.
269  function get_grid_node_count(grid_id, count) result(bmi_status) &
270  bind(C, name="get_grid_node_count")
271  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_node_count
272  ! -- dummy variables
273  integer(kind=c_int), intent(in) :: grid_id
274  integer(kind=c_int), intent(out) :: count
275  integer(kind=c_int) :: bmi_status
276  ! -- local variables
277  character(len=LENMODELNAME) :: model_name
278  integer(I4B), pointer :: nvert_ptr
279 
280  ! make sure function is only used for DISU grids
281  bmi_status = bmi_failure
282  if (.not. confirm_grid_type(grid_id, "DISU")) return
283 
284  model_name = get_model_name(grid_id)
285  call mem_setptr(nvert_ptr, "NVERT", create_mem_path(model_name, 'DIS'))
286  count = nvert_ptr
287  bmi_status = bmi_success
288  end function get_grid_node_count
289 
290  ! TODO_JH: This currently only works for 2D DISU models
291  ! Get the number of faces in an unstructured grid.
292  function get_grid_face_count(grid_id, count) result(bmi_status) &
293  bind(C, name="get_grid_face_count")
294  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_face_count
295  ! -- modules
296  use listsmodule, only: basemodellist
298  ! -- dummy variables
299  integer(kind=c_int), intent(in) :: grid_id
300  integer(kind=c_int), intent(out) :: count
301  integer(kind=c_int) :: bmi_status
302  ! -- local variables
303  character(len=LENMODELNAME) :: model_name
304  integer(I4B) :: i
305  class(numericalmodeltype), pointer :: numericalmodel
306 
307  ! make sure function is only used for DISU grids
308  bmi_status = bmi_failure
309  if (.not. confirm_grid_type(grid_id, "DISU")) return
310 
311  model_name = get_model_name(grid_id)
312  do i = 1, basemodellist%Count()
313  numericalmodel => getnumericalmodelfromlist(basemodellist, i)
314  if (numericalmodel%name == model_name) then
315  count = numericalmodel%dis%nodes
316  end if
317  end do
318  bmi_status = bmi_success
319  end function get_grid_face_count
320 
321  ! Get the face-node connectivity.
322  function get_grid_face_nodes(grid_id, face_nodes) result(bmi_status) &
323  bind(C, name="get_grid_face_nodes")
324  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_face_nodes
325  ! -- dummy variables
326  integer(kind=c_int), intent(in) :: grid_id
327  integer(kind=c_int), intent(out) :: face_nodes(*)
328  integer(kind=c_int) :: bmi_status
329  ! -- local variables
330  character(len=LENMODELNAME) :: model_name
331  integer, dimension(:), pointer, contiguous :: javert_ptr
332  integer, dimension(:), allocatable :: nodes_per_face
333  integer :: face_count
334  integer :: face_nodes_count
335 
336  ! make sure function is only used for DISU grids
337  bmi_status = bmi_failure
338  if (.not. confirm_grid_type(grid_id, "DISU")) return
339 
340  model_name = get_model_name(grid_id)
341  call mem_setptr(javert_ptr, "JAVERT", create_mem_path(model_name, 'DIS'))
342 
343  bmi_status = get_grid_face_count(grid_id, face_count)
344  if (bmi_status == bmi_failure) return
345 
346  allocate (nodes_per_face(face_count))
347  bmi_status = get_grid_nodes_per_face(grid_id, nodes_per_face)
348  if (bmi_status == bmi_failure) return
349 
350  face_nodes_count = sum(nodes_per_face + 1)
351 
352  face_nodes(1:face_nodes_count) = javert_ptr(:)
353  bmi_status = bmi_success
354  end function get_grid_face_nodes
355 
356  ! Get the number of nodes for each face.
357  function get_grid_nodes_per_face(grid_id, nodes_per_face) result(bmi_status) &
358  bind(C, name="get_grid_nodes_per_face")
359  !DIR$ ATTRIBUTES DLLEXPORT :: get_grid_nodes_per_face
360  ! -- dummy variables
361  integer(kind=c_int), intent(in) :: grid_id
362  integer(kind=c_int), intent(out) :: nodes_per_face(*)
363  integer(kind=c_int) :: bmi_status
364  ! -- local variables
365  integer(I4B) :: i
366  character(len=LENMODELNAME) :: model_name
367  integer, dimension(:), pointer, contiguous :: iavert_ptr
368 
369  ! make sure function is only used for DISU grids
370  bmi_status = bmi_failure
371  if (.not. confirm_grid_type(grid_id, "DISU")) return
372 
373  model_name = get_model_name(grid_id)
374  call mem_setptr(iavert_ptr, "IAVERT", create_mem_path(model_name, 'DIS'))
375 
376  do i = 1, size(iavert_ptr) - 1
377  nodes_per_face(i) = iavert_ptr(i + 1) - iavert_ptr(i) - 1
378  end do
379  bmi_status = bmi_success
380  end function get_grid_nodes_per_face
381 end module mf6bmigrid
class(basemodeltype) function, pointer, public getbasemodelfromlist(list, idx)
Definition: BaseModel.f90:171
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter lenmodelname
maximum length of the model name
Definition: Constants.f90:22
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
This module defines variable data types.
Definition: kind.f90:8
type(listtype), public basemodellist
Definition: mf6lists.f90:16
character(len=lenmempath) function create_mem_path(component, subcomponent, context)
returns the path to the memory object
Detailed error information for the BMI.
Definition: mf6bmiError.f90:6
integer, parameter bmi_failure
BMI status code for failure (taken from bmi.f90, CSDMS)
Definition: mf6bmiError.f90:12
integer, parameter bmi_success
BMI status code for success (taken from bmi.f90, CSDMS)
Definition: mf6bmiError.f90:13
This module contains BMI routines to expose the MODFLOW 6 discretization.
Definition: mf6bmiGrid.f90:8
integer(kind=c_int) function get_grid_rank(grid_id, grid_rank)
Definition: mf6bmiGrid.f90:92
integer(kind=c_int) function get_grid_face_count(grid_id, count)
Definition: mf6bmiGrid.f90:294
integer(kind=c_int) function get_grid_shape(grid_id, grid_shape)
Definition: mf6bmiGrid.f90:154
integer(kind=c_int) function get_grid_face_nodes(grid_id, face_nodes)
Definition: mf6bmiGrid.f90:324
integer(kind=c_int) function get_grid_type(grid_id, grid_type)
Definition: mf6bmiGrid.f90:62
integer(kind=c_int) function get_var_grid(c_var_address, var_grid)
Definition: mf6bmiGrid.f90:23
integer(kind=c_int) function get_grid_nodes_per_face(grid_id, nodes_per_face)
Definition: mf6bmiGrid.f90:359
integer(kind=c_int) function get_grid_size(grid_id, grid_size)
Definition: mf6bmiGrid.f90:120
integer(kind=c_int) function get_grid_node_count(grid_id, count)
Definition: mf6bmiGrid.f90:271
integer(kind=c_int) function get_grid_y(grid_id, grid_y)
Definition: mf6bmiGrid.f90:227
integer(kind=c_int) function get_grid_x(grid_id, grid_x)
Definition: mf6bmiGrid.f90:183
This module contains helper routines and parameters for the MODFLOW 6 BMI.
Definition: mf6bmiUtil.f90:4
character(len=lenmodelname) function get_model_name(grid_id)
Get the model name from the grid id.
Definition: mf6bmiUtil.f90:191
subroutine get_grid_type_model(model_name, grid_type_f)
Get the grid type for a named model as a fortran string.
Definition: mf6bmiUtil.f90:239
integer(c_int), bind(C, name="BMI_LENGRIDTYPE") bmi_lengridtype
max. length for grid type C-strings
Definition: mf6bmiUtil.f90:30
integer(i4b), parameter lengridtype
max length for Fortran grid type string
Definition: mf6bmiUtil.f90:24
logical function confirm_grid_type(grid_id, expected_type)
Confirm that grid is of an expected type.
Definition: mf6bmiUtil.f90:261
pure character(kind=c_char, len=1) function, dimension(length+1) string_to_char_array(string, length)
Convert Fortran string to C-style character string.
Definition: mf6bmiUtil.f90:149
pure integer(i4b) function strlen(char_array, max_len)
Returns the string length without the trailing null character.
Definition: mf6bmiUtil.f90:113
pure character(len=length) function char_array_to_string(char_array, length)
Convert C-style string to Fortran character string.
Definition: mf6bmiUtil.f90:133
character(len=lenmodelname) function extract_model_name(var_address, success)
Extract the model name from a memory address string.
Definition: mf6bmiUtil.f90:166
class(numericalmodeltype) function, pointer, public getnumericalmodelfromlist(list, idx)
Highest level model type. All models extend this parent type.
Definition: BaseModel.f90:16