MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
SfrCrossSectionUtils.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
15 
16  implicit none
17  private
18  public :: get_saturated_topwidth
19  public :: get_wetted_topwidth
20  public :: get_wetted_perimeter
21  public :: get_cross_section_area
22  public :: get_hydraulic_radius
23  public :: get_mannings_section
24 
25 contains
26 
27  !> @brief Calculate the saturated top width for a reach
28  !!
29  !! Function to calculate the maximum top width for a reach using the
30  !! cross-section station data.
31  !!
32  !! @return w saturated top width
33  !<
34  function get_saturated_topwidth(npts, stations) result(w)
35  ! -- dummy variables
36  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
37  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
38  ! -- local variables
39  real(dp) :: w
40  !
41  ! -- calculate the saturated top width
42  if (npts > 1) then
43  w = stations(npts) - stations(1)
44  else
45  w = stations(1)
46  end if
47  !
48  ! -- return
49  return
50  end function get_saturated_topwidth
51 
52  !> @brief Calculate the wetted top width for a reach
53  !!
54  !! Function to calculate the wetted top width for a reach using the
55  !! cross-section station-height data given a passed depth.
56  !!
57  !! @return w wetted top width
58  !<
59  function get_wetted_topwidth(npts, stations, heights, d) result(w)
60  ! -- dummy variables
61  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
62  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
63  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
64  real(dp), intent(in) :: d !< depth to evaluate cross-section
65  ! -- local variables
66  integer(I4B) :: n
67  real(dp) :: w
68  real(dp), dimension(npts - 1) :: widths
69  !
70  ! -- initialize the wetted perimeter for the reach
71  w = dzero
72  !
73  ! -- calculate the wetted top width for each line segment
74  call get_wetted_topwidths(npts, stations, heights, d, widths)
75  !
76  ! -- calculate the wetted top widths
77  do n = 1, npts - 1
78  w = w + widths(n)
79  end do
80  !
81  ! -- return
82  return
83  end function get_wetted_topwidth
84 
85  !> @brief Calculate wetted vertical height
86  !!
87  !! For segments flanked by vertically-oriented neighboring segments,
88  !! return the length of the submerged, vertically-oriented, neighboring face
89  !!
90  !<
91  function get_wet_vert_face(n, npts, heights, d, leftface) result(vwf)
92  ! -- dummy
93  integer(I4B), intent(in) :: n !< index to be evaluated
94  integer(I4B), intent(in) :: npts !< length of heights vector
95  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
96  real(dp), intent(in) :: d
97  logical, intent(in) :: leftface
98  ! -- local
99  real(dp) :: vwf !< vertically wetted face length
100  !
101  ! -- calculate the vertically-oriented wetted face length
102  if (leftface) then
103  if (heights(n - 1) > d) then
104  vwf = d - heights(n)
105  else if (heights(n - 1) > heights(n)) then
106  vwf = heights(n - 1) - heights(n)
107  end if
108  else
109  if (heights(n + 2) > d) then
110  vwf = d - heights(n + 1)
111  else if (heights(n + 2) > heights(n + 1)) then
112  vwf = heights(n + 2) - heights(n + 1)
113  end if
114  end if
115  !
116  ! -- return
117  return
118  end function get_wet_vert_face
119 
120  !> @brief Calculate the wetted perimeter for a reach
121  !!
122  !! Function to calculate the wetted perimeter for a reach using the
123  !! cross-section station-height data given a passed depth.
124  !!
125  !! @return p wetted perimeter
126  !<
127  function get_wetted_perimeter(npts, stations, heights, d) result(p)
128  ! -- dummy variables
129  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
130  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
131  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
132  real(dp), intent(in) :: d !< depth to evaluate cross-section
133  ! -- local variables
134  integer(I4B) :: n
135  real(dp) :: p
136  real(dp), dimension(npts - 1) :: perimeters
137  !
138  ! -- initialize the wetted perimeter for the reach
139  p = dzero
140  !
141  ! -- calculate the wetted perimeter for each line segment
142  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
143  !
144  ! -- calculate the wetted perimenter
145  do n = 1, npts - 1
146  p = p + perimeters(n)
147  end do
148  !
149  ! -- return
150  return
151  end function get_wetted_perimeter
152 
153  !> @brief Calculate the cross-sectional area for a reach
154  !!
155  !! Function to calculate the cross-sectional area for a reach using
156  !! the cross-section station-height data given a passed depth.
157  !!
158  !! @return a cross-sectional area
159  !<
160  function get_cross_section_area(npts, stations, heights, d) result(a)
161  ! -- dummy variables
162  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
163  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
164  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
165  real(dp), intent(in) :: d !< depth to evaluate cross-section
166  ! -- local variables
167  integer(I4B) :: n
168  real(dp) :: a
169  real(dp), dimension(npts - 1) :: areas
170  !
171  ! -- initialize the area
172  a = dzero
173  !
174  ! -- calculate the cross-sectional area for each line segment
175  call get_cross_section_areas(npts, stations, heights, d, areas)
176  !
177  ! -- calculate the cross-sectional area
178  do n = 1, npts - 1
179  a = a + areas(n)
180  end do
181  !
182  ! -- return
183  return
184  end function get_cross_section_area
185 
186  !> @brief Calculate the hydraulic radius for a reach
187  !!
188  !! Function to calculate the hydraulic radius for a reach using
189  !! the cross-section station-height data given a passed depth.
190  !!
191  !! @return r hydraulic radius
192  !<
193  function get_hydraulic_radius(npts, stations, heights, d) result(r)
194  ! -- dummy variables
195  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
196  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
197  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
198  real(dp), intent(in) :: d !< depth to evaluate cross-section
199  ! -- local variables
200  integer(I4B) :: n
201  real(dp) :: r
202  real(dp) :: p
203  real(dp) :: a
204  real(dp), dimension(npts - 1) :: areas
205  real(dp), dimension(npts - 1) :: perimeters
206  !
207  ! -- initialize the hydraulic radius, perimeter, and area
208  r = dzero
209  p = dzero
210  a = dzero
211  !
212  ! -- calculate the wetted perimeter for each line segment
213  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
214  !
215  ! -- calculate the wetted perimenter
216  do n = 1, npts - 1
217  p = p + perimeters(n)
218  end do
219  !
220  ! -- calculate the hydraulic radius only if the perimeter is non-zero
221  if (p > dzero) then
222  !
223  ! -- calculate the cross-sectional area for each line segment
224  call get_cross_section_areas(npts, stations, heights, d, areas)
225  !
226  ! -- calculate the cross-sectional area
227  do n = 1, npts - 1
228  a = a + areas(n)
229  end do
230  !
231  ! -- calculate the hydraulic radius
232  r = a / p
233  end if
234  !
235  ! -- return
236  return
237  end function get_hydraulic_radius
238 
239  !> @brief Calculate the manning's discharge for a reach
240  !!
241  !! Function to calculate the mannings discharge for a reach
242  !! by calculating the discharge for each section, which can
243  !! have a unique Manning's coefficient given a passed depth.
244  !!
245  !! @return q reach discharge
246  !<
247  function get_mannings_section(npts, stations, heights, roughfracs, &
248  roughness, conv_fact, slope, d) result(q)
249  ! -- dummy variables
250  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
251  real(dp), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
252  real(dp), dimension(npts), intent(in) :: heights !< cross-section height data
253  real(dp), dimension(npts), intent(in) :: roughfracs !< cross-section Mannings roughness fraction data
254  real(dp), intent(in) :: roughness !< base reach roughness
255  real(dp), intent(in) :: conv_fact !< unit conversion factor
256  real(dp), intent(in) :: slope !< reach slope
257  real(dp), intent(in) :: d !< depth to evaluate cross-section
258  ! -- local variables
259  integer(I4B) :: n
260  real(dp) :: q
261  real(dp) :: rh
262  real(dp) :: r
263  real(dp) :: p
264  real(dp) :: a
265  real(dp), dimension(npts - 1) :: areas
266  real(dp), dimension(npts - 1) :: perimeters
267  !
268  ! -- initialize the hydraulic radius, perimeter, and area
269  q = dzero
270  rh = dzero
271  r = dzero
272  p = dzero
273  a = dzero
274  !
275  ! -- calculate the wetted perimeter for each line segment
276  call get_wetted_perimeters(npts, stations, heights, d, perimeters)
277  !
278  ! -- calculate the wetted perimenter
279  do n = 1, npts - 1
280  p = p + perimeters(n)
281  end do
282  !
283  ! -- calculate the hydraulic radius only if the perimeter is non-zero
284  if (p > dzero) then
285  !
286  ! -- calculate the cross-sectional area for each line segment
287  call get_cross_section_areas(npts, stations, heights, d, areas)
288  !
289  ! -- calculate the cross-sectional area
290  do n = 1, npts - 1
291  p = perimeters(n)
292  r = roughness * roughfracs(n)
293  if (p * r > dzero) then
294  a = areas(n)
295  rh = a / p
296  q = q + conv_fact * a * rh**dtwothirds * sqrt(slope) / r
297  end if
298  end do
299  end if
300  !
301  ! -- return
302  return
303  end function get_mannings_section
304 
305  ! -- private functions and subroutines
306 
307  !> @brief Determine vertical segments
308  !!
309  !! Subroutine to cycle through each segment (npts - 1) and determine
310  !! whether neighboring segments are vertically-oriented.
311  !!
312  !<
313  subroutine determine_vert_neighbors(npts, stations, heights, leftv, rightv)
314  ! -- dummy
315  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
316  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
317  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
318  logical, dimension(npts - 1), intent(inout) :: leftv
319  logical, dimension(npts - 1), intent(inout) :: rightv
320  ! -- local
321  integer(I4B) :: n
322  !
323  ! -- default neighboring segments to false unless determined otherwise
324  ! o 2 pt x-section has 1 segment (no neighbors to eval)
325  ! o 3+ pt x-section has at the very least one neighbor to eval
326  do n = 1, npts - 1
327  leftv(n) = .false.
328  rightv(n) = .false.
329  ! -- left neighbor
330  if (n > 1) then
331  if (stations(n - 1) == stations(n) .and. heights(n - 1) > heights(n)) then
332  leftv(n) = .true.
333  end if
334  end if
335  ! -- right neighbor
336  if (n < npts - 1) then
337  if (stations(n + 2) == stations(n + 1) .and. &
338  heights(n + 2) > heights(n + 1)) then
339  rightv(n) = .true.
340  end if
341  end if
342  end do
343  !
344  ! -- return
345  return
346  end subroutine determine_vert_neighbors
347 
348  !> @brief Calculate the wetted perimeters for each line segment
349  !!
350  !! Subroutine to calculate the wetted perimeter for each line segment
351  !! that defines the reach using the cross-section station-height
352  !! data given a passed depth.
353  !!
354  !<
355  subroutine get_wetted_perimeters(npts, stations, heights, d, p)
356  ! -- dummy variables
357  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
358  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
359  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
360  real(DP), intent(in) :: d !< depth to evaluate cross-section
361  real(DP), dimension(npts - 1), intent(inout) :: p !< wetted perimeter for each line segment
362  ! -- local variables
363  integer(I4B) :: n
364  real(DP) :: x0
365  real(DP) :: x1
366  real(DP) :: d0
367  real(DP) :: d1
368  real(DP) :: dmax
369  real(DP) :: dmin
370  real(DP) :: xlen
371  real(DP) :: dlen
372  logical, dimension(npts - 1) :: leftv, rightv
373  !
374  ! -- set neighbor status
375  call determine_vert_neighbors(npts, stations, heights, leftv, rightv)
376  !
377  ! -- iterate over the station-height data
378  do n = 1, npts - 1
379  !
380  ! -- initialize the wetted perimeter
381  p(n) = dzero
382  !
383  ! -- initialize station-height data for segment
384  x0 = stations(n)
385  x1 = stations(n + 1)
386  d0 = heights(n)
387  d1 = heights(n + 1)
388  !
389  ! -- get the start and end station position of the wetted segment
390  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
391  !
392  ! -- calculate the wetted perimeter for the segment
393  ! - bottom wetted length
394  xlen = x1 - x0
395  dlen = dzero
396  if (xlen > dzero) then
397  if (d > dmax) then
398  dlen = dmax - dmin
399  else
400  dlen = d - dmin
401  end if
402  else
403  if (d > dmin) then
404  dlen = min(d, dmax) - dmin
405  else
406  dlen = dzero
407  end if
408  end if
409  p(n) = sqrt(xlen**dtwo + dlen**dtwo)
410  !
411  ! -- if neighboring segments are vertical, account for their
412  ! contribution to wetted perimeter
413  !
414  ! left neighbor (if applicable)
415  if (n > 1) then
416  if (leftv(n)) then
417  p(n) = p(n) + get_wet_vert_face(n, npts, heights, d, .true.)
418  end if
419  end if
420  ! right neighbor (if applicable)
421  if (n < npts - 1) then
422  if (rightv(n)) then
423  p(n) = p(n) + get_wet_vert_face(n, npts, heights, d, .false.)
424  end if
425  end if
426  end do
427  !
428  ! -- return
429  return
430  end subroutine get_wetted_perimeters
431 
432  !> @brief Calculate the cross-sectional areas for each line segment
433  !!
434  !! Subroutine to calculate the cross-sectional area for each line segment
435  !! that defines the reach using the cross-section station-height
436  !! data given a passed depth.
437  !!
438  !<
439  subroutine get_cross_section_areas(npts, stations, heights, d, a)
440  ! -- dummy variables
441  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
442  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
443  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
444  real(DP), intent(in) :: d !< depth to evaluate cross-section
445  real(DP), dimension(npts - 1), intent(inout) :: a !< cross-sectional area for each line segment
446  ! -- local variables
447  integer(I4B) :: n
448  real(DP) :: x0
449  real(DP) :: x1
450  real(DP) :: d0
451  real(DP) :: d1
452  real(DP) :: dmax
453  real(DP) :: dmin
454  real(DP) :: xlen
455  !
456  ! -- iterate over the station-height data
457  do n = 1, npts - 1
458  !
459  ! -- initialize the cross-sectional area
460  a(n) = dzero
461  !
462  ! -- initialize station-height data for segment
463  x0 = stations(n)
464  x1 = stations(n + 1)
465  d0 = heights(n)
466  d1 = heights(n + 1)
467  !
468  ! -- get the start and end station position of the wetted segment
469  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
470  !
471  ! -- calculate the cross-sectional area for the segment
472  xlen = x1 - x0
473  if (xlen > dzero) then
474  !
475  ! -- add the area above dmax
476  if (d > dmax) then
477  a(n) = xlen * (d - dmax)
478  end if
479  !
480  ! -- add the area below dmax
481  if (dmax /= dmin .and. d > dmin) then
482  if (d < dmax) then
483  a(n) = a(n) + dhalf * (d - dmin) * xlen
484  else
485  a(n) = a(n) + dhalf * (dmax - dmin) * xlen
486  end if
487  end if
488  end if
489  end do
490  !
491  ! -- return
492  return
493  end subroutine get_cross_section_areas
494 
495  !> @brief Calculate the wetted top widths for each line segment
496  !!
497  !! Subroutine to calculate the wetted top width for each line segment
498  !! that defines the reach using the cross-section station-height
499  !! data given a passed depth.
500  !!
501  !<
502  subroutine get_wetted_topwidths(npts, stations, heights, d, w)
503  ! -- dummy variables
504  integer(I4B), intent(in) :: npts !< number of station-height data for a reach
505  real(DP), dimension(npts), intent(in) :: stations !< cross-section station distances (x-distance)
506  real(DP), dimension(npts), intent(in) :: heights !< cross-section height data
507  real(DP), intent(in) :: d !< depth to evaluate cross-section
508  real(DP), dimension(npts - 1), intent(inout) :: w !< wetted top widths for each line segment
509  ! -- local variables
510  integer(I4B) :: n
511  real(DP) :: x0
512  real(DP) :: x1
513  real(DP) :: d0
514  real(DP) :: d1
515  real(DP) :: dmax
516  real(DP) :: dmin
517  !
518  ! -- iterate over the station-height data
519  do n = 1, npts - 1
520  !
521  ! -- initialize station-height data for segment
522  x0 = stations(n)
523  x1 = stations(n + 1)
524  d0 = heights(n)
525  d1 = heights(n + 1)
526  !
527  ! -- get the start and end station position of the wetted segment
528  call get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
529  !
530  ! -- calculate the wetted top width for the segment
531  w(n) = x1 - x0
532  end do
533  !
534  ! -- return
535  return
536  end subroutine get_wetted_topwidths
537 
538  !> @brief Calculate the station values for the wetted portion of the cross-section
539  !!
540  !! Subroutine to calculate the station values that define the extent of the
541  !! wetted portion of the cross section for a line segment. The left (x0) and
542  !! right (x1) station positions are altered if the passed depth is less
543  !! than the maximum line segment depth. If the line segment is dry the left
544  !! and right station are equal. Otherwise the wetted station values are equal
545  !! to the full line segment or smaller if the passed depth is less than
546  !! the maximum line segment depth.
547  !!
548  !<
549  pure subroutine get_wetted_station(x0, x1, d0, d1, dmax, dmin, d)
550  ! -- dummy variables
551  real(dp), intent(inout) :: x0 !< left station position
552  real(dp), intent(inout) :: x1 !< right station position
553  real(dp), intent(in) :: d0 !< depth at the left station
554  real(dp), intent(in) :: d1 !< depth at the right station
555  real(dp), intent(inout) :: dmax !< maximum depth
556  real(dp), intent(inout) :: dmin !< minimum depth
557  real(dp), intent(in) :: d !< depth to evaluate cross-section
558  ! -- local variables
559  real(dp) :: xlen
560  real(dp) :: dlen
561  real(dp) :: slope
562  real(dp) :: dx
563  real(dp) :: xt
564  real(dp) :: xt0
565  real(dp) :: xt1
566  !
567  ! -- calculate the minimum and maximum depth
568  dmin = min(d0, d1)
569  dmax = max(d0, d1)
570  !
571  ! -- if d is less than or equal to the minimum value the
572  ! station length (xlen) is zero
573  if (d <= dmin) then
574  x1 = x0
575  ! -- if d is between dmin and dmax station length is less
576  ! than d1 - d0
577  else if (d < dmax) then
578  xlen = x1 - x0
579  dlen = d1 - d0
580  if (abs(dlen) > dzero) then
581  slope = xlen / dlen
582  else
583  slope = dzero
584  end if
585  if (d0 > d1) then
586  dx = (d - d1) * slope
587  xt = x1 + dx
588  xt0 = xt
589  xt1 = x1
590  else
591  dx = (d - d0) * slope
592  xt = x0 + dx
593  xt0 = x0
594  xt1 = xt
595  end if
596  x0 = xt0
597  x1 = xt1
598  end if
599  !
600  ! -- return
601  return
602  end subroutine get_wetted_station
603 
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dtwothirds
real constant 2/3
Definition: Constants.f90:69
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 dtwo
real constant 2
Definition: Constants.f90:78
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
This module contains stateless sfr subroutines and functions.
real(dp) function, public get_wetted_topwidth(npts, stations, heights, d)
Calculate the wetted top width for a reach.
real(dp) function get_wet_vert_face(n, npts, heights, d, leftface)
Calculate wetted vertical height.
subroutine get_wetted_topwidths(npts, stations, heights, d, w)
Calculate the wetted top widths for each line segment.
subroutine get_cross_section_areas(npts, stations, heights, d, a)
Calculate the cross-sectional areas for each line segment.
subroutine get_wetted_perimeters(npts, stations, heights, d, p)
Calculate the wetted perimeters for each line segment.
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_wetted_perimeter(npts, stations, heights, d)
Calculate the wetted perimeter for a reach.
real(dp) function, public get_cross_section_area(npts, stations, heights, d)
Calculate the cross-sectional area for a reach.
real(dp) function, public get_saturated_topwidth(npts, stations)
Calculate the saturated top width for a reach.
subroutine determine_vert_neighbors(npts, stations, heights, leftv, rightv)
Determine vertical segments.
real(dp) function, public get_mannings_section(npts, stations, heights, roughfracs, roughness, conv_fact, slope, d)
Calculate the manning's discharge for a reach.
real(dp) function, public get_hydraulic_radius(npts, stations, heights, d)
Calculate the hydraulic radius for a reach.
This module defines variable data types.
Definition: kind.f90:8