5.13.3-dev0
mHM
The mesoscale Hydrological Model
Loading...
Searching...
No Matches
mo_mrm_signatures.f90
Go to the documentation of this file.
1!> \file mo_mrm_signatures.f90
2!> \brief \copybrief mo_mrm_signatures
3!> \details \copydetails mo_mrm_signatures
4
5!> \brief Module with calculations for several hydrological signatures.
6!> \details This module contains calculations for hydrological signatures.
7!!
8!! It contains:
9!! - Autocorrelation
10!! - Rising and declining limb densities
11!! - Flow duration curves
12!! - Peak distribution
13!> \authors Remko Nijzink,
14!> \date March 2014
15!> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
16!! mHM is released under the LGPLv3+ license \license_note
17!> \ingroup f_mrm
19
20 USE mo_kind, ONLY : i4, sp, dp
21 use mo_message, only : message, error_message
22
23 IMPLICIT NONE
24
25 PUBLIC :: autocorrelation ! Autocorrelation function
26 PUBLIC :: flowdurationcurve ! Flow duration curve (i.e. CDF of runoff)
27 PUBLIC :: limb_densities ! Rising and declining limb densities
28 PUBLIC :: moments ! Moments of data and log-transformed data, e.g. mean and standard deviation.
29 PUBLIC :: peakdistribution ! Peak distribution parameter
30 PUBLIC :: runoffratio ! Runoff ratio (accumulated daily discharge [mm/d] / accumulated daily precipitation [mm/d])
31 PUBLIC :: zeroflowratio ! Ratio of zero flow days to total observation days
32
33 ! ------------------------------------------------------------------
34
35CONTAINS
36
37 !-------------------------------------------------------------------------------
38 ! NAME
39 ! Autocorrelation
40
41 ! PURPOSE
42 !> \brief Autocorrelation of a given data series.
43
44 !> \details Calculates the autocorrelation of a data series at given lags.
45 !> An optional argument for masking data points can be given.
46 !> The function is basically a wrapper of the function autocorr
47 !> from the module mo_corr.
48 !> An optional mask of data points can be specified.
49
50 !> ADDITIONAL INFORMATION
51 !> Used as hydrologic signature with lag 1 in
52 !> Euser, T., Winsemius, H. C., Hrachowitz, M., Fenicia, F., Uhlenbrook, S., & Savenije, H. H. G. (2013).
53 !> A framework to assess the realism of model structures using hydrological signatures.
54 !> Hydrology and Earth System Sciences, 17(5), 1893-1912. doi:10.5194/hess-17-1893-2013
55
56 ! INTENT(IN)
57 !> \param[in] "real(dp), dimension(:) :: data" Array of data
58 !> \param[in] "integer(i4), dimension(:) :: lags" Array of lags where autocorrelation is requested
59
60 ! INTENT(IN), OPTIONAL
61 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" Mask for data points givenWorks only with 1d
62 !> double precision input data.
63
64 ! HISTORY
65 !> \authors Juliane Mai
66
67 !> \date Jun 2015
68
69 ! Modifications:
70 ! Robert Schweppe Jun 2018 - refactoring and reformatting
71
72 FUNCTION autocorrelation(data, lags, mask)
73
74 use mo_corr, only : autocorr
75
76 implicit none
77
78 ! Array of data
79 real(dp), dimension(:), intent(in) :: data
80
81 ! Array of lags where autocorrelation is requested
82 integer(i4), dimension(:), intent(in) :: lags
83
84 ! Mask for data points givenWorks only with 1d double precision input data.
85 logical, dimension(size(data, 1)), optional, intent(in) :: mask
86
87 ! Autocorrelation of data at given lags
88 real(dp), dimension(size(lags, 1)) :: autocorrelation
89
90
91 if (present(mask)) then
92 autocorrelation = autocorr(data, lags, mask = mask)
93 else
94 autocorrelation = autocorr(data, lags)
95 end if
96
97 END FUNCTION autocorrelation
98
99 !-------------------------------------------------------------------------------
100 ! NAME
101 ! FlowDurationCurve
102
103 ! PURPOSE
104 !> \brief Flow duration curves.
105
106 !> \details Calculates the flow duration curves for a given data vector. The Flow duration curve at a
107 !> certain quantile x is the data point p where x% of the data points are above the value p.
108 !> Hence the function percentile of the module mo_percentile is used. But percentile is
109 !> determining the point p where x% of the data points are below that value. Therfore, the
110 !> given quantiles are transformed by (1.0-quantile) to get the percentiles of exceedance probabilities.
111
112 !> Optionally, the concavity index CI can be calculated [Zhang2014]. CI is defined by
113 !> \f[ CI = \frac{q_{10\%}-q_{99\%}}{q_{1\%}-q_{99\%}} \f]
114 !> where \f$ q_{x} \f$ is the data point where x% of the data points are above that value.
115 !> Hence, exceedance probabilities are used.
116
117 !> Optionally, the FDC mid-segment slope \f$FDC_{MSS}\f$ as used by Shafii et. al (2014) can be returned.
118 !> The \f$FDC_{MSS}\f$ is defined as
119 !> \f[ FDC_{MSS} = \log(q_{m_1})-\log(q_{m_2}) \f]
120 !> where \f$ m_1 \f$ and \f$ m_2 \f$ are the lowest and highest flow exceedance probabilities within the
121 !> midsegment of FDC. The settings \f$m_1=0.2\f$ and \f$0.7\f$ are used by Shafii et. al (2014) and are
122 !> implemented like that.
123
124 !> Optionally, the FDC medium high-segment volume \f$FDC_{MHSV}\f$ as used by Shafii et. al (2014) can be
125 !> returned. The \f$FDC_{MHSV}\f$ is defined as
126 !> \f[ FDC_{MHSV} = \sum_{h=1}^{H} q_h \f]
127 !> where \f$ h=1,2,...,H \f$ are flow indeces located within the high-flow segment (exceedance probabilities
128 !> lower than \f$m_1\f$). \f$H\f$ is the index of the maximum flow. The settings \f$m_1=0.2\f$ is used here
129 !> to be consistent with the definitions of the low-segment (0.7-1.0) and the mid-segment (0.2-0.7).
130
131 !> Optionally, the FDC high-segment volume \f$FDC_{HSV}\f$ as used by Shafii et. al (2014) can be returned.
132 !> The \f$FDC_{HSV}\f$ is defined as
133 !> \f[ FDC_{HSV} = \sum_{h=1}^{H} q_h \f]
134 !> where \f$ h=1,2,...,H \f$ are flow indeces located within the high-flow segment (exceedance probabilities
135 !> lower than \f$m_1\f$). \f$H\f$ is the index of the maximum flow. The settings \f$m_1=0.02\f$ is used by
136 !> Shafii et. al (2014) and is implemented like that.
137
138 !> Optionally, the FDC low-segment volume \f$FDC_{LSV}\f$ as used by Shafii et. al (2014) can be returned.
139 !> The \f$FDC_{LSV}\f$ is defined as
140 !> \f[ FDC_{LSV} = -\sum_{l=1}^{L} (\log(q_l) - \log(q_L)) \f]
141 !> where \f$ l=1,2,...,L \f$ are flow indeces located within the low-flow segment (exceedance probabilities
142 !> larger than \f$m_1\f$). \f$L\f$ is the index of the minimum flow. The settings \f$m_1=0.7\f$ is used by
143 !> Shafii et. al (2014) and is implemented like that.
144
145 !> An optional mask of data points can be specified.
146 !> ADDITIONAL INFORMATION
147
148 !> Thresholds in mid_segment_slope, mhigh_segment_volume, high_segment_volume, low_segment_volume are hard
149 !> coded.
150 !> FDC is used as hydrologic signature (quantiles not specified) in
151 !> Euser, T., Winsemius, H. C., Hrachowitz, M., Fenicia, F., Uhlenbrook, S., & Savenije, H. H. G. (2013).
152 !> A framework to assess the realism of model structures using hydrological signatures.
153 !> Hydrology and Earth System Sciences, 17(5), 1893-1912. doi:10.5194/hess-17-1893-2013
154 !> Concavity Index used as hydrologic signature in
155 !> Zhang, Y., Vaze, J., Chiew, F. H. S., Teng, J., & Li, M. (2014).
156 !> Predicting hydrological signatures in ungauged catchments using spatial interpolation, index model, and
157 !> rainfall-runoff modelling.
158 !> Journal of Hydrology, 517(C), 936-948. doi:10.1016/j.jhydrol.2014.06.032
159 !> Concavity index is defined using exceedance probabilities by
160 !> Sauquet, E., & Catalogne, C. (2011).
161 !> Comparison of catchment grouping methods for flow duration curve estimation at ungauged sites in France.
162 !> Hydrology and Earth System Sciences, 15(8), 2421-2435. doi:10.5194/hess-15-2421-2011
163 !> mid_segment_slope, high_segment_volume, low_segment_volume used as hydrologic signature in
164 !> Shafii, M., & Tolson, B. A. (2015).
165 !> Optimizing hydrological consistency by incorporating hydrological signatures into model calibration
166 !> objectives.
167 !> Water Resources Research, 51(5), 3796-3814. doi:10.1002/2014WR016520
168
169 ! INTENT(IN)
170 !> \param[in] "real(dp), dimension(:) :: data" data series
171 !> \param[in] "real(dp), dimension(:) :: quantiles" Percentages of exceedance (x-axis of FDC)
172
173 ! INTENT(IN), OPTIONAL
174 !> \param[in] "logical, dimension(:), optional :: mask" mask of data array
175
176 ! INTENT(OUT), OPTIONAL
177 !> \param[out] "real(dp), optional :: concavity_index" concavity index as defined by Sauquet et al. (2011)
178 !> \param[out] "real(dp), optional :: mid_segment_slope" mid-segment slope as defined by Shafii et al. (2014)
179 !> \param[out] "real(dp), optional :: mhigh_segment_volume" medium high-segment volume
180 !> \param[out] "real(dp), optional :: high_segment_volume" high-segment volume as defined by Shafii et al.
181 !> (2014)
182 !> \param[out] "real(dp), optional :: low_segment_volume" low-segment volume as defined by Shafii et al.
183 !> (2014)
184
185 ! RETURN
186 !> \return real(dp), dimension(size(quantiles,1)) :: FlowDurationCurve — Flow Duration Curve value at
187 !> resp. quantile
188
189 ! HISTORY
190 !> \authors Remko Nijzink, Juliane Mai
191
192 !> \date March 2014
193
194 ! Modifications:
195 ! Juliane Mai Jun 2015 - mask added
196 ! - function instead of subroutine
197 ! - use of percentile
198 ! - add concavity_index
199 ! Juliane Mai Jun 2015 - add mid_segment_slope, mhigh_segment_volume, high_segment_volume, low_segment_volume
200 ! Robert Schweppe Jun 2018 - refactoring and reformatting
201
202 FUNCTION flowdurationcurve(data, quantiles, mask, concavity_index, mid_segment_slope, mhigh_segment_volume, &
203 high_segment_volume, low_segment_volume)
204
205 use mo_percentile, only : percentile
206 use mo_utils, only : ge, le
207
208 implicit none
209
210 ! data series
211 real(dp), dimension(:), intent(in) :: data
212
213 ! Percentages of exceedance (x-axis of FDC)
214 real(dp), dimension(:), intent(in) :: quantiles
215
216 ! mask of data array
217 logical, dimension(:), optional, intent(in) :: mask
218
219 ! concavity index as defined by Sauquet et al. (2011)
220 real(dp), optional, intent(out) :: concavity_index
221
222 ! mid-segment slope as defined by Shafii et al. (2014)
223 real(dp), optional, intent(out) :: mid_segment_slope
224
225 ! medium high-segment volume
226 real(dp), optional, intent(out) :: mhigh_segment_volume
227
228 ! high-segment volume as defined by Shafii et al. (2014)
229 real(dp), optional, intent(out) :: high_segment_volume
230
231 ! low-segment volume as defined by Shafii et al. (2014)
232 real(dp), optional, intent(out) :: low_segment_volume
233
234 ! data point where x% of the data points
235 ! are above that value
236 real(dp), dimension(size(quantiles, 1)) :: flowdurationcurve
237
238 ! mask for data points
239 logical, dimension(size(data, 1)) :: maske
240
241 ! minimal flow value
242 real(dp) :: min_flow_value
243
244 ! flow value at a threshold quantile
245 real(dp) :: flow_value_thres
246
247
248 ! checking optionals
249 if (present(mask)) then
250 maske = mask
251 else
252 maske = .true.
253 end if
254
255 flowdurationcurve = percentile(data, (1._dp - quantiles) * 100._dp, mask = maske, mode_in = 5)
256
257 if (present(concavity_index)) then
258 concavity_index = &
259 (percentile(data, (1._dp - 0.10_dp) * 100._dp, mask = maske, mode_in = 5) - &
260 percentile(data, (1._dp - 0.99_dp) * 100._dp, mask = maske, mode_in = 5)) / &
261 (percentile(data, (1._dp - 0.01_dp) * 100._dp, mask = maske, mode_in = 5) - &
262 percentile(data, (1._dp - 0.99_dp) * 100._dp, mask = maske, mode_in = 5))
263 end if
264
265 if (present(mid_segment_slope)) then
266 ! mid-flows are defined to be between 0.2 and 0.7 by Shafii et. al (2014)
267 mid_segment_slope = &
268 log(percentile(data, (1._dp - 0.2_dp) * 100._dp, mask = maske, mode_in = 5)) - &
269 log(percentile(data, (1._dp - 0.7_dp) * 100._dp, mask = maske, mode_in = 5))
270 end if
271
272 if (present(mhigh_segment_volume)) then
273 ! medium high-flows are defined to be between 0.0 and 0.2 as to be constistent
274 ! with the mid-segment (0.2-0.7) and low-segment (0.7-1.0) definitions
275 flow_value_thres = percentile(data, (1._dp - 0.2_dp) * 100._dp, mask = maske, mode_in = 5)
276 mhigh_segment_volume = sum(data, mask = (maske .and. ge(data, flow_value_thres)))
277 ! print*, 'flow_value_thres = ',flow_value_thres
278 ! print*, 'mhigh_segment_volume = ',mhigh_segment_volume
279 end if
280
281 if (present(high_segment_volume)) then
282 ! high-flows are defined to be between 0.0 and 0.02 by Shafii et. al (2014)
283 flow_value_thres = percentile(data, (1._dp - 0.02_dp) * 100._dp, mask = maske, mode_in = 5)
284 high_segment_volume = sum(data, mask = (maske .and. ge(data, flow_value_thres)))
285 end if
286
287 if (present(low_segment_volume)) then
288 ! low-flows are defined to be between 0.7 and 1.0 by Shafii et. al (2014)
289 min_flow_value = minval(data, mask = maske)
290 flow_value_thres = percentile(data, (1._dp - 0.7) * 100._dp, mask = maske, mode_in = 5)
291 low_segment_volume = -1.0_dp * &
292 sum(log(data) - log(min_flow_value), mask = (maske .and. le(data, flow_value_thres)))
293 end if
294
295 END FUNCTION flowdurationcurve
296
297 !-------------------------------------------------------------------------------
298 ! NAME
299 ! Limb_densities
300
301 ! PURPOSE
302 !> \brief Calculates limb densities
303
304 !> \details Calculates rising and declinging limb densities. The peaks of the given series are
305 !> first determined by looking for points where preceding and subsequent datapoint are lower.
306 !> Second, the number of datapoints with rising values (nrise) and declining values (ndecline)
307 !> are counted basically by comparing neighbors.
308 !> The duration the data increase (nrise) divided by the number of peaks (npeaks)
309 !> gives the rising limb density RLD
310 !> \f[ RLD=t_{rise}/n_{peak} \f]
311 !> whereas the duration the data decrease (ndecline) divided by the number of peaks (npeaks)
312 !> gives the declining limb density DLD
313 !> \f[ DLD=t_{fall}/n_{peak}. \f]
314 !> An optional mask of data points can be specified.
315
316 !> ADDITIONAL INFORMATION
317 !> Rising limb density used as hydrologic signature in
318 !> Euser, T., Winsemius, H. C., Hrachowitz, M., Fenicia, F., Uhlenbrook, S., & Savenije, H. H. G. (2013).
319 !> A framework to assess the realism of model structures using hydrological signatures.
320 !> Hydrology and Earth System Sciences, 17(5), 1893-1912. doi:10.5194/hess-17-1893-2013
321
322 ! INTENT(IN)
323 !> \param[in] "real(dp), dimension(:) :: data" data series
324
325 ! INTENT(IN), OPTIONAL
326 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask for data series
327
328 ! INTENT(OUT), OPTIONAL
329 !> \param[out] "real(dp), optional :: RLD" rising limb density
330 !> \param[out] "real(dp), optional :: DLD" declining limb density
331
332 ! HISTORY
333 !> \authors Remko Nijzink
334
335 !> \date March 2014
336
337 ! Modifications:
338 ! Juliane Mai Jun 2015 - RLD and DLD as optional
339 ! - optional mask for data can be given
340 ! Robert Schweppe Jun 2018 - refactoring and reformatting
341
342 SUBROUTINE limb_densities(data, mask, RLD, DLD)
343
344 implicit none
345
346 ! data series
347 real(dp), dimension(:), intent(in) :: data
348
349 ! mask for data series
350 logical, dimension(size(data, 1)), optional, intent(in) :: mask
351
352 ! rising limb density
353 real(dp), optional, intent(out) :: rld
354
355 ! declining limb density
356 real(dp), optional, intent(out) :: dld
357
358 ! mask for data points
359 logical, dimension(size(data, 1)) :: maske
360
361 ! Counter
362 integer(i4) :: jj
363
364 ! Number of peaks
365 integer(i4) :: n_peak
366
367 ! Number of declining data points
368 integer(i4) :: n_decline
369
370 ! Number of rising data points
371 integer(i4) :: n_rise
372
373 ! True if rising, False if declining or contains masked values
374 logical, dimension(size(data, 1)) :: goes_up
375
376 ! Threshold that is has to rise at least to be detected as rising value
377 real(dp) :: thres_rise
378
379
380 ! checking optionals
381 if (present(mask)) then
382 maske = mask
383 else
384 maske = .true.
385 end if
386
387 if ((.not. present(rld)) .and. (.not. present(dld))) then
388 call error_message('mo_signatures: limb_densities: Neither RLD or DLD is specified in calling sequence.')
389 end if
390
391 ! initialize
392 n_rise = 0_i4
393 n_decline = 0_i4
394 n_peak = 0_i4
395
396 goes_up = .false.
397 thres_rise = 1.0_dp
398 do jj = 1, size(data, 1) - 1
399 if (maske(jj) .and. maske(jj + 1)) then
400 if (data(jj) < data(jj + 1) - thres_rise) then
401 goes_up(jj) = .true.
402 ! print*, jj, ' ', data(jj), ' ', data(jj+1)
403 end if
404 end if
405 end do
406 n_rise = count(goes_up)
407 n_decline = count(maske) - count(goes_up)
408
409 ! write(*,*) 'goes_up = ', goes_up(1:178)
410
411 ! peak is where goes_up switches from true to false
412 n_peak = 0_i4
413 do jj = 1, size(data, 1) - 1
414 if (maske(jj) .and. maske(jj + 1)) then
415 if (goes_up(jj) .and. .not.(goes_up(jj + 1))) then
416 n_peak = n_peak + 1_i4
417 ! print*, jj
418 end if
419 end if
420 end do
421
422 ! do jj=2, size(data,1)-1
423
424 ! ! check for peak
425 ! if (maske(jj-1) .and. maske(jj) .and. maske(jj+1)) then
426 ! if ( (data(jj-1) .lt. data(jj)-1.0_dp) .and. (data(jj+1) .lt. data(jj)-1.0_dp) ) then
427 ! n_peak = n_peak+1_i4
428 ! write(*,*) jj-1
429 ! end if
430 ! end if
431
432 ! ! check if data has rised
433 ! if (maske(jj-1) .and. maske(jj)) then
434 ! if (data(jj-1) .lt. data(jj)-1.0_dp) then
435 ! n_rise = n_rise+1_i4
436 ! else
437 ! n_decline = n_decline+1_i4
438 ! end if
439 ! end if
440
441 ! ! ! check if data has declined
442 ! ! if (maske(jj-1) .and. maske(jj)) then
443 ! ! if (data(jj-1) .gt. data(jj)) then
444 ! ! n_decline = n_decline+1_i4
445 ! ! end if
446 ! ! end if
447
448 ! end do
449
450 ! write(*,*) 'n_peak = ', n_peak
451
452 if (present(rld)) then
453 if (n_peak>0_i4) then
454 rld = real(n_rise, dp) / real(n_peak, dp)
455 else
456 rld = 0.0_dp
457 end if
458 end if
459
460 if (present(dld)) then
461 if (n_peak>0_i4) then
462 dld = real(n_decline, dp) / real(n_peak, dp)
463 else
464 dld = 0.0_dp
465 end if
466 end if
467
468 END SUBROUTINE limb_densities
469
470 !-------------------------------------------------------------------------------
471 ! NAME
472 ! MaximumMonthlyFlow
473
474 ! PURPOSE
475 !> \brief Maximum of average flows per months.
476
477 !> \details Maximum of average flow per month is defined as
478 !> \f[ max_{monthly flow} = Max( F(i), i=1,..12 ) \f]
479 !> where \$f F(i) $\f is the average flow of month i.
480 !> ADDITIONAL INFORMATION
481 !> used as hydrologic signature in
482 !> Shafii, M., & Tolson, B. A. (2015).
483 !> Optimizing hydrological consistency by incorporating hydrological signatures into model calibration
484 !> objectives.
485 !> Water Resources Research, 51(5), 3796-3814. doi:10.1002/2014WR016520
486
487 ! INTENT(IN)
488 !> \param[in] "real(dp), dimension(:) :: data" array of data
489
490 ! INTENT(IN), OPTIONAL
491 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask for data points given
492 !> \param[in] "integer(i4), optional :: yr_start" year of date of first data point given
493 !> \param[in] "integer(i4), optional :: mo_start" month of date of first data point given
494 !> (default: 1)
495 !> \param[in] "integer(i4), optional :: dy_start" month of date of first data point given
496 !> (default: 1)
497
498 ! RETURN
499 !> \return real(dp) :: MaximumMonthlyFlow &mdash; Maximum of average flow per month
500 !> Works only with 1d double precision input data.
501 !> Assumes data are daily values.
502
503 ! HISTORY
504 !> \authors Juliane Mai
505
506 !> \date Jun 2015
507
508 ! Modifications:
509 ! Robert Schweppe Jun 2018 - refactoring and reformatting
510
511 FUNCTION maximummonthlyflow(data, mask, yr_start, mo_start, dy_start)
512
513 use mo_julian, only : date2dec, dec2date
514
515 implicit none
516
517 ! array of data
518 real(dp), dimension(:), intent(in) :: data
519
520 ! mask for data points given
521 logical, dimension(size(data, 1)), optional, intent(in) :: mask
522
523 ! year of date of first data point given
524 integer(i4), optional, intent(in) :: yr_start
525
526 ! month of date of first data point given (default: 1)
527 integer(i4), optional, intent(in) :: mo_start
528
529 ! month of date of first data point given (default: 1)
530 integer(i4), optional, intent(in) :: dy_start
531
532 ! return: maximum of average monthly flow
533 real(dp) :: maximummonthlyflow
534
535 logical, dimension(size(data, 1)) :: maske
536
537 ! counter
538 integer(i4) :: ii
539
540 ! date variables
541 integer(i4) :: yr, mo, dy, imo
542
543 ! number of data points per month
544 integer(i4), dimension(12) :: counter
545
546 ! summed data points per months
547 real(dp), dimension(12) :: flow_month
548
549 ! julian day of one day before start day
550 real(dp) :: ref_jul_day
551
552
553 if (present(mask)) then
554 maske = mask
555 else
556 maske = .true.
557 end if
558
559 if (.not. present(yr_start)) then
560 call error_message('mo_signatures: MaximumMonthlyFlow: Year of of data point has to be given!')
561 else
562 yr = yr_start
563 end if
564
565 if (present(mo_start)) then
566 mo = mo_start
567 else
568 mo = 1
569 end if
570
571 if (present(dy_start)) then
572 dy = dy_start
573 else
574 dy = 1
575 end if
576
577 flow_month = 0.0_dp
578 counter = 0_i4
579 ref_jul_day = date2dec(yy = yr, mm = mo, dd = dy) - 1.0_dp
580
581 do ii = 1, size(data, 1)
582 if (maske(ii)) then
583 ! determine current month
584 call dec2date(ref_jul_day + real(ii, dp), mm = imo)
585 ! add value
586 counter(imo) = counter(imo) + 1
587 flow_month(imo) = flow_month(imo) + data(ii)
588 end if
589 end do
590
591 if (any(counter == 0_i4)) then
592 call error_message('mo_signatures: MaximumMonthlyFlow: There are months with no data points!')
593 end if
594
595 ! average
596 maximummonthlyflow = maxval(flow_month / real(counter, dp))
597
598 END FUNCTION maximummonthlyflow
599
600 !-------------------------------------------------------------------------------
601 ! NAME
602 ! Moments
603
604 ! PURPOSE
605 !> \brief Moments of data and log-transformed data, e.g. mean and standard deviation.
606
607 !> \details Returns several moments of data series given, i.e.
608 !> * mean of data
609 !> * standard deviation of data
610 !> * median of data
611 !> * maximum/ peak of data
612 !> * mean of log-transformed data
613 !> * standard deviation of log-transformed data
614 !> * median of log-transformed data
615 !> * maximum/ peak of log-transformed data
616 !> An optional mask of data points can be specified.
617 !> ADDITIONAL INFORMATION
618 !> mean_log and stddev_log used as hydrologic signature in
619 !> Zhang, Y., Vaze, J., Chiew, F. H. S., Teng, J., & Li, M. (2014).
620 !> Predicting hydrological signatures in ungauged catchments using spatial interpolation, index model, and
621 !> rainfall-runoff modelling.
622 !> Journal of Hydrology, 517(C), 936-948. doi:10.1016/j.jhydrol.2014.06.032
623 !> mean_data, stddev_data, median_data, max_data, mean_log, and stddev_log used as hydrologic signature in
624 !> Shafii, M., & Tolson, B. A. (2015).
625 !> Optimizing hydrological consistency by incorporating hydrological signatures into model calibration
626 !> objectives.
627 !> Water Resources Research, 51(5), 3796-3814. doi:10.1002/2014WR016520
628
629 ! INTENT(IN)
630 !> \param[in] "real(dp), dimension(:) :: data" array of data
631
632 ! INTENT(IN), OPTIONAL
633 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask for data points given
634
635 ! INTENT(OUT), OPTIONAL
636 !> \param[out] "real(dp), optional :: mean_data" mean of data
637 !> \param[out] "real(dp), optional :: stddev_data" standard deviation of data
638 !> \param[out] "real(dp), optional :: median_data" median of data
639 !> \param[out] "real(dp), optional :: max_data" maximum/ peak of data
640 !> \param[out] "real(dp), optional :: mean_log" mean of log-transformed data
641 !> \param[out] "real(dp), optional :: stddev_log" standard deviation of log-transformed data
642 !> \param[out] "real(dp), optional :: median_log" median of log-transformed data
643 !> \param[out] "real(dp), optional :: max_log" maximum/ peak of log-transformed dataWorks only with 1d
644 !> double precision input data.
645
646 ! HISTORY
647 !> \authors Juliane Mai
648
649 !> \date Jun 2015
650
651 ! Modifications:
652 ! Robert Schweppe Jun 2018 - refactoring and reformatting
653
654 SUBROUTINE moments(data, mask, mean_data, stddev_data, median_data, max_data, mean_log, stddev_log, median_log, &
655 max_log)
656
657 use mo_moment, only : mean, stddev
658 use mo_percentile, only : median
659
660 implicit none
661
662 ! array of data
663 real(dp), dimension(:), intent(in) :: data
664
665 ! mask for data points given
666 logical, dimension(size(data, 1)), optional, intent(in) :: mask
667
668 ! mean of data
669 real(dp), optional, intent(out) :: mean_data
670
671 ! standard deviation of data
672 real(dp), optional, intent(out) :: stddev_data
673
674 ! median of data
675 real(dp), optional, intent(out) :: median_data
676
677 ! maximum/ peak of data
678 real(dp), optional, intent(out) :: max_data
679
680 ! mean of log-transformed data
681 real(dp), optional, intent(out) :: mean_log
682
683 ! standard deviation of log-transformed data
684 real(dp), optional, intent(out) :: stddev_log
685
686 ! median of log-transformed data
687 real(dp), optional, intent(out) :: median_log
688
689 ! maximum/ peak of log-transformed dataWorks only with 1d double precision input data.
690 real(dp), optional, intent(out) :: max_log
691
692 logical, dimension(size(data, 1)) :: maske
693
694 real(dp), dimension(size(data, 1)) :: logdata
695
696
697 if (present(mask)) then
698 maske = mask
699 else
700 maske = .true.
701 end if
702
703 if (.not.(present(mean_data)) .and. .not.(present(stddev_data)) .and. &
704 .not.(present(median_data)) .and. .not.(present(max_data)) .and. &
705 .not.(present(mean_log)) .and. .not.(present(stddev_log)) .and. &
706 .not.(present(median_log)) .and. .not.(present(max_log))) then
707 call error_message('mo_signatures: Moments: None of the optional output arguments is specified')
708 end if
709
710 if (present(mean_data)) mean_data = mean(data, mask = maske)
711 if (present(stddev_data)) stddev_data = stddev(data, mask = maske)
712 if (present(median_data)) median_data = median(data, mask = maske)
713 if (present(max_data)) max_data = maxval(data, mask = maske)
714
715 if (present(mean_log) .or. present(stddev_log)) then
716 where (data > 0.0_dp)
717 logdata = log(data)
718 elsewhere
719 logdata = -9999.0_dp ! will not be used, since mask is set to .false.
720 maske = .false.
721 end where
722
723 if (present(mean_log)) mean_log = mean(logdata, mask = maske)
724 if (present(stddev_log)) stddev_log = stddev(logdata, mask = maske)
725 if (present(median_log)) median_log = median(logdata, mask = maske)
726 if (present(max_log)) max_log = maxval(logdata, mask = maske)
727 end if
728
729 END SUBROUTINE moments
730
731 !-------------------------------------------------------------------------------
732 ! NAME
733 ! PeakDistribution
734
735 ! PURPOSE
736 !> \brief Calculates the peak distribution.
737
738 !> \details First, the peaks of the time series given are identified. For the peak distribution
739 !> only this subset of data points are considered. Second, the peak distribution at the
740 !> quantiles given is calculated. Calculates the peak distribution at the quantiles given
741 !> using mo_percentile. Since the exceedance probabilities are usually used in
742 !> hydrology the function percentile is used with (1.0-quantiles).
743
744 !> Optionally, the slope of the peak distribution between 10th and 50th percentile, i.e.
745 !> \f[ slope = \frac{\mathrm{peak\_{data}}_{0.1}-\mathrm{peak\_{data}}_{0.5}}{0.9-0.5} \f]
746 !> can be returned.
747 !> An optional mask for the data points can be given.
748 !> ADDITIONAL INFORMATION
749 !> slope_peak_distribution used as hydrologic signature in
750 !> Euser, T., Winsemius, H. C., Hrachowitz, M., Fenicia, F., Uhlenbrook, S., & Savenije, H. H. G. (2013).
751 !> A framework to assess the realism of model structures using hydrological signatures.
752 !> Hydrology and Earth System Sciences, 17(5), 1893-1912. doi:10.5194/hess-17-1893-2013
753
754 ! INTENT(IN)
755 !> \param[in] "real(dp), dimension(:) :: data" data array
756 !> \param[in] "real(dp), dimension(:) :: quantiles" requested quantiles for distribution
757
758 ! INTENT(IN), OPTIONAL
759 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask of data array
760
761 ! INTENT(OUT), OPTIONAL
762 !> \param[out] "real(dp), optional :: slope_peak_distribution" slope of the Peak distribution between10th and
763 !> 50th percentile
764
765 ! RETURN
766 !> \return real(dp), dimension(size(quantiles,1)) :: PeakDistribution &mdash; Distribution of peak values
767 !> at resp. quantiles
768
769 ! HISTORY
770 !> \authors Remko Nijzink
771
772 !> \date March 2014
773
774 ! Modifications:
775 ! Juliane Mai Jun 2015 - mask added
776 ! - function instead of subroutine
777 ! - use of percentile
778 ! Robert Schweppe Jun 2018 - refactoring and reformatting
779
780 FUNCTION peakdistribution(data, quantiles, mask, slope_peak_distribution)
781
782 use mo_percentile, only : percentile
783
784 implicit none
785
786 ! data array
787 real(dp), dimension(:), intent(in) :: data
788
789 ! requested quantiles for distribution
790 real(dp), dimension(:), intent(in) :: quantiles
791
792 ! mask of data array
793 logical, dimension(size(data, 1)), optional, intent(in) :: mask
794
795 ! slope of the Peak distribution between10th and 50th percentile
796 real(dp), optional, intent(out) :: slope_peak_distribution
797
798 ! distribution of peaks in data
799 ! returns values of distribution at
800 ! given quantiles
801 real(dp), dimension(size(quantiles, 1)) :: peakdistribution
802
803 ! counters
804 integer(i4) :: ii, jj
805
806 ! mask of data
807 logical, dimension(size(data, 1)) :: maske
808
809 ! Number of peaks
810 integer(i4) :: n_peak
811
812 ! array containing some quantiles
813 real(dp), dimension(2) :: pp
814
815 ! data points of quantiles pp
816 real(dp), dimension(2) :: data_pp
817
818 ! series containing only peak values of original series data
819 real(dp), allocatable, dimension(:) :: data_peak
820
821
822 ! checking optionals
823 if (present(mask)) then
824 maske = mask
825 else
826 maske = .true.
827 end if
828
829 ! count peaks
830 n_peak = 0_i4
831 do jj = 2, size(data, 1) - 1
832 if (maske(jj - 1) .and. maske(jj) .and. maske(jj + 1)) then
833 if ((data(jj - 1) .le. data(jj)) .and. (data(jj + 1) .le. data(jj))) then
834 n_peak = n_peak + 1_i4
835 end if
836 end if
837 end do
838
839 allocate(data_peak(n_peak))
840
841 ! find peaks
842 jj = 0
843 do ii = 2, size(data, 1) - 1
844 if (maske(ii - 1) .and. maske(ii) .and. maske(ii + 1)) then
845 if((data(ii - 1) .le. data(ii)) .and. (data(ii + 1) .le. data(ii))) then
846 jj = jj + 1_i4
847 data_peak(jj) = data(ii)
848 end if
849 end if
850 end do
851
852 if (present(slope_peak_distribution)) then
853 ! calculate slope between 10% and 50% quantiles, per definition
854 pp = (/ 0.1_dp, 0.5_dp /)
855 data_pp = percentile(data_peak, (1.0_dp - pp) * 100._dp, mode_in = 5) ! (1-p) because exceedence probability is required
856 slope_peak_distribution = (data_pp(1) - data_pp(2)) / (0.9_dp - 0.5_dp)
857 end if
858
859 peakdistribution = percentile(data_peak, (1.0_dp - quantiles) * 100._dp, mode_in = 5)
860 deallocate(data_peak)
861
862 END FUNCTION peakdistribution
863
864 !-------------------------------------------------------------------------------
865 ! NAME
866 ! RunoffRatio
867
868 ! PURPOSE
869 !> \brief Runoff ratio (accumulated daily discharge [mm/d] / accumulated daily precipitation [mm/d]).
870
871 !> \details The runoff ratio is defined as
872 !> \f[ runoff_ratio = \frac{\sum_{t=1}^{N} q_t}{\sum_{t=1}^{N} p_t}\f]
873 !> where \f$p_t\f$ and \f$q_t\f$ are precipitation and discharge, respectively.
874 !> Therefore, precipitation over the entire domain is required and both discharge and precipitation
875 !> have to be converted to the same units [mm/d].
876
877 !> Input discharge is given in [m**3/s] as this is mHM default while precipitation has to be given
878 !> in [mm/km**2 / day].
879
880 !> Either "precip_sum" or "precip_series" has to be specified.
881 !> If "precip_series" is used the optional mask is also applied to precipitation values.
882 !> The "precip_sum" is the accumulated "precip_series".
883
884 !> Optionally, a mask for the data (=discharge) can be given. If optional "log_data" is set to .true.
885 !> the runoff ratio will be calculated as
886 !> \f[ runoff\_ratio = \frac{\sum_{t=1}^{N} \log(q_t)}{\sum_{t=1}^{N} p_t}\f]
887 !> where \f$p_t\f$ and \f$q_t\f$ are precipitation and discharge, respectively.
888 !> ADDITIONAL INFORMATION
889 !> \return real(dp), dimension(size(lags,1)) :: RunoffRation &mdash; Ratio of discharge and precipitation
890 !> Used as hydrologic signature in
891 !> Shafii, M., & Tolson, B. A. (2015).
892 !> Optimizing hydrological consistency by incorporating hydrological signatures into model calibration
893 !> objectives.
894 !> Water Resources Research, 51(5), 3796-3814. doi:10.1002/2014WR016520
895
896 ! INTENT(IN)
897 !> \param[in] "real(dp), dimension(:) :: data" array of data [m**3/s]
898 !> \param[in] "real(dp) :: domain_area" area of domain [km**2]
899
900 ! INTENT(IN), OPTIONAL
901 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask for data points given
902 !> \param[in] "real(dp), dimension(size(data, 1)), optional :: precip_series" daily precipitation values
903 !> [mm/km**2 / day]
904 !> \param[in] "real(dp), optional :: precip_sum" sum of daily precip. values of
905 !> whole period[mm/km**2 / day]
906 !> \param[in] "logical, optional :: log_data" ratio using logarithmic dataWorks
907 !> only with 1d double precision input data.
908
909 ! HISTORY
910 !> \authors Juliane Mai
911
912 !> \date Jun 2015
913
914 ! Modifications:
915 ! Robert Schweppe Jun 2018 - refactoring and reformatting
916
917 FUNCTION runoffratio(data, domain_area, mask, precip_series, precip_sum, log_data)
918
919 implicit none
920
921 ! array of data [m**3/s]
922 real(dp), dimension(:), intent(in) :: data
923
924 ! area of domain [km**2]
925 real(dp), intent(in) :: domain_area
926
927 ! mask for data points given
928 logical, dimension(size(data, 1)), optional, intent(in) :: mask
929
930 ! daily precipitation values [mm/km**2 / day]
931 real(dp), dimension(size(data, 1)), optional, intent(in) :: precip_series
932
933 ! sum of daily precip. values of whole period[mm/km**2 / day]
934 real(dp), optional, intent(in) :: precip_sum
935
936 ! ratio using logarithmic dataWorks only with 1d double precision input data.
937 logical, optional, intent(in) :: log_data
938
939 ! sum(data) / sum(precip)
940 real(dp) :: runoffratio
941
942 ! mask of data
943 logical, dimension(size(data, 1)) :: maske
944
945 ! if logarithmic data are used --> sum(log(data)) / sum(precip)
946 logical :: log_dat
947
948 real(dp) :: sum_discharge
949
950 real(dp) :: sum_precip
951
952
953 ! checking optionals
954 if (present(mask)) then
955 maske = mask
956 else
957 maske = .true.
958 end if
959
960 if (present(log_data)) then
961 log_dat = log_data
962 else
963 log_dat = .false.
964 end if
965
966 if ((present(precip_series) .and. present(precip_sum)) .or. &
967 (.not. present(precip_series) .and. .not. present(precip_sum))) then
968 call error_message('mo_signatures: RunoffRatio: Exactly one precipitation information', raise=.false.)
969 call error_message(' (precipitation series or sum of precipitation) ', raise=.false.)
970 call error_message(' has to be specified!')
971 end if
972
973 if (present(mask) .and. present(precip_sum)) then
974 call error_message('mo_signatures: RunoffRatio: Already aggregated precipitation (precip_sum) and', raise=.false.)
975 call error_message(' mask can not be used together.', raise=.false.)
976 call error_message(' Precip_series should be used instead!')
977 end if
978
979 ! mhm output [m**3/s] --> required [mm/d]
980 ! [m**3/s] / [km**2] = [m**3/(s km**2)]
981 ! => [m**3/(s km**2) * 60*60*24/1000**2] = [m/d]
982 ! => [m**3/(s km**2) * 60*60*24*1000/1000**2] = [mm/d]
983 ! => [m**3/(s km**2) * 86.4 ] = [mm/d]
984 ! => discharge value [m**3/s] / catchment area [km**2] * 86.4 [km**2 s/m**3 * mm/d]
985 if (log_dat) then
986 sum_discharge = sum(log(data) * 86.4_dp / domain_area, mask = maske)
987 else
988 sum_discharge = sum(data * 86.4_dp / domain_area, mask = maske)
989 end if
990
991 if (present(precip_sum)) then
992 sum_precip = precip_sum
993 else
994 sum_precip = sum(precip_series, mask = maske)
995 end if
996
997 runoffratio = sum_discharge / sum_precip
998
999 END FUNCTION runoffratio
1000
1001 !-------------------------------------------------------------------------------
1002 ! NAME
1003 ! ZeroFlowRatio
1004
1005 ! PURPOSE
1006 !> \brief Ratio of zero values to total number of data points.
1007
1008 !> \details An optional mask of data points can be specified.
1009 !> ADDITIONAL INFORMATION
1010 !> \return real(dp), dimension(size(lags,1)) :: ZeroFlowRatio &mdash; Ratio of zero values to total number
1011 !> of data points
1012 !> Used as hydrologic signature in
1013 !> Zhang, Y., Vaze, J., Chiew, F. H. S., Teng, J., & Li, M. (2014).
1014 !> Predicting hydrological signatures in ungauged catchments using spatial interpolation, index model, and
1015 !> rainfall-runoff modelling.
1016 !> Journal of Hydrology, 517(C), 936-948. doi:10.1016/j.jhydrol.2014.06.032
1017
1018 ! INTENT(IN)
1019 !> \param[in] "real(dp), dimension(:) :: data" array of data
1020
1021 ! INTENT(IN), OPTIONAL
1022 !> \param[in] "logical, dimension(size(data, 1)), optional :: mask" mask for data points givenWorks only with 1d
1023 !> double precision input data.
1024
1025 ! HISTORY
1026 !> \authors Juliane Mai
1027
1028 !> \date Jun 2015
1029
1030 ! Modifications:
1031 ! Robert Schweppe Jun 2018 - refactoring and reformatting
1032
1033 FUNCTION zeroflowratio(data, mask)
1034
1035 use mo_utils, only : eq
1036
1037 implicit none
1038
1039 ! array of data
1040 real(dp), dimension(:), intent(in) :: data
1041
1042 ! mask for data points givenWorks only with 1d double precision input data.
1043 logical, dimension(size(data, 1)), optional, intent(in) :: mask
1044
1045 ! Autocorrelation of data at given lags
1046 real(dp) :: zeroflowratio
1047
1048 ! mask of data
1049 logical, dimension(size(data, 1)) :: maske
1050
1051 ! total number of data points
1052 integer(i4) :: nall
1053
1054 ! number of zero data points
1055 integer(i4) :: nzero
1056
1057
1058 ! checking optionals
1059 if (present(mask)) then
1060 maske = mask
1061 else
1062 maske = .true.
1063 end if
1064
1065 nall = count(maske)
1066 nzero = count(maske .and. (eq(data, 0.0_dp)))
1067
1068 if (nall > 0) then
1069 zeroflowratio = real(nzero, dp) / real(nall, dp)
1070 else
1071 call error_message('mo_signatures: ZeroFlowRatio: all data points are masked')
1072 end if
1073
1074 END FUNCTION zeroflowratio
1075
1076END MODULE mo_mrm_signatures
Module with calculations for several hydrological signatures.
real(dp) function, public runoffratio(data, domain_area, mask, precip_series, precip_sum, log_data)
Runoff ratio (accumulated daily discharge [mm/d] / accumulated daily precipitation [mm/d]).
real(dp) function, public zeroflowratio(data, mask)
Ratio of zero values to total number of data points.
real(dp) function, dimension(size(quantiles, 1)), public peakdistribution(data, quantiles, mask, slope_peak_distribution)
Calculates the peak distribution.
subroutine, public moments(data, mask, mean_data, stddev_data, median_data, max_data, mean_log, stddev_log, median_log, max_log)
Moments of data and log-transformed data, e.g.
subroutine, public limb_densities(data, mask, rld, dld)
Calculates limb densities.
real(dp) function, dimension(size(lags, 1)), public autocorrelation(data, lags, mask)
Autocorrelation of a given data series.
real(dp) function, dimension(size(quantiles, 1)), public flowdurationcurve(data, quantiles, mask, concavity_index, mid_segment_slope, mhigh_segment_volume, high_segment_volume, low_segment_volume)
Flow duration curves.
real(dp) function maximummonthlyflow(data, mask, yr_start, mo_start, dy_start)
Maximum of average flows per months.