diff --git a/include/evalhyd/detail/determinist/elements.hpp b/include/evalhyd/detail/determinist/elements.hpp
index fb8dc43fa7ee5dcfde87093cad0c960e97c2c5d5..876e1eca75ab82b8f61ff14d84a9959c948899f4 100644
--- a/include/evalhyd/detail/determinist/elements.hpp
+++ b/include/evalhyd/detail/determinist/elements.hpp
@@ -37,9 +37,9 @@ namespace evalhyd
             /// \return
             ///     Mean observed streamflow.
             ///     shape: (subsets, samples, series, 1)
-            template <class XD2>
+            template <class XD2o>
             inline xt::xtensor<double, 4> calc_mean_obs(
-                    const XD2& q_obs,
+                    const XD2o& q_obs,
                     const xt::xtensor<bool, 3>& t_msk,
                     const std::vector<xt::xkeep_slice<int>>& b_exp,
                     std::size_t n_srs,
@@ -90,9 +90,9 @@ namespace evalhyd
             /// \return
             ///     Mean predicted streamflow.
             ///     shape: (subsets, samples, series, 1)
-            template <class XD2>
+            template <class XD2p>
             inline xt::xtensor<double, 4> calc_mean_prd(
-                    const XD2& q_prd,
+                    const XD2p& q_prd,
                     const xt::xtensor<bool, 3>& t_msk,
                     const std::vector<xt::xkeep_slice<int>>& b_exp,
                     std::size_t n_srs,
@@ -134,10 +134,10 @@ namespace evalhyd
             /// \return
             ///     Quadratic errors between observations and predictions.
             ///     shape: (series, time)
-            template <class XD2>
+            template <class XD2o, class XD2p>
             inline xt::xtensor<double, 2> calc_quad_err(
-                    const XD2& q_obs,
-                    const XD2& q_prd
+                    const XD2o& q_obs,
+                    const XD2p& q_prd
             )
             {
                 return xt::square(q_obs - q_prd);
@@ -165,9 +165,9 @@ namespace evalhyd
             /// \return
             ///     Quadratic errors between observations and mean observation.
             ///     shape: (subsets, samples, series, time)
-            template <class XD2>
+            template <class XD2o>
             inline xt::xtensor<double, 4> calc_quad_obs(
-                    const XD2& q_obs,
+                    const XD2o& q_obs,
                     const xt::xtensor<double, 4>& mean_obs,
                     const xt::xtensor<bool, 3>& t_msk,
                     std::size_t n_srs,
@@ -219,9 +219,9 @@ namespace evalhyd
             /// \return
             ///     Quadratic errors between predictions and mean prediction.
             ///     shape: (subsets, samples, series, time)
-            template <class XD2>
+            template <class XD2p>
             inline xt::xtensor<double, 4> calc_quad_prd(
-                    const XD2& q_prd,
+                    const XD2p& q_prd,
                     const xt::xtensor<double, 4>& mean_prd,
                     const xt::xtensor<bool, 3>& t_msk,
                     std::size_t n_srs,
@@ -286,10 +286,10 @@ namespace evalhyd
             /// \return
             ///     Pearson correlation coefficients.
             ///     shape: (subsets, samples, series)
-            template <class XD2>
+            template <class XD2o, class XD2p>
             inline xt::xtensor<double, 3> calc_r_pearson(
-                    const XD2& q_obs,
-                    const XD2& q_prd,
+                    const XD2o& q_obs,
+                    const XD2p& q_prd,
                     const xt::xtensor<double, 4>& mean_obs,
                     const xt::xtensor<double, 4>& mean_prd,
                     const xt::xtensor<double, 4>& quad_obs,
@@ -368,10 +368,10 @@ namespace evalhyd
             /// \return
             ///     Alphas, ratios of standard deviations.
             ///     shape: (subsets, samples, series)
-            template <class XD2>
+            template <class XD2o, class XD2p>
             inline xt::xtensor<double, 3> calc_alpha(
-                    const XD2& q_obs,
-                    const XD2& q_prd,
+                    const XD2o& q_obs,
+                    const XD2p& q_prd,
                     const xt::xtensor<double, 4>& mean_obs,
                     const xt::xtensor<double, 4>& mean_prd,
                     const xt::xtensor<bool, 3>& t_msk,
@@ -431,10 +431,10 @@ namespace evalhyd
             /// \return
             ///     Biases.
             ///     shape: (subsets, samples, series)
-            template <class XD2>
+            template <class XD2o, class XD2p>
             inline xt::xtensor<double, 3> calc_bias(
-                    const XD2& q_obs,
-                    const XD2& q_prd,
+                    const XD2o& q_obs,
+                    const XD2p& 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 6314795e7096cfc29a69c988e5fcfdd0a5f64605..7d8220c8e84d2d79a13eb8faf6a6ae1a26d2001a 100644
--- a/include/evalhyd/detail/determinist/evaluator.hpp
+++ b/include/evalhyd/detail/determinist/evaluator.hpp
@@ -19,13 +19,13 @@ namespace evalhyd
 {
     namespace determinist
     {
-        template <class XD2, class XB3>
+        template <class XD2o, class XD2p, class XB3>
         class Evaluator
         {
         private:
             // members for input data
-            const XD2& q_obs;
-            const XD2& q_prd;
+            const XD2o& q_obs;
+            const XD2p& q_prd;
             // members for optional input data
             XB3 t_msk;
             const std::vector<xt::xkeep_slice<int>>& b_exp;
@@ -148,8 +148,8 @@ namespace evalhyd
 
         public:
             // constructor method
-            Evaluator(const XD2& obs,
-                      const XD2& prd,
+            Evaluator(const XD2o& obs,
+                      const XD2p& prd,
                       const XB3& msk,
                       const std::vector<xt::xkeep_slice<int>>& exp) :
                     q_obs{obs}, q_prd{prd}, t_msk{msk}, b_exp{exp}
@@ -181,6 +181,40 @@ namespace evalhyd
                 }
             };
 
+            Evaluator(XD2o&& obs,
+                      XD2p&& prd,
+                      const XB3& msk,
+                      const std::vector<xt::xkeep_slice<int>>& exp) :
+                    q_obs{obs}, q_prd{prd}, t_msk{msk}, b_exp{exp}
+            {
+                std::cout << "universal" << std::endl;
+                // initialise a mask if none provided
+                // (corresponding to no temporal subset)
+                if (msk.size() < 1)
+                {
+                    t_msk = xt::ones<bool>(
+                            {q_prd.shape(0), std::size_t {1}, q_prd.shape(1)}
+                    );
+                }
+
+                // determine size for recurring dimensions
+                n_srs = q_prd.shape(0);
+                n_tim = q_prd.shape(1);
+                n_msk = msk.shape(1);
+                n_exp = b_exp.size();
+
+                // drop time steps where observations or predictions are NaN
+                for (std::size_t s = 0; s < n_srs; s++)
+                {
+                    auto obs_nan = xt::isnan(xt::view(q_obs, 0));
+                    auto prd_nan = xt::isnan(xt::view(q_prd, s));
+
+                    auto msk_nan = xt::where(obs_nan || prd_nan)[0];
+
+                    xt::view(t_msk, s, xt::all(), xt::keep(msk_nan)) = false;
+                }
+            };
+
             // methods to compute metrics
             xt::xtensor<double, 3> get_RMSE()
             {
diff --git a/include/evalhyd/detail/probabilist/evaluator.hpp b/include/evalhyd/detail/probabilist/evaluator.hpp
index edae68bb644f9cd7f1aff94778a624e80f919fbe..00bf169744f4b9e10027e9edde79189dd0c59912 100644
--- a/include/evalhyd/detail/probabilist/evaluator.hpp
+++ b/include/evalhyd/detail/probabilist/evaluator.hpp
@@ -12,6 +12,7 @@
 #include <xtensor/xtensor.hpp>
 #include <xtensor/xview.hpp>
 
+#include "../determinist/evaluator.hpp"
 #include "brier.hpp"
 #include "quantiles.hpp"
 #include "contingency.hpp"
@@ -31,9 +32,29 @@ namespace evalhyd
             // members for optional input data
             const XD2& _q_thr;
             xtl::xoptional<const std::string, bool> _events;
+            xtl::xoptional<const std::string, bool> _reducer;
             XB4 t_msk;
             const std::vector<xt::xkeep_slice<int>>& b_exp;
 
+            // member to access determinist metrics
+            xtl::xoptional<xt::xtensor<double, 3>, bool> _q_prd_reduced;
+
+            using xv2d2 = decltype(xt::view(std::declval<const XD2&>(),
+                                            std::declval<std::size_t>(),
+                                            xt::newaxis(),
+                                            xt::all()));
+            using xv2d3 = decltype(xt::view(std::declval<xt::xtensor<double, 3>>(),
+                                            std::declval<std::size_t>(),
+                                            xt::all(),
+                                            xt::all()));
+            using xv3b4 = decltype(xt::view(std::declval<XB4&>(),
+                                            std::declval<std::size_t>(),
+                                            xt::all(),
+                                            xt::all(),
+                                            xt::all()));
+
+            std::vector<determinist::Evaluator<xv2d2, xv2d3, xv3b4>> _d_evaluators;
+
             // members for dimensions
             std::size_t n_sit;
             std::size_t n_ldt;
@@ -86,6 +107,11 @@ namespace evalhyd
             xtl::xoptional<xt::xtensor<double, 6>, bool> FAR;
             xtl::xoptional<xt::xtensor<double, 6>, bool> CSI;
             xtl::xoptional<xt::xtensor<double, 5>, bool> ROCSS;
+            // > Determinist-based
+            xtl::xoptional<xt::xtensor<double, 4>, bool> RMSE;
+            xtl::xoptional<xt::xtensor<double, 4>, bool> NSE;
+            xtl::xoptional<xt::xtensor<double, 4>, bool> KGE;
+            xtl::xoptional<xt::xtensor<double, 4>, bool> KGEPRIME;
 
             // methods to get optional parameters
             auto get_q_thr()
@@ -130,6 +156,53 @@ namespace evalhyd
                 }
             }
 
+            auto get_reducer()
+            {
+                if (_reducer.has_value())
+                {
+                    if (_reducer.value() == "median"
+                        || _reducer.value() == "mean")
+                    {
+                        return _reducer.value();
+                    }
+                    else
+                    {
+                        throw std::runtime_error(
+                                "invalid value for member *reducer*: "
+                                + _events.value()
+                        );
+                    }
+                }
+                else
+                {
+                    throw std::runtime_error(
+                            "determinist metric requested, "
+                            "but member *reducer* not provided"
+                    );
+                }
+            }
+
+            auto get_d_evaluators()
+            {
+                if (_d_evaluators.empty())
+                {
+                    // initialise all determinist evaluators
+                    for (std::size_t s = 0; s < n_sit; s++)
+                    {
+                        xv2d2 obs = xt::view(q_obs, s, xt::newaxis(), xt::all());
+                        xv2d3 prd = xt::view(get_q_prd_reduced(), s, xt::all(), xt::all());
+                        xv3b4 msk = xt::view(t_msk, s, xt::all(), xt::all(), xt::all());
+
+                        determinist::Evaluator<xv2d2, xv2d3, xv3b4> evaluator(
+                                obs, prd, msk, b_exp
+                        );
+
+                        _d_evaluators.push_back(std::move(evaluator));
+                    }
+                }
+                return _d_evaluators;
+            }
+
             // methods to compute elements
             xt::xtensor<double, 3> get_o_k()
             {
@@ -242,6 +315,22 @@ namespace evalhyd
                 return ct_d.value();
             };
 
+            xt::xtensor<double, 3> get_q_prd_reduced()
+            {
+                if (!_q_prd_reduced.has_value())
+                {
+                    if (get_reducer() == "median")
+                    {
+                        _q_prd_reduced = xt::median(q_prd, 2);
+                    }
+                    else // (get_reducer() == "mean")
+                    {
+                        _q_prd_reduced = xt::mean(q_prd, 2);
+                    }
+                }
+                return _q_prd_reduced.value();
+            }
+
             // methods to compute intermediate metrics
             xt::xtensor<double, 4> get_bs()
             {
@@ -327,9 +416,10 @@ namespace evalhyd
                       const XD2& thr,
                       xtl::xoptional<const std::string&, bool> events,
                       const XB4& msk,
-                      const std::vector<xt::xkeep_slice<int>>& exp) :
-                    q_obs{obs}, q_prd{prd},
-                    _q_thr{thr}, _events{events}, t_msk(msk), b_exp(exp)
+                      const std::vector<xt::xkeep_slice<int>>& exp,
+                      xtl::xoptional<const std::string&, bool> reducer) :
+                    q_obs{obs}, q_prd{prd}, _q_thr{thr}, _events{events},
+                    t_msk(msk), b_exp(exp), _reducer{reducer}
             {
                 // initialise a mask if none provided
                 // (corresponding to no temporal subset)
@@ -511,6 +601,70 @@ namespace evalhyd
                 }
                 return ROCSS.value();
             };
+
+            xt::xtensor<double, 4> get_RMSE()
+            {
+                if (!RMSE.has_value())
+                {
+                    RMSE = xt::zeros<double>({n_sit, n_ldt, n_msk, n_exp});
+                    
+                    auto evaluators = get_d_evaluators();
+
+                    for (std::size_t s = 0; s < n_sit; s++)
+                    {
+                        xt::view(RMSE.value(), s) = evaluators[s].get_RMSE();
+                    }
+                }
+                return RMSE.value();
+            };
+
+            xt::xtensor<double, 4> get_NSE()
+            {
+                if (!NSE.has_value())
+                {
+                    NSE = xt::zeros<double>({n_sit, n_ldt, n_msk, n_exp});
+
+                    auto evaluators = get_d_evaluators();
+
+                    for (std::size_t s = 0; s < n_sit; s++)
+                    {
+                        xt::view(NSE.value(), s) = evaluators[s].get_NSE();
+                    }
+                }
+                return NSE.value();
+            };
+
+            xt::xtensor<double, 4> get_KGE()
+            {
+                if (!KGE.has_value())
+                {
+                    KGE = xt::zeros<double>({n_sit, n_ldt, n_msk, n_exp});
+
+                    auto evaluators = get_d_evaluators();
+
+                    for (std::size_t s = 0; s < n_sit; s++)
+                    {
+                        xt::view(KGE.value(), s) = evaluators[s].get_KGE();
+                    }
+                }
+                return KGE.value();
+            };
+
+            xt::xtensor<double, 4> get_KGEPRIME()
+            {
+                if (!KGEPRIME.has_value())
+                {
+                    KGEPRIME = xt::zeros<double>({n_sit, n_ldt, n_msk, n_exp});
+
+                    auto evaluators = get_d_evaluators();
+
+                    for (std::size_t s = 0; s < n_sit; s++)
+                    {
+                        xt::view(KGEPRIME.value(), s) = evaluators[s].get_KGEPRIME();
+                    }
+                }
+                return KGEPRIME.value();
+            };
         };
     }
 }
diff --git a/include/evalhyd/evald.hpp b/include/evalhyd/evald.hpp
index 31bc5440410e44783d7af37c80795a2549f14866..825086b5424838f477a0492e1e3f15490eb098db 100644
--- a/include/evalhyd/evald.hpp
+++ b/include/evalhyd/evald.hpp
@@ -397,7 +397,7 @@ namespace evalhyd
         }
 
         // instantiate determinist evaluator
-        determinist::Evaluator<XD2, XB3> evaluator(
+        determinist::Evaluator<XD2, XD2, XB3> evaluator(
                 obs, prd,
                 t_msk_.size() > 0 ? t_msk_: (m_cdt.size() > 0 ? c_msk : t_msk_),
                 exp
diff --git a/include/evalhyd/evalp.hpp b/include/evalhyd/evalp.hpp
index 6c52fac81e46400fd108bdfd386a483b3f40948a..012e5115dd03c43884434f0678055c48d85ed791 100644
--- a/include/evalhyd/evalp.hpp
+++ b/include/evalhyd/evalp.hpp
@@ -73,6 +73,12 @@ namespace evalhyd
     ///       occurring when streamflow goes below threshold). It must be
     ///       provided if *q_thr* is provided.
     ///
+    ///    reducer: ``std::string``, optional
+    ///       The type of reduction to apply to the prediction ensemble members
+    ///       for the computation of deterministic metrics. It can either be set
+    ///       as "median" or as "mean". It must be provided in case *metrics*
+    ///       contains deterministic-based metrics.
+    ///
     ///    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
@@ -163,7 +169,9 @@ namespace evalhyd
             const xt::xtensor<std::array<char, 32>, 2>& m_cdt = {},
             xtl::xoptional<const std::unordered_map<std::string, int>, bool> bootstrap =
                     xtl::missing<const std::unordered_map<std::string, int>>(),
-            const std::vector<std::string>& dts = {}
+            const std::vector<std::string>& dts = {},
+            xtl::xoptional<const std::string, bool> reducer =
+                    xtl::missing<const std::string>()
     )
     {
         // check ranks of tensors
@@ -198,7 +206,8 @@ namespace evalhyd
                 metrics,
                 {"BS", "BSS", "BS_CRD", "BS_LBD",
                  "QS", "CRPS",
-                 "POD", "POFD", "FAR", "CSI", "ROCSS"}
+                 "POD", "POFD", "FAR", "CSI", "ROCSS",
+                 "RMSE", "NSE", "KGE", "KGEPRIME"}
         );
 
         // check optional parameters
@@ -362,7 +371,7 @@ namespace evalhyd
         probabilist::Evaluator<XD2, XD4, XB4> evaluator(
                 q_obs_, q_prd_, q_thr_, events,
                 t_msk_.size() > 0 ? t_msk_: (m_cdt.size() > 0 ? c_msk : t_msk_),
-                b_exp
+                b_exp, reducer
         );
 
         // initialise data structure for outputs
@@ -436,6 +445,30 @@ namespace evalhyd
                         uncertainty::summarise(evaluator.get_ROCSS(), summary)
                 );
             }
+            else if ( metric == "RMSE" )
+            {
+                r.emplace_back(
+                        uncertainty::summarise(evaluator.get_RMSE(), summary)
+                );
+            }
+            else if ( metric == "NSE" )
+            {
+                r.emplace_back(
+                        uncertainty::summarise(evaluator.get_NSE(), summary)
+                );
+            }
+            else if ( metric == "KGE" )
+            {
+                r.emplace_back(
+                        uncertainty::summarise(evaluator.get_KGE(), summary)
+                );
+            }
+            else if ( metric == "KGEPRIME" )
+            {
+                r.emplace_back(
+                        uncertainty::summarise(evaluator.get_KGEPRIME(), summary)
+                );
+            }
         }
 
         return r;