Soil Water Balance (SWB2)
Loading...
Searching...
No Matches
runoff__curve_number.F90
Go to the documentation of this file.
2
3 use iso_c_binding, only : c_int, c_float, c_double, c_bool
5 use datetime
6 use exceptions
8 use fstring
10 use parameters, only : params, params_dict
11 implicit none
12
13 private
14
15 real (c_float), allocatable :: cn_arciii(:,:)
16 real (c_float), allocatable :: cn_arcii(:,:)
17 real (c_float), allocatable :: cn_arci(:,:)
18 real (c_float), allocatable :: prev_5_days_rain(:,:)
19 integer (c_int), allocatable :: ilandusecodes(:)
20 integer (c_int) :: daycount
21 integer (c_int), parameter :: five_day_sum = 6
22
23
28 public :: cn_arci, cn_arcii, cn_arciii
30
31 real (c_float), parameter :: amc_dry_growing = 1.40_c_float
32 real (c_float), parameter :: amc_dry_dormant = 0.50_c_float
33 real (c_float), parameter :: amc_wet_growing = 2.10_c_float
34 real (c_float), parameter :: amc_wet_dormant = 1.10_c_float
35
37
38contains
39
40 subroutine runoff_curve_number_initialize( cell_is_active )
41
42 use ieee_arithmetic, only : ieee_is_nan, ieee_is_finite
43
44 logical (c_bool), intent(in) :: cell_is_active(:,:)
45
46 ! [ LOCALS ]
47 integer (c_int) :: inumberoflanduses
48 integer (c_int) :: inumberofsoilgroups
49 integer (c_int), allocatable :: icurvenumberseqnums(:)
50 type (fstring_list_t) :: sllist
51 type (fstring_list_t) :: slcurvenumber
52 integer (c_int) :: istat
53 integer (c_int) :: isoilsindex
54 character (len=:), allocatable :: stext
55 real (c_float), allocatable :: cn(:)
56
57 call sllist%append("LU_Code")
58 call sllist%append("Landuse_Lookup_Code")
59
60 !> Determine how many soil groups are present
61
62 ! retrieve a string list of all keys associated with curve number (i.e. "CN_1", "CN_2", "CN_3", etc)
63 slcurvenumber = params%grep_name( "CN", lfatal=true )
64
65 ! Convert the string list to an vector of integers; this call strips off the "CN_" part of label
66 icurvenumberseqnums = slcurvenumber%get_integer()
67
68 ! count how many items are present in the vector; this should equal the number of soils groups
69 inumberofsoilgroups = count( icurvenumberseqnums > 0 )
70
71 !> Determine how many landuse codes are present
72 call params%get_parameters( slkeys=sllist, ivalues=ilandusecodes )
73 inumberoflanduses = count( ilandusecodes >= 0 )
74
75 allocate( cn_arciii(inumberoflanduses, inumberofsoilgroups), stat=istat )
76 call assert( istat == 0, "Failed to allocate memory for curve number table - AMC III", &
77 __file__, __line__)
78
79 allocate( cn_arcii(inumberoflanduses, inumberofsoilgroups), stat=istat )
80 call assert( istat == 0, "Failed to allocate memory for curve number table - AMC II", &
81 __file__, __line__)
82
83 allocate( cn_arci(inumberoflanduses, inumberofsoilgroups), stat=istat )
84 call assert( istat == 0, "Failed to allocate memory for curve number table - AMC I", &
85 __file__, __line__)
86
87 ! we should have the curve number table fully filled out following this block
88 do isoilsindex = 1, inumberofsoilgroups
89 stext = "CN_"//ascharacter(isoilsindex)
90 call params%get_parameters( skey=stext, fvalues=cn )
91 cn_arcii(:, isoilsindex) = cn
92 enddo
93
94 allocate( prev_5_days_rain( count(cell_is_active), 6 ), stat=istat )
95 call assert( istat == 0, "Failed to allocate memory for curve number PREV_5_DAYS_RAIN table", &
96 __file__, __line__)
97
98 prev_5_days_rain = 0.0_c_float
99
100 ! DAYCOUNT will vary from 1 to 5 and serve as the second index value for
101 ! PREV_5_DAYS_RAIN
102 daycount = 0
103
104 ! calculate curve numbers for antecedent runof conditions I and III
107
108 call date_last_updated%parsedate("01/01/1000")
109
110 end subroutine runoff_curve_number_initialize
111
112!--------------------------------------------------------------------------------------------------
113
114 elemental function return_landuse_index_fn(iLanduseCode) result(iLanduseIndex)
115
116 integer (c_int), intent(in) :: ilandusecode
117 integer (c_int) :: ilanduseindex
118
119 ilanduseindex = 0
120
121 do while (ilanduseindex < ubound( ilandusecodes, 1) )
122
123 ilanduseindex = ilanduseindex + 1
124
125 if (ilandusecode == ilandusecodes(ilanduseindex) ) exit
126
127 end do
128
129 end function return_landuse_index_fn
130
131!--------------------------------------------------------------------------------------------------
132
133 elemental function prob_runoff_enhancement( fCFGI, fCFGI_UL, fCFGI_LL ) result(fPf)
134
135 real (c_float), intent(in) :: fcfgi
136 real (c_float), intent(in) :: fcfgi_ul
137 real (c_float), intent(in) :: fcfgi_ll
138
139 real (c_float) :: fpf
140
141 if(fcfgi <= fcfgi_ll ) then
142 fpf = 0_c_float
143 elseif(fcfgi >= fcfgi_ul) then
144 fpf = 1.0_c_float
145 else
146 fpf = ( fcfgi - fcfgi_ll ) / ( fcfgi_ul - fcfgi_ll )
147 end if
148
149 end function prob_runoff_enhancement
150
151!--------------------------------------------------------------------------------------------------
152
153 subroutine update_previous_5_day_rainfall( infil, indx )
154
155 real (c_float), intent(in) :: infil
156 integer (c_int), intent(in) :: indx
157
158 ! we only want to increment DAYCOUNT 1x per day!
159 if ( .not. date_last_updated == sim_dt%curr ) then
160
161 if ( daycount < 5 ) then
162 daycount = daycount + 1
163 else
164 daycount = 1
165 endif
166
168
169 endif
170
171 prev_5_days_rain( indx, daycount ) = infil
172 prev_5_days_rain( indx, five_day_sum ) = sum( prev_5_days_rain( indx, 1:5 ) )
173
174 end subroutine
175!--------------------------------------------------------------------------------------------------
176
177! elemental function update_curve_number_fn( iLanduseIndex, iSoilsIndex, fSoilStorage, &
178! fSoilStorage_Max, fCFGI ) result( CN_adj )
179
180! integer (c_int), intent(in) :: iLanduseIndex
181! integer (c_int), intent(in) :: iSoilsIndex
182! real (c_float), intent(in) :: fSoilStorage
183! real (c_float), intent(in) :: fSoilStorage_Max
184! real (c_float), intent(in) :: fCFGI
185! real (c_float) :: CN_adj
186
187! ! [ LOCALS ]
188! real (c_float) :: Pf
189! real (c_float) :: CN_base
190! real (c_float) :: fFraction_FC ! fraction of field capacity
191! real (c_float) :: frac
192
193! fFraction_FC = 0.0
194! if (fSoilStorage_Max > 0.0) fFraction_FC = min( 1.0_c_float, fSoilStorage / fSoilStorage_Max )
195
196! ! Correct the curve number...
197! !
198! ! current thinking on this: if the soil moisture is at average levels or *greater*
199! ! *and* the CFGI indicates frozen ground conditions, assume that runoff enhancement
200! ! is probable. If the soils froze under relatively dry conditions, assume that some pore
201! ! space is open in the frozen ground.
202! if( fCFGI > CFGI_LL .and. fFraction_FC > 0.5 ) then
203
204! Pf = prob_runoff_enhancement( fCFGI )
205
206! ! use probability of runoff enhancement to calculate a weighted
207! ! average of curve number under Type II vs Type III antecedent
208! ! runoff conditions
209! CN_adj = CN_ARCII(iLanduseIndex, iSoilsIndex) * ( 1.0_c_float - Pf ) &
210! + CN_ARCIII(iLanduseIndex, iSoilsIndex) * Pf
211
212! elseif ( fFraction_FC > 0.5_c_float ) then ! adjust upward toward AMC III
213
214! ! we want a number ranging from 0 to 1 indicating the relative split
215! ! between CN AMCII and CN AMCIII
216! frac = ( fFraction_FC - 0.5_c_float ) / 0.5_c_float
217
218! CN_adj = CN_ARCII(iLanduseIndex, iSoilsIndex) * ( 1.0_c_float - frac ) &
219! + CN_ARCIII(iLanduseIndex, iSoilsIndex) * frac
220
221! elseif ( fFraction_FC < 0.5_c_float ) then ! adjust downward toward AMC I
222
223! ! we want a number ranging from 0 to 1 indicating the relative split
224! ! between CN AMCII and CN AMCI
225! !
226! ! ex. fFraction_FC = 0.48; frac = 2.0 * 0.48 = 0.96
227! ! CN_adj = 0.96 ( CN_ARCII ) + ( 1.0 - 0.96 ) * CN_ARCIII
228! !
229! frac = fFraction_FC / 0.5_c_float
230
231! CN_adj = CN_ARCI(iLanduseIndex, iSoilsIndex) * ( 1.0_c_float - frac ) &
232! + CN_ARCII(iLanduseIndex, iSoilsIndex) * frac
233
234! else
235
236! CN_adj = CN_ARCII(iLanduseIndex, iSoilsIndex)
237
238! endif
239
240
241! ! ensure that whatever modification have been made to the curve number
242! ! remain within reasonable bounds
243! CN_adj = MIN( CN_adj, 100.0_c_float )
244! CN_adj = MAX( CN_adj, 0.0_c_float )
245
246! end function update_curve_number_fn
247
248!--------------------------------------------------------------------------------------------------
249
250 elemental function update_curve_number_fn( iLanduseIndex, iSoilsIndex, cell_index, &
251 it_is_growing_season, fSoilStorage_Max, &
252 fCFGI, fCFGI_LL, fCFGI_UL ) result( CN_adj )
253
254 integer (c_int), intent(in) :: ilanduseindex
255 integer (c_int), intent(in) :: isoilsindex
256 integer (c_int), intent(in) :: cell_index
257 logical (c_bool), intent(in) :: it_is_growing_season
258 real (c_float), intent(in) :: fsoilstorage_max
259 real (c_float), intent(in) :: fcfgi
260 real (c_float), intent(in) :: fcfgi_ll
261 real (c_float), intent(in) :: fcfgi_ul
262 real (c_float) :: cn_adj
263
264 ! [ LOCALS ]
265 real (c_float) :: pf
266 real (c_float) :: frac
267 real (c_float) :: finflow
268
269 finflow = prev_5_days_rain( cell_index, five_day_sum )
270
271 associate( cn_i => cn_arci( ilanduseindex, isoilsindex ), &
272 cn_ii => cn_arcii( ilanduseindex, isoilsindex ), &
273 cn_iii => cn_arciii( ilanduseindex, isoilsindex ) )
274
275 ! Correct the curve number...
276
277 if( ( fcfgi > fcfgi_ll ) .and. ( fsoilstorage_max > 0.0_c_float ) ) then
278
279 pf = prob_runoff_enhancement( fcfgi, fcfgi_ll, fcfgi_ul )
280
281 ! use probability of runoff enhancement to calculate a weighted
282 ! average of curve number under Type II vs Type III antecedent
283 ! runoff conditions
284 cn_adj = cn_ii * (1-pf) + cn_iii * pf
285
286 else if ( it_is_growing_season ) then
287
288 if ( finflow < amc_dry_growing ) then
289
290 cn_adj = cn_i
291
292 else if ( finflow >= amc_dry_growing &
293 .and. finflow < amc_wet_growing ) then
294
295 cn_adj = cn_ii
296
297 else
298
299 cn_adj = cn_iii
300
301 end if
302
303 else ! dormant (non-growing) season
304
305 if ( finflow < amc_dry_dormant ) then
306
307 cn_adj = cn_i
308
309 else if ( finflow >= amc_dry_dormant &
310 .and. finflow < amc_wet_dormant ) then
311
312 cn_adj = cn_ii
313
314 else
315
316 cn_adj = cn_iii
317
318 end if
319
320 end if
321
322 end associate
323
324 ! ensure that whatever modification have been made to the curve number
325 ! remain within reasonable bounds
326 cn_adj = min(cn_adj,100.0_c_float)
327 cn_adj = max(cn_adj,30.0_c_float)
328
329
330 end function update_curve_number_fn
331
332!--------------------------------------------------------------------------------------------------
333
334 elemental function cn_ii_to_cn_iii(CN_II) result(CN_III)
335
336 real (c_float), intent(in) :: cn_ii
337 real (c_float) :: cn_iii
338
339 cn_iii = cn_ii / ( 0.427_c_float + 0.00573_c_float * cn_ii )
340
341 cn_iii = max( cn_iii, 30.0_c_float )
342 cn_iii = min( cn_iii, 100.0_c_float )
343
344 end function cn_ii_to_cn_iii
345
346!--------------------------------------------------------------------------------------------------
347
348 elemental function cn_ii_to_cn_i(CN_II) result(CN_I)
349
350 real (c_float), intent(in) :: cn_ii
351 real (c_float) :: cn_i
352
353 ! The following comes from page 192, eq. 3.145 of "SCS Curve Number Methodology"
354 cn_i = cn_ii / (2.281_c_float - 0.01281_c_float * cn_ii )
355
356 cn_i = max( cn_i, 30.0_c_float )
357 cn_i = min( cn_i, 100.0_c_float )
358
359 end function cn_ii_to_cn_i
360
361!--------------------------------------------------------------------------------------------------
362 !> Calculate the runoff by means of the curve number method.
363 !!
364 !! Runoff is calculated using a modification of the curve number method.
365 !! Specifically, the initial abstraction is defined as 0.05 * SMax rather than the
366 !! standard 0.20 * SMax of the original method. This redefinition of the initial abstraction
367 !! term was found to be more appropriate for long-term continuous simulations than the
368 !! standard 0.2 * SMax of the original.
369 !! @param[in] iLanduseIndex Pre-configured index number corresponding to a line in the landuse lookup table.
370 !! @param[in] iSoilsGroup The numerical index associated with the soils group of interest.
371 !! @param[in] fInflow The sum of net rainfall, snowmelt, and runon from upslope cells (and possibly irrigation).
372 !! @param[in] fCFGI The current value of the continuous frozen-ground index.
373 !! @param[in] lIsGrowingSeason Logical value indicating whether dormant season or growing season
374 !! criteria values are to be used when calculating runoff.
375 !!
376 !! @retval fRunoff Daily runoff calculated by means of the SCS Curve Number method.
377 !!
378 !! @note Reference: Woodward, D. E., R. H. Hawkins, R. Jiang, A. Hjelmfeldt Jr, J. Van Mullem,
379 !! and Q. D. Quan. "Runoff Curve Number Method: Examination of the Initial Abstraction Ratio."
380 !! In Conference Proceeding Paper, World Water and Environmental Resources Congress, 2003.
381 elemental subroutine runoff_curve_number_calculate(runoff, &
382 curve_num_adj, &
383 cell_index, &
384 landuse_index, &
385 soil_group, &
386 it_is_growing_season, &
387 inflow, &
388 soil_storage_max, &
389 continuous_frozen_ground_index, &
390 cfgi_lower_limit, &
391 cfgi_upper_limit )
392
393 real (c_float), intent(inout) :: runoff
394 real (c_float), intent(inout) :: curve_num_adj
395 integer (c_int), intent(in) :: cell_index
396 integer (c_int), intent(in) :: landuse_index
397 integer (c_int), intent(in) :: soil_group
398 logical (c_bool), intent(in) :: it_is_growing_season
399 real (c_float), intent(in) :: inflow
400 real (c_float), intent(in) :: soil_storage_max
401 real (c_float), intent(in) :: continuous_frozen_ground_index
402 real (c_float), intent(in) :: cfgi_lower_limit
403 real (c_float), intent(in) :: cfgi_upper_limit
404
405 ! [ LOCALS ]
406! real (c_float) :: CN_05
407 real (c_float) :: smax
408 real (c_float) :: cn_adj
409
410 curve_num_adj = update_curve_number_fn( landuse_index, soil_group, &
411 cell_index, &
412 it_is_growing_season, &
413 soil_storage_max, &
415 cfgi_lower_limit, &
416 cfgi_upper_limit )
417
418 smax = ( 1000.0_c_float / curve_num_adj ) - 10.0_c_float
419
420! ! Equation 9, Hawkins and others, 2002
421! CN_05 = 100_c_float / &
422! ((1.879_c_float * ((100.0_c_float / CN_adj ) - 1.0_c_float )**1.15_c_float) + 1.0_c_float)
423
424 ! Equation 8, Hawkins and others, 2002
425 ! adjust Smax for alternate initial abstraction amount
426 smax = 1.33_c_float * ( smax**1.15_c_float )
427
428 ! now consider runoff if Ia ~ 0.05S
429 if ( inflow > 0.05_c_float * smax ) then
430 runoff = ( inflow - 0.05_c_float * smax )**2 / ( inflow + 0.95_c_float * smax )
431 else
432 runoff = 0.0_c_float
433 end if
434
435 end subroutine runoff_curve_number_calculate
436
437end module runoff__curve_number
This module contains physical constants and convenience functions aimed at performing unit conversion...
logical(c_bool), parameter, public true
logical(c_bool), parameter, public false
This module contains the DATETIME_T class and associated time and date-related routines,...
Definition datetime.F90:9
type(parameters_t), public params
type(dict_t), public params_dict
integer(c_int), parameter, public five_day_sum
elemental real(c_float) function, public update_curve_number_fn(ilanduseindex, isoilsindex, cell_index, it_is_growing_season, fsoilstorage_max, fcfgi, fcfgi_ll, fcfgi_ul)
type(datetime_t) date_last_updated
elemental real(c_float) function cn_ii_to_cn_i(cn_ii)
real(c_float), parameter amc_dry_growing
elemental real(c_float) function cn_ii_to_cn_iii(cn_ii)
elemental subroutine, public runoff_curve_number_calculate(runoff, curve_num_adj, cell_index, landuse_index, soil_group, it_is_growing_season, inflow, soil_storage_max, continuous_frozen_ground_index, cfgi_lower_limit, cfgi_upper_limit)
Calculate the runoff by means of the curve number method.
real(c_float), dimension(:,:), allocatable, public prev_5_days_rain
subroutine, public update_previous_5_day_rainfall(infil, indx)
real(c_float), parameter amc_wet_growing
real(c_float), parameter amc_dry_dormant
real(c_float), dimension(:,:), allocatable, public cn_arciii
real(c_float), dimension(:,:), allocatable, public cn_arci
subroutine, public runoff_curve_number_initialize(cell_is_active)
real(c_float), parameter amc_wet_dormant
real(c_float), dimension(:,:), allocatable, public cn_arcii
integer(c_int), dimension(:), allocatable ilandusecodes
elemental integer(c_int) function return_landuse_index_fn(ilandusecode)
elemental real(c_float) function prob_runoff_enhancement(fcfgi, fcfgi_ul, fcfgi_ll)
type(date_range_t), public sim_dt