MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
distributedsimmodule Module Reference

Data Types

type  distributedsimtype
 

Functions/Subroutines

class(distributedsimtype) function, pointer, public get_dsim ()
 Get pointer to the distributed simulation object. More...
 
subroutine create (this)
 Create the distributed simulation object from the simulation input ctx. More...
 
integer(i4b) function, dimension(:), pointer get_load_mask (this)
 Return pointer to the load mask for models. More...
 
subroutine create_load_mask (this)
 Create a load mask for IDM from the load balance array. More...
 
integer(i4b) function, dimension(:), pointer get_load_balance (this)
 Get the model load balance for the simulation. More...
 
subroutine set_load_balance_from_input (this)
 Load load balance from the input configuration. More...
 
subroutine set_load_balance_default (this)
 Distribute the models over the available processes in a parallel run. Expects an array sized. More...
 
subroutine validate_load_balance (this)
 Check validity of load balance configuration. More...
 
subroutine destroy (this)
 clean up More...
 

Variables

class(distributedsimtype), pointer, private dist_sim => null()
 

Function/Subroutine Documentation

◆ create()

subroutine distributedsimmodule::create ( class(distributedsimtype this)
private

Definition at line 56 of file DistributedSim.f90.

57  class(DistributedSimType) :: this
58  ! local
59  character(len=LENMEMPATH) :: input_mempath
60  integer(I4B), pointer :: nmod
61 
62  this%memory_path = create_mem_path(component='SIM')
63 
64  input_mempath = create_mem_path(component='SIM', context=idm_context)
65  call mem_setptr(nmod, 'NUMMODELS', input_mempath)
66 
67  call mem_allocate(this%nr_models, 'NUMMODELS', this%memory_path)
68  this%nr_models = nmod
69 
Here is the call graph for this function:

◆ create_load_mask()

subroutine distributedsimmodule::create_load_mask ( class(distributedsimtype this)
private

Definition at line 94 of file DistributedSim.f90.

95  class(DistributedSimType) :: this
96  ! local
97  integer(I4B), dimension(:), pointer :: model_ranks => null() !< the load balance
98  integer(I4B) :: m_id !< model id
99 
100  call mem_allocate(this%load_mask, this%nr_models, 'LOADMASK', &
101  this%memory_path)
102  this%load_mask = 0
103 
104  ! get load balance (probably the first call, so creates it)
105  model_ranks => this%get_load_balance()
106 
107  ! set mask from balance
108  do m_id = 1, this%nr_models
109  if (model_ranks(m_id) == proc_id) then
110  this%load_mask(m_id) = 1
111  else
112  this%load_mask(m_id) = 0
113  end if
114  end do
115 

◆ destroy()

subroutine distributedsimmodule::destroy ( class(distributedsimtype this)
private

Definition at line 455 of file DistributedSim.f90.

456  class(DistributedSimType) :: this
457 
458  call mem_deallocate(this%load_mask)
459  call mem_deallocate(this%model_ranks)
460 
461  call mem_deallocate(this%nr_models)
462 
463  ! delete singleton instance
464  if (associated(dist_sim)) deallocate (dist_sim)
465 

◆ get_dsim()

class(distributedsimtype) function, pointer, public distributedsimmodule::get_dsim

Definition at line 43 of file DistributedSim.f90.

44  class(DistributedSimType), pointer :: ds
45 
46  if (.not. associated(dist_sim)) then
47  allocate (dist_sim)
48  call dist_sim%create()
49  end if
50  ds => dist_sim
51 
Here is the caller graph for this function:

◆ get_load_balance()

integer(i4b) function, dimension(:), pointer distributedsimmodule::get_load_balance ( class(distributedsimtype this)
private
Parameters
thisthis distributed sim instance
Returns
the load balance: array of ranks per model id

Definition at line 120 of file DistributedSim.f90.

121  class(DistributedSimType) :: this !< this distributed sim instance
122  integer(I4B), dimension(:), pointer :: mranks !< the load balance: array of ranks per model id
123  ! local
124  integer(I4B) :: isize
125  character(len=LENMEMPATH) :: hpc_mempath
126 
127  ! if load balance available, return here:
128  if (associated(this%model_ranks)) then
129  mranks => this%model_ranks
130  return
131  end if
132 
133  call mem_allocate(this%model_ranks, this%nr_models, 'MODELRANKS', &
134  this%memory_path)
135 
136  ! check if exists (partitions block is optional in HPC file)
137  hpc_mempath = create_mem_path('UTL', 'HPC', idm_context)
138  call get_isize('MNAME', hpc_mempath, isize)
139 
140  if (isize > 0) then
141  ! HPC file present
142  if (nr_procs == 1) then
143  write (warnmsg, *) "Ignoring PARTITIONS block in HPC file when "// &
144  "running a serial process"
145  call store_warning(warnmsg)
146 
147  ! single process, everything on cpu 0:
148  this%model_ranks = 0
149  else
150  ! set balance from HPC file
151  call this%set_load_balance_from_input()
152  ! check if valid configuration
153  call this%validate_load_balance()
154  end if
155  else
156  ! no HPC file present
157  if (nr_procs == 1) then
158  ! single process, everything on cpu 0:
159  this%model_ranks = 0
160  else
161  ! set balance from default algorithm
162  call this%set_load_balance_default()
163  end if
164  end if
165 
166  mranks => this%model_ranks
167 
Here is the call graph for this function:

◆ get_load_mask()

integer(i4b) function, dimension(:), pointer distributedsimmodule::get_load_mask ( class(distributedsimtype this)
private

Get a load mask to determine which models should be loaded by idm on this process. This is in sync with models create. The mask array is allocated with its size equal to the global number of models. It is returned as (1, 1, 0, 0, ... 0) with each entry being a load mask for the model at the corresponding

Definition at line 81 of file DistributedSim.f90.

82  class(DistributedSimType) :: this
83  integer(I4B), dimension(:), pointer :: load_mask
84 
85  if (.not. associated(this%load_mask)) then
86  call this%create_load_mask()
87  end if
88  load_mask => this%load_mask
89 

◆ set_load_balance_default()

subroutine distributedsimmodule::set_load_balance_default ( class(distributedsimtype this)
private
Parameters
thisthis distributed sim. instance

Definition at line 267 of file DistributedSim.f90.

268  class(DistributedSimType) :: this !< this distributed sim. instance
269  ! local
270  integer(I4B) :: im, imm, ie, ip, cnt
271  integer(I4B) :: nr_models, nr_gwf_models
272  integer(I4B) :: nr_exchanges
273  integer(I4B) :: min_per_proc, nr_left
274  integer(I4B) :: rank
275  integer(I4B), dimension(:), allocatable :: nr_models_proc
276  character(len=LENPACKAGETYPE) :: model_type_str
277  character(len=LENMEMPATH) :: input_mempath
278  type(CharacterStringType), dimension(:), contiguous, &
279  pointer :: mtypes !< model types
280  type(CharacterStringType), dimension(:), contiguous, &
281  pointer :: mnames !< model names
282  type(CharacterStringType), dimension(:), contiguous, &
283  pointer :: etypes !< exg types
284  type(CharacterStringType), dimension(:), contiguous, &
285  pointer :: emnames_a !< model a names
286  type(CharacterStringType), dimension(:), contiguous, &
287  pointer :: emnames_b !< model b names
288 
289  this%model_ranks = 0
290 
291  ! load IDM data
292  input_mempath = create_mem_path('SIM', 'NAM', idm_context)
293  call mem_setptr(mtypes, 'MTYPE', input_mempath)
294  call mem_setptr(mnames, 'MNAME', input_mempath)
295  call mem_setptr(etypes, 'EXGTYPE', input_mempath)
296  call mem_setptr(emnames_a, 'EXGMNAMEA', input_mempath)
297  call mem_setptr(emnames_b, 'EXGMNAMEB', input_mempath)
298 
299  ! count flow models
300  nr_models = size(mnames)
301  nr_gwf_models = 0
302  do im = 1, nr_models
303  if (mtypes(im) == 'GWF6') then
304  nr_gwf_models = nr_gwf_models + 1
305  end if
306 
307  if (mtypes(im) == 'GWF6' .or. &
308  mtypes(im) == 'GWT6' .or. &
309  mtypes(im) == 'GWE6') then
310  cycle
311  end if
312 
313  model_type_str = mtypes(im)
314  write (errmsg, *) 'Model type ', model_type_str, &
315  ' not supported in parallel mode.'
316  call store_error(errmsg, terminate=.true.)
317  end do
318 
319  ! calculate nr of flow models for each rank
320  allocate (nr_models_proc(nr_procs))
321  min_per_proc = nr_gwf_models / nr_procs
322  nr_left = nr_gwf_models - nr_procs * min_per_proc
323  cnt = 1
324  do ip = 1, nr_procs
325  rank = ip - 1
326  nr_models_proc(ip) = min_per_proc
327  if (rank < nr_left) then
328  nr_models_proc(ip) = nr_models_proc(ip) + 1
329  end if
330  end do
331 
332  ! assign ranks for flow models
333  rank = 0
334  do im = 1, nr_models
335  if (mtypes(im) == 'GWF6') then
336  if (nr_models_proc(rank + 1) == 0) then
337  rank = rank + 1
338  end if
339  this%model_ranks(im) = rank
340  nr_models_proc(rank + 1) = nr_models_proc(rank + 1) - 1
341  end if
342  end do
343 
344  ! match other models to flow
345  nr_exchanges = size(etypes)
346  do im = 1, nr_models
347  if (mtypes(im) == 'GWT6') then
348 
349  ! find match
350  do ie = 1, nr_exchanges
351  if (etypes(ie) == 'GWF6-GWT6' .and. mnames(im) == emnames_b(ie)) then
352  rank = 0
353  do imm = 1, nr_models
354  if (mnames(imm) == emnames_a(ie)) then
355  rank = this%model_ranks(imm)
356  exit
357  end if
358  end do
359  this%model_ranks(im) = rank
360  exit
361  end if
362  end do
363 
364  else if (mtypes(im) == 'GWE6') then
365  do ie = 1, nr_exchanges
366  if (etypes(ie) == 'GWF6-GWE6' .and. mnames(im) == emnames_b(ie)) then
367  rank = 0
368  do imm = 1, nr_models
369  if (mnames(imm) == emnames_a(ie)) then
370  rank = this%model_ranks(imm)
371  exit
372  end if
373  end do
374  this%model_ranks(im) = rank
375  exit
376  end if
377  end do
378 
379  else
380  cycle ! e.g., for a flow model
381  end if
382  end do
383 
384  ! cleanup
385  deallocate (nr_models_proc)
386 
Here is the call graph for this function:

◆ set_load_balance_from_input()

subroutine distributedsimmodule::set_load_balance_from_input ( class(distributedsimtype this)
private
Parameters
thisthis distributed sim instance

Definition at line 172 of file DistributedSim.f90.

173  class(DistributedSimType) :: this !< this distributed sim instance
174  ! local
175  character(len=LENMEMPATH) :: simnam_mempath, hpc_mempath
176  character(len=LENMODELNAME) :: model_name
177  type(CharacterStringType), dimension(:), contiguous, pointer :: mnames !< model names (all) from the simulation nam file
178  type(CharacterStringType), dimension(:), contiguous, pointer :: mnames_hpc !< model names in the hpc file
179  integer(I4B), dimension(:), contiguous, pointer :: mranks_hpc !< rank numbers in the hpc file
180  integer(I4B) :: i, model_idx
181  integer(I4B) :: target_rank
182  integer(I4B), dimension(:), allocatable :: rank_used
183  type(CharacterStringType), dimension(:), contiguous, pointer :: hpc_names !< helper array to get the hpc filename
184  character(len=LINELENGTH) :: hpc_filename !< the HPC option file
185 
186  ! set to uninitialized
187  this%model_ranks = -1
188 
189  ! from IDM
190  simnam_mempath = create_mem_path('SIM', 'NAM', idm_context)
191  hpc_mempath = create_mem_path('UTL', 'HPC', idm_context)
192  call mem_setptr(mnames, 'MNAME', simnam_mempath)
193  call mem_setptr(mnames_hpc, 'MNAME', hpc_mempath)
194  call mem_setptr(mranks_hpc, 'MRANK', hpc_mempath)
195  call mem_setptr(hpc_names, 'HPC6_FILENAME', simnam_mempath)
196 
197  ! FILEIN options give an array, so take the first:
198  hpc_filename = hpc_names(1)
199 
200  ! check: valid model names
201  do i = 1, size(mnames_hpc)
202  if (ifind(mnames, mnames_hpc(i)) == -1) then
203  model_name = mnames_hpc(i)
204  write (errmsg, *) "HPC input error: undefined model name (", &
205  trim(model_name), ")"
206  call store_error(errmsg)
207  end if
208  end do
209  ! check: valid ranks
210  do i = 1, size(mranks_hpc)
211  target_rank = mranks_hpc(i)
212  if (target_rank < 0 .or. target_rank > nr_procs - 1) then
213  model_name = mnames_hpc(i)
214  write (errmsg, '(a,i0,2a)') "HPC input error: invalid target rank (", &
215  target_rank, ") for model ", trim(model_name)
216  call store_error(errmsg)
217  end if
218  end do
219  if (count_errors() > 0) then
220  call store_error_filename(hpc_filename)
221  end if
222 
223  ! construct rank array
224  do i = 1, size(mnames_hpc)
225  model_idx = ifind(mnames, mnames_hpc(i))
226  this%model_ranks(model_idx) = mranks_hpc(i)
227  end do
228 
229  ! check: all models acquired rank
230  do i = 1, size(this%model_ranks)
231  if (this%model_ranks(i) == -1) then
232  model_name = mnames(i)
233  write (errmsg, '(2a)') "HPC input error: no target rank for model ", &
234  trim(model_name)
235  call store_error(errmsg)
236  end if
237  end do
238  if (count_errors() > 0) then
239  call store_error_filename(hpc_filename)
240  end if
241 
242  ! check: no idle ranks
243  allocate (rank_used(nr_procs))
244  rank_used = 0
245  do i = 1, size(this%model_ranks)
246  if (this%model_ranks(i) >= 0 .and. this%model_ranks(i) < nr_procs) then
247  rank_used(this%model_ranks(i) + 1) = 1
248  end if
249  end do
250  do i = 1, size(rank_used)
251  if (rank_used(i) == 0) then
252  write (errmsg, '(a,i0,a)') "HPC input error: rank ", i - 1, &
253  " has no models assigned"
254  call store_error(errmsg)
255  end if
256  end do
257  deallocate (rank_used)
258  if (count_errors() > 0) then
259  call store_error_filename(hpc_filename)
260  end if
261 
Here is the call graph for this function:

◆ validate_load_balance()

subroutine distributedsimmodule::validate_load_balance ( class(distributedsimtype this)
private

Definition at line 391 of file DistributedSim.f90.

392  class(DistributedSimType) :: this
393  character(len=LENMEMPATH) :: input_mempath
394  type(CharacterStringType), dimension(:), contiguous, &
395  pointer :: mtypes !< model types
396  type(CharacterStringType), dimension(:), contiguous, &
397  pointer :: mnames !< model names
398  type(CharacterStringType), dimension(:), contiguous, &
399  pointer :: etypes !< exg types
400  type(CharacterStringType), dimension(:), contiguous, &
401  pointer :: emnames_a !< model a names
402  type(CharacterStringType), dimension(:), contiguous, &
403  pointer :: emnames_b !< model b names
404  type(CharacterStringType), dimension(:), contiguous, &
405  pointer :: hpc_names !< helper array to get hpc filename
406  integer(I4B) :: ie
407  integer(I4B) :: idx_a, idx_b
408  integer(I4B) :: rank_a, rank_b
409  integer(I4B) :: nr_exchanges
410  character(len=LINELENGTH) :: hpc_filename !< the HPC option file
411  character(len=LENMODELNAME) :: name_a, name_b
412  character(len=LINELENGTH) :: exg_type
413 
414  ! load IDM data
415  input_mempath = create_mem_path('SIM', 'NAM', idm_context)
416  call mem_setptr(mtypes, 'MTYPE', input_mempath)
417  call mem_setptr(mnames, 'MNAME', input_mempath)
418  call mem_setptr(etypes, 'EXGTYPE', input_mempath)
419  call mem_setptr(emnames_a, 'EXGMNAMEA', input_mempath)
420  call mem_setptr(emnames_b, 'EXGMNAMEB', input_mempath)
421  call mem_setptr(hpc_names, 'HPC6_FILENAME', input_mempath)
422 
423  ! FILEIN options give an array, so take the first:
424  hpc_filename = hpc_names(1)
425 
426  nr_exchanges = size(etypes)
427 
428  ! loop over exchanges
429  do ie = 1, nr_exchanges
430  if (etypes(ie) == 'GWF6-GWT6' .or. etypes(ie) == 'GWF6-GWE6') then
431  idx_a = ifind(mnames, emnames_a(ie))
432  idx_b = ifind(mnames, emnames_b(ie))
433  rank_a = this%model_ranks(idx_a)
434  rank_b = this%model_ranks(idx_b)
435  if (rank_a /= rank_b) then
436  name_a = emnames_a(ie)
437  name_b = emnames_b(ie)
438  exg_type = etypes(ie)
439  write (errmsg, '(7a)') "HPC input error: models ", &
440  trim(name_a), " and ", trim(name_b), " with a ", &
441  trim(exg_type), " coupling have to be assigned to the same rank"
442  call store_error(errmsg)
443  end if
444  end if
445  end do
446 
447  if (count_errors() > 0) then
448  call store_error_filename(hpc_filename)
449  end if
450 
Here is the call graph for this function:

Variable Documentation

◆ dist_sim

class(distributedsimtype), pointer, private distributedsimmodule::dist_sim => null()
private

Definition at line 37 of file DistributedSim.f90.

37  class(DistributedSimType), private, pointer :: dist_sim => null()