An error occurred while loading the file. Please try again.
-
Le Roux Erwan authored46e79f66
#include <unordered_map>
#include <vector>
#include <array>
#include <stdexcept>
#include <xtensor/xexpression.hpp>
#include <xtensor/xarray.hpp>
#include <xtensor/xscalar.hpp>
#include "evalhyd/evald.hpp"
#include "utils.hpp"
#include "masks.hpp"
#include "maths.hpp"
#include "uncertainty.hpp"
#include "determinist/evaluator.hpp"
namespace eh = evalhyd;
namespace evalhyd
{
std::vector<xt::xarray<double>> evald(
const xt::xtensor<double, 2>& q_obs,
const xt::xtensor<double, 2>& q_prd,
const std::vector<std::string>& metrics,
const std::string& transform,
const double exponent,
double epsilon,
const xt::xtensor<bool, 2>& t_msk,
const xt::xtensor<std::array<char, 32>, 1>& m_cdt,
const std::unordered_map<std::string, int>& bootstrap,
const std::vector<std::string>& dts
)
{
// check that the metrics to be computed are valid
utils::check_metrics(
metrics,
{"RMSE", "NSE", "KGE", "KGEPRIME"}
);
// check that optional parameters are valid
eh::utils::check_bootstrap(bootstrap);
// check that data dimensions are compatible
// > time
if (q_obs.shape(1) != q_prd.shape(1))
throw std::runtime_error(
"observations and predictions feature different "
"temporal lengths"
);
if (t_msk.size() > 0)
if (q_obs.shape(1) != t_msk.shape(1))
throw std::runtime_error(
"observations and masks feature different "
"temporal lengths"
);
if (!dts.empty())
if (q_obs.shape(1) != dts.size())
throw std::runtime_error(
"observations and datetimes feature different "
"temporal lengths"
);
// > series
if (q_obs.shape(0) != 1)
throw std::runtime_error(
"observations contain more than one time series"
);
// retrieve dimensions
std::size_t n_tim = q_obs.shape(1);
std::size_t n_msk = t_msk.size() > 0 ? t_msk.shape(0) :
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
(m_cdt.size() > 0 ? m_cdt.shape(0) : 1);
// initialise a mask if none provided
// (corresponding to no temporal subset)
auto gen_msk = [&]() {
// if t_msk provided, it takes priority
if (t_msk.size() > 0)
return t_msk;
// else if m_cdt provided, use them to generate t_msk
else if (m_cdt.size() > 0)
{
xt::xtensor<bool, 2> c_msk = xt::zeros<bool>({n_msk, n_tim});
for (int m = 0; m < n_msk; m++)
xt::view(c_msk, m) =
eh::masks::generate_mask_from_conditions(
m_cdt[0], xt::view(q_obs, 0), q_prd
);
return c_msk;
}
// if neither t_msk nor m_cdt provided, generate dummy mask
else
return xt::xtensor<bool, 2>{xt::ones<bool>({std::size_t{1}, n_tim})};
};
auto msk = gen_msk();
// apply streamflow transformation if requested
auto q_transform = [&](const xt::xtensor<double, 2>& q)
{
if ( transform == "none" || (transform == "pow" && exponent == 1))
{
return q;
}
else if ( transform == "sqrt" )
{
return xt::eval(xt::sqrt(q));
}
else if ( transform == "inv" )
{
if ( epsilon == -9 )
// determine an epsilon value to avoid zero divide
epsilon = xt::mean(q_obs)() * 0.01;
return xt::eval(1. / (q + epsilon));
}
else if ( transform == "log" )
{
if ( epsilon == -9 )
// determine an epsilon value to avoid log zero
epsilon = xt::mean(q_obs)() * 0.01;
return xt::eval(xt::log(q + epsilon));
}
else if ( transform == "pow" )
{
if ( exponent < 0 )
{
if ( epsilon == -9 )
// determine an epsilon value to avoid zero divide
epsilon = xt::mean(q_obs)() * 0.01;
return xt::eval(xt::pow(q + epsilon, exponent));
}
else
{
return xt::eval(xt::pow(q, exponent));
}
}