MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
swf-dfw.f90
Go to the documentation of this file.
1 !> @brief Stream Network Flow (SWF) Diffusive Wave (DFW) Module
2 !!
3 !! This module solves one-dimensional flow routing using a diffusive
4 !< wave approach.
5 module swfdfwmodule
6 
7  use kindmodule, only: dp, i4b, lgp
9  dzero, dhalf, done, dtwo, &
11  dprec, dem10
15  use simvariablesmodule, only: errmsg
19  use basedismodule, only: disbasetype
20  use swfcxsmodule, only: swfcxstype
21  use obsmodule, only: obstype, obs_cr
22  use observemodule, only: observetype
24 
25  implicit none
26  private
27  public :: swfdfwtype, dfw_cr
28 
29  !> @brief flags indicating which DFW options were found in the input
30  type :: dfwoptionsfoundtype
31  logical(LGP) :: icentral = .false.
32  logical(LGP) :: iswrcond = .false.
33  logical(LGP) :: lengthconv = .false.
34  logical(LGP) :: timeconv = .false.
35  logical(LGP) :: iprflow = .false.
36  logical(LGP) :: ipakcb = .false.
37  logical(LGP) :: isavvelocity = .false.
38  logical(LGP) :: obs6_filename = .false.
39  end type dfwoptionsfoundtype
40 
41  !> @brief flags indicating which DFW griddata were found in the input
42  type :: dfwgriddatafoundtype
43  logical(LGP) :: manningsn = .false.
44  logical(LGP) :: idcxs = .false.
45  end type dfwgriddatafoundtype
46 
47  type, extends(numericalpackagetype) :: swfdfwtype
48 
49  ! user-provided input
50  integer(I4B), pointer :: is2d => null() !< flag to indicate this model is 2D overland flow and not 1d channel flow
51  integer(I4B), pointer :: icentral => null() !< flag to use central in space weighting (default is upstream weighting)
52  integer(I4B), pointer :: iswrcond => null() !< flag to activate the dev SWR conductance formulation
53  real(DP), pointer :: unitconv !< conversion factor used in mannings equation; calculated from timeconv and lengthconv
54  real(DP), pointer :: timeconv !< conversion factor from model length units to meters (1.0 if model uses meters for length)
55  real(DP), pointer :: lengthconv !< conversion factor from model time units to seconds (1.0 if model uses seconds for time)
56  real(DP), dimension(:), pointer, contiguous :: hnew => null() !< pointer to model xnew
57  real(DP), dimension(:), pointer, contiguous :: manningsn => null() !< mannings roughness for each reach
58  integer(I4B), dimension(:), pointer, contiguous :: idcxs => null() !< cross section id for each reach
59  integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !< pointer to model ibound
60  integer(I4B), dimension(:), pointer, contiguous :: icelltype => null() !< set to 1 and is accessed by chd for checking
61 
62  ! velocity
63  integer(I4B), pointer :: icalcvelocity => null() !< flag to indicate velocity will be calculated (always on)
64  integer(I4B), pointer :: isavvelocity => null() !< flag to indicate velocity will be saved
65  real(DP), dimension(:, :), pointer, contiguous :: vcomp => null() !< velocity components: vx, vy, vz (nodes, 3)
66  real(DP), dimension(:), pointer, contiguous :: vmag => null() !< velocity magnitude (of size nodes)
67  integer(I4B), pointer :: nedges => null() !< number of cell edges
68  integer(I4B), pointer :: lastedge => null() !< last edge number
69  integer(I4B), dimension(:), pointer, contiguous :: nodedge => null() !< array of node numbers that have edges
70  integer(I4B), dimension(:), pointer, contiguous :: ihcedge => null() !< edge type (horizontal or vertical)
71  real(DP), dimension(:, :), pointer, contiguous :: propsedge => null() !< edge properties (Q, area, nx, ny, distance)
72  real(DP), dimension(:), pointer, contiguous :: grad_dhds_mag => null() !< magnitude of the gradient (of size nodes)
73  real(DP), dimension(:), pointer, contiguous :: dhdsja => null() !< gradient for each connection (of size njas)
74 
75  ! observation data
76  integer(I4B), pointer :: inobspkg => null() !< unit number for obs package
77  type(ObsType), pointer :: obs => null() !< observation package
78 
79  ! pointer to cross section data
80  type(SwfCxsType), pointer :: cxs
81 
82  contains
83 
84  procedure :: dfw_df
85  procedure :: allocate_scalars
86  procedure :: allocate_arrays
87  procedure :: dfw_load
88  procedure :: source_options
89  procedure :: log_options
90  procedure :: source_griddata
91  procedure :: log_griddata
92  procedure :: dfw_ar
93  procedure :: dfw_rp
94  procedure :: dfw_ad
95  procedure :: dfw_fc
96  procedure :: dfw_qnm_fc_nr
97  !procedure :: dfw_qnm_fc
98  procedure :: dfw_fn
99  procedure :: dfw_nur
100  procedure :: dfw_cq
101  procedure :: dfw_bd
102  procedure :: dfw_save_model_flows
103  procedure :: dfw_print_model_flows
104  procedure :: dfw_da
105  procedure :: dfw_df_obs
106  procedure :: dfw_rp_obs
107  procedure :: dfw_bd_obs
108  procedure :: qcalc
109  procedure :: get_cond
110  procedure :: get_cond_swr
111  procedure :: get_cond_n
112  procedure :: get_flow_area_nm
113  procedure :: calc_velocity
114  procedure :: sav_velocity
115  procedure, public :: increase_edge_count
116  procedure, public :: set_edge_properties
117  procedure :: calc_dhds
118  procedure :: write_cxs_tables
119 
120  end type swfdfwtype
121 
122 contains
123 
124  !> @brief create package
125  !<
126  subroutine dfw_cr(dfwobj, name_model, input_mempath, inunit, iout, &
127  cxs)
128  ! modules
130  ! dummy
131  type(SwfDfwType), pointer :: dfwobj !< object to create
132  character(len=*), intent(in) :: name_model !< name of the SWF model
133  character(len=*), intent(in) :: input_mempath !< memory path
134  integer(I4B), intent(in) :: inunit !< flag to indicate if package is active
135  integer(I4B), intent(in) :: iout !< unit number for output
136  type(SwfCxsType), pointer, intent(in) :: cxs !< the pointer to the cxs package
137  ! locals
138  logical(LGP) :: found_fname
139  ! formats
140  character(len=*), parameter :: fmtheader = &
141  "(1x, /1x, 'DFW -- DIFFUSIVE WAVE (DFW) PACKAGE, VERSION 1, 9/25/2023', &
142  &' INPUT READ FROM MEMPATH: ', A, /)"
143  !
144  ! Create the object
145  allocate (dfwobj)
146 
147  ! create name and memory path
148  call dfwobj%set_names(1, name_model, 'DFW', 'DFW')
149 
150  ! Allocate scalars
151  call dfwobj%allocate_scalars()
152 
153  ! Set variables
154  dfwobj%input_mempath = input_mempath
155  dfwobj%inunit = inunit
156  dfwobj%iout = iout
157 
158  ! set name of input file
159  call mem_set_value(dfwobj%input_fname, 'INPUT_FNAME', dfwobj%input_mempath, &
160  found_fname)
161 
162  ! Set a pointers to passed in objects
163  dfwobj%cxs => cxs
164 
165  ! create obs package
166  call obs_cr(dfwobj%obs, dfwobj%inobspkg)
167 
168  ! check if dfw is enabled
169  if (inunit > 0) then
170 
171  ! Print a message identifying the package.
172  write (iout, fmtheader) input_mempath
173 
174  end if
175 
176  end subroutine dfw_cr
177 
178  !> @brief load data from IDM to package
179  !<
180  subroutine dfw_df(this, dis)
181  ! dummy
182  class(SwfDfwType) :: this !< this instance
183  class(DisBaseType), pointer, intent(inout) :: dis !< the pointer to the discretization
184 
185  ! Set a pointers to passed in objects
186  this%dis => dis
187 
188  ! Set the distype (either DISV1D or DIS2D)
189  if (this%dis%is_2d()) then
190  this%is2d = 1
191  end if
192 
193  ! check if dfw is enabled
194  ! this will need to become if (.not. present(dfw_options)) then
195  !if (inunit > 0) then
196 
197  ! allocate arrays
198  call this%allocate_arrays()
199 
200  ! load dfw
201  call this%dfw_load()
202 
203  !end if
204 
205  end subroutine dfw_df
206 
207  !> @ brief Allocate scalars
208  !!
209  !! Allocate and initialize scalars for the package. The base model
210  !! allocate scalars method is also called.
211  !!
212  !<
213  subroutine allocate_scalars(this)
214  ! modules
215  ! dummy
216  class(SwfDfwtype) :: this !< this instance
217  !
218  ! allocate scalars in NumericalPackageType
219  call this%NumericalPackageType%allocate_scalars()
220  !
221  ! Allocate scalars
222  call mem_allocate(this%is2d, 'IS2D', this%memoryPath)
223  call mem_allocate(this%icentral, 'ICENTRAL', this%memoryPath)
224  call mem_allocate(this%iswrcond, 'ISWRCOND', this%memoryPath)
225  call mem_allocate(this%unitconv, 'UNITCONV', this%memoryPath)
226  call mem_allocate(this%lengthconv, 'LENGTHCONV', this%memoryPath)
227  call mem_allocate(this%timeconv, 'TIMECONV', this%memoryPath)
228  call mem_allocate(this%inobspkg, 'INOBSPKG', this%memoryPath)
229  call mem_allocate(this%icalcvelocity, 'ICALCVELOCITY', this%memoryPath)
230  call mem_allocate(this%isavvelocity, 'ISAVVELOCITY', this%memoryPath)
231  call mem_allocate(this%nedges, 'NEDGES', this%memoryPath)
232  call mem_allocate(this%lastedge, 'LASTEDGE', this%memoryPath)
233 
234  this%is2d = 0
235  this%icentral = 0
236  this%iswrcond = 0
237  this%unitconv = done
238  this%lengthconv = done
239  this%timeconv = done
240  this%inobspkg = 0
241  this%icalcvelocity = 0
242  this%isavvelocity = 0
243  this%nedges = 0
244  this%lastedge = 0
245 
246  end subroutine allocate_scalars
247 
248  !> @brief allocate memory for arrays
249  !<
250  subroutine allocate_arrays(this)
251  ! dummy
252  class(SwfDfwType) :: this !< this instance
253  ! locals
254  integer(I4B) :: n
255  !
256  ! user-provided input
257  call mem_allocate(this%manningsn, this%dis%nodes, &
258  'MANNINGSN', this%memoryPath)
259  call mem_allocate(this%idcxs, this%dis%nodes, &
260  'IDCXS', this%memoryPath)
261  call mem_allocate(this%icelltype, this%dis%nodes, &
262  'ICELLTYPE', this%memoryPath)
263 
264  ! optional arrays
265  call mem_allocate(this%nodedge, 0, 'NODEDGE', this%memoryPath)
266  call mem_allocate(this%ihcedge, 0, 'IHCEDGE', this%memoryPath)
267  call mem_allocate(this%propsedge, 0, 0, 'PROPSEDGE', this%memoryPath)
268 
269  ! Specific discharge is (re-)allocated when nedges is known
270  call mem_allocate(this%vcomp, 3, 0, 'VCOMP', this%memoryPath)
271  call mem_allocate(this%vmag, 0, 'VMAG', this%memoryPath)
272 
273  do n = 1, this%dis%nodes
274  this%manningsn(n) = dzero
275  this%idcxs(n) = 0
276  this%icelltype(n) = 1
277  end do
278 
279  ! for 2d models, need to calculate and store dhds magnitude
280  if (this%is2d == 1) then
281  call mem_allocate(this%grad_dhds_mag, this%dis%nodes, &
282  'GRAD_DHDS_MAG', this%memoryPath)
283  call mem_allocate(this%dhdsja, this%dis%njas, &
284  'DHDSJA', this%memoryPath)
285  do n = 1, this%dis%nodes
286  this%grad_dhds_mag(n) = dzero
287  end do
288  do n = 1, this%dis%njas
289  this%dhdsja(n) = dzero
290  end do
291  end if
292 
293  end subroutine allocate_arrays
294 
295  !> @brief load data from IDM to package
296  !<
297  subroutine dfw_load(this)
298  ! dummy
299  class(SwfDfwType) :: this !< this instance
300 
301  ! source input data
302  call this%source_options()
303  call this%source_griddata()
304 
305  end subroutine dfw_load
306 
307  !> @brief Copy options from IDM into package
308  !<
309  subroutine source_options(this)
310  ! modules
311  use kindmodule, only: lgp
315  ! dummy
316  class(SwfDfwType) :: this !< this instance
317  ! locals
318  integer(I4B) :: isize
319  type(DfwOptionsFoundType) :: found
320  type(CharacterStringType), dimension(:), pointer, &
321  contiguous :: obs6_fnames
322 
323  ! update defaults with idm sourced values
324  call mem_set_value(this%icentral, 'ICENTRAL', &
325  this%input_mempath, found%icentral)
326  call mem_set_value(this%iswrcond, 'ISWRCOND', &
327  this%input_mempath, found%iswrcond)
328  call mem_set_value(this%lengthconv, 'LENGTHCONV', &
329  this%input_mempath, found%lengthconv)
330  call mem_set_value(this%timeconv, 'TIMECONV', &
331  this%input_mempath, found%timeconv)
332  call mem_set_value(this%iprflow, 'IPRFLOW', &
333  this%input_mempath, found%iprflow)
334  call mem_set_value(this%ipakcb, 'IPAKCB', &
335  this%input_mempath, found%ipakcb)
336  call mem_set_value(this%isavvelocity, 'ISAVVELOCITY', &
337  this%input_mempath, found%isavvelocity)
338 
339  ! save flows option active
340  if (found%icentral) this%icentral = 1
341  if (found%ipakcb) this%ipakcb = -1
342 
343  ! calculate unit conversion
344  this%unitconv = this%lengthconv**donethird
345  this%unitconv = this%unitconv * this%timeconv
346 
347  ! save velocity active
348  if (found%isavvelocity) this%icalcvelocity = this%isavvelocity
349 
350  ! check for obs6_filename
351  call get_isize('OBS6_FILENAME', this%input_mempath, isize)
352  if (isize > 0) then
353  !
354  if (isize /= 1) then
355  errmsg = 'Multiple OBS6 keywords detected in OPTIONS block.'// &
356  ' Only one OBS6 entry allowed.'
357  call store_error(errmsg)
358  call store_error_filename(this%input_fname)
359  end if
360 
361  call mem_setptr(obs6_fnames, 'OBS6_FILENAME', this%input_mempath)
362 
363  found%obs6_filename = .true.
364  this%obs%inputFilename = obs6_fnames(1)
365  this%obs%active = .true.
366  this%inobspkg = getunit()
367  this%obs%inUnitObs = this%inobspkg
368  call openfile(this%inobspkg, this%iout, this%obs%inputFilename, 'OBS')
369  call this%obs%obs_df(this%iout, this%packName, this%filtyp, this%dis)
370  call this%dfw_df_obs()
371 
372  call memorystore_release('OBS6_FILENAME', this%input_mempath)
373  end if
374 
375  ! log values to list file
376  if (this%iout > 0) then
377  call this%log_options(found)
378  end if
379 
380  end subroutine source_options
381 
382  !> @brief Write user options to list file
383  !<
384  subroutine log_options(this, found)
385  class(SwfDfwType) :: this !< this instance
386  type(DfwOptionsFoundType), intent(in) :: found
387 
388  write (this%iout, '(1x,a)') 'Setting DFW Options'
389 
390  if (found%lengthconv) then
391  write (this%iout, '(4x,a, G0)') 'Mannings length conversion value &
392  &specified as ', this%lengthconv
393  end if
394 
395  if (found%timeconv) then
396  write (this%iout, '(4x,a, G0)') 'Mannings time conversion value &
397  &specified as ', this%timeconv
398  end if
399 
400  if (found%lengthconv .or. found%timeconv) then
401  write (this%iout, '(4x,a, G0)') 'Mannings conversion value calculated &
402  &from user-provided length_conversion and &
403  &time_conversion is ', this%unitconv
404  end if
405 
406  if (found%iprflow) then
407  write (this%iout, '(4x,a)') 'Cell-by-cell flow information will be printed &
408  &to listing file whenever ICBCFL is not zero.'
409  end if
410 
411  if (found%ipakcb) then
412  write (this%iout, '(4x,a)') 'Cell-by-cell flow information will be printed &
413  &to listing file whenever ICBCFL is not zero.'
414  end if
415 
416  if (found%obs6_filename) then
417  write (this%iout, '(4x,a)') 'Observation package is active.'
418  end if
419 
420  if (found%isavvelocity) &
421  write (this%iout, '(4x,a)') 'Velocity will be calculated at cell &
422  &centers and written to DATA-VCOMP in budget &
423  &file when requested.'
424 
425  if (found%iswrcond) then
426  write (this%iout, '(4x,a, G0)') 'Conductance will be calculated using &
427  &the SWR development option.'
428  end if
429 
430  write (this%iout, '(1x,a,/)') 'End Setting DFW Options'
431 
432  end subroutine log_options
433 
434  !> @brief copy griddata from IDM to package
435  !<
436  subroutine source_griddata(this)
437  ! modules
442  ! dummy
443  class(SwfDfwType) :: this !< this instance
444  ! locals
445  character(len=LENMEMPATH) :: idmMemoryPath
446  type(DfwGriddataFoundType) :: found
447  integer(I4B), dimension(:), pointer, contiguous :: map
448 
449  ! set memory path
450  idmmemorypath = create_mem_path(this%name_model, 'DFW', idm_context)
451 
452  ! set map to convert user input data into reduced data
453  map => null()
454  if (this%dis%nodes < this%dis%nodesuser) map => this%dis%nodeuser
455 
456  ! update defaults with idm sourced values
457  call mem_set_value(this%manningsn, 'MANNINGSN', &
458  idmmemorypath, map, found%manningsn)
459  call mem_set_value(this%idcxs, 'IDCXS', idmmemorypath, map, found%idcxs)
460 
461  ! ensure MANNINGSN was found
462  if (.not. found%manningsn) then
463  write (errmsg, '(a)') 'Error in GRIDDATA block: MANNINGSN not found.'
464  call store_error(errmsg)
465  end if
466 
467  if (count_errors() > 0) then
468  call store_error_filename(this%input_fname)
469  end if
470 
471  ! log griddata
472  if (this%iout > 0) then
473  call this%log_griddata(found)
474  end if
475 
476  end subroutine source_griddata
477 
478  !> @brief log griddata to list file
479  !<
480  subroutine log_griddata(this, found)
481  class(SwfDfwType) :: this !< this instance
482  type(DfwGriddataFoundType), intent(in) :: found
483 
484  write (this%iout, '(1x,a)') 'Setting DFW Griddata'
485 
486  if (found%manningsn) then
487  write (this%iout, '(4x,a)') 'MANNINGSN set from input file'
488  end if
489 
490  if (found%idcxs) then
491  write (this%iout, '(4x,a)') 'IDCXS set from input file'
492  end if
493 
494  call this%write_cxs_tables()
495 
496  write (this%iout, '(1x,a,/)') 'End Setting DFW Griddata'
497 
498  end subroutine log_griddata
499 
500  subroutine write_cxs_tables(this)
501  ! modules
502  ! dummy
503  class(SwfDfwType) :: this !< this instance
504  ! local
505  ! integer(I4B) :: idcxs
506  ! integer(I4B) :: n
507 
508  !-- TODO: write cross section tables
509  ! do n = 1, this%dis%nodes
510  ! idcxs = this%idcxs(n)
511  ! if (idcxs > 0) then
512  ! call this%cxs%write_cxs_table(idcxs, this%width(n), this%slope(n), &
513  ! this%manningsn(n), this%unitconv)
514  ! end if
515  ! end do
516  end subroutine write_cxs_tables
517 
518  !> @brief allocate memory
519  !<
520  subroutine dfw_ar(this, ibound, hnew)
521  ! modules
522  ! dummy
523  class(SwfDfwType) :: this !< this instance
524  integer(I4B), dimension(:), pointer, contiguous :: ibound !< model ibound array
525  real(DP), dimension(:), pointer, contiguous, intent(inout) :: hnew !< pointer to model head array
526  ! local
527  integer(I4B) :: n
528 
529  ! store pointer to ibound
530  this%ibound => ibound
531  this%hnew => hnew
532 
533  if (this%icalcvelocity == 1) then
534  call mem_reallocate(this%vcomp, 3, this%dis%nodes, 'VCOMP', this%memoryPath)
535  call mem_reallocate(this%vmag, this%dis%nodes, 'VMAG', this%memoryPath)
536  call mem_reallocate(this%nodedge, this%nedges, 'NODEDGE', this%memoryPath)
537  call mem_reallocate(this%ihcedge, this%nedges, 'IHCEDGE', this%memoryPath)
538  call mem_reallocate(this%propsedge, 5, this%nedges, 'PROPSEDGE', &
539  this%memoryPath)
540  do n = 1, this%dis%nodes
541  this%vcomp(:, n) = dzero
542  this%vmag(n) = dzero
543  end do
544  end if
545 
546  ! observation data
547  call this%obs%obs_ar()
548 
549  end subroutine dfw_ar
550 
551  !> @brief allocate memory
552  !<
553  subroutine dfw_rp(this)
554  ! modules
555  ! dummy
556  class(SwfDfwType) :: this !< this instance
557 
558  ! read observations
559  call this%dfw_rp_obs()
560 
561  end subroutine dfw_rp
562 
563  !> @brief advance
564  !<
565  subroutine dfw_ad(this, irestore)
566  class(SwfDfwType) :: this !< this instance
567  integer(I4B), intent(in) :: irestore !< ATS flag for retrying time step (1) or advancing (0)
568 
569  ! Push simulated values to preceding time/subtime step
570  call this%obs%obs_ad()
571 
572  end subroutine dfw_ad
573 
574  !> @brief fill coefficients
575  !!
576  !! The DFW Package is entirely Newton based. All matrix and rhs terms
577  !! are added from thish routine.
578  !!
579  !<
580  subroutine dfw_fc(this, kiter, matrix_sln, idxglo, rhs, stage, stage_old)
581  ! modules
582  ! dummy
583  class(SwfDfwType) :: this !< this instance
584  integer(I4B) :: kiter
585  class(MatrixBaseType), pointer :: matrix_sln
586  integer(I4B), intent(in), dimension(:) :: idxglo
587  real(DP), intent(inout), dimension(:) :: rhs
588  real(DP), intent(inout), dimension(:) :: stage
589  real(DP), intent(inout), dimension(:) :: stage_old
590  ! local
591 
592  ! calculate dhds at cell center for 2d case
593  if (this%is2d == 1) then
594  call this%calc_dhds()
595  end if
596 
597  ! add qnm contributions to matrix equations
598  call this%dfw_qnm_fc_nr(kiter, matrix_sln, idxglo, rhs, stage, stage_old)
599 
600  end subroutine dfw_fc
601 
602  !> @brief fill coefficients
603  !!
604  !< Add qnm contributions to matrix equations
605  subroutine dfw_qnm_fc_nr(this, kiter, matrix_sln, idxglo, rhs, stage, stage_old)
606  ! modules
608  ! dummy
609  class(SwfDfwType) :: this !< this instance
610  integer(I4B) :: kiter
611  class(MatrixBaseType), pointer :: matrix_sln
612  integer(I4B), intent(in), dimension(:) :: idxglo
613  real(DP), intent(inout), dimension(:) :: rhs
614  real(DP), intent(inout), dimension(:) :: stage
615  real(DP), intent(inout), dimension(:) :: stage_old
616  ! local
617  integer(I4B) :: n, m, ii, idiag
618  real(DP) :: qnm
619  real(DP) :: qeps
620  real(DP) :: eps
621  real(DP) :: derv
622 
623  ! Calculate conductance and put into amat
624  do n = 1, this%dis%nodes
625 
626  ! Find diagonal position for row n
627  idiag = this%dis%con%ia(n)
628 
629  ! Loop through connections adding matrix terms
630  do ii = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
631 
632  ! skip for masked cells
633  if (this%dis%con%mask(ii) == 0) cycle
634 
635  ! connection variables
636  m = this%dis%con%ja(ii)
637 
638  ! Fill the qnm term on the right-hand side
639  qnm = this%qcalc(n, m, stage(n), stage(m), ii)
640  rhs(n) = rhs(n) - qnm
641 
642  ! Derivative calculation and fill of n terms
643  eps = get_perturbation(stage(n))
644  qeps = this%qcalc(n, m, stage(n) + eps, stage(m), ii)
645  derv = (qeps - qnm) / eps
646  call matrix_sln%add_value_pos(idxglo(idiag), derv)
647  rhs(n) = rhs(n) + derv * stage(n)
648 
649  ! Derivative calculation and fill of m terms
650  eps = get_perturbation(stage(m))
651  qeps = this%qcalc(n, m, stage(n), stage(m) + eps, ii)
652  derv = (qeps - qnm) / eps
653  call matrix_sln%add_value_pos(idxglo(ii), derv)
654  rhs(n) = rhs(n) + derv * stage(m)
655 
656  end do
657  end do
658 
659  end subroutine dfw_qnm_fc_nr
660 
661  !> @brief fill newton
662  !<
663  subroutine dfw_fn(this, kiter, matrix_sln, idxglo, rhs, stage)
664  ! dummy
665  class(SwfDfwType) :: this !< this instance
666  integer(I4B) :: kiter
667  class(MatrixBaseType), pointer :: matrix_sln
668  integer(I4B), intent(in), dimension(:) :: idxglo
669  real(DP), intent(inout), dimension(:) :: rhs
670  real(DP), intent(inout), dimension(:) :: stage
671  ! local
672 
673  ! add newton terms to solution matrix
674  ! todo: add newton terms here instead?
675  ! this routine is probably not necessary as method is fully newton
676 
677  end subroutine dfw_fn
678 
679  !> @brief calculate flow between cells n and m
680  !<
681  function qcalc(this, n, m, stage_n, stage_m, ipos) result(qnm)
682  ! dummy
683  class(SwfDfwType) :: this !< this instance
684  integer(I4B), intent(in) :: n !< number for cell n
685  integer(I4B), intent(in) :: m !< number for cell m
686  real(DP), intent(in) :: stage_n !< stage in reach n
687  real(DP), intent(in) :: stage_m !< stage in reach m
688  integer(I4B), intent(in) :: ipos !< connection number
689  ! local
690  integer(I4B) :: isympos
691  real(DP) :: qnm
692  real(DP) :: cond
693  real(DP) :: cl1
694  real(DP) :: cl2
695 
696  ! Set connection lengths
697  isympos = this%dis%con%jas(ipos)
698  if (n < m) then
699  cl1 = this%dis%con%cl1(isympos)
700  cl2 = this%dis%con%cl2(isympos)
701  else
702  cl1 = this%dis%con%cl2(isympos)
703  cl2 = this%dis%con%cl1(isympos)
704  end if
705 
706  ! Calculate conductance
707  if (this%iswrcond == 0) then
708  cond = this%get_cond(n, m, ipos, stage_n, stage_m, cl1, cl2)
709  else if (this%iswrcond == 1) then
710  cond = this%get_cond_swr(n, m, ipos, stage_n, stage_m, cl1, cl2)
711  end if
712 
713  ! calculate flow between n and m
714  qnm = cond * (stage_m - stage_n)
715 
716  end function qcalc
717 
718  !> @brief calculate effective conductance between cells n and m
719  !!
720  !! Calculate half-cell conductances for cell n and cell m and then use
721  !! harmonic averaging to calculate the effective conductance between the
722  !< two cells.
723  function get_cond(this, n, m, ipos, stage_n, stage_m, cln, clm) result(cond)
724  ! modules
725  use smoothingmodule, only: squadratic
726  ! dummy
727  class(SwfDfwType) :: this !< this instance
728  integer(I4B), intent(in) :: n !< number for cell n
729  integer(I4B), intent(in) :: m !< number for cell m
730  integer(I4B), intent(in) :: ipos !< connection number
731  real(DP), intent(in) :: stage_n !< stage in reach n
732  real(DP), intent(in) :: stage_m !< stage in reach m
733  real(DP), intent(in) :: cln !< distance from cell n to shared face with m
734  real(DP), intent(in) :: clm !< distance from cell m to shared face with n
735  ! local
736  real(DP) :: depth_n
737  real(DP) :: depth_m
738  real(DP) :: dhds_n
739  real(DP) :: dhds_m
740  real(DP) :: width_n
741  real(DP) :: width_m
742  real(DP) :: range = 1.d-6
743  real(DP) :: dydx
744  real(DP) :: smooth_factor
745  real(DP) :: length_nm
746  real(DP) :: cond
747  real(DP) :: cn
748  real(DP) :: cm
749 
750  ! we are using a harmonic conductance approach here; however
751  ! the SWR Process for MODFLOW-2005/NWT uses length-weighted
752  ! average areas and hydraulic radius instead.
753  length_nm = cln + clm
754  cond = dzero
755  if (length_nm > dprec) then
756 
757  ! Calculate depth in each reach
758  depth_n = stage_n - this%dis%bot(n)
759  depth_m = stage_m - this%dis%bot(m)
760 
761  ! assign gradients
762  if (this%is2d == 0) then
763  dhds_n = abs(stage_m - stage_n) / (cln + clm)
764  dhds_m = dhds_n
765  else
766  dhds_n = this%grad_dhds_mag(n)
767  dhds_m = this%grad_dhds_mag(m)
768  end if
769 
770  ! Assign upstream depth, if not central
771  if (this%icentral == 0) then
772  ! use upstream weighting
773  if (stage_n > stage_m) then
774  depth_m = depth_n
775  else
776  depth_n = depth_m
777  end if
778  end if
779 
780  ! Calculate a smoothed depth that goes to zero over
781  ! the specified range
782  call squadratic(depth_n, range, dydx, smooth_factor)
783  depth_n = depth_n * smooth_factor
784  call squadratic(depth_m, range, dydx, smooth_factor)
785  depth_m = depth_m * smooth_factor
786 
787  ! Get the flow widths for n and m from dis package
788  call this%dis%get_flow_width(n, m, ipos, width_n, width_m)
789 
790  ! Calculate half-cell conductance for reach
791  ! n and m
792  cn = this%get_cond_n(n, depth_n, cln, width_n, dhds_n)
793  cm = this%get_cond_n(m, depth_m, clm, width_m, dhds_m)
794 
795  ! Use harmonic mean to calculate weighted
796  ! conductance between the centers of reaches
797  ! n and m
798  if ((cn + cm) > dprec) then
799  cond = cn * cm / (cn + cm)
800  else
801  cond = dzero
802  end if
803 
804  end if
805 
806  end function get_cond
807 
808  !> @brief Calculate half cell conductance
809  !!
810  !! Calculate half-cell conductance for cell n
811  !< using conveyance and Manning's equation
812  function get_cond_n(this, n, depth, dx, width, dhds) result(c)
813  ! modules
814  ! dummy
815  class(SwfDfwType) :: this !< this instance
816  integer(I4B), intent(in) :: n !< reach number
817  real(DP), intent(in) :: depth !< simulated depth (stage - elevation) in reach n for this iteration
818  real(DP), intent(in) :: dx !< half-cell distance
819  real(DP), intent(in) :: width !< width of the reach perpendicular to flow
820  real(DP), intent(in) :: dhds !< gradient
821  ! return
822  real(DP) :: c
823  ! local
824  real(DP) :: rough
825  real(DP) :: dhds_sqr
826  real(DP) :: conveyance
827 
828  ! Calculate conveyance, which is a * r**DTWOTHIRDS / roughc
829  rough = this%manningsn(n)
830  conveyance = this%cxs%get_conveyance(this%idcxs(n), width, depth, rough)
831  dhds_sqr = dhds**dhalf
832  if (dhds_sqr < dem10) then
833  dhds_sqr = dem10
834  end if
835 
836  ! Multiply by unitconv and divide conveyance by sqrt of friction slope and dx
837  c = this%unitconv * conveyance / dx / dhds_sqr
838 
839  end function get_cond_n
840 
841  !> @brief Calculate effective conductance for cells n and m using SWR method
842  !!
843  !! The SWR Process for MODFLOW uses average cell parameters from cell n and
844  !! m to calculate an effective conductance. This is different from the
845  !! default approach used in SWF, which uses harmonic averaging on two half-
846  !< cell conductances.
847  function get_cond_swr(this, n, m, ipos, stage_n, stage_m, cln, clm) result(cond)
848  ! modules
849  use smoothingmodule, only: squadratic
850  ! dummy
851  class(SwfDfwType) :: this !< this instance
852  integer(I4B), intent(in) :: n !< number for cell n
853  integer(I4B), intent(in) :: m !< number for cell m
854  integer(I4B), intent(in) :: ipos !< connection number
855  real(DP), intent(in) :: stage_n !< stage in reach n
856  real(DP), intent(in) :: stage_m !< stage in reach m
857  real(DP), intent(in) :: cln !< distance from cell n to shared face with m
858  real(DP), intent(in) :: clm !< distance from cell m to shared face with n
859  ! local
860  real(DP) :: depth_n
861  real(DP) :: depth_m
862  real(DP) :: dhds_n
863  real(DP) :: dhds_m
864  real(DP) :: dhds_nm
865  real(DP) :: dhds_sqr
866  real(DP) :: width_n
867  real(DP) :: width_m
868  real(DP) :: range = 1.d-6
869  real(DP) :: dydx
870  real(DP) :: smooth_factor
871  real(DP) :: length_nm
872  real(DP) :: cond
873  real(DP) :: ravg
874  real(DP) :: rinv_avg
875  real(DP) :: area_n, area_m, area_avg
876  real(DP) :: rhn, rhm, rhavg
877  real(DP) :: weight_n
878  real(DP) :: weight_m
879  real(DP) :: rough_n
880  real(DP) :: rough_m
881 
882  ! Use harmonic weighting for 1/manningsn, but using length-weighted
883  ! averaging for other terms
884  length_nm = cln + clm
885  cond = dzero
886  if (length_nm > dprec) then
887 
888  ! Calculate depth in each reach
889  depth_n = stage_n - this%dis%bot(n)
890  depth_m = stage_m - this%dis%bot(m)
891 
892  ! Assign upstream depth, if not central
893  if (this%icentral == 0) then
894  ! use upstream weighting
895  if (stage_n > stage_m) then
896  depth_m = depth_n
897  else
898  depth_n = depth_m
899  end if
900  end if
901 
902  ! Calculate a smoothed depth that goes to zero over
903  ! the specified range
904  call squadratic(depth_n, range, dydx, smooth_factor)
905  depth_n = depth_n * smooth_factor
906  call squadratic(depth_m, range, dydx, smooth_factor)
907  depth_m = depth_m * smooth_factor
908 
909  ! Get the flow widths for n and m from dis package
910  call this%dis%get_flow_width(n, m, ipos, width_n, width_m)
911 
912  ! linear weight toward node closer to shared face
913  weight_n = clm / length_nm
914  weight_m = done - weight_n
915 
916  ! average cross sectional flow area
917  area_n = this%cxs%get_area(this%idcxs(n), width_n, depth_n)
918  area_m = this%cxs%get_area(this%idcxs(m), width_m, depth_m)
919  area_avg = weight_n * area_n + weight_m * area_m
920 
921  ! average hydraulic radius
922  if (this%is2d == 0) then
923  rhn = this%cxs%get_hydraulic_radius(this%idcxs(n), width_n, &
924  depth_n, area_n)
925  rhm = this%cxs%get_hydraulic_radius(this%idcxs(m), width_m, &
926  depth_m, area_m)
927  rhavg = weight_n * rhn + weight_m * rhm
928  else
929  rhavg = area_avg / width_n
930  end if
931  rhavg = rhavg**dtwothirds
932 
933  ! average gradient
934  if (this%is2d == 0) then
935  dhds_nm = abs(stage_m - stage_n) / (length_nm)
936  else
937  dhds_n = this%grad_dhds_mag(n)
938  dhds_m = this%grad_dhds_mag(m)
939  dhds_nm = weight_n * dhds_n + weight_m * dhds_m
940  end if
941  dhds_sqr = dhds_nm**dhalf
942  if (dhds_sqr < dem10) then
943  dhds_sqr = dem10
944  end if
945 
946  ! weighted harmonic mean for inverse mannings value
947  weight_n = cln / length_nm
948  weight_m = done - weight_n
949  rough_n = this%cxs%get_roughness(this%idcxs(n), width_n, depth_n, &
950  this%manningsn(n), dhds_nm)
951  rough_m = this%cxs%get_roughness(this%idcxs(m), width_m, depth_m, &
952  this%manningsn(m), dhds_nm)
953  ravg = (weight_n + weight_m) / &
954  (weight_n / rough_n + weight_m / rough_m)
955  rinv_avg = done / ravg
956 
957  ! calculate conductance using averaged values
958  cond = this%unitconv * rinv_avg * area_avg * rhavg / dhds_sqr / length_nm
959 
960  end if
961 
962  end function get_cond_swr
963 
964  !> @brief Calculate flow area between cell n and m
965  !!
966  !! Calculate an average flow area between cell n and m.
967  !! First calculate a flow area for cell n and then for
968  !! cell m and linearly weight the areas using the connection
969  !< distances.
970  function get_flow_area_nm(this, n, m, stage_n, stage_m, cln, clm, &
971  ipos) result(area_avg)
972  ! module
973  use smoothingmodule, only: squadratic
974  ! dummy
975  class(SwfDfwType) :: this !< this instance
976  integer(I4B), intent(in) :: n
977  integer(I4B), intent(in) :: m
978  real(DP), intent(in) :: stage_n
979  real(DP), intent(in) :: stage_m
980  real(DP), intent(in) :: cln
981  real(DP), intent(in) :: clm
982  integer(I4B), intent(in) :: ipos
983  ! local
984  real(DP) :: depth_n
985  real(DP) :: depth_m
986  real(DP) :: width_n
987  real(DP) :: width_m
988  real(DP) :: area_n
989  real(DP) :: area_m
990  real(DP) :: weight_n
991  real(DP) :: weight_m
992  real(DP) :: length_nm
993  real(DP) :: range = 1.d-6
994  real(DP) :: dydx
995  real(DP) :: smooth_factor
996  ! return
997  real(DP) :: area_avg
998 
999  ! depths
1000  depth_n = stage_n - this%dis%bot(n)
1001  depth_m = stage_m - this%dis%bot(m)
1002 
1003  ! Assign upstream depth, if not central
1004  if (this%icentral == 0) then
1005  ! use upstream weighting
1006  if (stage_n > stage_m) then
1007  depth_m = depth_n
1008  else
1009  depth_n = depth_m
1010  end if
1011  end if
1012 
1013  ! Calculate a smoothed depth that goes to zero over
1014  ! the specified range
1015  call squadratic(depth_n, range, dydx, smooth_factor)
1016  depth_n = depth_n * smooth_factor
1017  call squadratic(depth_m, range, dydx, smooth_factor)
1018  depth_m = depth_m * smooth_factor
1019 
1020  ! Get the flow widths for n and m from dis package
1021  call this%dis%get_flow_width(n, m, ipos, width_n, width_m)
1022 
1023  ! linear weight toward node closer to shared face
1024  length_nm = cln + clm
1025  weight_n = clm / length_nm
1026  weight_m = done - weight_n
1027 
1028  ! average cross sectional flow area
1029  area_n = this%cxs%get_area(this%idcxs(n), width_n, depth_n)
1030  area_m = this%cxs%get_area(this%idcxs(m), width_m, depth_m)
1031  area_avg = weight_n * area_n + weight_m * area_m
1032 
1033  end function get_flow_area_nm
1034 
1035  !> @brief Calculate average hydraulic gradient magnitude for each cell
1036  !!
1037  !! Go through each cell and calculate the average hydraulic gradient using
1038  !! an xt3d-style gradient interpolation. This is used for 2D grids in the
1039  !! calculation of an effective conductance. For 1D grids, gradients are
1040  !< calculated between cell centers.
1041  subroutine calc_dhds(this)
1042  ! modules
1044  ! dummy
1045  class(SwfDfwType) :: this !< this instance
1046  ! local
1047  integer(I4B) :: n
1048  integer(I4B) :: m
1049  integer(I4B) :: ipos
1050  integer(I4B) :: isympos
1051  real(DP) :: cl1
1052  real(DP) :: cl2
1053 
1054  do n = 1, this%dis%nodes
1055  this%grad_dhds_mag(n) = dzero
1056  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
1057  m = this%dis%con%ja(ipos)
1058  isympos = this%dis%con%jas(ipos)
1059 
1060  ! determine cl1 and cl2
1061  if (n < m) then
1062  cl1 = this%dis%con%cl1(isympos)
1063  cl2 = this%dis%con%cl2(isympos)
1064  else
1065  cl1 = this%dis%con%cl2(isympos)
1066  cl2 = this%dis%con%cl1(isympos)
1067  end if
1068 
1069  ! store for n < m in upper right triangular part of symmetric dhdsja array
1070  if (n < m) then
1071  if (cl1 + cl2 > dprec) then
1072  this%dhdsja(isympos) = (this%hnew(m) - this%hnew(n)) / (cl1 + cl2)
1073  else
1074  this%dhdsja(isympos) = dzero
1075  end if
1076  end if
1077  end do
1078  end do
1079 
1080  ! pass dhdsja into the vector interpolation to get the components
1081  ! of the gradient at the cell center
1082  call vector_interpolation_2d(this%dis, this%dhdsja, vmag=this%grad_dhds_mag)
1083 
1084  end subroutine calc_dhds
1085 
1086  !> @ brief Newton under relaxation
1087  !!
1088  !< If stage is below the bottom, then pull it up a bit
1089  subroutine dfw_nur(this, neqmod, x, xtemp, dx, inewtonur, dxmax, locmax)
1090  ! dummy
1091  class(SwfDfwType) :: this !< this instance
1092  integer(I4B), intent(in) :: neqmod !< number of equations
1093  real(DP), dimension(neqmod), intent(inout) :: x !< dependent variable
1094  real(DP), dimension(neqmod), intent(in) :: xtemp !< temporary dependent variable
1095  real(DP), dimension(neqmod), intent(inout) :: dx !< change in dependent variable
1096  integer(I4B), intent(inout) :: inewtonur !< flag to indication relaxation was applied
1097  real(DP), intent(inout) :: dxmax !< max change in x
1098  integer(I4B), intent(inout) :: locmax !< location of max change
1099  ! local
1100  integer(I4B) :: n
1101  real(DP) :: botm
1102  real(DP) :: xx
1103  real(DP) :: dxx
1104 
1105  ! Newton-Raphson under-relaxation
1106  do n = 1, this%dis%nodes
1107  if (this%ibound(n) < 1) cycle
1108  if (this%icelltype(n) > 0) then
1109  botm = this%dis%bot(n)
1110  ! only apply Newton-Raphson under-relaxation if
1111  ! solution head is below the bottom of the model
1112  if (x(n) < botm) then
1113  inewtonur = 1
1114  xx = xtemp(n) * (done - dp9) + botm * dp9
1115  dxx = x(n) - xx
1116  if (abs(dxx) > abs(dxmax)) then
1117  locmax = n
1118  dxmax = dxx
1119  end if
1120  x(n) = xx
1121  dx(n) = dzero
1122  end if
1123  end if
1124  end do
1125 
1126  end subroutine dfw_nur
1127 
1128  !> @ brief Calculate flow for each connection and store in flowja
1129  !<
1130  subroutine dfw_cq(this, stage, stage_old, flowja)
1131  ! dummy
1132  class(SwfDfwType) :: this !< this instance
1133  real(DP), intent(inout), dimension(:) :: stage !< calculated head
1134  real(DP), intent(inout), dimension(:) :: stage_old !< calculated head from previous time step
1135  real(DP), intent(inout), dimension(:) :: flowja !< vector of flows in CSR format
1136  ! local
1137  integer(I4B) :: n, ipos, m
1138  real(DP) :: qnm
1139 
1140  do n = 1, this%dis%nodes
1141  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
1142  m = this%dis%con%ja(ipos)
1143  if (m < n) cycle
1144  qnm = this%qcalc(n, m, stage(n), stage(m), ipos)
1145  flowja(ipos) = qnm
1146  flowja(this%dis%con%isym(ipos)) = -qnm
1147  end do
1148  end do
1149 
1150  end subroutine dfw_cq
1151 
1152  !> @ brief Model budget calculation for package
1153  !<
1154  subroutine dfw_bd(this, isuppress_output, model_budget)
1155  ! modules
1156  use budgetmodule, only: budgettype
1157  ! dummy variables
1158  class(SwfDfwType) :: this !< this instance
1159  integer(I4B), intent(in) :: isuppress_output !< flag to suppress model output
1160  type(BudgetType), intent(inout) :: model_budget !< model budget object
1161 
1162  ! Add any DFW budget terms
1163  ! none
1164 
1165  end subroutine dfw_bd
1166 
1167  !> @ brief save flows for package
1168  !<
1169  subroutine dfw_save_model_flows(this, flowja, icbcfl, icbcun)
1170  ! dummy
1171  class(SwfDfwType) :: this !< this instance
1172  real(DP), dimension(:), intent(in) :: flowja !< vector of flows in CSR format
1173  integer(I4B), intent(in) :: icbcfl !< flag to indicate if flows should be saved
1174  integer(I4B), intent(in) :: icbcun !< unit number for flow output
1175  ! local
1176  integer(I4B) :: ibinun
1177 
1178  ! Set unit number for binary output
1179  if (this%ipakcb < 0) then
1180  ibinun = icbcun
1181  elseif (this%ipakcb == 0) then
1182  ibinun = 0
1183  else
1184  ibinun = this%ipakcb
1185  end if
1186  if (icbcfl == 0) ibinun = 0
1187 
1188  ! Write the face flows if requested
1189  if (ibinun /= 0) then
1190  ! flowja
1191  call this%dis%record_connection_array(flowja, ibinun, this%iout)
1192  end if
1193 
1194  ! Calculate velocities at cell centers and write, if requested
1195  if (this%isavvelocity /= 0) then
1196  if (ibinun /= 0) call this%sav_velocity(ibinun)
1197  end if
1198 
1199  end subroutine dfw_save_model_flows
1200 
1201  !> @ brief print flows for package
1202  !<
1203  subroutine dfw_print_model_flows(this, ibudfl, flowja)
1204  ! modules
1205  use tdismodule, only: kper, kstp
1206  use constantsmodule, only: lenbigline
1207  ! dummy
1208  class(SwfDfwType) :: this !< this instance
1209  integer(I4B), intent(in) :: ibudfl !< print flag
1210  real(DP), intent(inout), dimension(:) :: flowja !< vector of flows in CSR format
1211  ! local
1212  character(len=LENBIGLINE) :: line
1213  character(len=30) :: tempstr
1214  integer(I4B) :: n, ipos, m
1215  real(DP) :: qnm
1216  ! formats
1217  character(len=*), parameter :: fmtiprflow = &
1218  &"(/,4x,'CALCULATED INTERCELL FLOW FOR PERIOD ', i0, ' STEP ', i0)"
1219 
1220  ! Write flowja to list file if requested
1221  if (ibudfl /= 0 .and. this%iprflow > 0) then
1222  write (this%iout, fmtiprflow) kper, kstp
1223  do n = 1, this%dis%nodes
1224  line = ''
1225  call this%dis%noder_to_string(n, tempstr)
1226  line = trim(tempstr)//':'
1227  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
1228  m = this%dis%con%ja(ipos)
1229  call this%dis%noder_to_string(m, tempstr)
1230  line = trim(line)//' '//trim(tempstr)
1231  qnm = flowja(ipos)
1232  write (tempstr, '(1pg15.6)') qnm
1233  line = trim(line)//' '//trim(adjustl(tempstr))
1234  end do
1235  write (this%iout, '(a)') trim(line)
1236  end do
1237  end if
1238 
1239  end subroutine dfw_print_model_flows
1240 
1241  !> @brief deallocate memory
1242  !<
1243  subroutine dfw_da(this)
1244  ! modules
1247  use simvariablesmodule, only: idm_context
1248  ! dummy
1249  class(SwfDfwType) :: this !< this instance
1250 
1251  ! Deallocate input memory
1252  call memorystore_remove(this%name_model, 'DFW', idm_context)
1253 
1254  ! Deallocate arrays
1255  call mem_deallocate(this%manningsn)
1256  call mem_deallocate(this%idcxs)
1257  call mem_deallocate(this%icelltype)
1258  call mem_deallocate(this%nodedge)
1259  call mem_deallocate(this%ihcedge)
1260  call mem_deallocate(this%propsedge)
1261  call mem_deallocate(this%vcomp)
1262  call mem_deallocate(this%vmag)
1263  if (this%is2d == 1) then
1264  call mem_deallocate(this%grad_dhds_mag)
1265  call mem_deallocate(this%dhdsja)
1266  end if
1267 
1268  ! Scalars
1269  call mem_deallocate(this%is2d)
1270  call mem_deallocate(this%icentral)
1271  call mem_deallocate(this%iswrcond)
1272  call mem_deallocate(this%unitconv)
1273  call mem_deallocate(this%lengthconv)
1274  call mem_deallocate(this%timeconv)
1275  call mem_deallocate(this%isavvelocity)
1276  call mem_deallocate(this%icalcvelocity)
1277  call mem_deallocate(this%nedges)
1278  call mem_deallocate(this%lastedge)
1279 
1280  ! obs package
1281  call mem_deallocate(this%inobspkg)
1282  call this%obs%obs_da()
1283  deallocate (this%obs)
1284  nullify (this%obs)
1285  nullify (this%cxs)
1286 
1287  ! deallocate parent
1288  call this%NumericalPackageType%da()
1289 
1290  ! pointers
1291  this%hnew => null()
1292 
1293  end subroutine dfw_da
1294 
1295  !> @brief Calculate the 3 components of velocity at the cell center
1296  !!
1297  !! todo: duplicated from NPF; should consolidate
1298  !<
1299  subroutine calc_velocity(this, flowja)
1300  ! modules
1301  ! dummy
1302  class(SwfDfwType) :: this !< this instance
1303  real(DP), intent(in), dimension(:) :: flowja !< vector of flows in CSR format
1304  ! local
1305  integer(I4B) :: n
1306  integer(I4B) :: m
1307  integer(I4B) :: ipos
1308  integer(I4B) :: isympos
1309  integer(I4B) :: ihc
1310  integer(I4B) :: ic
1311  integer(I4B) :: iz
1312  integer(I4B) :: nc
1313  integer(I4B) :: ncz
1314  real(DP) :: vx
1315  real(DP) :: vy
1316  real(DP) :: vz
1317  real(DP) :: xn
1318  real(DP) :: yn
1319  real(DP) :: zn
1320  real(DP) :: xc
1321  real(DP) :: yc
1322  real(DP) :: zc
1323  real(DP) :: cl1
1324  real(DP) :: cl2
1325  real(DP) :: dltot
1326  real(DP) :: ooclsum
1327  real(DP) :: dsumx
1328  real(DP) :: dsumy
1329  real(DP) :: dsumz
1330  real(DP) :: denom
1331  real(DP) :: area
1332  real(DP) :: axy
1333  real(DP) :: ayx
1334  real(DP), allocatable, dimension(:) :: vi
1335  real(DP), allocatable, dimension(:) :: di
1336  real(DP), allocatable, dimension(:) :: viz
1337  real(DP), allocatable, dimension(:) :: diz
1338  real(DP), allocatable, dimension(:) :: nix
1339  real(DP), allocatable, dimension(:) :: niy
1340  real(DP), allocatable, dimension(:) :: wix
1341  real(DP), allocatable, dimension(:) :: wiy
1342  real(DP), allocatable, dimension(:) :: wiz
1343  real(DP), allocatable, dimension(:) :: bix
1344  real(DP), allocatable, dimension(:) :: biy
1345  logical :: nozee = .true.
1346 
1347  ! Ensure dis has necessary information
1348  ! todo: do we need this for SWF?
1349  if (this%icalcvelocity /= 0 .and. this%dis%con%ianglex == 0) then
1350  call store_error('Error. ANGLDEGX not provided in '// &
1351  'discretization file. ANGLDEGX required for '// &
1352  'calculation of velocity.', terminate=.true.)
1353  end if
1354 
1355  ! Find max number of connections and allocate weight arrays
1356  nc = 0
1357  do n = 1, this%dis%nodes
1358 
1359  ! Count internal model connections
1360  ic = this%dis%con%ia(n + 1) - this%dis%con%ia(n) - 1
1361 
1362  ! Count edge connections
1363  do m = 1, this%nedges
1364  if (this%nodedge(m) == n) then
1365  ic = ic + 1
1366  end if
1367  end do
1368 
1369  ! Set max number of connections for any cell
1370  if (ic > nc) nc = ic
1371  end do
1372 
1373  ! Allocate storage arrays needed for cell-centered calculation
1374  allocate (vi(nc))
1375  allocate (di(nc))
1376  allocate (viz(nc))
1377  allocate (diz(nc))
1378  allocate (nix(nc))
1379  allocate (niy(nc))
1380  allocate (wix(nc))
1381  allocate (wiy(nc))
1382  allocate (wiz(nc))
1383  allocate (bix(nc))
1384  allocate (biy(nc))
1385 
1386  ! Go through each cell and calculate specific discharge
1387  do n = 1, this%dis%nodes
1388 
1389  ! first calculate geometric properties for x and y directions and
1390  ! the specific discharge at a face (vi)
1391  ic = 0
1392  iz = 0
1393  vi(:) = dzero
1394  di(:) = dzero
1395  viz(:) = dzero
1396  diz(:) = dzero
1397  nix(:) = dzero
1398  niy(:) = dzero
1399  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
1400  m = this%dis%con%ja(ipos)
1401  isympos = this%dis%con%jas(ipos)
1402  ihc = this%dis%con%ihc(isympos)
1403  ic = ic + 1
1404  call this%dis%connection_normal(n, m, ihc, xn, yn, zn, ipos)
1405  call this%dis%connection_vector(n, m, nozee, done, done, &
1406  ihc, xc, yc, zc, dltot)
1407  cl1 = this%dis%con%cl1(isympos)
1408  cl2 = this%dis%con%cl2(isympos)
1409  if (m < n) then
1410  cl1 = this%dis%con%cl2(isympos)
1411  cl2 = this%dis%con%cl1(isympos)
1412  end if
1413  ooclsum = done / (cl1 + cl2)
1414  nix(ic) = -xn
1415  niy(ic) = -yn
1416  di(ic) = dltot * cl1 * ooclsum
1417  area = this%get_flow_area_nm(n, m, this%hnew(n), this%hnew(m), &
1418  cl1, cl2, ipos)
1419  if (area > dzero) then
1420  vi(ic) = flowja(ipos) / area
1421  else
1422  vi(ic) = dzero
1423  end if
1424 
1425  end do
1426 
1427  ! Look through edge flows that may have been provided by an exchange
1428  ! and incorporate them into the averaging arrays
1429  do m = 1, this%nedges
1430  if (this%nodedge(m) == n) then
1431 
1432  ! propsedge: (Q, area, nx, ny, distance)
1433  ihc = this%ihcedge(m)
1434  area = this%propsedge(2, m)
1435 
1436  ic = ic + 1
1437  nix(ic) = -this%propsedge(3, m)
1438  niy(ic) = -this%propsedge(4, m)
1439  di(ic) = this%propsedge(5, m)
1440  if (area > dzero) then
1441  vi(ic) = this%propsedge(1, m) / area
1442  else
1443  vi(ic) = dzero
1444  end if
1445 
1446  end if
1447  end do
1448 
1449  ! Assign number of vertical and horizontal connections
1450  ncz = iz
1451  nc = ic
1452 
1453  ! calculate z weight (wiz) and z velocity
1454  if (ncz == 1) then
1455  wiz(1) = done
1456  else
1457  dsumz = dzero
1458  do iz = 1, ncz
1459  dsumz = dsumz + diz(iz)
1460  end do
1461  denom = (ncz - done)
1462  if (denom < dzero) denom = dzero
1463  dsumz = dsumz + dem10 * dsumz
1464  do iz = 1, ncz
1465  if (dsumz > dzero) wiz(iz) = done - diz(iz) / dsumz
1466  if (denom > 0) then
1467  wiz(iz) = wiz(iz) / denom
1468  else
1469  wiz(iz) = dzero
1470  end if
1471  end do
1472  end if
1473  vz = dzero
1474  do iz = 1, ncz
1475  vz = vz + wiz(iz) * viz(iz)
1476  end do
1477 
1478  ! distance-based weighting
1479  nc = ic
1480  dsumx = dzero
1481  dsumy = dzero
1482  dsumz = dzero
1483  do ic = 1, nc
1484  wix(ic) = di(ic) * abs(nix(ic))
1485  wiy(ic) = di(ic) * abs(niy(ic))
1486  dsumx = dsumx + wix(ic)
1487  dsumy = dsumy + wiy(ic)
1488  end do
1489 
1490  ! Finish computing omega weights. Add a tiny bit
1491  ! to dsum so that the normalized omega weight later
1492  ! evaluates to (essentially) 1 in the case of a single
1493  ! relevant connection, avoiding 0/0.
1494  dsumx = dsumx + dem10 * dsumx
1495  dsumy = dsumy + dem10 * dsumy
1496  do ic = 1, nc
1497  wix(ic) = (dsumx - wix(ic)) * abs(nix(ic))
1498  wiy(ic) = (dsumy - wiy(ic)) * abs(niy(ic))
1499  end do
1500 
1501  ! compute B weights
1502  dsumx = dzero
1503  dsumy = dzero
1504  do ic = 1, nc
1505  bix(ic) = wix(ic) * sign(done, nix(ic))
1506  biy(ic) = wiy(ic) * sign(done, niy(ic))
1507  dsumx = dsumx + wix(ic) * abs(nix(ic))
1508  dsumy = dsumy + wiy(ic) * abs(niy(ic))
1509  end do
1510  if (dsumx > dzero) dsumx = done / dsumx
1511  if (dsumy > dzero) dsumy = done / dsumy
1512  axy = dzero
1513  ayx = dzero
1514  do ic = 1, nc
1515  bix(ic) = bix(ic) * dsumx
1516  biy(ic) = biy(ic) * dsumy
1517  axy = axy + bix(ic) * niy(ic)
1518  ayx = ayx + biy(ic) * nix(ic)
1519  end do
1520 
1521  ! Calculate specific discharge. The divide by zero checking below
1522  ! is problematic for cells with only one flow, such as can happen
1523  ! with triangular cells in corners. In this case, the resulting
1524  ! cell velocity will be calculated as zero. The method should be
1525  ! improved so that edge flows of zero are included in these
1526  ! calculations. But this needs to be done with consideration for LGR
1527  ! cases in which flows are submitted from an exchange.
1528  vx = dzero
1529  vy = dzero
1530  do ic = 1, nc
1531  vx = vx + (bix(ic) - axy * biy(ic)) * vi(ic)
1532  vy = vy + (biy(ic) - ayx * bix(ic)) * vi(ic)
1533  end do
1534  denom = done - axy * ayx
1535  if (denom /= dzero) then
1536  vx = vx / denom
1537  vy = vy / denom
1538  end if
1539 
1540  this%vcomp(1, n) = vx
1541  this%vcomp(2, n) = vy
1542  this%vcomp(3, n) = vz
1543  this%vmag(n) = sqrt(vx**2 + vy**2 + vz**2)
1544 
1545  end do
1546 
1547  ! cleanup
1548  deallocate (vi)
1549  deallocate (di)
1550  deallocate (nix)
1551  deallocate (niy)
1552  deallocate (wix)
1553  deallocate (wiy)
1554  deallocate (wiz)
1555  deallocate (bix)
1556  deallocate (biy)
1557 
1558  end subroutine calc_velocity
1559 
1560  !> @brief Reserve space for nedges cells that have an edge on them.
1561  !!
1562  !! todo: duplicated from NPF; should consolidate
1563  !! This must be called before the swf%allocate_arrays routine, which is
1564  !< called from swf%ar.
1565  subroutine increase_edge_count(this, nedges)
1566  ! dummy
1567  class(SwfDfwType) :: this !< this instance
1568  integer(I4B), intent(in) :: nedges
1569 
1570  this%nedges = this%nedges + nedges
1571 
1572  end subroutine increase_edge_count
1573 
1574  !> @brief Provide the swf package with edge properties
1575  !!
1576  !! todo: duplicated from NPF; should consolidate
1577  !<
1578  subroutine set_edge_properties(this, nodedge, ihcedge, q, area, nx, ny, &
1579  distance)
1580  ! dummy
1581  class(SwfDfwType) :: this !< this instance
1582  integer(I4B), intent(in) :: nodedge
1583  integer(I4B), intent(in) :: ihcedge
1584  real(DP), intent(in) :: q
1585  real(DP), intent(in) :: area
1586  real(DP), intent(in) :: nx
1587  real(DP), intent(in) :: ny
1588  real(DP), intent(in) :: distance
1589  ! local
1590  integer(I4B) :: lastedge
1591 
1592  this%lastedge = this%lastedge + 1
1593  lastedge = this%lastedge
1594  this%nodedge(lastedge) = nodedge
1595  this%ihcedge(lastedge) = ihcedge
1596  this%propsedge(1, lastedge) = q
1597  this%propsedge(2, lastedge) = area
1598  this%propsedge(3, lastedge) = nx
1599  this%propsedge(4, lastedge) = ny
1600  this%propsedge(5, lastedge) = distance
1601 
1602  ! If this is the last edge, then the next call must be starting a new
1603  ! edge properties assignment loop, so need to reset lastedge to 0
1604  if (this%lastedge == this%nedges) this%lastedge = 0
1605 
1606  end subroutine set_edge_properties
1607 
1608  !> @brief Save specific discharge in binary format to ibinun
1609  !!
1610  !! todo: should write 2d velocity; what about for 1D channel?
1611  !<
1612  subroutine sav_velocity(this, ibinun)
1613  ! dummy
1614  class(SwfDfwType) :: this !< this instance
1615  integer(I4B), intent(in) :: ibinun
1616  ! local
1617  character(len=16) :: text
1618  character(len=16), dimension(3) :: auxtxt
1619  integer(I4B) :: n
1620  integer(I4B) :: naux
1621 
1622  ! Write the header
1623  text = ' DATA-VCOMP'
1624  naux = 3
1625  auxtxt(:) = [' vx', ' vy', ' vz']
1626  call this%dis%record_srcdst_list_header(text, this%name_model, &
1627  this%packName, this%name_model, &
1628  this%packName, naux, auxtxt, ibinun, &
1629  this%dis%nodes, this%iout)
1630 
1631  ! Write a zero for Q, and then write qx, qy, qz as aux variables
1632  do n = 1, this%dis%nodes
1633  call this%dis%record_mf6_list_entry(ibinun, n, n, dzero, naux, &
1634  this%vcomp(:, n))
1635  end do
1636 
1637  end subroutine sav_velocity
1638 
1639  !> @brief Define the observation types available in the package
1640  !!
1641  !< Method to define the observation types available in the package.
1642  subroutine dfw_df_obs(this)
1643  ! dummy variables
1644  class(SwfDfwType) :: this !< this instance
1645  ! local variables
1646  integer(I4B) :: indx
1647 
1648  ! Store obs type and assign procedure pointer
1649  ! for ext-outflow observation type.
1650  call this%obs%StoreObsType('ext-outflow', .true., indx)
1651  this%obs%obsData(indx)%ProcessIdPtr => dfwobsidprocessor
1652 
1653  end subroutine dfw_df_obs
1654 
1655  subroutine dfwobsidprocessor(obsrv, dis, inunitobs, iout)
1656  ! dummy
1657  type(ObserveType), intent(inout) :: obsrv
1658  class(DisBaseType), intent(in) :: dis
1659  integer(I4B), intent(in) :: inunitobs
1660  integer(I4B), intent(in) :: iout
1661  ! local
1662  integer(I4B) :: n
1663  character(len=LINELENGTH) :: string
1664 
1665  ! Initialize variables
1666  string = obsrv%IDstring
1667  read (string, *) n
1668 
1669  if (n > 0) then
1670  obsrv%NodeNumber = n
1671  else
1672  errmsg = 'Error reading data from ID string'
1673  call store_error(errmsg)
1674  call store_error_unit(inunitobs)
1675  end if
1676 
1677  end subroutine dfwobsidprocessor
1678 
1679  !> @brief Save observations for the package
1680  !!
1681  !< Method to save simulated values for the package.
1682  subroutine dfw_bd_obs(this)
1683  ! dummy variables
1684  class(SwfDfwType) :: this !< this instance
1685  ! local variables
1686  integer(I4B) :: i
1687  integer(I4B) :: j
1688  integer(I4B) :: n
1689  real(DP) :: v
1690  character(len=100) :: msg
1691  type(ObserveType), pointer :: obsrv => null()
1692 
1693  ! Write simulated values for all observations
1694  if (this%obs%npakobs > 0) then
1695  call this%obs%obs_bd_clear()
1696  do i = 1, this%obs%npakobs
1697  obsrv => this%obs%pakobs(i)%obsrv
1698  do j = 1, obsrv%indxbnds_count
1699  n = obsrv%indxbnds(j)
1700  v = dzero
1701  select case (obsrv%ObsTypeId)
1702  case default
1703  msg = 'Unrecognized observation type: '//trim(obsrv%ObsTypeId)
1704  call store_error(msg)
1705  end select
1706  call this%obs%SaveOneSimval(obsrv, v)
1707  end do
1708  end do
1709 
1710  ! write summary of package error messages
1711  if (count_errors() > 0) then
1712  call store_error_filename(this%input_fname)
1713  end if
1714  end if
1715 
1716  end subroutine dfw_bd_obs
1717 
1718  !> @brief Read and prepare observations for a package
1719  !!
1720  !< Method to read and prepare observations for a package.
1721  subroutine dfw_rp_obs(this)
1722  ! modules
1723  use tdismodule, only: kper
1724  ! dummy
1725  class(SwfDfwType), intent(inout) :: this !< this instance
1726  ! local
1727  integer(I4B) :: i
1728  integer(I4B) :: j
1729  integer(I4B) :: nn1
1730  class(ObserveType), pointer :: obsrv => null()
1731 
1732  ! process each package observation
1733  ! only done the first stress period since boundaries are fixed
1734  ! for the simulation
1735  if (kper == 1) then
1736  do i = 1, this%obs%npakobs
1737  obsrv => this%obs%pakobs(i)%obsrv
1738 
1739  ! get node number 1
1740  nn1 = obsrv%NodeNumber
1741  if (nn1 < 1 .or. nn1 > this%dis%nodes) then
1742  write (errmsg, '(a,1x,a,1x,i0,1x,a,1x,i0,a)') &
1743  trim(adjustl(obsrv%ObsTypeId)), &
1744  'reach must be greater than 0 and less than or equal to', &
1745  this%dis%nodes, '(specified value is ', nn1, ')'
1746  call store_error(errmsg)
1747  else
1748  if (obsrv%indxbnds_count == 0) then
1749  call obsrv%AddObsIndex(nn1)
1750  else
1751  errmsg = 'Programming error in dfw_rp_obs'
1752  call store_error(errmsg)
1753  end if
1754  end if
1755 
1756  ! check that node number 1 is valid; call store_error if not
1757  do j = 1, obsrv%indxbnds_count
1758  nn1 = obsrv%indxbnds(j)
1759  if (nn1 < 1 .or. nn1 > this%dis%nodes) then
1760  write (errmsg, '(a,1x,a,1x,i0,1x,a,1x,i0,a)') &
1761  trim(adjustl(obsrv%ObsTypeId)), &
1762  'reach must be greater than 0 and less than or equal to', &
1763  this%dis%nodes, '(specified value is ', nn1, ')'
1764  call store_error(errmsg)
1765  end if
1766  end do
1767  end do
1768 
1769  ! evaluate if there are any observation errors
1770  if (count_errors() > 0) then
1771  call store_error_filename(this%input_fname)
1772  end if
1773 
1774  end if
1775 
1776  end subroutine dfw_rp_obs
1777 
1778 end module swfdfwmodule
This module contains the BudgetModule.
Definition: Budget.f90:20
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
real(dp), parameter dtwothirds
real constant 2/3
Definition: Constants.f90:70
real(dp), parameter dp9
real constant 9/10
Definition: Constants.f90:72
real(dp), parameter dem10
real constant 1e-10
Definition: Constants.f90:113
real(dp), parameter donethird
real constant 1/3
Definition: Constants.f90:67
integer(i4b), parameter lenbigline
maximum length of a big line
Definition: Constants.f90:15
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
real(dp), parameter dprec
real constant machine precision
Definition: Constants.f90:120
real(dp), parameter dtwo
real constant 2
Definition: Constants.f90:79
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
integer(i4b) function, public getunit()
Get a free unit number.
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
This module defines variable data types.
Definition: kind.f90:8
real(dp) function, public get_perturbation(x)
Calculate a numerical perturbation given the value of x.
Definition: MathUtil.f90:372
character(len=lenmempath) function create_mem_path(component, subcomponent, context)
returns the path to the memory object
subroutine, public memorystore_remove(component, subcomponent, context)
subroutine, public memorystore_release(varname, memory_path)
Release a single variable from the memory store.
subroutine, public get_isize(name, mem_path, isize)
@ brief Get the number of elements for this variable
This module contains the base numerical package type.
This module contains the derived types ObserveType and ObsDataType.
Definition: Observe.f90:15
This module contains the derived type ObsType.
Definition: Obs.f90:127
subroutine, public obs_cr(obs, inobs)
@ brief Create a new ObsType object
Definition: Obs.f90:225
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
integer(i4b) function, public count_errors()
Return number of errors.
Definition: Sim.f90:59
subroutine, public store_error_filename(filename, terminate)
Store the erroring file name.
Definition: Sim.f90:203
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
Definition: Sim.f90:168
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
character(len=linelength) idm_context
subroutine squadratic(x, range, dydx, y)
@ brief sQuadratic
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:27
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:26
subroutine, public vector_interpolation_2d(dis, flowja, nedges, nodedge, propsedge, vcomp, vmag, flowareaja)
Interpolate 2D vector components at cell center.
Derived type for the Budget object.
Definition: Budget.f90:39
This class is used to store a single deferred-length character string. It was designed to work in an ...
Definition: CharString.f90:23