MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
SwfCxsUtils.f90
Go to the documentation of this file.
1 !> @brief This module contains stateless sfr subroutines and functions
2 !!
3 !! This module contains the functions to calculate the wetted perimeter
4 !! and cross-sectional area for a reach cross-section that are used in
5 !! the streamflow routing (SFR) package. It also contains subroutines to
6 !! calculate the wetted perimeter and cross-sectional area for each
7 !! line segment in the cross-section. This module does not depend on the
8 !! SFR package.
9 !!
10 !<
12 
13  use kindmodule, only: dp, i4b, lgp
14  use constantsmodule, only: dzero, dhalf, dtwothirds, done, dtwo, &
15  dem5, dp999, dthree
16  use smoothingmodule, only: schsmooth
17 
18  implicit none
19  private
20  public :: calc_depth_from_q
21  public :: calc_qman
22  public :: get_saturated_topwidth
23  public :: get_wetted_topwidth
24  public :: get_wetted_perimeter
25  public :: get_cross_section_area
26  public :: get_hydraulic_radius
27  public :: get_hydraulic_radius_xf
28  public :: get_mannings_section
29  public :: calc_composite_roughness
30  public :: get_conveyance
31  public :: get_composite_conveyance
32 
33 contains
34 
35  !> @brief Calculate the depth at the midpoint of a irregular cross-section
36  !<
37  function calc_depth_from_q(qrch, width, rough, slope, &
38  cxs_xf, cxs_h, cxs_rf, unitconv, &
39  icalcmeth) result(depth)
40  ! -- dummy variables
41  real(dp), intent(in) :: qrch !< streamflow
42  real(dp), intent(in) :: width !< reach width
43  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
44  real(dp), intent(in) :: slope !< reach bottom slope
45  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
46  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
47  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
48  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
49  integer(I4B), intent(in) :: icalcmeth !< manning calculation method
50 
51  ! -- return
52  real(dp) :: depth !< stream depth at midpoint of reach
53 
54  if (icalcmeth == 1) then
55  ! slower but robust bisection method
56  depth = calc_depth_from_q_bisect(qrch, width, rough, slope, &
57  cxs_xf, cxs_h, cxs_rf, unitconv, &
58  icalcmeth)
59  else
60  ! faster but less forgiving newton-raphson method
61  depth = calc_depth_from_q_nr(qrch, width, rough, slope, &
62  cxs_xf, cxs_h, cxs_rf, unitconv, &
63  icalcmeth)
64  end if
65  !
66  ! -- return
67  return
68  end function calc_depth_from_q
69 
70  !> @brief Calculate the depth at the midpoint of a irregular cross-section
71  !!
72  !! Method to calculate the depth at the midpoint of a reach with a
73  !! irregular cross-section using bisection.
74  !!
75  !<
76  function calc_depth_from_q_bisect(qrch, width, rough, slope, &
77  cxs_xf, cxs_h, cxs_rf, unitconv, &
78  icalcmeth) result(depth)
79  ! -- dummy variables
80  !class(SfrType) :: this !< SfrType object
81  !integer(I4B), intent(in) :: n !< reach number
82  real(dp), intent(in) :: qrch !< streamflow
83  real(dp), intent(in) :: width !< reach width
84  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
85  real(dp), intent(in) :: slope !< reach bottom slope
86  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
87  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
88  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
89  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
90  integer(I4B), intent(in) :: icalcmeth !< manning calculation method
91 
92  ! -- return
93  real(dp) :: depth !< stream depth at midpoint of reach
94 
95  ! -- local variables
96  integer(I4B) :: maxiter = 100
97  integer(I4B) :: iter
98  real(dp) :: dmaxchg = dem5
99  real(dp) :: a
100  real(dp) :: b
101  real(dp) :: c
102  real(dp) :: f_a
103  real(dp) :: f_c
104 
105  ! constrain the bisection range
106  a = dzero
107  b = maxval(cxs_h) * dtwo
108  !
109  ! -- bisection iteration
110  bisectiter: do iter = 1, maxiter
111 
112  c = (a + b) / dtwo
113  f_c = calc_qman(c, width, rough, slope, &
114  cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth) - qrch
115  if (f_c == dzero .or. (b - a) / dtwo < dmaxchg) then
116  depth = c
117  return
118  end if
119 
120  f_a = calc_qman(a, width, rough, slope, &
121  cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth) - qrch
122  if (sign(done, f_c) == sign(done, f_a)) then
123  a = c
124  else
125  b = c
126  end if
127 
128  end do bisectiter
129  !
130  ! TODO: raise an error
131  print *, "bisection routine failed"
132  !
133  ! -- return
134  return
135  end function calc_depth_from_q_bisect
136 
137  !> @brief Calculate the depth at the midpoint of a irregular cross-section
138  !!
139  !! Method to calculate the depth at the midpoint of a reach with a
140  !! irregular cross-section using Newton-Raphson.
141  !!
142  !<
143  function calc_depth_from_q_nr(qrch, width, rough, slope, &
144  cxs_xf, cxs_h, cxs_rf, unitconv, &
145  icalcmeth) result(depth)
146  ! -- dummy variables
147  !class(SfrType) :: this !< SfrType object
148  !integer(I4B), intent(in) :: n !< reach number
149  real(dp), intent(in) :: qrch !< streamflow
150  real(dp), intent(in) :: width !< reach width
151  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
152  real(dp), intent(in) :: slope !< reach bottom slope
153  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
154  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
155  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
156  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
157  integer(I4B), intent(in) :: icalcmeth !< manning calculation method
158 
159  ! -- return
160  real(dp) :: depth !< stream depth at midpoint of reach
161 
162  ! -- local variables
163  integer(I4B) :: maxiter = 100
164  integer(I4B) :: iter
165  real(dp) :: dmaxchg = dem5
166  real(dp) :: deps = dem5 * dp999
167  real(dp) :: perturbation
168  real(dp) :: q0
169  real(dp) :: q1
170  real(dp) :: dq
171  real(dp) :: derv
172  real(dp) :: d
173  real(dp) :: dd
174  real(dp) :: residual
175  !
176  ! -- initialize variables
177  perturbation = deps * dtwo
178  d = dzero
179  q0 = dzero
180  residual = q0 - qrch
181  !
182  ! -- Newton-Raphson iteration
183  nriter: do iter = 1, maxiter
184  !call this%sfr_calc_qman(n, d + perturbation, q1)
185  q1 = calc_qman(d + perturbation, width, rough, slope, &
186  cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
187  dq = (q1 - q0)
188  if (dq /= dzero) then
189  derv = perturbation / (q1 - q0)
190  else
191  derv = dzero
192  end if
193  dd = derv * residual
194  d = d - dd
195  !call this%sfr_calc_qman(n, d, q0)
196  q0 = calc_qman(d, width, rough, slope, &
197  cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
198  residual = q0 - qrch
199  !
200  ! -- check for convergence
201  if (abs(dd) < dmaxchg) then
202  exit nriter
203  end if
204 
205  end do nriter
206  depth = d
207  !
208  ! -- return
209  return
210  end function calc_depth_from_q_nr
211 
212  !> @brief Calculate streamflow using Manning's equation
213  !!
214  !! Method to calculate the streamflow using Manning's equation for a
215  !! single reach defined by a cross section.
216  !!
217  !<
218  function calc_qman(depth, width, rough, slope, &
219  cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth) result(qman)
220 
221  ! -- dummy variables
222  real(dp), intent(in) :: depth !< reach depth
223  real(dp), intent(in) :: width !< reach width
224  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
225  real(dp), intent(in) :: slope !< reach bottom slope
226  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
227  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
228  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
229  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
230  integer(I4B), intent(in) :: icalcmeth ! 0 is composite linear, 1 is by section, 2 is composite nonlinear
231 
232  ! return value
233  real(dp) :: qman !< calculated mannings flow
234 
235  ! -- local variables
236  integer(I4B) :: linmeth
237 
238  select case (icalcmeth)
239  case (0) ! composite linear
240  linmeth = 0
241  qman = calc_qman_composite(depth, width, rough, slope, &
242  cxs_xf, cxs_h, cxs_rf, unitconv, &
243  linmeth)
244  case (1) ! by section
245  qman = calc_qman_by_section(depth, width, rough, slope, &
246  cxs_xf, cxs_h, cxs_rf, unitconv)
247  case (2) ! composite nonlinear
248  linmeth = 1
249  qman = calc_qman_composite(depth, width, rough, slope, &
250  cxs_xf, cxs_h, cxs_rf, unitconv, &
251  linmeth)
252  end select
253  !
254  ! -- return
255  return
256  end function calc_qman
257 
258  function calc_qman_composite(depth, width, rough, slope, &
259  cxs_xf, cxs_h, cxs_rf, unitconv, &
260  linmeth) result(qman)
261 
262  ! -- dummy variables
263  real(dp), intent(in) :: depth !< reach depth
264  real(dp), intent(in) :: width !< reach width
265  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
266  real(dp), intent(in) :: slope !< reach bottom slope
267  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
268  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
269  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
270  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
271  integer(I4B), intent(in) :: linmeth !< method for composite (0) linear (1) nonlinear
272 
273  ! return value
274  real(dp) :: qman !< calculated mannings flow
275 
276  ! -- local variables
277  integer(I4B) :: npts
278  real(dp) :: sat
279  real(dp) :: derv
280  real(dp) :: aw
281  real(dp) :: wp
282  real(dp) :: rh
283  real(dp) :: rough_composite
284  !
285  ! -- initialize variables
286  qman = dzero
287  !
288  ! -- calculate Manning's discharge for non-zero depths
289  if (depth > dzero) then
290 
291  npts = size(cxs_xf)
292  rough_composite = calc_composite_roughness(npts, depth, width, &
293  rough, slope, &
294  cxs_xf, cxs_h, cxs_rf, &
295  linmeth)
296  wp = get_wetted_perimeter(npts, cxs_xf, cxs_h, width, depth)
297  aw = get_cross_section_area(npts, cxs_xf, cxs_h, width, depth)
298  if (wp > dzero) then
299  rh = aw / wp
300  else
301  rh = dzero
302  end if
303  qman = unitconv * aw * (rh**dtwothirds) * sqrt(slope) / rough
304  call schsmooth(depth, sat, derv)
305  qman = sat * qman
306  end if
307  !
308  ! -- return
309  return
310  end function calc_qman_composite
311 
312  function calc_composite_roughness(npts, depth, width, rough, slope, &
313  cxs_xf, cxs_h, cxs_rf, &
314  linmeth) result(rc)
315  ! -- dummy variables
316  integer(I4B), intent(in) :: npts
317  real(dp), intent(in) :: depth !< reach depth
318  real(dp), intent(in) :: width !< reach width
319  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
320  real(dp), intent(in) :: slope !< reach bottom slope
321  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
322  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
323  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
324  integer(I4B), intent(in) :: linmeth !< method for composite calculation; linear 0 or nonlinear 1
325 
326  ! return value
327  real(dp) :: rc !< composite roughness
328 
329  ! -- local variables
330  integer(I4B) :: n
331  real(dp) :: wp
332  real(dp) :: exp1
333  real(dp) :: exp2
334  real(dp), dimension(npts) :: stations
335  real(dp), dimension(npts - 1) :: perimeters
336  !
337  ! stations
338  do n = 1, npts
339  stations(n) = cxs_xf(n) * width
340  end do
341  !
342  ! -- select method
343  select case (linmeth)
344  case (0) ! linear
345  exp1 = done
346  exp2 = done
347  case (1) ! nonlinear
348  exp1 = 1.5d0
349  exp2 = dtwo / dthree
350  end select
351  !
352  ! -- initialize variables
353  rc = rough
354  !
355  ! -- calculate composite roughness
356  if (depth > dzero) then
357 
358  if (npts > 1) then
359  call get_wetted_perimeters(npts, stations, cxs_h, depth, perimeters)
360  wp = dzero
361  rc = dzero
362  do n = 1, npts - 1
363  rc = rc + (rough * cxs_rf(n))**exp1 * perimeters(n)
364  wp = wp + perimeters(n)
365  end do
366  if (wp > dzero) then
367  rc = (rc / wp)**exp2
368  else
369  rc = rough
370  end if
371  end if
372 
373  end if
374  !
375  ! -- return
376  return
377  end function calc_composite_roughness
378 
379  function calc_qman_by_section(depth, width, rough, slope, &
380  cxs_xf, cxs_h, cxs_rf, unitconv) result(qman)
381 
382  ! -- dummy variables
383  real(dp), intent(in) :: depth !< reach depth
384  real(dp), intent(in) :: width !< reach width
385  real(dp), intent(in) :: rough !< mannings reach roughness coefficient
386  real(dp), intent(in) :: slope !< reach bottom slope
387  real(dp), dimension(:), intent(in) :: cxs_xf ! xfraction distances for this cross section
388  real(dp), dimension(:), intent(in) :: cxs_h ! heights for this cross section
389  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
390  real(dp), intent(in) :: unitconv !< conversion unit numerator in mannings equation
391 
392  ! return value
393  real(dp) :: qman !< calculated mannings flow
394 
395  ! -- local variables
396  integer(I4B) :: npts
397  real(dp) :: sat
398  real(dp) :: derv
399  real(dp) :: aw
400  real(dp) :: wp
401  real(dp) :: rh
402  !
403  ! -- initialize variables
404  qman = dzero
405  !
406  ! -- calculate Manning's discharge for non-zero depths
407  if (depth > dzero) then
408  npts = size(cxs_xf)
409  !
410  ! -- set constant terms for Manning's equation
411  call schsmooth(depth, sat, derv)
412  !
413  ! -- calculate the mannings coefficient that is a
414  ! function of depth
415  if (npts > 1) then
416  !
417  ! -- call function that calculates flow for an
418  ! n-point cross section
419  qman = get_mannings_section(npts, &
420  cxs_xf, &
421  cxs_h, &
422  cxs_rf, &
423  rough, &
424  unitconv, &
425  width, &
426  slope, &
427  depth)
428  else
429 
430  ! hydraulically wide channel (only 1 point is defined)
431  aw = width * depth
432  wp = width
433  if (wp > dzero) then
434  rh = aw / wp
435  else
436  rh = dzero
437  end if
438  qman = unitconv * aw * (rh**dtwothirds) * sqrt(slope) / rough
439  end if
440  !
441  ! -- calculate stream flow
442  qman = sat * qman
443  end if
444  !
445  ! -- return
446  return
447  end function calc_qman_by_section
448 
449  !> @brief Calculate the saturated top width for a reach
450  !!
451  !! Function to calculate the maximum top width for a reach using the
452  !! cross-section station data.
453  !!
454  !! @return w saturated top width
455  !<
456  function get_saturated_topwidth(npts, stations) result(w)
457  ! -- dummy variables
458  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
459  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
460  ! -- local variables
461  real(dp) :: w
462  !
463  ! -- calculate the saturated top width
464  if (npts > 1) then
465  w = stations(npts) - stations(1)
466  else
467  w = stations(1)
468  end if
469  !
470  ! -- return
471  return
472  end function get_saturated_topwidth
473 
474  !> @brief Calculate the wetted top width for a reach
475  !!
476  !! Function to calculate the wetted top width for a reach using the
477  !! cross-section station-height data given a passed depth.
478  !!
479  !! @return w wetted top width
480  !<
481  function get_wetted_topwidth(npts, xfraction, heights, width, d) result(w)
482  ! -- dummy variables
483  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
484  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
485  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
486  real(dp), intent(in) :: width !< reach width
487  real(dp), intent(in) :: d !< depth to evaluate cross-section
488  ! -- local variables
489  integer(I4B) :: n
490  real(dp) :: w
491  real(dp), dimension(npts) :: stations
492  real(dp), dimension(npts - 1) :: widths
493  !
494  ! -- calculate station from xfractions and width
495  do n = 1, npts
496  stations(n) = xfraction(n) * width
497  end do
498  !
499  ! -- initialize the wetted perimeter for the reach
500  w = dzero
501  !
502  ! -- calculate the wetted top width for each line segment
503  call get_wetted_topwidths(npts, stations, heights, d, widths)
504  !
505  ! -- calculate the wetted top widths
506  do n = 1, npts - 1
507  w = w + widths(n)
508  end do
509  !
510  ! -- return
511  return
512  end function get_wetted_topwidth
513 
514  !> @brief Calculate the wetted perimeter for a reach
515  !!
516  !! Function to calculate the wetted perimeter for a reach using the
517  !! cross-section station-height data given a passed depth.
518  !!
519  !! @return p wetted perimeter
520  !<
521  function get_wetted_perimeter(npts, xfraction, heights, width, d) result(p)
522  ! -- dummy variables
523  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
524  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section x fractions
525  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
526  real(dp), intent(in) :: width !< cross section width
527  real(dp), intent(in) :: d !< depth to evaluate cross-section
528  ! -- local variables
529  integer(I4B) :: n
530  real(dp) :: p
531  real(dp), dimension(npts) :: stations
532  real(dp), dimension(npts - 1) :: perimeters
533  !
534  ! -- initialize stations
535  do n = 1, npts
536  stations(n) = xfraction(n) * width
537  end do
538  !
539  ! -- initialize the wetted perimeter for the reach
540  p = dzero
541  !
542  ! -- calculate the wetted perimeter for each line segment
543  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
544  !
545  ! -- calculate the wetted perimenter
546  do n = 1, npts - 1
547  p = p + perimeters(n)
548  end do
549  !
550  ! -- return
551  return
552  end function get_wetted_perimeter
553 
554  !> @brief Calculate the cross-sectional area for a reach
555  !!
556  !! Function to calculate the cross-sectional area for a reach using
557  !! the cross-section station-height data given a passed depth.
558  !!
559  !! @return a cross-sectional area
560  !<
561  function get_cross_section_area(npts, xfraction, heights, width, d) result(a)
562  ! -- dummy variables
563  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
564  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
565  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
566  real(dp), intent(in) :: width !< reach width
567  real(dp), intent(in) :: d !< depth to evaluate cross-section
568  ! -- local variables
569  integer(I4B) :: n
570  real(dp) :: a
571  real(dp), dimension(npts) :: stations
572  real(dp), dimension(npts - 1) :: areas
573  !
574  ! -- calculate station from xfractions and width
575  do n = 1, npts
576  stations(n) = xfraction(n) * width
577  end do
578  !
579  ! -- initialize the area
580  a = dzero
581  !
582  ! -- calculate the cross-sectional area for each line segment
583  call get_cross_section_areas(npts, stations, heights, d, areas)
584  !
585  ! -- calculate the cross-sectional area
586  do n = 1, npts - 1
587  a = a + areas(n)
588  end do
589  !
590  ! -- return
591  return
592  end function get_cross_section_area
593 
594  !> @brief Calculate conveyance
595  !!
596  !! Return a calculated conveyance. Calculation
597  !! depends on whether the channel is a rectangular channel
598  !< with vertical sides or something else.
599  function get_conveyance(npts, xfraction, heights, cxs_rf, &
600  width, rough, d) result(c)
601  ! -- dummy variables
602  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
603  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
604  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
605  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
606  real(dp), intent(in) :: width !< reach width
607  real(dp), intent(in) :: rough !< reach roughness
608  real(dp), intent(in) :: d !< depth to evaluate cross-section
609  ! result
610  real(dp) :: c
611  if (is_rectangular(xfraction)) then
612  c = get_rectangular_conveyance(npts, xfraction, heights, cxs_rf, &
613  width, rough, d)
614  else
615  c = get_composite_conveyance(npts, xfraction, heights, cxs_rf, &
616  width, rough, d)
617  end if
618  end function get_conveyance
619 
620  !> @brief Calculate conveyance for rectangular channel
621  !<
622  function get_rectangular_conveyance(npts, xfraction, heights, cxs_rf, &
623  width, rough, d) result(c)
624  ! -- dummy variables
625  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
626  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
627  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
628  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
629  real(dp), intent(in) :: width !< reach width
630  real(dp), intent(in) :: rough !< reach roughness
631  real(dp), intent(in) :: d !< depth to evaluate cross-section
632  ! -- local variables
633  real(dp) :: c
634  real(dp) :: a
635  real(dp) :: p
636  real(dp) :: ravg
637 
638  ! find cross sectional area and wetted perimeter
639  a = get_cross_section_area(npts, xfraction, heights, width, d)
640  p = get_wetted_perimeter(npts, xfraction, heights, width, d)
641 
642  ! calculate roughness or determine an average
643  if (has_uniform_resistance(cxs_rf)) then
644  ravg = rough * cxs_rf(1)
645  else
646  ravg = calc_composite_roughness(npts, d, width, rough, dzero, &
647  xfraction, heights, cxs_rf, 0)
648  end if
649 
650  ! make conveyance calculation
651  c = dzero
652  if (p > dzero) then
653  c = a / ravg * (a / p)**dtwothirds
654  end if
655  end function get_rectangular_conveyance
656 
657  !> @brief Determine if cross section is rectangular
658  !<
659  function is_rectangular(xfraction)
660  ! dummy
661  real(dp), dimension(:), intent(in) :: xfraction !< cross-section station distances (x-distance)
662  ! result
663  logical(LGP) :: is_rectangular
664  is_rectangular = .false.
665  if (size(xfraction) == 4) then
666  if (xfraction(1) == xfraction(2) .and. &
667  xfraction(3) == xfraction(4)) then
668  is_rectangular = .true.
669  end if
670  end if
671  end function is_rectangular
672 
673  !> @brief Determine if roughness is uniform for the section
674  !<
675  function has_uniform_resistance(cxs_rf)
676  ! dummy
677  real(dp), dimension(:), intent(in) :: cxs_rf !< cross-section station distances (x-distance)
678  ! result
679  logical(LGP) :: has_uniform_resistance
680  ! local
681  real(dp) :: rmin
682  real(dp) :: rmax
683  rmin = minval(cxs_rf(1:3))
684  rmax = maxval(cxs_rf(1:3))
685  has_uniform_resistance = (rmin == rmax)
686  end function has_uniform_resistance
687 
688  !> @brief Calculate composite conveyance
689  !!
690  !! Function to calculate the composite conveyance.
691  !! This is based on the approach in HEC-RAS, where
692  !! a conveyance is calculated for each line segment
693  !! in the cross section, and then summed to produce
694  !! a total conveyance.
695  !!
696  !<
697  function get_composite_conveyance(npts, xfraction, heights, cxs_rf, &
698  width, rough, d) result(c)
699  ! -- dummy variables
700  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
701  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
702  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
703  real(dp), dimension(:), intent(in) :: cxs_rf ! mannings fractions for this cross section
704  real(dp), intent(in) :: width !< reach width
705  real(dp), intent(in) :: rough !< reach roughness
706  real(dp), intent(in) :: d !< depth to evaluate cross-section
707  ! -- local variables
708  integer(I4B) :: n
709  real(dp) :: c
710  real(dp) :: p
711  real(dp) :: rc
712  real(dp) :: rh
713  real(dp) :: cn
714  real(dp), dimension(npts) :: stations
715  real(dp), dimension(npts - 1) :: areas
716  real(dp), dimension(npts - 1) :: perimeters
717  !
718  ! -- calculate station from xfractions and width
719  do n = 1, npts
720  stations(n) = xfraction(n) * width
721  end do
722  !
723  ! -- calculate the cross-sectional area for each line segment
724  call get_cross_section_areas(npts, stations, heights, d, areas)
725  !
726  ! -- calculate the wetted perimeter for each line segment
727  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
728  !
729  ! -- calculate the composite conveyance
730  c = dzero
731  do n = 1, npts - 1
732  rc = cxs_rf(n) * rough
733  p = perimeters(n)
734  cn = dzero
735  if (p > dzero) then
736  rh = areas(n) / p
737  cn = areas(n) / rc * rh**dtwothirds
738  end if
739  c = c + cn
740  end do
741  !
742  ! -- return
743  return
744  end function get_composite_conveyance
745 
746  !> @brief Calculate the hydraulic radius for a reach
747  !!
748  !! Function to calculate the hydraulic radius for a reach using
749  !! the cross-section station-height data given a passed depth.
750  !!
751  !! @return r hydraulic radius
752  !<
753  function get_hydraulic_radius(npts, stations, heights, d) result(r)
754  ! -- dummy variables
755  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
756  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
757  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
758  real(dp), intent(in) :: d !< depth to evaluate cross-section
759  ! -- local variables
760  integer(I4B) :: n
761  real(dp) :: r
762  real(dp) :: p
763  real(dp) :: a
764  real(dp), dimension(npts - 1) :: areas
765  real(dp), dimension(npts - 1) :: perimeters
766  !
767  ! -- initialize the hydraulic radius, perimeter, and area
768  r = dzero
769  p = dzero
770  a = dzero
771  !
772  ! -- calculate the wetted perimeter for each line segment
773  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
774  !
775  ! -- calculate the wetted perimenter
776  do n = 1, npts - 1
777  p = p + perimeters(n)
778  end do
779  !
780  ! -- calculate the hydraulic radius only if the perimeter is non-zero
781  if (p > dzero) then
782  !
783  ! -- calculate the cross-sectional area for each line segment
784  call get_cross_section_areas(npts, stations, heights, d, areas)
785  !
786  ! -- calculate the cross-sectional area
787  do n = 1, npts - 1
788  a = a + areas(n)
789  end do
790  !
791  ! -- calculate the hydraulic radius
792  r = a / p
793  end if
794  !
795  ! -- return
796  return
797  end function get_hydraulic_radius
798 
799  !> @brief Calculate the hydraulic radius for a reach
800  !!
801  !! Function to calculate the hydraulic radius for a reach using
802  !! the cross-section xfraction-height data given a passed depth.
803  !! This is different from get_hydraulic_radius as it requires
804  !! xfraction and width instead of station.
805  !!
806  !! @return r hydraulic radius
807  !<
808  function get_hydraulic_radius_xf(npts, xfraction, heights, width, d) result(r)
809  ! -- dummy variables
810  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
811  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
812  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
813  real(dp), intent(in) :: width !< reach width
814  real(dp), intent(in) :: d !< depth to evaluate cross-section
815  ! -- local variables
816  integer(I4B) :: n
817  real(dp) :: r
818  real(dp), dimension(npts) :: stations
819  !
820  ! -- calculate station from xfractions and width
821  do n = 1, npts
822  stations(n) = xfraction(n) * width
823  end do
824  !
825  ! -- calculate hydraulic radius
826  r = get_hydraulic_radius(npts, stations, heights, d)
827  !
828  ! -- return
829  return
830  end function get_hydraulic_radius_xf
831 
832  !> @brief Calculate the manning's discharge for a reach
833  !!
834  !! Function to calculate the mannings discharge for a reach
835  !! by calculating the discharge for each section, which can
836  !! have a unique Manning's coefficient given a passed depth.
837  !!
838  !! @return q reach discharge
839  !<
840  function get_mannings_section(npts, xfraction, heights, roughfracs, &
841  roughness, conv_fact, width, slope, d) result(q)
842  ! -- dummy variables
843  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
844  real(dp), dimension(npts), intent(in) :: xfraction !< cross-section station distances (x-distance)
845  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
846  real(dp), dimension(npts), intent(in) :: roughfracs !< cross-section Mannings roughness fraction data
847  real(dp), intent(in) :: roughness !< base reach roughness
848  real(dp), intent(in) :: conv_fact !< unit conversion factor
849  real(dp), intent(in) :: width !< reach width
850  real(dp), intent(in) :: slope !< reach slope
851  real(dp), intent(in) :: d !< depth to evaluate cross-section
852  ! -- local variables
853  integer(I4B) :: n
854  real(dp) :: q
855  real(dp) :: rh
856  real(dp) :: r
857  real(dp) :: p
858  real(dp) :: a
859  real(dp), dimension(npts) :: stations
860  real(dp), dimension(npts - 1) :: areas
861  real(dp), dimension(npts - 1) :: perimeters
862  !
863  ! -- initialize the hydraulic radius, perimeter, and area
864  q = dzero
865  rh = dzero
866  r = dzero
867  p = dzero
868  a = dzero
869  !
870  ! -- calculate station from xfractions and width
871  do n = 1, npts
872  stations(n) = xfraction(n) * width
873  end do
874  !
875  ! -- calculate the wetted perimeter for each line segment
876  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
877  !
878  ! -- calculate the wetted perimenter
879  do n = 1, npts - 1
880  p = p + perimeters(n)
881  end do
882  !
883  ! -- calculate the hydraulic radius only if the perimeter is non-zero
884  if (p > dzero) then
885  !
886  ! -- calculate the cross-sectional area for each line segment
887  call get_cross_section_areas(npts, stations, heights, d, areas)
888  !
889  ! -- calculate the cross-sectional area
890  do n = 1, npts - 1
891  p = perimeters(n)
892  r = roughness * roughfracs(n)
893  if (p * r > dzero) then
894  a = areas(n)
895  rh = a / p
896  q = q + conv_fact * a * rh**dtwothirds * sqrt(slope) / r
897  end if
898  end do
899  end if
900  !
901  ! -- return
902  return
903  end function get_mannings_section
904 
905  ! -- private functions and subroutines
906 
907  !> @brief Calculate the wetted perimeters for each line segment
908  !!
909  !! Subroutine to calculate the wetted perimeter for each line segment
910  !! that defines the reach using the cross-section station-height
911  !! data given a passed depth.
912  !!
913  !<
914  subroutine get_wetted_perimeters(npts, stations, heights, d, p)
915  ! -- dummy variables
916  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
917  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
918  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
919  real(DP), intent(in) :: d !< depth to evaluate cross-section
920  real(DP), dimension(npts - 1), intent(inout) :: p !< wetted perimeter for each line segment
921  ! -- local variables
922  integer(I4B) :: n
923  real(DP) :: x0
924  real(DP) :: x1
925  real(DP) :: d0
926  real(DP) :: d1
927  real(DP) :: dmax
928  real(DP) :: dmin
929  real(DP) :: xlen
930  real(DP) :: dlen
931  !
932  ! -- iterate over the station-height data
933  do n = 1, npts - 1
934  !
935  ! -- initialize the wetted perimeter
936  p(n) = dzero
937  !
938  ! -- initialize station-height data for segment
939  x0 = stations(n)
940  x1 = stations(n + 1)
941  d0 = heights(n)
942  d1 = heights(n + 1)
943  !
944  ! -- get the start and end station position of the wetted segment
945  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
946  !
947  ! -- calculate the wetted perimeter for the segment
948  xlen = x1 - x0
949  dlen = dzero
950  if (xlen > dzero) then
951  if (d > dmax) then
952  dlen = dmax - dmin
953  else
954  dlen = d - dmin
955  end if
956  else
957  if (d > dmin) then
958  dlen = min(d, dmax) - dmin
959  else
960  dlen = dzero
961  end if
962  end if
963  p(n) = sqrt(xlen**dtwo + dlen**dtwo)
964  end do
965  !
966  ! -- return
967  return
968  end subroutine get_wetted_perimeters
969 
970  !> @brief Calculate the cross-sectional areas for each line segment
971  !!
972  !! Subroutine to calculate the cross-sectional area for each line segment
973  !! that defines the reach using the cross-section station-height
974  !! data given a passed depth.
975  !!
976  !<
977  subroutine get_cross_section_areas(npts, stations, heights, d, a)
978  ! -- dummy variables
979  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
980  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
981  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
982  real(DP), intent(in) :: d !< depth to evaluate cross-section
983  real(DP), dimension(npts - 1), intent(inout) :: a !< cross-sectional area for each line segment
984  ! -- local variables
985  integer(I4B) :: n
986  real(DP) :: x0
987  real(DP) :: x1
988  real(DP) :: d0
989  real(DP) :: d1
990  real(DP) :: dmax
991  real(DP) :: dmin
992  real(DP) :: xlen
993  !
994  ! -- iterate over the station-height data
995  do n = 1, npts - 1
996  !
997  ! -- initialize the cross-sectional area
998  a(n) = dzero
999  !
1000  ! -- initialize station-height data for segment
1001  x0 = stations(n)
1002  x1 = stations(n + 1)
1003  d0 = heights(n)
1004  d1 = heights(n + 1)
1005  !
1006  ! -- get the start and end station position of the wetted segment
1007  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
1008  !
1009  ! -- calculate the cross-sectional area for the segment
1010  xlen = x1 - x0
1011  if (xlen > dzero) then
1012  !
1013  ! -- add the area above dmax
1014  if (d > dmax) then
1015  a(n) = xlen * (d - dmax)
1016  end if
1017  !
1018  ! -- add the area below dmax
1019  if (dmax /= dmin .and. d > dmin) then
1020  if (d < dmax) then
1021  a(n) = a(n) + dhalf * (d - dmin) * xlen
1022  else
1023  a(n) = a(n) + dhalf * (dmax - dmin) * xlen
1024  end if
1025  end if
1026  end if
1027  end do
1028  !
1029  ! -- return
1030  return
1031  end subroutine get_cross_section_areas
1032 
1033  !> @brief Calculate the wetted top widths for each line segment
1034  !!
1035  !! Subroutine to calculate the wetted top width for each line segment
1036  !! that defines the reach using the cross-section station-height
1037  !! data given a passed depth.
1038  !!
1039  !<
1040  subroutine get_wetted_topwidths(npts, stations, heights, d, w)
1041  ! -- dummy variables
1042  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
1043  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
1044  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
1045  real(DP), intent(in) :: d !< depth to evaluate cross-section
1046  real(DP), dimension(npts - 1), intent(inout) :: w !< wetted top widths for each line segment
1047  ! -- local variables
1048  integer(I4B) :: n
1049  real(DP) :: x0
1050  real(DP) :: x1
1051  real(DP) :: d0
1052  real(DP) :: d1
1053  real(DP) :: dmax
1054  real(DP) :: dmin
1055  !
1056  ! -- iterate over the station-height data
1057  do n = 1, npts - 1
1058  !
1059  ! -- initialize station-height data for segment
1060  x0 = stations(n)
1061  x1 = stations(n + 1)
1062  d0 = heights(n)
1063  d1 = heights(n + 1)
1064  !
1065  ! -- get the start and end station position of the wetted segment
1066  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
1067  !
1068  ! -- calculate the wetted top width for the segment
1069  w(n) = x1 - x0
1070  end do
1071  !
1072  ! -- return
1073  return
1074  end subroutine get_wetted_topwidths
1075 
1076  !> @brief Calculate the station values for the wetted portion of the cross-section
1077  !!
1078  !! Subroutine to calculate the station values that define the extent of the
1079  !! wetted portion of the cross section for a line segment. The left (x0) and
1080  !! right (x1) station positions are altered if the passed depth is less
1081  !! than the maximum line segment depth. If the line segment is dry the left
1082  !! and right station are equal. Otherwise the wetted station values are equal
1083  !! to the full line segment or smaller if the passed depth is less than
1084  !! the maximum line segment depth.
1085  !!
1086  !<
1087  pure subroutine get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
1088  ! -- dummy variables
1089  real(dp), intent(inout) :: x0 !< left station position
1090  real(dp), intent(inout) :: x1 !< right station position
1091  real(dp), intent(in) :: d0 !< depth at the left station
1092  real(dp), intent(in) :: d1 !< depth at the right station
1093  real(dp), intent(inout) :: dmax !< maximum depth
1094  real(dp), intent(inout) :: dmin !< minimum depth
1095  real(dp), intent(in) :: d !< depth to evaluate cross-section
1096  ! -- local variables
1097  real(dp) :: xlen
1098  real(dp) :: dlen
1099  real(dp) :: slope
1100  real(dp) :: dx
1101  real(dp) :: xt
1102  real(dp) :: xt0
1103  real(dp) :: xt1
1104  !
1105  ! -- calculate the minimum and maximum depth
1106  dmin = min(d0, d1)
1107  dmax = max(d0, d1)
1108  !
1109  ! -- if d is less than or equal to the minimum value the
1110  ! station length (xlen) is zero
1111  if (d <= dmin) then
1112  x1 = x0
1113  ! -- if d is between dmin and dmax station length is less
1114  ! than d1 - d0
1115  else if (d < dmax) then
1116  xlen = x1 - x0
1117  dlen = d1 - d0
1118  if (abs(dlen) > dzero) then
1119  slope = xlen / dlen
1120  else
1121  slope = dzero
1122  end if
1123  if (d0 > d1) then
1124  dx = (d - d1) * slope
1125  xt = x1 + dx
1126  xt0 = xt
1127  xt1 = x1
1128  else
1129  dx = (d - d0) * slope
1130  xt = x0 + dx
1131  xt0 = x0
1132  xt1 = xt
1133  end if
1134  x0 = xt0
1135  x1 = xt1
1136  end if
1137  !
1138  ! -- return
1139  return
1140  end subroutine get_wetted_station
1141 
1142 end module swfcxsutilsmodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dtwothirds
real constant 2/3
Definition: Constants.f90:69
real(dp), parameter dp999
real constant 999/1000
Definition: Constants.f90:73
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 dtwo
real constant 2
Definition: Constants.f90:78
real(dp), parameter dthree
real constant 3
Definition: Constants.f90:79
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
This module defines variable data types.
Definition: kind.f90:8
subroutine schsmooth(d, smooth, dwdh)
@ brief sChSmooth
This module contains stateless sfr subroutines and functions.
Definition: SwfCxsUtils.f90:11
real(dp) function, public calc_qman(depth, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
Calculate streamflow using Manning's equation.
real(dp) function, public get_composite_conveyance(npts, xfraction, heights, cxs_rf, width, rough, d)
Calculate composite conveyance.
real(dp) function, public get_mannings_section(npts, xfraction, heights, roughfracs, roughness, conv_fact, width, slope, d)
Calculate the manning's discharge for a reach.
real(dp) function, public get_hydraulic_radius_xf(npts, xfraction, heights, width, d)
Calculate the hydraulic radius for a reach.
subroutine get_cross_section_areas(npts, stations, heights, d, a)
Calculate the cross-sectional areas for each line segment.
logical(lgp) function has_uniform_resistance(cxs_rf)
Determine if roughness is uniform for the section.
logical(lgp) function is_rectangular(xfraction)
Determine if cross section is rectangular.
real(dp) function, public calc_composite_roughness(npts, depth, width, rough, slope, cxs_xf, cxs_h, cxs_rf, linmeth)
real(dp) function, public calc_depth_from_q(qrch, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
Calculate the depth at the midpoint of a irregular cross-section.
Definition: SwfCxsUtils.f90:40
pure subroutine get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
Calculate the station values for the wetted portion of the cross-section.
real(dp) function, public get_hydraulic_radius(npts, stations, heights, d)
Calculate the hydraulic radius for a reach.
real(dp) function calc_qman_by_section(depth, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv)
subroutine get_wetted_topwidths(npts, stations, heights, d, w)
Calculate the wetted top widths for each line segment.
real(dp) function, public get_cross_section_area(npts, xfraction, heights, width, d)
Calculate the cross-sectional area for a reach.
real(dp) function, public get_wetted_topwidth(npts, xfraction, heights, width, d)
Calculate the wetted top width for a reach.
real(dp) function calc_qman_composite(depth, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv, linmeth)
real(dp) function calc_depth_from_q_nr(qrch, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
Calculate the depth at the midpoint of a irregular cross-section.
real(dp) function, public get_conveyance(npts, xfraction, heights, cxs_rf, width, rough, d)
Calculate conveyance.
subroutine get_wetted_perimeters(npts, stations, heights, d, p)
Calculate the wetted perimeters for each line segment.
real(dp) function, public get_wetted_perimeter(npts, xfraction, heights, width, d)
Calculate the wetted perimeter for a reach.
real(dp) function get_rectangular_conveyance(npts, xfraction, heights, cxs_rf, width, rough, d)
Calculate conveyance for rectangular channel.
real(dp) function, public get_saturated_topwidth(npts, stations)
Calculate the saturated top width for a reach.
real(dp) function calc_depth_from_q_bisect(qrch, width, rough, slope, cxs_xf, cxs_h, cxs_rf, unitconv, icalcmeth)
Calculate the depth at the midpoint of a irregular cross-section.
Definition: SwfCxsUtils.f90:79