MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
mf6bmiUtil.f90
Go to the documentation of this file.
1 !> @brief This module contains helper routines and parameters for the MODFLOW 6 BMI
2 !!
3 !<
4 module mf6bmiutil
5  use mf6bmierror
6  use iso_c_binding, only: c_int, c_char, c_null_char
10  use kindmodule, only: dp, i4b, lgp
11  use messagemodule, only: write_message
12  use simvariablesmodule, only: istdout
14  implicit none
15 
16  ! the following exported parameters will trigger annoying warnings with
17  ! the Intel Fortran compiler (4049,4217). We know that these can be ignored:
18  !
19  ! https://community.intel.com/t5/Intel-Fortran-Compiler/suppress-linker-warnings/td-p/855137
20  ! https://community.intel.com/t5/Intel-Fortran-Compiler/Locally-Defined-Symbol-Imported/m-p/900805
21  !
22  ! and gfortran does so anyway. They have been disabled in the linker config.
23 
24  integer(I4B), parameter :: lengridtype = 16 !< max length for Fortran grid type string
25  integer(I4B), parameter :: lenunits = 16 !< max length for Fortran units string
26  integer(I4B), parameter :: lenvarlocation = 16 !< max length for Fortran variable location string
27 
28  integer(c_int), bind(C, name="BMI_LENVARTYPE") :: bmi_lenvartype = &
29  lenmemtype + 1 !< max. length for variable type C-strings
30  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENVARTYPE
31 
32  integer(c_int), bind(C, name="BMI_LENGRIDTYPE") :: bmi_lengridtype = &
33  lengridtype + 1 !< max. length for grid type C-strings
34  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENGRIDTYPE
35 
36  integer(c_int), bind(C, name="BMI_LENUNITS") :: bmi_lenunits = &
37  lenunits + 1 !< max. length for units C-strings
38  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENUNITS
39 
40  integer(c_int), bind(C, name="BMI_LENVARLOCATION") :: bmi_lenvarlocation = &
41  lenvarlocation + 1 !< max. length for variable location C-strings
42  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENVARLOCATION
43 
44  integer(c_int), bind(C, name="BMI_LENVARADDRESS") :: bmi_lenvaraddress = &
45  lenmemaddress + 1 !< max. length for the variable's address C-string
46  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENVARADDRESS
47 
48  integer(c_int), bind(C, name="BMI_LENCOMPONENTNAME") :: bmi_lencomponentname = &
49  256 !< component name length, i.e. 'MODFLOW 6'
50  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENCOMPONENTNAME
51 
52  integer(c_int), bind(C, name="BMI_LENVERSION") :: bmi_lenversion = 256 !< length of version string, e.g. '6.3.1' or '6.4.1-dev'
53  !DIR$ ATTRIBUTES DLLEXPORT :: BMI_LENVERSION
54 
55 contains
56 
57  !> @brief Split the variable address string
58  !!
59  !! Splits the full address string into a memory path and variable name,
60  !! following the rules used by the memory manager. The error handling
61  !! is inside to avoid lots of duplication
62  !<
63  subroutine split_address(c_var_address, mem_path, var_name, success)
64  ! -- modules
66  ! -- dummy variables
67  character(kind=c_char), intent(in) :: c_var_address(*) !< full address of a variable
68  character(len=LENMEMPATH), intent(out) :: mem_path !< memory path used by the memory manager
69  character(len=LENVARNAME), intent(out) :: var_name !< name of the variable
70  logical(LGP), intent(out) :: success !< false when invalid
71  ! -- local variables
72  character(len=LENMEMADDRESS) :: var_address
73  logical(LGP) :: valid, found
74 
75  success = .false.
76 
77  ! try and split the address string:
78  var_address = char_array_to_string(c_var_address, &
79  strlen(c_var_address, lenmemaddress + 1))
80  call split_mem_address(var_address, mem_path, var_name, valid)
81  if (.not. valid) then
82  write (bmi_last_error, fmt_invalid_var) trim(var_address)
84  return
85  end if
86 
87  ! check if the variable even exists:
88  call check_mem_address(mem_path, var_name, found)
89  if (.not. found) then
90  write (bmi_last_error, fmt_unknown_var) trim(var_name), trim(mem_path)
92  return
93  end if
94 
95  ! warn if the variable is addressed by a deprecated name
96  call warn_deprecated_address(mem_path, var_name)
97 
98  success = .true.
99 
100  end subroutine split_address
101 
102  !> @brief Check if the variable exists in the memory manager
103  !<
104  subroutine check_mem_address(mem_path, var_name, found)
105  ! -- modules
107  use memorytypemodule, only: memorytype
108  ! -- dummy variables
109  character(len=LENMEMPATH), intent(in) :: mem_path !< memory path used by the memory manager
110  character(len=LENVARNAME), intent(in) :: var_name !< name of the variable
111  logical(LGP), intent(out) :: found !< true when found
112  ! -- local variables
113  type(memorytype), pointer :: mt
114 
115  found = .false.
116  mt => null()
117 
118  ! check = false: otherwise stop is called when the variable does not exist
119  call get_from_memorystore(var_name, mem_path, mt, found, check=.false.)
120 
121  end subroutine check_mem_address
122 
123  !> @brief Store a warning the first time a deprecated variable name is used
124  !<
125  subroutine warn_deprecated_address(mem_path, var_name)
126  ! -- modules
128  use simmodule, only: store_warning
129  ! -- dummy variables
130  character(len=LENMEMPATH), intent(in) :: mem_path !< memory path used by the memory manager
131  character(len=LENVARNAME), intent(in) :: var_name !< name of the variable
132  ! -- local variables
133  logical(LGP) :: found
134  character(len=LENMEMADDRESS) :: replacement
135  character(len=LINELENGTH) :: version
136  character(len=MAXCHARLEN) :: msg
137 
138  call get_deprecated_alias(var_name, mem_path, found, replacement, version)
139  if (.not. found) return
140 
141  write (msg, '(a)') &
142  "API VARIABLE '"//trim(var_name)//"' IN '"//trim(mem_path)// &
143  "' WAS DEPRECATED IN VERSION "//trim(version)//". USE '"// &
144  trim(replacement)//"' INSTEAD."
145  call store_warning(trim(msg), substring=trim(msg))
146 
147  end subroutine warn_deprecated_address
148 
149  !> @brief Returns the string length without the trailing null character
150  !<
151  pure function strlen(char_array, max_len) result(string_length)
152  ! -- dummy variables
153  integer(I4B), intent(in) :: max_len
154  character(c_char), intent(in) :: char_array(max_len) !< C-style character string
155  integer(I4B) :: string_length !< Fortran string length
156  ! -- local variables
157  integer(I4B) :: i
158 
159  string_length = 0
160  do i = 1, size(char_array)
161  if (char_array(i) .eq. c_null_char) then
162  string_length = i - 1
163  exit
164  end if
165  end do
166 
167  end function strlen
168 
169  !> @brief Convert C-style string to Fortran character string
170  !<
171  pure function char_array_to_string(char_array, length) result(f_string)
172  ! -- dummy variables
173  integer(c_int), intent(in) :: length !< string length without terminating null character
174  character(c_char), intent(in) :: char_array(length) !< string to convert
175  character(len=length) :: f_string !< Fortran fixed length character string
176  ! -- local variables
177  integer(I4B) :: i
178 
179  do i = 1, length
180  f_string(i:i) = char_array(i)
181  end do
182 
183  end function char_array_to_string
184 
185  !> @brief Convert Fortran string to C-style character string
186  !<
187  pure function string_to_char_array(string, length) result(c_array)
188  ! -- dummy variables
189  integer(c_int), intent(in) :: length !< Fortran string length
190  character(len=length), intent(in) :: string !< string to convert
191  character(kind=c_char, len=1) :: c_array(length + 1) !< C-style character string
192  ! -- local variables
193  integer(I4B) :: i
194 
195  do i = 1, length
196  c_array(i) = string(i:i)
197  end do
198  c_array(length + 1) = c_null_char
199 
200  end function string_to_char_array
201 
202  !> @brief Extract the model name from a memory address string
203  !<
204  function extract_model_name(var_address, success) result(model_name)
205  ! -- dummy variables
206  character(len=*), intent(in) :: var_address !< the memory address for the variable
207  character(len=LENMODELNAME) :: model_name !< the extracted model name
208  logical(LGP), intent(out) :: success
209  ! -- local variables
210  character(len=LENMEMPATH) :: mem_path
211  character(len=LENCOMPONENTNAME) :: dummy_component
212  character(len=LENVARNAME) :: var_name
213  logical(LGP) :: split_succeeded
214 
215  success = .false.
216 
217  call split_mem_address(var_address, mem_path, var_name, split_succeeded)
218  if (.not. split_succeeded) then
219  return
220  end if
221 
222  call split_mem_path(mem_path, model_name, dummy_component)
223  success = .true.
224 
225  end function extract_model_name
226 
227  !> @brief Get the model name from the grid id
228  !<
229  function get_model_name(grid_id) result(model_name)
230  ! -- modules
231  use listsmodule, only: basemodellist
233  ! -- dummy variables
234  integer(kind=c_int), intent(in) :: grid_id !< grid id
235  character(len=LENMODELNAME) :: model_name !< model name
236  ! -- local variables
237  integer(I4B) :: i
238  class(basemodeltype), pointer :: basemodel
239  character(len=LINELENGTH) :: error_msg
240 
241  model_name = ''
242 
243  do i = 1, basemodellist%Count()
244  basemodel => getbasemodelfromlist(basemodellist, i)
245  if (basemodel%id == grid_id) then
246  model_name = basemodel%name
247  return
248  end if
249  end do
250 
251  write (error_msg, '(a,i0)') 'BMI error: no model for grid id ', grid_id
252  call write_message(error_msg, iunit=istdout, skipbefore=1, skipafter=1)
253  end function get_model_name
254 
255  !> @brief Get the solution object for this index
256  !<
257  function getsolution(subcomponent_idx) result(solution)
258  ! -- modules
262  ! -- dummy variables
263  integer(I4B), intent(in) :: subcomponent_idx !< index of solution
264  class(basesolutiontype), pointer :: solution !< Base Solution
265  ! -- local variables
266  class(solutiongrouptype), pointer :: sgp
267  integer(I4B) :: solutionidx
268 
269  ! this is equivalent to how it's done in sgp_ca
271  solutionidx = sgp%idsolutions(subcomponent_idx)
272  solution => getbasesolutionfromlist(basesolutionlist, solutionidx)
273  end function getsolution
274 
275  !> @brief Get the grid type for a named model as a fortran string
276  !<
277  subroutine get_grid_type_model(model_name, grid_type_f)
278  ! -- modules
279  use listsmodule, only: basemodellist
281  ! -- dummy variables
282  character(len=LENMODELNAME) :: model_name
283  character(len=LENGRIDTYPE) :: grid_type_f
284  ! -- local variables
285  integer(I4B) :: i
286  class(basemodeltype), pointer :: baseModel
287 
288  do i = 1, basemodellist%Count()
289  basemodel => getbasemodelfromlist(basemodellist, i)
290  if (basemodel%name == model_name) then
291  call basemodel%dis%get_dis_type(grid_type_f)
292  return
293  end if
294  end do
295  end subroutine get_grid_type_model
296 
297  !> @brief Confirm that grid is of an expected type
298  !<
299  function confirm_grid_type(grid_id, expected_type) result(is_match)
300  ! -- dummy variables
301  integer(kind=c_int), intent(in) :: grid_id
302  character(len=*), intent(in) :: expected_type
303  logical :: is_match
304  ! -- local variables
305  character(len=LENMODELNAME) :: model_name
306  character(len=LENGRIDTYPE) :: grid_type
307 
308  is_match = .false.
309 
310  model_name = get_model_name(grid_id)
311  call get_grid_type_model(model_name, grid_type)
312  ! careful comparison:
313  if (expected_type == grid_type) is_match = .true.
314  end function confirm_grid_type
315 
316 end module mf6bmiutil
class(basemodeltype) function, pointer, public getbasemodelfromlist(list, idx)
Definition: BaseModel.f90:171
class(basesolutiontype) function, pointer, public getbasesolutionfromlist(list, idx)
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
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 lenmodelname
maximum length of the model name
Definition: Constants.f90:22
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
integer(i4b), parameter, public lenmemtype
maximum length of a memory manager type
Definition: Constants.f90:62
integer(i4b), parameter maxcharlen
maximum length of char string
Definition: Constants.f90:47
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
type(listtype), public solutiongrouplist
Definition: mf6lists.f90:22
type(listtype), public basesolutionlist
Definition: mf6lists.f90:19
Previous variable names checked in as deprecated aliases.
Definition: MemoryAlias.f90:7
subroutine, public get_deprecated_alias(name, mem_path, found, replacement, version)
Get the replacement and version of a deprecated name.
character(len=lenmemseparator), parameter mempathseparator
used to build up the memory address for the stored variables
subroutine split_mem_address(mem_address, mem_path, var_name, success)
Split a memory address string into memory path and variable name.
subroutine split_mem_path(mem_path, component, subcomponent)
Split the memory path into component(s)
subroutine, public get_from_memorystore(name, mem_path, mt, found, check)
@ brief Get a memory type entry from the memory list
Store and issue logging messages to output units.
Definition: Message.f90:2
subroutine, public write_message(text, iunit, fmt, skipbefore, skipafter, advance)
Write a message to an output unit.
Definition: Message.f90:212
Detailed error information for the BMI.
Definition: mf6bmiError.f90:6
character(len= *), parameter fmt_invalid_var
Definition: mf6bmiError.f90:26
character(len= *), parameter fmt_unknown_var
Definition: mf6bmiError.f90:24
character(len=lenerrmessage) bmi_last_error
module variable containing the last error as a Fortran string
Definition: mf6bmiError.f90:20
subroutine report_bmi_error(err_msg)
Sets the last BMI error message and copies it to an exported C-string.
Definition: mf6bmiError.f90:53
This module contains helper routines and parameters for the MODFLOW 6 BMI.
Definition: mf6bmiUtil.f90:4
integer(c_int), bind(C, name="BMI_LENVARTYPE") bmi_lenvartype
max. length for variable type C-strings
Definition: mf6bmiUtil.f90:28
integer(c_int), bind(C, name="BMI_LENCOMPONENTNAME") bmi_lencomponentname
component name length, i.e. 'MODFLOW 6'
Definition: mf6bmiUtil.f90:48
character(len=lenmodelname) function get_model_name(grid_id)
Get the model name from the grid id.
Definition: mf6bmiUtil.f90:230
subroutine split_address(c_var_address, mem_path, var_name, success)
Split the variable address string.
Definition: mf6bmiUtil.f90:64
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:278
integer(c_int), bind(C, name="BMI_LENVERSION") bmi_lenversion
length of version string, e.g. '6.3.1' or '6.4.1-dev'
Definition: mf6bmiUtil.f90:52
integer(c_int), bind(C, name="BMI_LENGRIDTYPE") bmi_lengridtype
max. length for grid type C-strings
Definition: mf6bmiUtil.f90:32
integer(i4b), parameter lenvarlocation
max length for Fortran variable location string
Definition: mf6bmiUtil.f90:26
class(basesolutiontype) function, pointer getsolution(subcomponent_idx)
Get the solution object for this index.
Definition: mf6bmiUtil.f90:258
integer(i4b), parameter lenunits
max length for Fortran units string
Definition: mf6bmiUtil.f90:25
subroutine warn_deprecated_address(mem_path, var_name)
Store a warning the first time a deprecated variable name is used.
Definition: mf6bmiUtil.f90:126
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:300
subroutine check_mem_address(mem_path, var_name, found)
Check if the variable exists in the memory manager.
Definition: mf6bmiUtil.f90:105
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:188
pure integer(i4b) function strlen(char_array, max_len)
Returns the string length without the trailing null character.
Definition: mf6bmiUtil.f90:152
integer(c_int), bind(C, name="BMI_LENUNITS") bmi_lenunits
max. length for units C-strings
Definition: mf6bmiUtil.f90:36
pure character(len=length) function char_array_to_string(char_array, length)
Convert C-style string to Fortran character string.
Definition: mf6bmiUtil.f90:172
character(len=lenmodelname) function extract_model_name(var_address, success)
Extract the model name from a memory address string.
Definition: mf6bmiUtil.f90:205
integer(c_int), bind(C, name="BMI_LENVARLOCATION") bmi_lenvarlocation
max. length for variable location C-strings
Definition: mf6bmiUtil.f90:40
integer(c_int), bind(C, name="BMI_LENVARADDRESS") bmi_lenvaraddress
max. length for the variable's address C-string
Definition: mf6bmiUtil.f90:44
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_warning(msg, substring)
Store warning message.
Definition: Sim.f90:237
This module contains simulation variables.
Definition: SimVariables.f90:9
integer(i4b) istdout
unit number for stdout
class(solutiongrouptype) function, pointer, public getsolutiongroupfromlist(list, idx)
Highest level model type. All models extend this parent type.
Definition: BaseModel.f90:16