diff --git a/CHANGELOG b/CHANGELOG
index 4e3704a6af90c0243fe62eba7fc33de83b6778ce..a39c2dc3cf3ddbb02711ae82d7e893a91faf34d4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
 > API changes:
      1) Python: major changes in SpecularSimulation interface. Please consult web-documentation for details.
+     2) GUI: project compatibility with previous versions is broken.
 
 BornAgain-1.15.0, released 2019.02.25
   > API changes:
diff --git a/Core/Fitting/FitObjective.cpp b/Core/Fitting/FitObjective.cpp
index 29fb54ade3fa0c036d51e7d2b2ee3885d32a44d3..0dc1296d31fc10eb61f0d21cee3699c77a63e01c 100644
--- a/Core/Fitting/FitObjective.cpp
+++ b/Core/Fitting/FitObjective.cpp
@@ -18,6 +18,7 @@
 #include "FitStatus.h"
 #include "MinimizerResult.h"
 #include "ObjectiveMetric.h"
+#include "ObjectiveMetricUtils.h"
 #include "Parameters.h"
 #include "PyFittingCallbacks.h"
 #include "Simulation.h"
@@ -200,14 +201,30 @@ void FitObjective::run_simulations(const Fit::Parameters& params)
 
 void FitObjective::setChiSquaredModule(const IChiSquaredModule& module)
 {
+    std::cout << "Warning in FitObjective::setChiSquaredModule: setChiSquaredModule is deprecated "
+                 "and will be removed in future versions. Please use "
+                 "FitObjective::setObjectiveMetric instead."
+              << std::endl;
+
     std::unique_ptr<IChiSquaredModule> chi_module(module.clone());
     m_metric_module = std::make_unique<ChiModuleWrapper>(std::move(chi_module));
 }
 
+void FitObjective::setObjectiveMetric(std::unique_ptr<ObjectiveMetric> metric)
+{
+    m_metric_module = std::make_unique<ObjectiveMetricWrapper>(std::move(metric));
+}
+
+void FitObjective::setObjectiveMetric(const std::string& metric)
+{
+    m_metric_module = std::make_unique<ObjectiveMetricWrapper>(
+        ObjectiveMetricUtils::createMetric(metric, ObjectiveMetricUtils::defaultNormName()));
+}
+
 void FitObjective::setObjectiveMetric(const std::string& metric, const std::string& norm)
 {
     m_metric_module =
-        std::make_unique<ObjectiveMetricWrapper>(ObjectiveMetric::createMetric(metric, norm));
+        std::make_unique<ObjectiveMetricWrapper>(ObjectiveMetricUtils::createMetric(metric, norm));
 }
 
 std::vector<double> FitObjective::composeArray(DataPairAccessor getter) const
diff --git a/Core/Fitting/FitObjective.h b/Core/Fitting/FitObjective.h
index e536b23991c0b87ae80dc8895adab190f0191144..717336b7345ec5686ee76aedc8e140b90ac447cf 100644
--- a/Core/Fitting/FitObjective.h
+++ b/Core/Fitting/FitObjective.h
@@ -25,6 +25,7 @@
 class FitStatus;
 class IChiSquaredModule;
 class IMetricWrapper;
+class ObjectiveMetric;
 class PyBuilderCallback;
 class PyObserverCallback;
 
@@ -130,10 +131,15 @@ public:
 
     void setChiSquaredModule(const IChiSquaredModule& module);
 
+#ifndef SWIG
+    void setObjectiveMetric(std::unique_ptr<ObjectiveMetric> metric);
+#endif //SWIG
+
+    void setObjectiveMetric(const std::string& metric);
     //! Sets objective metric to the FitObjective.
     //! @param metric: metric name
-    //! @param norm: metric norm name (defaults to "l2")
-    void setObjectiveMetric(const std::string& metric, const std::string& norm = "l2");
+    //! @param norm: metric norm name (defaults to L2-norm)
+    void setObjectiveMetric(const std::string& metric, const std::string& norm);
 
 private:
     typedef std::vector<double> (SimDataPair::*DataPairAccessor)() const;
diff --git a/Core/Fitting/ObjectiveMetric.cpp b/Core/Fitting/ObjectiveMetric.cpp
index 60c094f97a1d6f3b4642d410625d0cd520133575..c17cb2fb6425d0202542b4773d4d505d995d3aef 100644
--- a/Core/Fitting/ObjectiveMetric.cpp
+++ b/Core/Fitting/ObjectiveMetric.cpp
@@ -13,6 +13,7 @@
 // ************************************************************************** //
 
 #include "ObjectiveMetric.h"
+#include "ObjectiveMetricUtils.h"
 #include "OutputData.h"
 #include "SimDataPair.h"
 #include <cmath>
@@ -23,18 +24,6 @@ const double double_max = std::numeric_limits<double>::max();
 const double double_min = std::numeric_limits<double>::min();
 const double ln10 = std::log(10.0);
 
-const std::map<std::string, std::function<std::unique_ptr<ObjectiveMetric>()>> metric_factory = {
-    {"Chi2", []() { return std::make_unique<Chi2Metric>(); }},
-    {"PoissonLike", []() { return std::make_unique<PoissonLikeMetric>(); }},
-    {"Log", []() { return std::make_unique<LogMetric>(); }},
-    {"RelativeDifference", []() { return std::make_unique<RelativeDifferenceMetric>(); }},
-    {"RQ4", []() { return std::make_unique<RQ4Metric>(); }}
-};
-
-const std::map<std::string, std::function<double(double)>> norm_factory = {
-    {"l1", ObjectiveMetric::l1_norm}, {"l2", ObjectiveMetric::l2_norm}
-};
-
 template<class T>
 T* copyMetric(const T& metric)
 {
@@ -69,37 +58,6 @@ void checkIntegrity(const std::vector<double>& sim_data, const std::vector<doubl
 }
 }
 
-const std::function<double(double)> ObjectiveMetric::l1_norm = [](double term) {
-    return std::abs(term);
-};
-
-const std::function<double(double)> ObjectiveMetric::l2_norm = [](double term) {
-    return term * term;
-};
-
-std::unique_ptr<ObjectiveMetric> ObjectiveMetric::createMetric(const std::string& metric,
-                                                               const std::string& norm)
-{
-    const auto metric_iter = metric_factory.find(metric);
-    const auto norm_iter = norm_factory.find(norm);
-    if (metric_iter == metric_factory.end() || norm_iter == norm_factory.end()) {
-        std::stringstream ss;
-        ss << "Error in ObjectiveMetric::createMetric: either metric (" << metric << ") or norm ("
-           << norm << ") name is unknown.\n";
-        ss << "Available metrics:\n";
-        for (auto& item: metric_factory)
-            ss << "\t" << item.first << "\n";
-        ss << "Available norms:\n";
-        for (auto& item: norm_factory)
-            ss << "\t" << item.first << "\n";
-        throw std::runtime_error(ss.str());
-    }
-
-    auto result = metric_iter->second();
-    result->setNorm(norm_iter->second);
-    return result;
-}
-
 ObjectiveMetric::ObjectiveMetric(std::function<double(double)> norm)
     : m_norm(std::move(norm))
 {}
