def fdc_flv(obs: DataArray, sim: DataArray, l: float = 0.3) -> float: """Low flow bias derived from the flow duration curve. Reference: Yilmaz, K. K., Gupta, H. V., and Wagener, T. ( 2008), A process‐based diagnostic approach to model evaluation: Application to the NWS distributed hydrologic model, Water Resour. Res., 44, W09417, doi:10.1029/2007WR006716. """ # verify inputs _validate_inputs(obs, sim) # get time series with only valid observations obs, sim = _mask_valid(obs, sim) if (l <= 0) or (l >= 1): raise ValueError( "l has to be in range ]0,1[. Consider small values, e.g. 0.3 for 30% low flows" ) # get arrays of sorted (descending) discharges obs = _get_fdc(obs) sim = _get_fdc(sim) # for numerical reasons change 0s to 1e-6. Simulations can still contain negatives, so also reset those. sim[sim <= 0] = 1e-6 obs[obs == 0] = 1e-6 obs = obs[-np.round(l * len(obs)).astype(int) :] sim = sim[-np.round(l * len(sim)).astype(int) :] # transform values to log scale obs = np.log(obs) sim = np.log(sim) # calculate flv part by part qsl = np.sum(sim - sim.min()) qol = np.sum(obs - obs.min()) flv = -1 * (qsl - qol) / (qol + 1e-6) return flv * 100
def fdc_flv(obs: DataArray, sim: DataArray, l: float = 0.3) -> float: r"""Calculate the low flow bias of the flow duration curve [#]_ .. math:: \%\text{BiasFMS} = -1 \frac{\sum_{l=1}^{L}[\log(Q_{s,l}) - \log(Q_{s,L})] - \sum_{l=1}^{L}[\log(Q_{o,l}) - \log(Q_{o,L})]}{\sum_{l=1}^{L}[\log(Q_{o,l}) - \log(Q_{o,L})]} \times 100, where :math:`Q_s` are the simulations (here, `sim`), :math:`Q_o` the observations (here, `obs`) and `L` is the lower fraction of flows of the FDC (here, `l`). Parameters ---------- obs : DataArray Observed time series. sim : DataArray Simulated time series. l : float, optional Fraction of lower flows to consider as low flows of range ]0,1[, be default 0.3. Returns ------- float Low flow bias. References ---------- .. [#] Yilmaz, K. K., Gupta, H. V., and Wagener, T. ( 2008), A process-based diagnostic approach to model evaluation: Application to the NWS distributed hydrologic model, Water Resour. Res., 44, W09417, doi:10.1029/2007WR006716. """ # verify inputs _validate_inputs(obs, sim) # get time series with only valid observations obs, sim = _mask_valid(obs, sim) if len(obs) < 1: return np.nan if (l <= 0) or (l >= 1): raise ValueError( "l has to be in range ]0,1[. Consider small values, e.g. 0.3 for 30% low flows" ) # get arrays of sorted (descending) discharges obs = _get_fdc(obs) sim = _get_fdc(sim) # for numerical reasons change 0s to 1e-6. Simulations can still contain negatives, so also reset those. sim[sim <= 0] = 1e-6 obs[obs == 0] = 1e-6 obs = obs[-np.round(l * len(obs)).astype(int):] sim = sim[-np.round(l * len(sim)).astype(int):] # transform values to log scale obs = np.log(obs) sim = np.log(sim) # calculate flv part by part qsl = np.sum(sim - sim.min()) qol = np.sum(obs - obs.min()) flv = -1 * (qsl - qol) / (qol + 1e-6) return flv * 100