Commit ced03451 authored by Thibault Hallouin's avatar Thibault Hallouin
Browse files

remove default values from function signatures

since those default values were only used to check whether these
parameters were provided or not, they are turned into optional
parameters
1 merge request!3release v0.1.0
Pipeline #42968 passed with stage
in 2 minutes and 6 seconds
Showing with 112 additions and 64 deletions
+112 -64
...@@ -52,6 +52,13 @@ namespace evalhyd ...@@ -52,6 +52,13 @@ namespace evalhyd
"number of samples missing for bootstrap" "number of samples missing for bootstrap"
); );
} }
auto n_samples = bootstrap.find("n_samples")->second;
if (n_samples < 1)
{
throw std::runtime_error(
"number of samples must be greater than zero"
);
}
// check len_sample // check len_sample
if (bootstrap.find("len_sample") == bootstrap.end()) if (bootstrap.find("len_sample") == bootstrap.end())
{ {
...@@ -59,6 +66,13 @@ namespace evalhyd ...@@ -59,6 +66,13 @@ namespace evalhyd
"length of sample missing for bootstrap" "length of sample missing for bootstrap"
); );
} }
auto len_sample = bootstrap.find("len_sample")->second;
if (len_sample < 1)
{
throw std::runtime_error(
"length of sample must be greater than zero"
);
}
// check summary // check summary
if (bootstrap.find("summary") == bootstrap.end()) if (bootstrap.find("summary") == bootstrap.end())
{ {
...@@ -66,9 +80,9 @@ namespace evalhyd ...@@ -66,9 +80,9 @@ namespace evalhyd
"summary missing for bootstrap" "summary missing for bootstrap"
); );
} }
auto s = bootstrap.find("summary")->second; auto summary = bootstrap.find("summary")->second;
// TODO: change upper bound when mean+stddev and quantiles implemented // TODO: change upper bound when mean+stddev and quantiles implemented
if ((s < 0) || (s > 0)) if ((summary < 0) || (summary > 0))
{ {
throw std::runtime_error( throw std::runtime_error(
"invalid value for bootstrap summary" "invalid value for bootstrap summary"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <xtl/xoptional.hpp>
#include <xtensor/xexpression.hpp> #include <xtensor/xexpression.hpp>
#include <xtensor/xtensor.hpp> #include <xtensor/xtensor.hpp>
#include <xtensor/xarray.hpp> #include <xtensor/xarray.hpp>
...@@ -58,18 +59,17 @@ namespace evalhyd ...@@ -58,18 +59,17 @@ namespace evalhyd
/// ///
/// exponent: ``double``, optional /// exponent: ``double``, optional
/// The value of the exponent n to use when the *transform* is the /// The value of the exponent n to use when the *transform* is the
/// power function. If not provided (or set to default value 1), /// power function. If not provided, the streamflow observations
/// the streamflow observations and predictions remain untransformed. /// and predictions remain untransformed.
/// ///
/// epsilon: ``double``, optional /// epsilon: ``double``, optional
/// The value of the small constant ε to add to both the streamflow /// The value of the small constant ε to add to both the streamflow
/// observations and predictions prior to the calculation of the /// observations and predictions prior to the calculation of the
/// *metrics* when the *transform* is the reciprocal function, the /// *metrics* when the *transform* is the reciprocal function, the
/// natural logarithm, or the power function with a negative exponent /// natural logarithm, or the power function with a negative exponent
/// (since none are defined for 0). If not provided (or set to default /// (since none are defined for 0). If not provided, one hundredth of
/// value -9), one hundredth of the mean of the streamflow /// the mean of the streamflow observations is used as value for
/// observations is used as value for epsilon, as recommended by /// epsilon, as recommended by `Pushpalatha et al. (2012)
/// `Pushpalatha et al. (2012)
/// <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_. /// <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_.
/// ///
/// t_msk: ``XB2``, optional /// t_msk: ``XB2``, optional
...@@ -159,13 +159,16 @@ namespace evalhyd ...@@ -159,13 +159,16 @@ namespace evalhyd
const xt::xexpression<XD2>& q_obs, const xt::xexpression<XD2>& q_obs,
const xt::xexpression<XD2>& q_prd, const xt::xexpression<XD2>& q_prd,
const std::vector<std::string>& metrics, const std::vector<std::string>& metrics,
const std::string& transform = "none", xtl::xoptional<const std::string, bool> transform =
double exponent = 1, xtl::missing<const std::string>(),
double epsilon = -9, xtl::xoptional<double, bool> exponent =
xtl::missing<double>(),
xtl::xoptional<double, bool> epsilon =
xtl::missing<double>(),
const xt::xexpression<XB2>& t_msk = XB2({}), const xt::xexpression<XB2>& t_msk = XB2({}),
const xt::xtensor<std::array<char, 32>, 1>& m_cdt = {}, const xt::xtensor<std::array<char, 32>, 1>& m_cdt = {},
const std::unordered_map<std::string, int>& bootstrap = xtl::xoptional<const std::unordered_map<std::string, int>> bootstrap =
{{"n_samples", -9}, {"len_sample", -9}, {"summary", 0}}, xtl::missing<const std::unordered_map<std::string, int>>(),
const std::vector<std::string>& dts = {} const std::vector<std::string>& dts = {}
) )
{ {
...@@ -196,7 +199,10 @@ namespace evalhyd ...@@ -196,7 +199,10 @@ namespace evalhyd
); );
// check that optional parameters are valid // check that optional parameters are valid
utils::check_bootstrap(bootstrap); if (bootstrap.has_value())
{
utils::check_bootstrap(bootstrap.value());
}
// check that data dimensions are compatible // check that data dimensions are compatible
// > time // > time
...@@ -277,68 +283,90 @@ namespace evalhyd ...@@ -277,68 +283,90 @@ namespace evalhyd
// apply streamflow transformation if requested // apply streamflow transformation if requested
auto q_transform = [&](const XD2& q) auto q_transform = [&](const XD2& q)
{ {
if ( transform == "none" || (transform == "pow" && exponent == 1)) if (transform.has_value())
{
return q;
}
else if ( transform == "sqrt" )
{
return XD2(xt::sqrt(q));
}
else if ( transform == "inv" )
{ {
if ( epsilon == -9 ) if ( transform.value() == "sqrt" )
{ {
// determine an epsilon value to avoid zero divide return XD2(xt::sqrt(q));
epsilon = xt::mean(q_obs_)() * 0.01;
} }
else if ( transform.value() == "inv" )
return XD2(1. / (q + epsilon));
}
else if ( transform == "log" )
{
if ( epsilon == -9 )
{ {
// determine an epsilon value to avoid log zero if ( !epsilon.has_value() )
epsilon = xt::mean(q_obs_)() * 0.01; {
} // determine an epsilon value to avoid zero divide
epsilon = xt::mean(q_obs_)() * 0.01;
}
return XD2(xt::log(q + epsilon)); return XD2(1. / (q + epsilon.value()));
} }
else if ( transform == "pow" ) else if ( transform.value() == "log" )
{
if ( exponent < 0 )
{ {
if ( epsilon == -9 ) if ( !epsilon.has_value() )
{ {
// determine an epsilon value to avoid zero divide // determine an epsilon value to avoid log zero
epsilon = xt::mean(q_obs_)() * 0.01; epsilon = xt::mean(q_obs_)() * 0.01;
} }
return XD2(xt::pow(q + epsilon, exponent)); return XD2(xt::log(q + epsilon.value()));
}
else if ( transform.value() == "pow" )
{
if ( exponent.has_value() )
{
if ( exponent.value() == 1)
{
return q;
}
else if ( exponent.value() < 0 )
{
if ( !epsilon.has_value() )
{
// determine an epsilon value to avoid zero divide
epsilon = xt::mean(q_obs_)() * 0.01;
}
return XD2(xt::pow(q + epsilon.value(),
exponent.value()));
}
else
{
return XD2(xt::pow(q, exponent.value()));
}
}
else
{
throw std::runtime_error(
"missing exponent for power transformation"
);
}
} }
else else
{ {
return XD2(xt::pow(q, exponent)); throw std::runtime_error(
"invalid streamflow transformation: "
+ transform.value()
);
} }
} }
else else
{ {
throw std::runtime_error( return q;
"invalid streamflow transformation: " + transform
);
} }
}; };
const XD2 obs = q_transform(q_obs_); const XD2& obs = q_transform(q_obs_);
const XD2 prd = q_transform(q_prd_); const XD2& prd = q_transform(q_prd_);
// generate bootstrap experiment if requested // generate bootstrap experiment if requested
std::vector<xt::xkeep_slice<int>> exp; std::vector<xt::xkeep_slice<int>> exp;
auto n_samples = bootstrap.find("n_samples")->second; int summary;
auto len_sample = bootstrap.find("len_sample")->second;
if ((n_samples != -9) && (len_sample != -9)) if (bootstrap.has_value())
{ {
auto n_samples = bootstrap.value().find("n_samples")->second;
auto len_sample = bootstrap.value().find("len_sample")->second;
summary = bootstrap.value().find("summary")->second;
if (dts.empty()) if (dts.empty())
{ {
throw std::runtime_error( throw std::runtime_error(
...@@ -354,6 +382,7 @@ namespace evalhyd ...@@ -354,6 +382,7 @@ namespace evalhyd
{ {
// if no bootstrap requested, generate one sample // if no bootstrap requested, generate one sample
// containing all the time indices once // containing all the time indices once
summary = 0;
xt::xtensor<int, 1> all = xt::arange(n_tim); xt::xtensor<int, 1> all = xt::arange(n_tim);
exp.push_back(xt::keep(all)); exp.push_back(xt::keep(all));
} }
...@@ -364,8 +393,6 @@ namespace evalhyd ...@@ -364,8 +393,6 @@ namespace evalhyd
// retrieve or compute requested metrics // retrieve or compute requested metrics
std::vector<xt::xarray<double>> r; std::vector<xt::xarray<double>> r;
auto summary = bootstrap.find("summary")->second;
for ( const auto& metric : metrics ) for ( const auto& metric : metrics )
{ {
if ( metric == "RMSE" ) if ( metric == "RMSE" )
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <xtl/xoptional.hpp>
#include <xtensor/xexpression.hpp> #include <xtensor/xexpression.hpp>
#include <xtensor/xtensor.hpp> #include <xtensor/xtensor.hpp>
#include <xtensor/xarray.hpp> #include <xtensor/xarray.hpp>
...@@ -144,8 +145,8 @@ namespace evalhyd ...@@ -144,8 +145,8 @@ namespace evalhyd
const xt::xexpression<XD2>& q_thr = XD2({}), const xt::xexpression<XD2>& q_thr = XD2({}),
const xt::xexpression<XB4>& t_msk = XB4({}), const xt::xexpression<XB4>& t_msk = XB4({}),
const xt::xtensor<std::array<char, 32>, 2>& m_cdt = {}, const xt::xtensor<std::array<char, 32>, 2>& m_cdt = {},
const std::unordered_map<std::string, int>& bootstrap = xtl::xoptional<const std::unordered_map<std::string, int>> bootstrap =
{{"n_samples", -9}, {"len_sample", -9}, {"summary", 0}}, xtl::missing<const std::unordered_map<std::string, int>>(),
const std::vector<std::string>& dts = {} const std::vector<std::string>& dts = {}
) )
{ {
...@@ -184,7 +185,10 @@ namespace evalhyd ...@@ -184,7 +185,10 @@ namespace evalhyd
// check that optional parameters are given as arguments // check that optional parameters are given as arguments
utils::evalp::check_optionals(metrics, q_thr_); utils::evalp::check_optionals(metrics, q_thr_);
utils::check_bootstrap(bootstrap); if (bootstrap.has_value())
{
utils::check_bootstrap(bootstrap.value());
}
// check that data dimensions are compatible // check that data dimensions are compatible
// > time // > time
...@@ -266,8 +270,8 @@ namespace evalhyd ...@@ -266,8 +270,8 @@ namespace evalhyd
std::size_t n_thr = q_thr_.shape(1); std::size_t n_thr = q_thr_.shape(1);
std::size_t n_msk = t_msk_.size() > 0 ? t_msk_.shape(2) : std::size_t n_msk = t_msk_.size() > 0 ? t_msk_.shape(2) :
(m_cdt.size() > 0 ? m_cdt.shape(1) : 1); (m_cdt.size() > 0 ? m_cdt.shape(1) : 1);
std::size_t n_exp = bootstrap.find("n_samples")->second == -9 ? 1: std::size_t n_exp = !bootstrap.has_value() ? 1:
bootstrap.find("n_samples")->second; bootstrap.value().find("n_samples")->second;
// register metrics number of dimensions // register metrics number of dimensions
std::unordered_map<std::string, std::vector<std::size_t>> dim; std::unordered_map<std::string, std::vector<std::size_t>> dim;
...@@ -308,10 +312,14 @@ namespace evalhyd ...@@ -308,10 +312,14 @@ namespace evalhyd
// generate bootstrap experiment if requested // generate bootstrap experiment if requested
std::vector<xt::xkeep_slice<int>> b_exp; std::vector<xt::xkeep_slice<int>> b_exp;
auto n_samples = bootstrap.find("n_samples")->second; int summary;
auto len_sample = bootstrap.find("len_sample")->second;
if ((n_samples != -9) && (len_sample != -9)) if (bootstrap.has_value())
{ {
auto n_samples = bootstrap.value().find("n_samples")->second;
auto len_sample = bootstrap.value().find("len_sample")->second;
summary = bootstrap.value().find("summary")->second;
if (dts.empty()) if (dts.empty())
{ {
throw std::runtime_error( throw std::runtime_error(
...@@ -325,6 +333,7 @@ namespace evalhyd ...@@ -325,6 +333,7 @@ namespace evalhyd
{ {
// if no bootstrap requested, generate one sample // if no bootstrap requested, generate one sample
// containing all the time indices once // containing all the time indices once
summary = 0;
xt::xtensor<int, 1> all = xt::arange(n_tim); xt::xtensor<int, 1> all = xt::arange(n_tim);
b_exp.push_back(xt::keep(all)); b_exp.push_back(xt::keep(all));
} }
...@@ -336,8 +345,6 @@ namespace evalhyd ...@@ -336,8 +345,6 @@ namespace evalhyd
r.emplace_back(xt::zeros<double>(dim[metric])); r.emplace_back(xt::zeros<double>(dim[metric]));
} }
auto summary = bootstrap.find("summary")->second;
// compute variables one site at a time to minimise memory imprint // compute variables one site at a time to minimise memory imprint
for (std::size_t s = 0; s < n_sit; s++) for (std::size_t s = 0; s < n_sit; s++)
{ {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment