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