From df97a0599cead5b87a94fa6f88ce6158807d1086 Mon Sep 17 00:00:00 2001 From: Thibault Hallouin <thibault.hallouin@inrae.fr> Date: Tue, 3 Jan 2023 16:50:27 +0100 Subject: [PATCH] document template parameters --- .../evalhyd/detail/determinist/elements.hpp | 40 ++++++------- .../evalhyd/detail/determinist/evaluator.hpp | 28 +++++----- include/evalhyd/detail/probabilist/brier.hpp | 28 +++++----- .../evalhyd/detail/probabilist/evaluator.hpp | 8 +-- .../evalhyd/detail/probabilist/quantiles.hpp | 8 +-- include/evalhyd/evald.hpp | 56 +++++++++++-------- include/evalhyd/evalp.hpp | 52 ++++++++++------- 7 files changed, 122 insertions(+), 98 deletions(-) diff --git a/include/evalhyd/detail/determinist/elements.hpp b/include/evalhyd/detail/determinist/elements.hpp index 8c8fe1b..6218e6d 100644 --- a/include/evalhyd/detail/determinist/elements.hpp +++ b/include/evalhyd/detail/determinist/elements.hpp @@ -33,9 +33,9 @@ namespace evalhyd // \return // Mean observed streamflow. // shape: (subsets, samples, series, 1) - template <class D2> + template <class XD2> inline xt::xtensor<double, 4> calc_mean_obs( - const D2& q_obs, + const XD2& q_obs, const xt::xtensor<bool, 3>& t_msk, const std::vector<xt::xkeep_slice<int>>& b_exp, std::size_t n_srs, @@ -85,9 +85,9 @@ namespace evalhyd // \return // Mean predicted streamflow. // shape: (subsets, samples, series, 1) - template <class D2> + template <class XD2> inline xt::xtensor<double, 4> calc_mean_prd( - const D2& q_prd, + const XD2& q_prd, const xt::xtensor<bool, 3>& t_msk, const std::vector<xt::xkeep_slice<int>>& b_exp, std::size_t n_srs, @@ -128,10 +128,10 @@ namespace evalhyd // \return // Quadratic errors between observations and predictions. // shape: (series, time) - template <class D2> + template <class XD2> inline xt::xtensor<double, 2> calc_quad_err( - const D2& q_obs, - const D2& q_prd + const XD2& q_obs, + const XD2& q_prd ) { return xt::square(q_obs - q_prd); @@ -159,9 +159,9 @@ namespace evalhyd // \return // Quadratic errors between observations and mean observation. // shape: (subsets, samples, series, time) - template <class D2> + template <class XD2> inline xt::xtensor<double, 4> calc_quad_obs( - const D2& q_obs, + const XD2& q_obs, const xt::xtensor<double, 4>& mean_obs, const xt::xtensor<bool, 3>& t_msk, std::size_t n_srs, @@ -212,9 +212,9 @@ namespace evalhyd // \return // Quadratic errors between predictions and mean prediction. // shape: (subsets, samples, series, time) - template <class D2> + template <class XD2> inline xt::xtensor<double, 4> calc_quad_prd( - const D2& q_prd, + const XD2& q_prd, const xt::xtensor<double, 4>& mean_prd, const xt::xtensor<bool, 3>& t_msk, std::size_t n_srs, @@ -278,10 +278,10 @@ namespace evalhyd // \return // Pearson correlation coefficients. // shape: (subsets, samples, series) - template <class D2> + template <class XD2> inline xt::xtensor<double, 3> calc_r_pearson( - const D2& q_obs, - const D2& q_prd, + const XD2& q_obs, + const XD2& q_prd, const xt::xtensor<double, 4>& mean_obs, const xt::xtensor<double, 4>& mean_prd, const xt::xtensor<double, 4>& quad_obs, @@ -358,10 +358,10 @@ namespace evalhyd // \return // Alphas, ratios of standard deviations. // shape: (subsets, samples, series) - template <class D2> + template <class XD2> inline xt::xtensor<double, 3> calc_alpha( - const D2& q_obs, - const D2& q_prd, + const XD2& q_obs, + const XD2& q_prd, const xt::xtensor<double, 4>& mean_obs, const xt::xtensor<double, 4>& mean_prd, const xt::xtensor<bool, 3>& t_msk, @@ -419,10 +419,10 @@ namespace evalhyd // \return // Biases. // shape: (subsets, samples, series) - template <class D2> + template <class XD2> inline xt::xtensor<double, 3> calc_bias( - const D2& q_obs, - const D2& q_prd, + const XD2& q_obs, + const XD2& q_prd, const xt::xtensor<bool, 3>& t_msk, const std::vector<xt::xkeep_slice<int>>& b_exp, std::size_t n_srs, diff --git a/include/evalhyd/detail/determinist/evaluator.hpp b/include/evalhyd/detail/determinist/evaluator.hpp index 8e494af..03ed47a 100644 --- a/include/evalhyd/detail/determinist/evaluator.hpp +++ b/include/evalhyd/detail/determinist/evaluator.hpp @@ -15,13 +15,13 @@ namespace evalhyd { namespace determinist { - template <class D2, class B2> + template <class XD2, class XB2> class Evaluator { private: // members for input data - const D2& q_obs; - const D2& q_prd; + const XD2& q_obs; + const XD2& q_prd; xt::xtensor<bool, 3> t_msk; const std::vector<xt::xkeep_slice<int>>& b_exp; @@ -52,7 +52,7 @@ namespace evalhyd { if (!mean_obs.has_value()) { - mean_obs = elements::calc_mean_obs<D2>( + mean_obs = elements::calc_mean_obs<XD2>( q_obs, t_msk, b_exp, n_srs, n_msk, n_exp ); } @@ -63,7 +63,7 @@ namespace evalhyd { if (!mean_prd.has_value()) { - mean_prd = elements::calc_mean_prd<D2>( + mean_prd = elements::calc_mean_prd<XD2>( q_prd, t_msk, b_exp, n_srs, n_msk, n_exp ); } @@ -74,7 +74,7 @@ namespace evalhyd { if (!quad_err.has_value()) { - quad_err = elements::calc_quad_err<D2>( + quad_err = elements::calc_quad_err<XD2>( q_obs, q_prd ); } @@ -85,7 +85,7 @@ namespace evalhyd { if (!quad_obs.has_value()) { - quad_obs = elements::calc_quad_obs<D2>( + quad_obs = elements::calc_quad_obs<XD2>( q_obs, get_mean_obs(), t_msk, n_srs, n_tim, n_msk, n_exp ); @@ -97,7 +97,7 @@ namespace evalhyd { if (!quad_prd.has_value()) { - quad_prd = elements::calc_quad_prd<D2>( + quad_prd = elements::calc_quad_prd<XD2>( q_prd, get_mean_prd(), t_msk, n_srs, n_tim, n_msk, n_exp ); @@ -109,7 +109,7 @@ namespace evalhyd { if (!r_pearson.has_value()) { - r_pearson = elements::calc_r_pearson<D2>( + r_pearson = elements::calc_r_pearson<XD2>( q_obs, q_prd, get_mean_obs(), get_mean_prd(), get_quad_obs(), get_quad_prd(), t_msk, b_exp, n_srs, n_msk, n_exp @@ -122,7 +122,7 @@ namespace evalhyd { if (!alpha.has_value()) { - alpha = elements::calc_alpha<D2>( + alpha = elements::calc_alpha<XD2>( q_obs, q_prd, get_mean_obs(), get_mean_prd(), t_msk, b_exp, n_srs, n_msk, n_exp ); @@ -134,7 +134,7 @@ namespace evalhyd { if (!bias.has_value()) { - bias = elements::calc_bias<D2>( + bias = elements::calc_bias<XD2>( q_obs, q_prd, t_msk, b_exp, n_srs, n_msk, n_exp ); } @@ -143,9 +143,9 @@ namespace evalhyd public: // constructor method - Evaluator(const D2& obs, - const D2& prd, - const B2& msk, + Evaluator(const XD2& obs, + const XD2& prd, + const XB2& msk, const std::vector<xt::xkeep_slice<int>>& exp) : q_obs{obs}, q_prd{prd}, b_exp{exp} { diff --git a/include/evalhyd/detail/probabilist/brier.hpp b/include/evalhyd/detail/probabilist/brier.hpp index 70293fc..fec0f09 100644 --- a/include/evalhyd/detail/probabilist/brier.hpp +++ b/include/evalhyd/detail/probabilist/brier.hpp @@ -23,10 +23,10 @@ namespace evalhyd // \return // Event observed outcome. // shape: (thresholds, time) - template<class V1D2> + template<class XV1D2> inline xt::xtensor<double, 2> calc_o_k( - const V1D2& q_obs, - const V1D2& q_thr + const XV1D2& q_obs, + const XV1D2& q_thr ) { // determine observed realisation of threshold(s) exceedance @@ -100,10 +100,10 @@ namespace evalhyd // \return // Event probability forecast. // shape: (thresholds, time) - template<class V2D4, class V1D2> + template<class XV2D4, class XV1D2> inline xt::xtensor<double, 2> calc_y_k( - const V2D4& q_prd, - const V1D2& q_thr, + const XV2D4& q_prd, + const XV1D2& q_thr, std::size_t n_mbr ) { @@ -171,10 +171,10 @@ namespace evalhyd // \return // Brier score for each subset and for each threshold. // shape: (subsets, samples, thresholds) - template <class V1D2> + template <class XV1D2> inline xt::xtensor<double, 3> calc_BS( const xt::xtensor<double, 2>& bs, - const V1D2& q_thr, + const XV1D2& q_thr, const xt::xtensor<bool, 2>& t_msk, const std::vector<xt::xkeep_slice<int>>& b_exp, std::size_t n_thr, @@ -253,9 +253,9 @@ namespace evalhyd // Brier score components (reliability, resolution, uncertainty) // for each subset and for each threshold. // shape: (subsets, samples, thresholds, components) - template <class V1D2> + template <class XV1D2> inline xt::xtensor<double, 4> calc_BS_CRD( - const V1D2& q_thr, + const XV1D2& q_thr, const xt::xtensor<double, 2>& o_k, const xt::xtensor<double, 2>& y_k, const xt::xtensor<double, 3>& bar_o, @@ -402,9 +402,9 @@ namespace evalhyd // Brier score components (type 2 bias, discrimination, sharpness) // for each subset and for each threshold. // shape: (subsets, samples, thresholds, components) - template <class V1D2> + template <class XV1D2> inline xt::xtensor<double, 4> calc_BS_LBD( - const V1D2& q_thr, + const XV1D2& q_thr, const xt::xtensor<double, 2>& o_k, const xt::xtensor<double, 2>& y_k, const xt::xtensor<bool, 2>& t_msk, @@ -560,10 +560,10 @@ namespace evalhyd // \return // Brier skill score for each subset and for each threshold. // shape: (subsets, samples, thresholds) - template <class V1D2> + template <class XV1D2> inline xt::xtensor<double, 3> calc_BSS( const xt::xtensor<double, 2>& bs, - const V1D2& q_thr, + const XV1D2& q_thr, const xt::xtensor<double, 2>& o_k, const xt::xtensor<double, 3>& bar_o, const xt::xtensor<bool, 2>& t_msk, diff --git a/include/evalhyd/detail/probabilist/evaluator.hpp b/include/evalhyd/detail/probabilist/evaluator.hpp index 7f0e2eb..e58fb3e 100644 --- a/include/evalhyd/detail/probabilist/evaluator.hpp +++ b/include/evalhyd/detail/probabilist/evaluator.hpp @@ -16,13 +16,13 @@ namespace evalhyd { namespace probabilist { - template <class D2, class D4, class B4> + template <class XD2, class XD4, class XB4> class Evaluator { private: using view1d_xtensor2d_double_type = decltype( xt::view( - std::declval<const D2&>(), + std::declval<const XD2&>(), std::declval<std::size_t>(), xt::all() ) @@ -30,7 +30,7 @@ namespace evalhyd using view2d_xtensor4d_double_type = decltype( xt::view( - std::declval<const D4&>(), + std::declval<const XD4&>(), std::declval<std::size_t>(), std::declval<std::size_t>(), xt::all(), @@ -40,7 +40,7 @@ namespace evalhyd using view2d_xtensor4d_bool_type = decltype( xt::view( - std::declval<const B4&>(), + std::declval<const XB4&>(), std::declval<std::size_t>(), std::declval<std::size_t>(), xt::all(), diff --git a/include/evalhyd/detail/probabilist/quantiles.hpp b/include/evalhyd/detail/probabilist/quantiles.hpp index c05273d..b4963c5 100644 --- a/include/evalhyd/detail/probabilist/quantiles.hpp +++ b/include/evalhyd/detail/probabilist/quantiles.hpp @@ -20,9 +20,9 @@ namespace evalhyd // \return // Streamflow forecast quantiles. // shape: (quantiles, time) - template <class V2D4> + template <class XV2D4> inline xt::xtensor<double, 2> calc_q_qnt( - const V2D4& q_prd + const XV2D4& q_prd ) { return xt::sort(q_prd, 0); @@ -42,9 +42,9 @@ namespace evalhyd // \return // Quantile scores for each time step. // shape: (quantiles, time) - template <class V1D2> + template <class XV1D2> inline xt::xtensor<double, 2> calc_qs( - const V1D2 &q_obs, + const XV1D2 &q_obs, const xt::xtensor<double, 2>& q_qnt, std::size_t n_mbr ) diff --git a/include/evalhyd/evald.hpp b/include/evalhyd/evald.hpp index c10ed21..2efb647 100644 --- a/include/evalhyd/evald.hpp +++ b/include/evalhyd/evald.hpp @@ -21,16 +21,26 @@ namespace evalhyd /// /// \rst /// + /// :Template Parameters: + /// + /// XD2: Any 2-dimensional container class storing numeric elements + /// (e.g. ``xt::xtensor<double, 2>``, ``xt::pytensor<double, 2>``, + /// ``xt::rtensor<double, 2>``, etc.). + /// + /// XB2: Any 2-dimensional container class storing boolean elements + /// (e.g. ``xt::xtensor<bool, 4>``, ``xt::pytensor<bool, 4>``, + /// ``xt::rtensor<bool, 4>``, etc.). + /// /// :Parameters: /// - /// q_obs: ``xt::xtensor<double, 2>`` + /// q_obs: ``XD2`` /// Streamflow observations. Time steps with missing observations /// must be assigned `NAN` values. Those time steps will be ignored /// both in the observations and the predictions before the /// *metrics* are computed. /// shape: (1, time) /// - /// q_prd: ``xt::xtensor<double, 2>`` + /// q_prd: ``XD2`` /// Streamflow predictions. Time steps with missing predictions /// must be assigned `NAN` values. Those time steps will be ignored /// both in the observations and the predictions before the @@ -62,7 +72,7 @@ namespace evalhyd /// `Pushpalatha et al. (2012) /// <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_. /// - /// t_msk: ``xt::xtensor<bool, 2>``, optional + /// t_msk: ``XB2``, optional /// Mask used to temporally subset of the whole streamflow time series /// (where True/False is used for the time steps to include/discard in /// the subset). If provided, masks must feature the same number of @@ -144,15 +154,15 @@ namespace evalhyd /// evalhyd::evald(obs, prd, {"NSE"}, "none", 1, -9, msk); /// /// \endrst - template <class D2, class B2> + template <class XD2, class XB2> std::vector<xt::xarray<double>> evald( - const xt::xexpression<D2>& q_obs, - const xt::xexpression<D2>& q_prd, + const xt::xexpression<XD2>& q_obs, + const xt::xexpression<XD2>& q_prd, const std::vector<std::string>& metrics, const std::string& transform = "none", double exponent = 1, double epsilon = -9, - const xt::xexpression<B2>& t_msk = B2({}), + const xt::xexpression<XB2>& t_msk = XB2({}), const xt::xtensor<std::array<char, 32>, 1>& m_cdt = {}, const std::unordered_map<std::string, int>& bootstrap = {{"n_samples", -9}, {"len_sample", -9}, {"summary", 0}}, @@ -160,13 +170,13 @@ namespace evalhyd ) { // check ranks of tensors - if (xt::get_rank<D2>::value != 2) + if (xt::get_rank<XD2>::value != 2) { throw std::runtime_error( "observations and/or predictions are not two-dimensional" ); } - if (xt::get_rank<B2>::value != 2) + if (xt::get_rank<XB2>::value != 2) { throw std::runtime_error( "temporal masks are not two-dimensional" @@ -174,10 +184,10 @@ namespace evalhyd } // retrieve real types of the expressions - const D2& q_obs_ = q_obs.derived_cast(); - const D2& q_prd_ = q_prd.derived_cast(); + const XD2& q_obs_ = q_obs.derived_cast(); + const XD2& q_prd_ = q_prd.derived_cast(); - const B2& t_msk_ = t_msk.derived_cast(); + const XB2& t_msk_ = t_msk.derived_cast(); // check that the metrics to be computed are valid utils::check_metrics( @@ -243,7 +253,7 @@ namespace evalhyd // else if m_cdt provided, use them to generate t_msk else if (m_cdt.size() > 0) { - B2 c_msk = xt::zeros<bool>({n_msk, n_tim}); + XB2 c_msk = xt::zeros<bool>({n_msk, n_tim}); for (std::size_t m = 0; m < n_msk; m++) { @@ -258,14 +268,14 @@ namespace evalhyd // if neither t_msk nor m_cdt provided, generate dummy mask else { - return B2({xt::ones<bool>({std::size_t{1}, n_tim})}); + return XB2({xt::ones<bool>({std::size_t{1}, n_tim})}); } }; auto msk = gen_msk(); // apply streamflow transformation if requested - auto q_transform = [&](const D2& q) + auto q_transform = [&](const XD2& q) { if ( transform == "none" || (transform == "pow" && exponent == 1)) { @@ -273,7 +283,7 @@ namespace evalhyd } else if ( transform == "sqrt" ) { - return D2(xt::sqrt(q)); + return XD2(xt::sqrt(q)); } else if ( transform == "inv" ) { @@ -283,7 +293,7 @@ namespace evalhyd epsilon = xt::mean(q_obs_)() * 0.01; } - return D2(1. / (q + epsilon)); + return XD2(1. / (q + epsilon)); } else if ( transform == "log" ) { @@ -293,7 +303,7 @@ namespace evalhyd epsilon = xt::mean(q_obs_)() * 0.01; } - return D2(xt::log(q + epsilon)); + return XD2(xt::log(q + epsilon)); } else if ( transform == "pow" ) { @@ -305,11 +315,11 @@ namespace evalhyd epsilon = xt::mean(q_obs_)() * 0.01; } - return D2(xt::pow(q + epsilon, exponent)); + return XD2(xt::pow(q + epsilon, exponent)); } else { - return D2(xt::pow(q, exponent)); + return XD2(xt::pow(q, exponent)); } } else @@ -320,8 +330,8 @@ namespace evalhyd } }; - const D2 obs = q_transform(q_obs_); - const D2 prd = q_transform(q_prd_); + const XD2 obs = q_transform(q_obs_); + const XD2 prd = q_transform(q_prd_); // generate bootstrap experiment if requested std::vector<xt::xkeep_slice<int>> exp; @@ -349,7 +359,7 @@ namespace evalhyd } // instantiate determinist evaluator - determinist::Evaluator<D2, B2> evaluator(obs, prd, msk, exp); + determinist::Evaluator<XD2, XB2> evaluator(obs, prd, msk, exp); // retrieve or compute requested metrics std::vector<xt::xarray<double>> r; diff --git a/include/evalhyd/evalp.hpp b/include/evalhyd/evalp.hpp index 0a48ae3..89088ad 100644 --- a/include/evalhyd/evalp.hpp +++ b/include/evalhyd/evalp.hpp @@ -21,16 +21,30 @@ namespace evalhyd /// /// \rst /// + /// :Template Parameters: + /// + /// XD2: Any 2-dimensional container class storing numeric elements + /// (e.g. ``xt::xtensor<double, 2>``, ``xt::pytensor<double, 2>``, + /// ``xt::rtensor<double, 2>``, etc.). + /// + /// XD4: Any 4-dimensional container class storing numeric elements + /// (e.g. ``xt::xtensor<double, 4>``, ``xt::pytensor<double, 4>``, + /// ``xt::rtensor<double, 4>``, etc.). + /// + /// XB4: Any 4-dimensional container class storing boolean elements + /// (e.g. ``xt::xtensor<bool, 4>``, ``xt::pytensor<bool, 4>``, + /// ``xt::rtensor<bool, 4>``, etc.). + /// /// :Parameters: /// - /// q_obs: ``xt::xtensor<double, 2>`` + /// q_obs: ``XD2`` /// Streamflow observations. Time steps with missing observations /// must be assigned `NAN` values. Those time steps will be ignored /// both in the observations and the predictions before the /// *metrics* are computed. /// shape: (sites, time) /// - /// q_prd: ``xt::xtensor<double, 4>`` + /// q_prd: ``XD4`` /// Streamflow predictions. Time steps with missing predictions /// must be assigned `NAN` values. Those time steps will be ignored /// both in the observations and the predictions before the @@ -40,11 +54,11 @@ namespace evalhyd /// metrics: ``std::vector<std::string>`` /// The sequence of evaluation metrics to be computed. /// - /// q_thr: ``xt::xtensor<double, 2>``, optional + /// q_thr: ``XD2``, optional /// Streamflow exceedance threshold(s). /// shape: (sites, thresholds) /// - /// t_msk: ``xt::xtensor<bool, 4>``, optional + /// t_msk: ``XB4``, optional /// Mask(s) used to generate temporal subsets of the whole streamflow /// time series (where True/False is used for the time steps to /// include/discard in a given subset). If not provided and neither @@ -122,13 +136,13 @@ namespace evalhyd /// evalhyd::evalp(obs, prd, {"CRPS"}); /// /// \endrst - template <class D2, class D4, class B4> + template <class XD2, class XD4, class XB4> std::vector<xt::xarray<double>> evalp( - const xt::xexpression<D2>& q_obs, - const xt::xexpression<D4>& q_prd, + const xt::xexpression<XD2>& q_obs, + const xt::xexpression<XD4>& q_prd, const std::vector<std::string>& metrics, - const xt::xexpression<D2>& q_thr = D2({}), - const xt::xexpression<B4>& t_msk = B4({}), + const xt::xexpression<XD2>& q_thr = XD2({}), + const xt::xexpression<XB4>& t_msk = XB4({}), const xt::xtensor<std::array<char, 32>, 2>& m_cdt = {}, const std::unordered_map<std::string, int>& bootstrap = {{"n_samples", -9}, {"len_sample", -9}, {"summary", 0}}, @@ -136,19 +150,19 @@ namespace evalhyd ) { // check ranks of tensors - if (xt::get_rank<D2>::value != 2) + if (xt::get_rank<XD2>::value != 2) { throw std::runtime_error( "observations and/or thresholds are not two-dimensional" ); } - if (xt::get_rank<D4>::value != 4) + if (xt::get_rank<XD4>::value != 4) { throw std::runtime_error( "predictions are not four-dimensional" ); } - if (xt::get_rank<B4>::value != 4) + if (xt::get_rank<XB4>::value != 4) { throw std::runtime_error( "temporal masks are not four-dimensional" @@ -156,11 +170,11 @@ namespace evalhyd } // retrieve real types of the expressions - const D2& q_obs_ = q_obs.derived_cast(); - const D4& q_prd_ = q_prd.derived_cast(); - const D2& q_thr_ = q_thr.derived_cast(); + const XD2& q_obs_ = q_obs.derived_cast(); + const XD4& q_prd_ = q_prd.derived_cast(); + const XD2& q_thr_ = q_thr.derived_cast(); - const B4& t_msk_ = t_msk.derived_cast(); + const XB4& t_msk_ = t_msk.derived_cast(); // check that the metrics to be computed are valid utils::check_metrics( @@ -268,7 +282,7 @@ namespace evalhyd // generate masks from conditions if provided auto gen_msk = [&]() { - B4 c_msk = xt::zeros<bool>({n_sit, n_ltm, n_msk, n_tim}); + XB4 c_msk = xt::zeros<bool>({n_sit, n_ltm, n_msk, n_tim}); if (m_cdt.size() > 0) { for (std::size_t s = 0; s < n_sit; s++) @@ -290,7 +304,7 @@ namespace evalhyd return c_msk; }; - const B4 c_msk = gen_msk(); + const XB4 c_msk = gen_msk(); // generate bootstrap experiment if requested std::vector<xt::xkeep_slice<int>> b_exp; @@ -341,7 +355,7 @@ namespace evalhyd xt::view(c_msk, s, l, xt::all(), xt::all()) : xt::view(t_msk_, s, l, xt::all(), xt::all())); - probabilist::Evaluator<D2, D4, B4> evaluator( + probabilist::Evaluator<XD2, XD4, XB4> evaluator( q_obs_v, q_prd_v, q_thr_v, t_msk_v, b_exp ); -- GitLab