@@ -126,7 +84,7 @@ void ObjectiveMetric::setNorm(std::function<double(double)> norm)
 // ----------------------- Chi2 metric ---------------------------
 
 Chi2Metric::Chi2Metric()
-    : ObjectiveMetric(l2_norm)
+    : ObjectiveMetric(ObjectiveMetricUtils::l2Norm())
 {}
 
 Chi2Metric* Chi2Metric::clone() const
@@ -197,7 +155,7 @@ double PoissonLikeMetric::computeFromArrays(std::vector<double> sim_data,
 // ----------------------- Log metric ---------------------------
 
 LogMetric::LogMetric()
-    : ObjectiveMetric(l2_norm)
+    : ObjectiveMetric(ObjectiveMetricUtils::l2Norm())
 {}
 
 LogMetric* LogMetric::clone() const
diff --git a/Core/Fitting/ObjectiveMetric.h b/Core/Fitting/ObjectiveMetric.h
index 132b56c3f48b6ca79a0389c075c44f035aaae2af..59991282095ea2818f7365bcd8a0842f8394dc54 100644
--- a/Core/Fitting/ObjectiveMetric.h
+++ b/Core/Fitting/ObjectiveMetric.h
@@ -26,14 +26,6 @@ class SimDataPair;
 class BA_CORE_API_ ObjectiveMetric : public ICloneable
 {
 public:
-    //! L1 normalization function.
-    static const std::function<double(double)> l1_norm;
-    //! L2 normalization function.
-    static const std::function<double(double)> l2_norm;
-
-    static std::unique_ptr<ObjectiveMetric> createMetric(const std::string& metric,
-                                                         const std::string& norm);
-
     ObjectiveMetric(std::function<double(double)> norm);
 
     ObjectiveMetric* clone() const override = 0;
diff --git a/Core/Fitting/ObjectiveMetricUtils.cpp b/Core/Fitting/ObjectiveMetricUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5afa423e4df251cb6b143472cff1b6a86111dcc
--- /dev/null
+++ b/Core/Fitting/ObjectiveMetricUtils.cpp
@@ -0,0 +1,117 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Core/Fitting/ObjectiveMetricUtils.cpp
+//! @brief     Implements ObjectiveMetric utilities.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#include "ObjectiveMetricUtils.h"
+#include "ObjectiveMetric.h"
+#include <algorithm>
+#include <cmath>
+#include <map>
+#include <sstream>
+
+namespace {
+const std::function<double(double)> l1_norm = [](double term) { return std::abs(term); };
+const std::function<double(double)> l2_norm = [](double term) { return term * term; };
+
+const std::map<std::string, std::function<std::unique_ptr<ObjectiveMetric>()>> metric_factory = {
+    {"chi2", []() { return std::make_unique<Chi2Metric>(); }},
+    {"poisson-like", []() { return std::make_unique<PoissonLikeMetric>(); }},
+    {"log", []() { return std::make_unique<LogMetric>(); }},
+    {"reldiff", []() { return std::make_unique<RelativeDifferenceMetric>(); }},
+    {"rq4", []() { return std::make_unique<RQ4Metric>(); }}
+};
+const std::string default_metric_name = "poisson-like";
+
+const std::map<std::string, std::function<double(double)>> norm_factory = {{"l1", l1_norm},
+                                                                           {"l2", l2_norm}};
+const std::string default_norm_name = "l2";
+
+template<class U>
+std::vector<std::string> keys(const std::map<std::string, U>& map)
+{
+    std::vector<std::string> result;
+    result.reserve(map.size());
+    for (auto& item: map)
+        result.push_back(item.first);
+    return result;
+}
+} // namespace
+
+const std::function<double(double)> ObjectiveMetricUtils::l1Norm()
+{
+    return l1_norm;
+}
+
+const std::function<double(double)> ObjectiveMetricUtils::l2Norm()
+{
+    return l2_norm;
+}
+
+std::unique_ptr<ObjectiveMetric> ObjectiveMetricUtils::createMetric(const std::string& metric)
+{
+    return createMetric(metric, defaultNormName());
+}
+
+std::unique_ptr<ObjectiveMetric> ObjectiveMetricUtils::createMetric(std::string metric,
+                                                                    std::string norm)
+{
+    std::transform(metric.begin(), metric.end(), metric.begin(), ::tolower);
+    std::transform(norm.begin(), norm.end(), norm.begin(), ::tolower);
+    const auto metric_iter = metric_factory.find(metric);
+    const auto norm_iter = norm_factory.find(norm);
+    if (metric_iter == metric_factory.end() || norm_iter == norm_factory.end()) {
+        std::stringstream ss;
+        ss << "Error in ObjectiveMetricUtils::createMetric: either metric (" << metric
+           << ") or norm (" << norm << ") name is unknown.\n";
+        ss << availableMetricOptions();
+        throw std::runtime_error(ss.str());
+    }
+
+    auto result = metric_iter->second();
+    result->setNorm(norm_iter->second);
+    return result;
+}
+
+std::string ObjectiveMetricUtils::availableMetricOptions()
+{
+    std::stringstream ss;
+    ss << "Available metrics:\n";
+    for (auto& item: metricNames())
+        ss << "\t" << item << "\n";
+    ss << "default metric: " << defaultMetricName() << "\n";
+    ss << "Available norms:\n";
+    for (auto& item: normNames())
+        ss << "\t" << item << "\n";
+    ss << "default norm: " << defaultNormName() << "\n";
+    return ss.str();
+}
+
+std::vector<std::string> ObjectiveMetricUtils::normNames()
+{
+    return keys(norm_factory);
+}
+
+std::vector<std::string> ObjectiveMetricUtils::metricNames()
+{
+    return keys(metric_factory);
+}
+
+std::string ObjectiveMetricUtils::defaultNormName()
+{
+    return default_norm_name;
+}
+
+std::string ObjectiveMetricUtils::defaultMetricName()
+{
+    return default_metric_name;
+}
diff --git a/Core/Fitting/ObjectiveMetricUtils.h b/Core/Fitting/ObjectiveMetricUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..8c57f65caec9c1a3f3ff05023934e825ae278707
--- /dev/null
+++ b/Core/Fitting/ObjectiveMetricUtils.h
@@ -0,0 +1,55 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Core/Fitting/ObjectiveMetricUtils.h
+//! @brief     Defines ObjectiveMetric utilities and corresponding namespace.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#ifndef OBJECTIVEMETRICUTILS_H
+#define OBJECTIVEMETRICUTILS_H
+
+#include "WinDllMacros.h"
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+class ObjectiveMetric;
+
+namespace ObjectiveMetricUtils
+{
+//! Returns L1 normalization function.
+BA_CORE_API_ const std::function<double(double)> l1Norm();
+//! Returns L2 normalization function.
+BA_CORE_API_ const std::function<double(double)> l2Norm();
+
+//! Creates the specified metric with the default norm.
+BA_CORE_API_ std::unique_ptr<ObjectiveMetric> createMetric(const std::string& metric);
+
+//! Creates the metric with the specified norm
+BA_CORE_API_ std::unique_ptr<ObjectiveMetric> createMetric(std::string metric, std::string norm);
+
+//! Prints available metric options
+BA_CORE_API_ std::string availableMetricOptions();
+
+//! Returns the names of the norms used by ObjectiveMetric.
+BA_CORE_API_ std::vector<std::string> normNames();
+
+//! Returns the names of the objective metrics used.
+BA_CORE_API_ std::vector<std::string> metricNames();
+
+//! Returns default norm name.
+BA_CORE_API_ std::string defaultNormName();
+
+//! Returns default metric name.
+BA_CORE_API_ std::string defaultMetricName();
+} // namespace ObjectiveMetricUtils
+
+#endif // OBJECTIVEMETRICUTILS_H
diff --git a/Fit/Kernel/Kernel.cpp b/Fit/Kernel/Kernel.cpp
index 7bb064e2d6ac7c8d3af7fb3b4f1230eebbae5105..521e58873485f92d406b834033b6bfdf1691eaec 100644
--- a/Fit/Kernel/Kernel.cpp
+++ b/Fit/Kernel/Kernel.cpp
@@ -47,6 +47,10 @@ MinimizerResult Kernel::minimize(fcn_scalar_t fcn, const Parameters& parameters)
     setParameters(parameters);
 
     m_time_interval.start();
+    if (m_minimizer->requiresResiduals())
+        throw std::runtime_error(
+            "Error in Kernel::minimize: the chosen minimizer requires residuals computation. "
+            "Please use FitObjective::evaluate_residuals with this minimizer.");
     auto result = m_minimizer->minimize_scalar(fcn, parameters);
     m_time_interval.stop();
 
diff --git a/Fit/Minimizer/IMinimizer.h b/Fit/Minimizer/IMinimizer.h
index 179cd239adff86bb93643ad0af4f941294c5ba19..b77642898ec0871f5865e575fe4aabeab34e900b 100644
--- a/Fit/Minimizer/IMinimizer.h
+++ b/Fit/Minimizer/IMinimizer.h
@@ -55,6 +55,11 @@ public:
 
     //! Sets option string to the minimizer
     virtual void setOptions(const std::string& options);
+
+#ifndef SWIG
+    //! Returns true if minimizer computations are residual-based, false otherwise
+    virtual bool requiresResiduals() { return false; }
+#endif //SWIG
 };
 
 #endif // IMINIMIZER_H
diff --git a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp
index 218245fc8999be6af134b359ece6c1bb1322127d..d162e8967e9c136ed1fb2e5ff5b8f061ec9a9879 100644
--- a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp
+++ b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.cpp
@@ -100,8 +100,6 @@ std::map<std::string, std::string> GSLLevenbergMarquardtMinimizer::statusMap() c
     return result;
 }
 
-bool GSLLevenbergMarquardtMinimizer::isGradientBasedAgorithm() { return true; }
-
 void GSLLevenbergMarquardtMinimizer::propagateOptions()
 {
     m_gsl_minimizer->SetTolerance(tolerance());
diff --git a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h
index 9fb2d2e93f29591bf2e02d887aa54526241b4343..cc8dc32603b5d8e88867b703c1e4da11d7450c4b 100644
--- a/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h
+++ b/Fit/RootAdapter/GSLLevenbergMarquardtMinimizer.h
@@ -28,7 +28,7 @@ class BA_CORE_API_ GSLLevenbergMarquardtMinimizer : public RootMinimizerAdapter
 {
 public:
     GSLLevenbergMarquardtMinimizer();
-    ~GSLLevenbergMarquardtMinimizer();
+    ~GSLLevenbergMarquardtMinimizer() override;
 
     //!< Sets tolerance on the function value at the minimum.
     void setTolerance(double value);
@@ -47,8 +47,9 @@ public:
     std::string statusToString() const override;
     std::map<std::string, std::string> statusMap() const override;
 
+    bool requiresResiduals() override { return true; }
+
 protected:
-    virtual bool isGradientBasedAgorithm() override;
     void propagateOptions() override;
     const root_minimizer_t* rootMinimizer() const override;
     void setParameter(unsigned int index, const Fit::Parameter& par) override;
diff --git a/Fit/RootAdapter/Minuit2Minimizer.cpp b/Fit/RootAdapter/Minuit2Minimizer.cpp
index 852a7d4fef4831e8505dcfa5dec880b7e3143c1d..1885f4e2993fd6d06480595797ca8768608fd3dd 100644
--- a/Fit/RootAdapter/Minuit2Minimizer.cpp
+++ b/Fit/RootAdapter/Minuit2Minimizer.cpp
@@ -136,12 +136,9 @@ std::map<std::string, std::string> Minuit2Minimizer::statusMap() const
 // Fumili algorithm can work only with gradient based objective function, while others can
 // work with both, gradient based and chi2 based functions. Historically however, we use
 // simplified approach: if not Fumili, then chi2 only. Think of refactoring TODO.
-bool Minuit2Minimizer::isGradientBasedAgorithm()
+bool Minuit2Minimizer::requiresResiduals()
 {
-    if (algorithmName() == AlgorithmNames::Fumili)
-        return true;
-
-    return false;
+    return algorithmName() == AlgorithmNames::Fumili;
 }
 
 //! Propagate options down to ROOT's Minuit2Minimizer.
diff --git a/Fit/RootAdapter/Minuit2Minimizer.h b/Fit/RootAdapter/Minuit2Minimizer.h
index 6595430c539cc755a6fdea79b45d252898dd5904..071ec8cd6e1ec356736134ad7f9bb4b6ddf78e2d 100644
--- a/Fit/RootAdapter/Minuit2Minimizer.h
+++ b/Fit/RootAdapter/Minuit2Minimizer.h
@@ -69,8 +69,9 @@ public:
     std::string statusToString() const override;
     std::map<std::string, std::string> statusMap() const override;
 
+    bool requiresResiduals() override;
+
 protected:
-    bool isGradientBasedAgorithm() override;
     void propagateOptions() override;
     const root_minimizer_t* rootMinimizer() const override;
 
diff --git a/Fit/RootAdapter/RootMinimizerAdapter.h b/Fit/RootAdapter/RootMinimizerAdapter.h
index 4318c8acd89ab815f6326648e6df6b1aeed34f98..08e2ea8b7f9fe5113157644825e975b2f6928a21 100644
--- a/Fit/RootAdapter/RootMinimizerAdapter.h
+++ b/Fit/RootAdapter/RootMinimizerAdapter.h
@@ -72,7 +72,6 @@ protected:
 
     void propagateResults(Fit::Parameters& parameters);
 
-    virtual bool isGradientBasedAgorithm() { return false; }
     virtual void setParameter(unsigned int index, const Fit::Parameter& par);
     size_t fitDimension() const;
     std::vector<double> parValuesAtMinimum() const;
diff --git a/Fit/RootAdapter/SimAnMinimizer.cpp b/Fit/RootAdapter/SimAnMinimizer.cpp
index f1dd4f31b7f00879ae2ac1ba167ab5b376af47e0..929233406a154ceb0f2f06246d302d5945aac987 100644
--- a/Fit/RootAdapter/SimAnMinimizer.cpp
+++ b/Fit/RootAdapter/SimAnMinimizer.cpp
@@ -131,11 +131,6 @@ std::map<std::string, std::string> SimAnMinimizer::statusMap() const
     return result;
 }
 
-bool SimAnMinimizer::isGradientBasedAgorithm()
-{
-    return false;
-}
-
 void SimAnMinimizer::propagateOptions()
 {
     ROOT::Math::GSLSimAnParams& pars = m_siman_minimizer->getSolver().Params();
diff --git a/Fit/RootAdapter/SimAnMinimizer.h b/Fit/RootAdapter/SimAnMinimizer.h
index d556d0fb7a6a5ece63f111cc1c530dc9a2525855..5bbb2ae8807ff84347c38dea98cdeccf3c27d2d9 100644
--- a/Fit/RootAdapter/SimAnMinimizer.h
+++ b/Fit/RootAdapter/SimAnMinimizer.h
@@ -26,7 +26,7 @@ class BA_CORE_API_ SimAnMinimizer : public RootMinimizerAdapter
 {
 public:
     SimAnMinimizer();
-    ~SimAnMinimizer();
+    ~SimAnMinimizer() override;
 
     //! Sets minimizer internal print level.
     //! Default value is 0 (silent).
@@ -62,7 +62,6 @@ public:
     double boltzmannMinTemp() const;
 
     std::map<std::string, std::string> statusMap() const override;
-    virtual bool isGradientBasedAgorithm() override;
 
 protected:
     void propagateOptions() override;
diff --git a/GUI/coregui/Models/ItemCatalogue.cpp b/GUI/coregui/Models/ItemCatalogue.cpp
index 4721fa00d2d8203ad5af7e3fbb94cd4c1f7fb6b0..fa616a20b6c5872fa169f1c0abc8d38500bd1bba 100644
--- a/GUI/coregui/Models/ItemCatalogue.cpp
+++ b/GUI/coregui/Models/ItemCatalogue.cpp
@@ -64,7 +64,6 @@
 #include "SpecularDataItem.h"
 #include "SphericalDetectorItem.h"
 #include "TransformationItem.h"
-#include "VarianceFunctionItems.h"
 #include "VectorItem.h"
 
 ItemCatalogue::ItemCatalogue()
@@ -244,9 +243,6 @@ ItemCatalogue::ItemCatalogue()
     add(Constants::ProjectionContainerType, create_new<ProjectionContainerItem>);
 
     add(Constants::DepthProbeInstrumentType, create_new<DepthProbeInstrumentItem>);
-
-    add(Constants::VarianceConstantFunctionType, create_new<VarianceConstantFunctionItem>);
-    add(Constants::VarianceSimFunctionType, create_new<VarianceSimFunctionItem>);
 }
 
 bool ItemCatalogue::contains(const QString& modelType) const
diff --git a/GUI/coregui/Models/MinimizerItem.cpp b/GUI/coregui/Models/MinimizerItem.cpp
index 11e130ac802eba9e64f9128d1f1c50b60b3d4a9b..854faacbbcff81b1d9537cc9c28ef6517777fb78 100644
--- a/GUI/coregui/Models/MinimizerItem.cpp
+++ b/GUI/coregui/Models/MinimizerItem.cpp
@@ -13,49 +13,45 @@
 // ************************************************************************** //
 
 #include "MinimizerItem.h"
-#include "MinimizerItemCatalogue.h"
-#include "MinimizerConstants.h"
-#include "Minuit2Minimizer.h"
+#include "GSLLevenbergMarquardtMinimizer.h"
 #include "GSLMultiMinimizer.h"
 #include "GeneticMinimizer.h"
-#include "GSLLevenbergMarquardtMinimizer.h"
+#include "MinimizerConstants.h"
+#include "MinimizerItemCatalogue.h"
+#include "Minuit2Minimizer.h"
+#include "ObjectiveMetric.h"
+#include "ObjectiveMetricUtils.h"
 #include "SimAnMinimizer.h"
 #include "TestMinimizer.h"
-#include "IIntensityFunction.h"
-#include "VarianceFunctions.h"
-#include "VarianceFunctionItems.h"
-
-namespace  {
-const QString none_fun = "None";
-const QString sqrt_fun = "sqrt";
-const QString log10_fun = "log";
-}
-
-// ----------------------------------------------------------------------------
 
-MinimizerItem::MinimizerItem(const QString &model_type) : SessionItem(model_type)
-{
-}
+MinimizerItem::MinimizerItem(const QString& model_type) : SessionItem(model_type) {}
 
 // ----------------------------------------------------------------------------
 
 const QString MinimizerContainerItem::P_MINIMIZERS = "Minimizer";
-const QString MinimizerContainerItem::P_INTENSITY_FUNCTION = "Intensity function";
-const QString MinimizerContainerItem::P_VARIANCE_FUNCTIONS = "Variance";
+const QString MinimizerContainerItem::P_METRIC = "Objective metric";
+const QString MinimizerContainerItem::P_NORM = "Norm function";
 
 MinimizerContainerItem::MinimizerContainerItem() : MinimizerItem(Constants::MinimizerContainerType)
 {
     addGroupProperty(P_MINIMIZERS, Constants::MinimizerLibraryGroup)
         ->setToolTip(QStringLiteral("Minimizer library"));
 
-    ComboProperty combo = ComboProperty() << none_fun << sqrt_fun << log10_fun;
-    addProperty(P_INTENSITY_FUNCTION, combo.variant())->setToolTip(
-                "Function to apply for both simulated and experimental intensities \n"
-                "before calculating the value of residual.");
-
-    addGroupProperty(P_VARIANCE_FUNCTIONS, Constants::VarianceFunctionGroup)
-        ->setToolTip(QStringLiteral("Variance functions for residual normalization"));
+    ComboProperty metric_combo;
+    for (auto& item : ObjectiveMetricUtils::metricNames())
+        metric_combo << QString::fromStdString(item);
+    metric_combo.setValue(QString::fromStdString(ObjectiveMetricUtils::defaultMetricName()));
+    addProperty(P_METRIC, metric_combo.variant())
+        ->setToolTip("Objective metric to use for estimating distance between simulated and "
+                     "experimental data.");
 
+    ComboProperty norm_combo;
+    for (auto& item : ObjectiveMetricUtils::normNames())
+        norm_combo << QString::fromStdString(item);
+    norm_combo.setValue(QString::fromStdString(ObjectiveMetricUtils::defaultNormName()));
+    addProperty(P_NORM, norm_combo.variant())
+        ->setToolTip("Normalization to use for estimating distance between simulated and "
+                     "experimental data.");
 }
 
 std::unique_ptr<IMinimizer> MinimizerContainerItem::createMinimizer() const
@@ -63,23 +59,11 @@ std::unique_ptr<IMinimizer> MinimizerContainerItem::createMinimizer() const
     return groupItem<MinimizerItem>(P_MINIMIZERS).createMinimizer();
 }
 
-std::unique_ptr<IIntensityFunction> MinimizerContainerItem::createIntensityFunction() const
-{
-    QString value = getItemValue(P_INTENSITY_FUNCTION).value<ComboProperty>().getValue();
-
-    if (value == sqrt_fun) {
-        return std::make_unique<IntensityFunctionSqrt>();
-    } else if(value == log10_fun) {
-        return std::make_unique<IntensityFunctionLog>();
-    } else {
-        return std::unique_ptr<IIntensityFunction>();
-    }
-}
-
-std::unique_ptr<IVarianceFunction> MinimizerContainerItem::createVarianceFunction() const
+std::unique_ptr<ObjectiveMetric> MinimizerContainerItem::createMetric() const
 {
-    auto& variance_item = groupItem<IVarianceFunctionItem>(P_VARIANCE_FUNCTIONS);
-    return variance_item.createVarianceFunction();
+    QString metric = getItemValue(P_METRIC).value<ComboProperty>().getValue();
+    QString norm = getItemValue(P_NORM).value<ComboProperty>().getValue();
+    return ObjectiveMetricUtils::createMetric(metric.toStdString(), norm.toStdString());
 }
 
 // ----------------------------------------------------------------------------
@@ -89,8 +73,8 @@ const QString MinuitMinimizerItem::P_STRATEGY = QString::fromStdString(OptionNam
 const QString MinuitMinimizerItem::P_ERRORDEF = QString::fromStdString(OptionNames::ErrorDef);
 const QString MinuitMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance);
 const QString MinuitMinimizerItem::P_PRECISION = QString::fromStdString(OptionNames::Precision);
-const QString MinuitMinimizerItem::P_MAXFUNCTIONCALLS
-    = QString::fromStdString(OptionNames::MaxFunctionCalls);
+const QString MinuitMinimizerItem::P_MAXFUNCTIONCALLS =
+    QString::fromStdString(OptionNames::MaxFunctionCalls);
 
 MinuitMinimizerItem::MinuitMinimizerItem() : MinimizerItem(Constants::MinuitMinimizerType)
 {
@@ -116,7 +100,7 @@ std::unique_ptr<IMinimizer> MinuitMinimizerItem::createMinimizer() const
 {
     QString algorithmName = getItemValue(P_ALGORITHMS).value<ComboProperty>().getValue();
 
-    Minuit2Minimizer *domainMinimizer = new Minuit2Minimizer(algorithmName.toStdString());
+    Minuit2Minimizer* domainMinimizer = new Minuit2Minimizer(algorithmName.toStdString());
     domainMinimizer->setStrategy(getItemValue(P_STRATEGY).toInt());
     domainMinimizer->setErrorDefinition(getItemValue(P_ERRORDEF).toDouble());
     domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble());
@@ -129,8 +113,8 @@ std::unique_ptr<IMinimizer> MinuitMinimizerItem::createMinimizer() const
 // ----------------------------------------------------------------------------
 
 const QString GSLMultiMinimizerItem::P_ALGORITHMS = "Algorithms";
-const QString GSLMultiMinimizerItem::P_MAXITERATIONS
-    = QString::fromStdString(OptionNames::MaxIterations);
+const QString GSLMultiMinimizerItem::P_MAXITERATIONS =
+    QString::fromStdString(OptionNames::MaxIterations);
 
 GSLMultiMinimizerItem::GSLMultiMinimizerItem() : MinimizerItem(Constants::GSLMultiMinimizerType)
 {
@@ -142,7 +126,7 @@ std::unique_ptr<IMinimizer> GSLMultiMinimizerItem::createMinimizer() const
 {
     QString algorithmName = getItemValue(P_ALGORITHMS).value<ComboProperty>().getValue();
 
-    GSLMultiMinimizer *domainMinimizer = new GSLMultiMinimizer(algorithmName.toStdString());
+    GSLMultiMinimizer* domainMinimizer = new GSLMultiMinimizer(algorithmName.toStdString());
     domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt());
     return std::unique_ptr<IMinimizer>(domainMinimizer);
 }
@@ -150,10 +134,10 @@ std::unique_ptr<IMinimizer> GSLMultiMinimizerItem::createMinimizer() const
 // ----------------------------------------------------------------------------
 
 const QString GeneticMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance);
-const QString GeneticMinimizerItem::P_MAXITERATIONS
-    = QString::fromStdString(OptionNames::MaxIterations);
-const QString GeneticMinimizerItem::P_POPULATIONSIZE
-    = QString::fromStdString(OptionNames::PopulationSize);
+const QString GeneticMinimizerItem::P_MAXITERATIONS =
+    QString::fromStdString(OptionNames::MaxIterations);
+const QString GeneticMinimizerItem::P_POPULATIONSIZE =
+    QString::fromStdString(OptionNames::PopulationSize);
 const QString GeneticMinimizerItem::P_RANDOMSEED = QString::fromStdString(OptionNames::RandomSeed);
 
 GeneticMinimizerItem::GeneticMinimizerItem() : MinimizerItem(Constants::GeneticMinimizerType)
@@ -166,7 +150,7 @@ GeneticMinimizerItem::GeneticMinimizerItem() : MinimizerItem(Constants::GeneticM
 
 std::unique_ptr<IMinimizer> GeneticMinimizerItem::createMinimizer() const
 {
-    GeneticMinimizer *domainMinimizer = new GeneticMinimizer();
+    GeneticMinimizer* domainMinimizer = new GeneticMinimizer();
     domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble());
     domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt());
     domainMinimizer->setPopulationSize(getItemValue(P_POPULATIONSIZE).toInt());
@@ -176,17 +160,17 @@ std::unique_ptr<IMinimizer> GeneticMinimizerItem::createMinimizer() const
 
 // ----------------------------------------------------------------------------
 
-const QString SimAnMinimizerItem::P_MAXITERATIONS
-    = QString::fromStdString(OptionNames::MaxIterations);
-const QString SimAnMinimizerItem::P_ITERATIONSTEMP
-    = QString::fromStdString(OptionNames::IterationTemp);
+const QString SimAnMinimizerItem::P_MAXITERATIONS =
+    QString::fromStdString(OptionNames::MaxIterations);
+const QString SimAnMinimizerItem::P_ITERATIONSTEMP =
+    QString::fromStdString(OptionNames::IterationTemp);
 const QString SimAnMinimizerItem::P_STEPSIZE = QString::fromStdString(OptionNames::StepSize);
 const QString SimAnMinimizerItem::P_BOLTZMANN_K = QString::fromStdString(OptionNames::BoltzmannK);
-const QString SimAnMinimizerItem::P_BOLTZMANN_TINIT
-    = QString::fromStdString(OptionNames::BoltzmannInitT);
+const QString SimAnMinimizerItem::P_BOLTZMANN_TINIT =
+    QString::fromStdString(OptionNames::BoltzmannInitT);
 const QString SimAnMinimizerItem::P_BOLTZMANN_MU = QString::fromStdString(OptionNames::BoltzmannMu);
-const QString SimAnMinimizerItem::P_BOLTZMANN_TMIN
-    = QString::fromStdString(OptionNames::BoltzmannTmin);
+const QString SimAnMinimizerItem::P_BOLTZMANN_TMIN =
+    QString::fromStdString(OptionNames::BoltzmannTmin);
 
 SimAnMinimizerItem::SimAnMinimizerItem() : MinimizerItem(Constants::GSLSimAnMinimizerType)
 {
@@ -201,7 +185,7 @@ SimAnMinimizerItem::SimAnMinimizerItem() : MinimizerItem(Constants::GSLSimAnMini
 
 std::unique_ptr<IMinimizer> SimAnMinimizerItem::createMinimizer() const
 {
-    SimAnMinimizer *domainMinimizer = new SimAnMinimizer();
+    SimAnMinimizer* domainMinimizer = new SimAnMinimizer();
     domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt());
     domainMinimizer->setIterationsAtEachTemp(getItemValue(P_ITERATIONSTEMP).toInt());
     domainMinimizer->setStepSize(getItemValue(P_STEPSIZE).toDouble());
@@ -215,11 +199,10 @@ std::unique_ptr<IMinimizer> SimAnMinimizerItem::createMinimizer() const
 // ----------------------------------------------------------------------------
 
 const QString GSLLMAMinimizerItem::P_TOLERANCE = QString::fromStdString(OptionNames::Tolerance);
-const QString GSLLMAMinimizerItem::P_MAXITERATIONS
-    = QString::fromStdString(OptionNames::MaxIterations);
+const QString GSLLMAMinimizerItem::P_MAXITERATIONS =
+    QString::fromStdString(OptionNames::MaxIterations);
 
-GSLLMAMinimizerItem::GSLLMAMinimizerItem()
-    : MinimizerItem(Constants::GSLLMAMinimizerType)
+GSLLMAMinimizerItem::GSLLMAMinimizerItem() : MinimizerItem(Constants::GSLLMAMinimizerType)
 {
     addProperty(P_TOLERANCE, 0.01)->setToolTip("Tolerance on the function value at the minimum");
     addProperty(P_MAXITERATIONS, 0)->setToolTip("Maximum number of iterations");
@@ -227,7 +210,7 @@ GSLLMAMinimizerItem::GSLLMAMinimizerItem()
 
 std::unique_ptr<IMinimizer> GSLLMAMinimizerItem::createMinimizer() const
 {
-    GSLLevenbergMarquardtMinimizer *domainMinimizer = new GSLLevenbergMarquardtMinimizer();
+    GSLLevenbergMarquardtMinimizer* domainMinimizer = new GSLLevenbergMarquardtMinimizer();
     domainMinimizer->setTolerance(getItemValue(P_TOLERANCE).toDouble());
     domainMinimizer->setMaxIterations(getItemValue(P_MAXITERATIONS).toInt());
     return std::unique_ptr<IMinimizer>(domainMinimizer);
@@ -235,11 +218,7 @@ std::unique_ptr<IMinimizer> GSLLMAMinimizerItem::createMinimizer() const
 
 // ----------------------------------------------------------------------------
 
-TestMinimizerItem::TestMinimizerItem()
-    : MinimizerItem(Constants::TestMinimizerType)
-{
-
-}
+TestMinimizerItem::TestMinimizerItem() : MinimizerItem(Constants::TestMinimizerType) {}
 
 std::unique_ptr<IMinimizer> TestMinimizerItem::createMinimizer() const
 {
diff --git a/GUI/coregui/Models/MinimizerItem.h b/GUI/coregui/Models/MinimizerItem.h
index d78e681555f0e59b9fdb4f702f51be87398de10d..c75721f22f692df9b2f71f696061402b8e7c5f2f 100644
--- a/GUI/coregui/Models/MinimizerItem.h
+++ b/GUI/coregui/Models/MinimizerItem.h
@@ -18,15 +18,14 @@
 #include "SessionItem.h"
 
 class IMinimizer;
-class IIntensityFunction;
-class IVarianceFunction;
+class ObjectiveMetric;
 
 //! The MinimizerItem class is the base item to hold minimizer settings.
 
 class BA_CORE_API_ MinimizerItem : public SessionItem
 {
 public:
-    explicit MinimizerItem(const QString &model_type);
+    explicit MinimizerItem(const QString& model_type);
     virtual std::unique_ptr<IMinimizer> createMinimizer() const = 0;
 };
 
@@ -36,13 +35,13 @@ class BA_CORE_API_ MinimizerContainerItem : public MinimizerItem
 {
 public:
     static const QString P_MINIMIZERS;
-    static const QString P_INTENSITY_FUNCTION;
-    static const QString P_VARIANCE_FUNCTIONS;
+    static const QString P_METRIC;
+    static const QString P_NORM;
+
     MinimizerContainerItem();
 
     std::unique_ptr<IMinimizer> createMinimizer() const;
-    std::unique_ptr<IIntensityFunction> createIntensityFunction() const;
-    std::unique_ptr<IVarianceFunction> createVarianceFunction() const;
+    std::unique_ptr<ObjectiveMetric> createMetric() const;
 };
 
 //! The MinuitMinimizerItem class represents settings for ROOT Minuit2 minimizer.
diff --git a/GUI/coregui/Models/VarianceFunctionItems.cpp b/GUI/coregui/Models/VarianceFunctionItems.cpp
deleted file mode 100644
index 3e30be9db50e836af1a2adb5cfaeeb9e93a88ee5..0000000000000000000000000000000000000000
--- a/GUI/coregui/Models/VarianceFunctionItems.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      GUI/coregui/Models/VarianceFunctionItems.cpp
-//! @brief     Implements classes VarianceFunctionItems
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#include "VarianceFunctionItems.h"
-#include "item_constants.h"
-#include "VarianceFunctions.h"
-
-IVarianceFunctionItem::IVarianceFunctionItem(const QString& model_type)
-    : SessionItem(model_type)
-{
-
-}
-
-VarianceConstantFunctionItem::VarianceConstantFunctionItem()
-    : IVarianceFunctionItem(Constants::VarianceConstantFunctionType)
-{
-    setToolTip("Leaves residual value unnormalized");
-
-}
-
-std::unique_ptr<IVarianceFunction> VarianceConstantFunctionItem::createVarianceFunction() const
-{
-    return std::make_unique<VarianceConstantFunction>();
-}
-
-const QString VarianceSimFunctionItem::P_EPSILON = "epsilon";
-
-VarianceSimFunctionItem::VarianceSimFunctionItem()
-    : IVarianceFunctionItem(Constants::VarianceSimFunctionType)
-{
-    setToolTip("Normalizes residual on max(sim, epsilon)");
-    addProperty(P_EPSILON, 1.0)->setToolTip(QStringLiteral(
-            "Defines threshold on simulated amplitude in max(sim, epsilon)"));
-}
-
-std::unique_ptr<IVarianceFunction> VarianceSimFunctionItem::createVarianceFunction() const
-{
-    return std::make_unique<VarianceSimFunction>(getItemValue(P_EPSILON).toDouble());
-}
diff --git a/GUI/coregui/Models/VarianceFunctionItems.h b/GUI/coregui/Models/VarianceFunctionItems.h
deleted file mode 100644
index 1f1df68a629b2d37c0acac7cd806b5f7c349543f..0000000000000000000000000000000000000000
--- a/GUI/coregui/Models/VarianceFunctionItems.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// ************************************************************************** //
-//
-//  BornAgain: simulate and fit scattering at grazing incidence
-//
-//! @file      GUI/coregui/Models/VarianceFunctionItems.h
-//! @brief     Defines classes VarianceFunctionItems
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-// ************************************************************************** //
-
-#ifndef VARIANTFUNCTIONITEMS_H
-#define VARIANTFUNCTIONITEMS_H
-
-#include "SessionItem.h"
-#include <QString>
-#include <memory>
-
-class IVarianceFunction;
-
-//! Base class for variance in Minimizer's residual settings.
-
-class BA_CORE_API_ IVarianceFunctionItem : public SessionItem
-{
-public:
-    virtual std::unique_ptr<IVarianceFunction> createVarianceFunction() const = 0;
-protected:
-    IVarianceFunctionItem(const QString& model_type);
-};
-
-class BA_CORE_API_ VarianceConstantFunctionItem : public IVarianceFunctionItem
-{
-public:
-    VarianceConstantFunctionItem();
-
-    std::unique_ptr<IVarianceFunction> createVarianceFunction() const;
-};
-
-class BA_CORE_API_ VarianceSimFunctionItem : public IVarianceFunctionItem
-{
-public:
-    static const QString P_EPSILON;
-    VarianceSimFunctionItem();
-
-    std::unique_ptr<IVarianceFunction> createVarianceFunction() const;
-};
-
-#endif // VARIANTFUNCTIONITEMS_H
diff --git a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp
index 4754f4d8182c6830c5f2f99347db56205bd78198..b547dab137aa12d405c696e0a041138d99c81f77 100644
--- a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp
+++ b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.cpp
@@ -13,29 +13,29 @@
 // ************************************************************************** //
 
 #include "FitObjectiveBuilder.h"
-#include "JobItem.h"
-#include "Parameters.h"
-#include "KernelTypes.h"
-#include "FitObjective.h"
-#include "Simulation.h"
-#include "MultiLayer.h"
-#include "FitParameterItems.h"
-#include "RealDataItem.h"
-#include "GUIHelpers.h"
+#include "ChiSquaredModule.h"
 #include "DataItem.h"
-#include "OutputData.h"
 #include "DomainSimulationBuilder.h"
+#include "FitObjective.h"
+#include "FitParameterItems.h"
 #include "FitSuiteItem.h"
-#include "MinimizerItem.h"
-#include "IMinimizer.h"
-#include "Minimizer.h"
 #include "GUIFitObserver.h"
-#include "ChiSquaredModule.h"
+#include "GUIHelpers.h"
 #include "IIntensityFunction.h"
+#include "IMinimizer.h"
+#include "JobItem.h"
+#include "KernelTypes.h"
+#include "Minimizer.h"
+#include "MinimizerItem.h"
+#include "MultiLayer.h"
+#include "ObjectiveMetric.h"
+#include "OutputData.h"
+#include "Parameters.h"
+#include "RealDataItem.h"
+#include "Simulation.h"
 #include "VarianceFunctions.h"
 
-FitObjectiveBuilder::FitObjectiveBuilder(JobItem* jobItem)
-    : m_jobItem(jobItem)
+FitObjectiveBuilder::FitObjectiveBuilder(JobItem* jobItem) : m_jobItem(jobItem)
 {
     Q_ASSERT(m_jobItem->fitSuiteItem());
 }
@@ -46,24 +46,30 @@ void FitObjectiveBuilder::runFit()
 {
     m_fit_objective = createFitObjective();
 
-    auto module = createChiSquaredModule();
-    m_fit_objective->setChiSquaredModule(*module);
-
-    fcn_residual_t residual_func = [&](const Fit::Parameters& params) {
-        return m_fit_objective->evaluate_residuals(params);
-    };
+    auto module = m_jobItem->fitSuiteItem()->minimizerContainerItem()->createMetric();
+    m_fit_objective->setObjectiveMetric(std::move(module));
 
     if (m_observer) {
-        fit_observer_t plot_observer = [&](const FitObjective& obj) {
-            m_observer->update(&obj);
-        };
+        fit_observer_t plot_observer = [&](const FitObjective& obj) { m_observer->update(&obj); };
         m_fit_objective->initPlot(1, plot_observer);
     }
 
-    Fit::Minimizer minimizer;
-    minimizer.setMinimizer(createMinimizer().release());
+    auto minimizer_impl = createMinimizer();
+    const bool requires_residuals = minimizer_impl->requiresResiduals();
 
-    auto result = minimizer.minimize(residual_func, createParameters());
+    Fit::Minimizer minimizer;
+    minimizer.setMinimizer(minimizer_impl.release());
+
+    auto result =
+        requires_residuals
+            ? minimizer.minimize(
+                  [&](const Fit::Parameters& params) {
+                      return m_fit_objective->evaluate_residuals(params);
+                  },
+                  createParameters())
+            : minimizer.minimize(
+                  [&](const Fit::Parameters& params) { return m_fit_objective->evaluate(params); },
+                  createParameters());
     m_fit_objective->finalize(result);
 }
 
@@ -86,19 +92,6 @@ std::unique_ptr<IMinimizer> FitObjectiveBuilder::createMinimizer() const
     return fitSuiteItem->minimizerContainerItem()->createMinimizer();
 }
 
-std::unique_ptr<IChiSquaredModule> FitObjectiveBuilder::createChiSquaredModule() const
-{
-    std::unique_ptr<IChiSquaredModule> result = std::make_unique<ChiSquaredModule>();
-
-    auto fitSuiteItem = m_jobItem->fitSuiteItem();
-    auto intensityFunction = fitSuiteItem->minimizerContainerItem()->createIntensityFunction();
-    if (intensityFunction)
-        result->setIntensityFunction(*intensityFunction);
-    auto variaceFunction = fitSuiteItem->minimizerContainerItem()->createVarianceFunction();
-    result->setVarianceFunction(*variaceFunction);
-    return result;
-}
-
 Fit::Parameters FitObjectiveBuilder::createParameters() const
 {
     auto fitSuiteItem = m_jobItem->fitSuiteItem();
@@ -115,20 +108,22 @@ void FitObjectiveBuilder::interruptFitting()
     m_fit_objective->interruptFitting();
 }
 
-std::unique_ptr<Simulation> FitObjectiveBuilder::buildSimulation(const Fit::Parameters& params) const
+std::unique_ptr<Simulation>
+FitObjectiveBuilder::buildSimulation(const Fit::Parameters& params) const
 {
     static std::mutex build_simulation_mutex;
     std::unique_lock<std::mutex> lock(build_simulation_mutex);
 
     update_fit_parameters(params);
     return DomainSimulationBuilder::createSimulation(m_jobItem->multiLayerItem(),
-            m_jobItem->instrumentItem(), m_jobItem->simulationOptionsItem());
+                                                     m_jobItem->instrumentItem(),
+                                                     m_jobItem->simulationOptionsItem());
 }
 
-std::unique_ptr<OutputData<double> > FitObjectiveBuilder::createOutputData() const
+std::unique_ptr<OutputData<double>> FitObjectiveBuilder::createOutputData() const
 {
     auto realDataItem = m_jobItem->realDataItem();
-    if(!realDataItem)
+    if (!realDataItem)
         throw GUIHelpers::Error("FitObjectiveBuilder::createOutputData() -> No Real Data defined.");
 
     const DataItem* intensity_item = realDataItem->dataItem();
@@ -138,7 +133,6 @@ std::unique_ptr<OutputData<double> > FitObjectiveBuilder::createOutputData() con
     return std::unique_ptr<OutputData<double>>(intensity_item->getOutputData()->clone());
 }
 
-
 void FitObjectiveBuilder::update_fit_parameters(const Fit::Parameters& params) const
 {
     QVector<double> values = GUIHelpers::fromStdVector(params.values());
diff --git a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h
index 58e2a7cc6501269f4a74104023d1be51d0a16b10..ecc3c444afd971dfa41ef796ed2386782226b484 100644
--- a/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h
+++ b/GUI/coregui/Views/FitWidgets/FitObjectiveBuilder.h
@@ -22,7 +22,7 @@ class JobItem;
 class FitObjective;
 class Simulation;
 namespace Fit { class Parameters; }
-template<class T> class OutputData;
+template <class T> class OutputData;
 class IMinimizer;
 class GUIFitObserver;
 class IChiSquaredModule;
@@ -44,6 +44,7 @@ public:
     void attachObserver(std::shared_ptr<GUIFitObserver> observer);
 
     void interruptFitting();
+
 private:
     JobItem* m_jobItem;
 
diff --git a/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp b/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp
index 60e80c32616cc605898a682cc74159b68fb11469..3168d6987c877e4e3b2c20598b3e8507b9f04960 100644
--- a/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp
+++ b/Tests/UnitTests/Core/Fitting/ObjectiveMetricTest.cpp
@@ -1,5 +1,6 @@
 #include "google_test.h"
 #include "ObjectiveMetric.h"
+#include "ObjectiveMetricUtils.h"
 #include <cmath>
 
 class ObjectiveMetricTest : public ::testing::Test
@@ -39,7 +40,7 @@ TEST_F(ObjectiveMetricTest, Chi2WellFormed)
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties_1, weight_factors),
                      204.0);
 
-    metric.setNorm(ObjectiveMetric::l1_norm);
+    metric.setNorm(ObjectiveMetricUtils::l1Norm());
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors),
                      26.0);
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0);
@@ -100,7 +101,7 @@ TEST_F(ObjectiveMetricTest, PoissionLikeWellFormed)
     sim_data_1[0] = 0.0;
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data, weight_factors), 5.25);
 
-    metric.setNorm(ObjectiveMetric::l1_norm);
+    metric.setNorm(ObjectiveMetricUtils::l1Norm());
 
     std::vector<double> sim_data_2 = sim_data;
     sim_data_2[1] = 1.0;
@@ -163,7 +164,7 @@ TEST_F(ObjectiveMetricTest, LogWellFormed)
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties_1, weight_factors),
                      4.0101e+6 * std::log(10.0) * std::log(10.0));
 
-    metric.setNorm(ObjectiveMetric::l1_norm);
+    metric.setNorm(ObjectiveMetricUtils::l1Norm());
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors),
                      4.0211e+5 * std::log(10.0));
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0);
@@ -223,7 +224,7 @@ TEST_F(ObjectiveMetricTest, RelativeDifferenceWellFormed)
     exp_data_1[0] = 0.0;
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data_1, weight_factors), 4.0 / 9.0);
 
-    metric.setNorm(ObjectiveMetric::l1_norm);
+    metric.setNorm(ObjectiveMetricUtils::l1Norm());
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 5.0 / 3.0);
 
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors),
@@ -280,7 +281,7 @@ TEST_F(ObjectiveMetricTest, RQ4WellFormed)
     exp_data_1[0] = 0.0;
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data_1, exp_data_1, weight_factors), 13.0);
 
-    metric.setNorm(ObjectiveMetric::l1_norm);
+    metric.setNorm(ObjectiveMetricUtils::l1Norm());
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors), 8.0);
 
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, uncertainties, weight_factors),
@@ -313,3 +314,51 @@ TEST_F(ObjectiveMetricTest, RQ4IllFormed)
                      0.0);
     EXPECT_DOUBLE_EQ(metric.computeFromArrays(sim_data, exp_data, weight_factors_1), 0.0);
 }
+
+TEST_F(ObjectiveMetricTest, createMetric)
+{
+    auto result = ObjectiveMetricUtils::createMetric("Poisson-like");
+    EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get()));
+    // Since norm functions lack equality comparison, check the equality of applying them
+    EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0));
+
+    result = ObjectiveMetricUtils::createMetric("Poisson-Like", "L1");
+    EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get()));
+    EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l1Norm()(2.0));
+
+    result = ObjectiveMetricUtils::createMetric("poisson-like", "l1");
+    EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get()));
+    EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l1Norm()(2.0));
+
+    result = ObjectiveMetricUtils::createMetric("poissoN-likE", "L2");
+    EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get()));
+    EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0));
+
+    result = ObjectiveMetricUtils::createMetric("poisson-like");
+    EXPECT_TRUE(dynamic_cast<PoissonLikeMetric*>(result.get()));
+    EXPECT_DOUBLE_EQ(result->norm()(2.0), ObjectiveMetricUtils::l2Norm()(2.0));
+
+    result = ObjectiveMetricUtils::createMetric("chi2");
+    EXPECT_TRUE(dynamic_cast<Chi2Metric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("Chi2");
+    EXPECT_TRUE(dynamic_cast<Chi2Metric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("Log");
+    EXPECT_TRUE(dynamic_cast<LogMetric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("log");
+    EXPECT_TRUE(dynamic_cast<LogMetric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("reldiff");
+    EXPECT_TRUE(dynamic_cast<RelativeDifferenceMetric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("RelDiff");
+    EXPECT_TRUE(dynamic_cast<RelativeDifferenceMetric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("RQ4");
+    EXPECT_TRUE(dynamic_cast<RQ4Metric*>(result.get()));
+
+    result = ObjectiveMetricUtils::createMetric("rq4");
+    EXPECT_TRUE(dynamic_cast<RQ4Metric*>(result.get()));
+}