diff --git a/Framework/PythonInterface/mantid/api/src/Exports/MultiDomainFunction.cpp b/Framework/PythonInterface/mantid/api/src/Exports/MultiDomainFunction.cpp
index ab9102c16a2d5894a3ceac47ceeadb5205d48d42..7d0975a91ea3c6953ef13ecb5e5a6b55fd86e538 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/MultiDomainFunction.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/MultiDomainFunction.cpp
@@ -24,6 +24,8 @@ void export_MultiDomainFunction() {
            (arg("self"), arg("i")), "Get the i-th function.")
       .def("add", &MultiDomainFunction::addFunction,
            (arg("self"), arg("function")), "Add a member function.")
+      .def("replaceFunction", &MultiDomainFunction::replaceFunction,
+           (arg("self"), arg("function")), "Replace a member function.")
       .def("setDomainIndex", &MultiDomainFunction::setDomainIndex,
            (arg("self"), arg("funIndex"), arg("domainIndex")),
            "Associate a function and a domain.");
diff --git a/qt/python/mantidqt/_common.sip b/qt/python/mantidqt/_common.sip
index 41d7f3cd66839219696b372fa367e70e645fcd50..08da6391588a347db2d84019d1d085f3c5bf15d8 100644
--- a/qt/python/mantidqt/_common.sip
+++ b/qt/python/mantidqt/_common.sip
@@ -804,6 +804,9 @@ public:
     void removeDatasets(QList<int> indices);
     QStringList getDatasetNames() const;
     void setErrorsEnabled(bool enabled);
+    void hideGlobalCheckbox();
+    void showGlobalCheckbox();
+
     QStringList getGlobalParameters() const;
     void setGlobalParameters(const QStringList &globals);
     void clear();
@@ -855,6 +858,10 @@ public:
     %MethodCode
       sipRes = boost::python::to_python_value<const Mantid::API::IFunction_sptr &>()(sipCpp->getFunctionByIndex(*a0));
     %End
+    SIP_PYOBJECT getFunctionWithIndex(const int index);
+    %MethodCode
+      sipRes = boost::python::to_python_value<const Mantid::API::IFunction_sptr &>()(sipCpp->getFunctionWithIndex(a0));
+    %End
 signals:
     void functionStructureChanged();
     void parameterChanged(const QString &funcIndex, const QString &paramName);
diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionBrowser.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionBrowser.h
index 6eb3f93c97aadfea58eacdf37a7964b78f5abaaf..fd15dc44d07d039732b0185851ac64be331a57ca 100644
--- a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionBrowser.h
+++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionBrowser.h
@@ -63,6 +63,8 @@ public:
   bool hasFunction() const;
   /// Return a function with specified index
   IFunction_sptr getFunctionByIndex(const QString &index);
+  /// Return a function with specified index
+  IFunction_sptr getFunctionWithIndex(const int index);
   /// Return index of the current function, if one is selected
   boost::optional<QString> currentFunctionIndex();
   /// Update the function parameter value
@@ -119,6 +121,10 @@ public:
   void clearErrors() override;
   /// Set a parameter that is responsible for the background level
   void setBackgroundA0(double value);
+  // hide the global options
+  void hideGlobalCheckbox();
+  // show the global options
+  void showGlobalCheckbox();
 
 signals:
   void parameterChanged(const QString &funcIndex, const QString &paramName);
diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionMultiDomainPresenter.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionMultiDomainPresenter.h
index d8dbb8b035441ce78dcf9a390a6d320b6f4830dc..77ad3087c68ee546f31f3c5401e3c34c9099bfa2 100644
--- a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionMultiDomainPresenter.h
+++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionMultiDomainPresenter.h
@@ -36,6 +36,7 @@ public:
   QString getFunctionString() const;
   IFunction_sptr getFunction() const;
   IFunction_sptr getFunctionByIndex(const QString &index);
+  IFunction_sptr getFunctionWithIndex(const int index);
   IFunction_sptr getFitFunction() const;
   QString getFitFunctionString() const;
   bool hasFunction() const;
@@ -73,6 +74,8 @@ public:
 
   void setColumnSizes(int s0, int s1, int s2);
   void setErrorsEnabled(bool enabled);
+  void hideGlobals();
+  void showGlobals();
 signals:
   void functionStructureChanged();
   void parameterChanged(const QString &funcIndex, const QString &paramName);
diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
index b65ffb3999383b0735c98db6d9e83279a4208af1..719ebbbe4b18422721e98d1ee9c7a54b79aca096 100644
--- a/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
+++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FunctionTreeView.h
@@ -122,6 +122,12 @@ public:
   /// Resize the browser's columns
   void setColumnSizes(int s0, int s1, int s2 = -1);
 
+  // Hide global boxes
+  void hideGlobals();
+
+  // Show global boxes
+  void showGlobals();
+
 protected:
   /// Create the Qt property browser
   void createBrowser();
diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h b/qt/widgets/common/inc/MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h
index 1cdd0d849cf9a5e0a1cc3208b7343d92415395fd..26f669ffee991ae8b40f97c3f4fb2d39e74bc707 100644
--- a/qt/widgets/common/inc/MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h
+++ b/qt/widgets/common/inc/MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h
@@ -151,6 +151,9 @@ public:
   bool isItemVisible(QtBrowserItem *item) const;
   void setItemVisible(QtBrowserItem *item, bool visible);
 
+  void hideColumn(int col);
+  void showColumn(int col);
+
   void setBackgroundColor(QtBrowserItem *item, const QColor &color);
   QColor backgroundColor(QtBrowserItem *item) const;
   QColor calculatedBackgroundColor(QtBrowserItem *item) const;
@@ -275,6 +278,8 @@ public:
   void disableItem(QTreeWidgetItem *item) const;
   void enableItem(QTreeWidgetItem *item) const;
   bool hasValue(QTreeWidgetItem *item) const;
+  void hideColumn(int col);
+  void showColumn(int col);
 
   void slotCollapsed(const QModelIndex &index);
   void slotExpanded(const QModelIndex &index);
diff --git a/qt/widgets/common/src/FunctionBrowser.cpp b/qt/widgets/common/src/FunctionBrowser.cpp
index a2c781a10151e224432876bf71f8f8ee2bff6079..7fcd1754d2224e44105850847fbc8110d3381af7 100644
--- a/qt/widgets/common/src/FunctionBrowser.cpp
+++ b/qt/widgets/common/src/FunctionBrowser.cpp
@@ -93,6 +93,14 @@ IFunction_sptr FunctionBrowser::getFunctionByIndex(const QString &index) {
   return m_presenter->getFunctionByIndex(index);
 }
 
+/**
+ * Return function at specified function index (e.g. f0.)
+ * @param index :: Index of the function, or empty string for top-level function
+ * @return Function at index, or null pointer if not found
+ */
+IFunction_sptr FunctionBrowser::getFunctionWithIndex(const int index) {
+  return m_presenter->getFunctionWithIndex(index);
+}
 /**
  * Updates the function parameter value
  * @param paramName :: Fully qualified parameter name (includes function index)
@@ -344,5 +352,9 @@ void FunctionBrowser::setBackgroundA0(double value) {
   m_presenter->setBackgroundA0(value);
 }
 
+void FunctionBrowser::hideGlobalCheckbox() { m_presenter->hideGlobals(); }
+
+void FunctionBrowser::showGlobalCheckbox() { m_presenter->showGlobals(); }
+
 } // namespace MantidWidgets
 } // namespace MantidQt
diff --git a/qt/widgets/common/src/FunctionMultiDomainPresenter.cpp b/qt/widgets/common/src/FunctionMultiDomainPresenter.cpp
index 273678c56203ab3e6f4aa5e0e4580931b34d7f78..6543fb3b858550c3dc2dbc1315d771eb99c32235 100644
--- a/qt/widgets/common/src/FunctionMultiDomainPresenter.cpp
+++ b/qt/widgets/common/src/FunctionMultiDomainPresenter.cpp
@@ -79,6 +79,11 @@ FunctionMultiDomainPresenter::getFunctionByIndex(const QString &index) {
   return getFunctionWithPrefix(index, m_model->getCurrentFunction());
 }
 
+IFunction_sptr
+FunctionMultiDomainPresenter::getFunctionWithIndex(const int index) {
+  return m_model->getSingleFunction(index);
+}
+
 void FunctionMultiDomainPresenter::setParameter(const QString &paramName,
                                                 double value) {
   m_model->setParameter(paramName, value);
@@ -409,5 +414,19 @@ void FunctionMultiDomainPresenter::updateViewFromModel() {
   }
 }
 
+void FunctionMultiDomainPresenter::hideGlobals() {
+  auto treeView = dynamic_cast<FunctionTreeView *>(m_view);
+  if (treeView) {
+    treeView->hideGlobals();
+  }
+}
+
+void FunctionMultiDomainPresenter::showGlobals() {
+  auto treeView = dynamic_cast<FunctionTreeView *>(m_view);
+  if (treeView) {
+    treeView->showGlobals();
+  }
+}
+
 } // namespace MantidWidgets
 } // namespace MantidQt
diff --git a/qt/widgets/common/src/FunctionTreeView.cpp b/qt/widgets/common/src/FunctionTreeView.cpp
index df5efa6c406da63fb940fec37e24fbb41b124240..6d063152bd1ff71bd44895b3bd146bf23bf15715 100644
--- a/qt/widgets/common/src/FunctionTreeView.cpp
+++ b/qt/widgets/common/src/FunctionTreeView.cpp
@@ -1793,6 +1793,11 @@ void FunctionTreeView::setColumnSizes(int s0, int s1, int s2) {
   m_browser->setColumnSizes(s0, s1, s2);
 }
 
+/// Show global column
+void FunctionTreeView::hideGlobals() { m_browser->hideColumn(2); }
+// Hide global column
+void FunctionTreeView::showGlobals() { m_browser->showColumn(2); }
+
 /**
  * Emit a signal when any of the Global options change.
  */
diff --git a/qt/widgets/common/src/QtPropertyBrowser/qttreepropertybrowser.cpp b/qt/widgets/common/src/QtPropertyBrowser/qttreepropertybrowser.cpp
index a0b85250e787498e5dc92b0e0cffe12202696a44..ede13f42570eba791d6ecdef5dec5535ef2a0405 100644
--- a/qt/widgets/common/src/QtPropertyBrowser/qttreepropertybrowser.cpp
+++ b/qt/widgets/common/src/QtPropertyBrowser/qttreepropertybrowser.cpp
@@ -728,6 +728,14 @@ void QtTreePropertyBrowserPrivate::setColumnSizes(int s0, int s1, int s2) {
   }
 }
 
+void QtTreePropertyBrowserPrivate::hideColumn(int col) {
+  m_treeWidget->header()->hideSection(col);
+}
+
+void QtTreePropertyBrowserPrivate::showColumn(int col) {
+  m_treeWidget->header()->showSection(col);
+}
+
 /**
     \class QtTreePropertyBrowser
 
@@ -1091,6 +1099,10 @@ void QtTreePropertyBrowser::setColumnSizes(int s0, int s1, int s2) {
   d_ptr->setColumnSizes(s0, s1, s2);
 }
 
+void QtTreePropertyBrowser::hideColumn(int col) { d_ptr->hideColumn(col); }
+
+void QtTreePropertyBrowser::showColumn(int col) { d_ptr->showColumn(col); }
+
 QTreeWidgetItem *QtTreePropertyBrowser::getItemWidget(QtBrowserItem *item) {
   return d_ptr->getItemWidget(item);
 }
diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
index f14dee9123530cc972bb620597842b6f34e0f8b5..badd95aa6bfdd4e482e82b57ea03e3750e737e90 100644
--- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
+++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
@@ -34,6 +34,7 @@ class FittingTabPresenter(object):
         self.manual_selection_made = False
         self.automatically_update_fit_name = True
         self.thread_success = True
+        self.fitting_calculation_model = None
         self.update_selected_workspace_guess()
         self.gui_context_observer = GenericObserverWithArgPassing(
             self.handle_gui_changes_made)
@@ -51,6 +52,26 @@ class FittingTabPresenter(object):
         self.update_view_from_model_observer = GenericObserverWithArgPassing(
             self.update_view_from_model)
 
+    @property
+    def selected_data(self):
+        return self._selected_data
+
+    @selected_data.setter
+    def selected_data(self, selected_data):
+        if self._selected_data == selected_data:
+            return
+
+        self._selected_data = selected_data
+        self.clear_and_reset_gui_state()
+
+    @property
+    def start_x(self):
+        return self._start_x
+
+    @property
+    def end_x(self):
+        return self._end_x
+
     def handle_select_fit_data_clicked(self):
         selected_data, dialog_return = WorkspaceSelectorView.get_selected_data(
             self.context.data_context.current_runs,
@@ -76,49 +97,12 @@ class FittingTabPresenter(object):
     def handle_selected_plot_type_changed(self):
         self.update_selected_workspace_guess()
 
-    def update_selected_workspace_guess(self):
-        if self.view.fit_type == self.view.simultaneous_fit:
-            self.update_fit_specifier_list()
-        if self.manual_selection_made:
-            guess_selection = self.selected_data
-            self.selected_data = guess_selection
-        else:
-            self.selected_data = self.get_workspace_selected_list()
-
-    def get_workspace_selected_list(self):
-        if self.context._frequency_context is not None:
-            freq = self.context._frequency_context.plot_type
-        else:
-            freq = 'None'
-
-        runs = 'All'
-        groups_and_pairs = self._get_selected_groups_and_pairs()
-        if self.view.fit_type == self.view.simultaneous_fit:
-            if self.view.simultaneous_fit_by == "Run":
-                runs = self.view.simultaneous_fit_by_specifier
-            elif self.view.simultaneous_fit_by == "Group/Pair":
-                groups_and_pairs = [self.view.simultaneous_fit_by_specifier]
-
-        guess_selection = []
-        for grppair in groups_and_pairs:
-            guess_selection += self.context.get_names_of_workspaces_to_fit(
-                runs=runs,
-                group_and_pair=grppair,
-                phasequad=False,
-                rebin=not self.view.fit_to_raw, freq=freq)
-
-        guess_selection = list(set(self._check_data_exists(guess_selection)))
-        return guess_selection
-
     def handle_display_workspace_changed(self):
         current_index = self.view.get_index_for_start_end_times()
         self.view.start_time = self.start_x[current_index]
         self.view.end_time = self.end_x[current_index]
-
-        self.view.set_datasets_in_function_browser(
-            [self.view.display_workspace])
-        self.view.function_browser_multi.setCurrentDataset(current_index)
-
+        self.view.function_browser.setCurrentDataset(current_index)
+        self._update_stored_fit_functions()
         self.update_fit_status_information_in_view()
 
     def handle_use_rebin_changed(self):
@@ -138,10 +122,14 @@ class FittingTabPresenter(object):
             self.view.workspace_combo_box_label.setText(
                 'Display parameters for')
             self.view.simul_fit_by_specifier.setEnabled(True)
+            self.view.switch_to_simultaneous()
+            self._update_stored_fit_functions()
             self.update_fit_specifier_list()
         else:
             self.update_selected_workspace_guess()
             self.view.workspace_combo_box_label.setText('Select Workspace')
+            self.view.switch_to_single()
+            self._update_stored_fit_functions()
             self.view.simul_fit_by_specifier.setEnabled(False)
 
     def handle_plot_guess_changed(self):
@@ -159,33 +147,6 @@ class FittingTabPresenter(object):
 
         self.model.change_plot_guess(self.view.plot_guess, parameters)
 
-    def fitting_domain_type_changed(self):
-        # If the fit function has been removed we should remove clear both browsers and the fit information
-        # and return.
-        if self._fit_function[0] is None:
-            self.clear_fit_information()
-            self.view.function_browser.clear()
-            self.view.function_browser_multi.clear()
-
-        if self.view.fit_type == self.view.simultaneous_fit:
-            self.view.switch_to_simultaneous()
-        else:
-            self.view.switch_to_single()
-
-    def sync_single_domain_browser_with_multi_domain_browser(self):
-        multi_domain_function = self.create_multi_domain_function(self._fit_function)
-        if multi_domain_function:
-            self.view.function_browser_multi.blockSignals(True)
-            self.view.function_browser_multi.setFunction(str(multi_domain_function))
-            self.view.function_browser_multi.blockSignals(False)
-
-    def sync_multi_domain_browser_with_single_domain_browser(self):
-        single_domain_function = self._fit_function[0]
-        if single_domain_function:
-            self.view.function_browser.blockSignals(True)
-            self.view.function_browser.setFunction(str(single_domain_function))
-            self.view.function_browser.blockSignals(False)
-
     def handle_fit_clicked(self):
         self.context.fitting_context.number_of_fits = 0
         if self._tf_asymmetry_mode:
@@ -193,90 +154,6 @@ class FittingTabPresenter(object):
         else:
             self.perform_standard_fit()
 
-    def perform_standard_fit(self):
-        self._fit_function_cache = [item.clone() for item in self._fit_function if item]
-        fit_type = self.view.fit_type
-
-        try:
-            if fit_type == self.view.simultaneous_fit:
-                self._number_of_fits_cached = 1
-                simultaneous_fit_parameters = self.get_multi_domain_fit_parameters()
-                global_parameters = self.view.get_global_parameters()
-                print(simultaneous_fit_parameters)
-                calculation_function = functools.partial(
-                    self.model.do_simultaneous_fit,
-                    simultaneous_fit_parameters, global_parameters)
-                self.calculation_thread = self.create_thread(
-                    calculation_function)
-            else:
-                self._number_of_fits_cached = 1
-                single_fit_parameters = self.get_parameters_for_single_fit()
-                calculation_function = functools.partial(
-                    self.model.do_single_fit, single_fit_parameters)
-                self.calculation_thread = self.create_thread(
-                    calculation_function)
-
-            self.calculation_thread.threadWrapperSetUp(self.handle_started,
-                                                       self.handle_finished,
-                                                       self.handle_error)
-            self.calculation_thread.start()
-        except ValueError as error:
-            self.view.warning_popup(error)
-
-    def perform_tf_asymmetry_fit(self):
-        self._fit_function_cache = [item.clone() for item in self._fit_function if item]
-        fit_type = self.view.fit_type
-
-        try:
-            if fit_type == self.view.simultaneous_fit:
-                simultaneous_fit_parameters = self.get_multi_domain_tf_fit_parameters()
-                global_parameters = self.view.get_global_parameters()
-                calculation_function = functools.partial(
-                    self.model.do_simultaneous_tf_fit,
-                    simultaneous_fit_parameters, global_parameters)
-                self.calculation_thread = self.create_thread(calculation_function)
-            else:
-                single_fit_parameters = self.get_parameters_for_tf_single_fit_calculation()
-                calculation_function = functools.partial(
-                    self.model.do_single_tf_fit, single_fit_parameters)
-                self.calculation_thread = self.create_thread(calculation_function)
-
-            self.calculation_thread.threadWrapperSetUp(self.handle_started,
-                                                       self.handle_finished,
-                                                       self.handle_error)
-            self.calculation_thread.start()
-        except ValueError as error:
-            self.view.warning_popup(error)
-
-    def get_parameters_for_tf_single_fit_calculation(self):
-        workspace, workspace_directory = self.model.create_fitted_workspace_name(self.view.display_workspace,
-                                                                                 self.view.fit_object)
-
-        return {
-            'InputFunction': self.view.fit_object,
-            'ReNormalizedWorkspaceList': self.view.display_workspace,
-            'UnNormalizedWorkspaceList': self.context.group_pair_context.get_unormalisised_workspace_list(
-                [self.view.display_workspace])[0],
-            'OutputFitWorkspace': workspace,
-            'StartX': self.start_x[0],
-            'EndX': self.end_x[0],
-            'Minimizer': self.view.minimizer
-        }
-
-    def get_multi_domain_tf_fit_parameters(self):
-        workspace, workspace_directory = self.model.create_multi_domain_fitted_workspace_name(
-            self.view.display_workspace, self.view.fit_object)
-        return {
-            'InputFunction': self.view.fit_object,
-            'ReNormalizedWorkspaceList': self.selected_data,
-            'UnNormalizedWorkspaceList': self.context.group_pair_context.get_unormalisised_workspace_list(
-                self.selected_data),
-            'OutputFitWorkspace': workspace,
-            'StartX': self.start_x[self.view.get_index_for_start_end_times()],
-            'EndX': self.end_x[self.view.get_index_for_start_end_times()],
-            'Minimizer': self.view.minimizer
-        }
-
     def handle_started(self):
         self.view.setEnabled(False)
         self.thread_success = True
@@ -289,12 +166,16 @@ class FittingTabPresenter(object):
         fit_function, fit_status, fit_chi_squared = self.fitting_calculation_model.result
         if any([not fit_function, not fit_status, not fit_chi_squared]):
             return
-        index = self.view.get_index_for_start_end_times()
 
-        if self.view.fit_type == self.view.simultaneous_fit:
-            self._fit_function = [fit_function] * len(self.start_x)
+        if self.view.is_simul_fit():
+            self._fit_function[0] = fit_function
             self._fit_status = [fit_status] * len(self.start_x)
             self._fit_chi_squared = [fit_chi_squared] * len(self.start_x)
+        else:
+            current_index = self.view.get_index_for_start_end_times()
+            self._fit_function[current_index] = fit_function
+            self._fit_status[current_index] = fit_status
+            self._fit_chi_squared[current_index] = fit_chi_squared
 
         self.update_fit_status_information_in_view()
         self.view.undo_fit_button.setEnabled(True)
@@ -326,24 +207,18 @@ class FittingTabPresenter(object):
             self.view.function_browser.setFunction(str(self._fit_function[self.view.get_index_for_start_end_times()]))
             self.view.function_browser.blockSignals(False)
             return
-
         if not self.view.fit_object:
-            self._fit_function = [None] * len(self.selected_data) if self.selected_data else [None]
+            self._fit_function = [None]
         else:
-            self._fit_function = [self.view.fit_object.clone() for _ in self.selected_data] \
-                if self.selected_data else [self.view.fit_object.clone()]
+            self._fit_function = [func.clone() for func in self._get_fit_function()]
+
+        self.clear_fit_information()
 
         if self.automatically_update_fit_name:
-            self.view.function_name = self.model.get_function_name(
-                self.view.fit_object)
+            name = self._get_fit_function()[0]
+            self.view.function_name = self.model.get_function_name(name)
             self.model.function_name = self.view.function_name
 
-        # resync browsers
-        if self.view.fit_type == self.view.simultaneous_fit:
-            self.sync_multi_domain_browser_with_single_domain_browser()
-        else:
-            self.sync_single_domain_browser_with_multi_domain_browser()
-
     def handle_tf_asymmetry_mode_changed(self):
         def calculate_tf_fit_function(original_fit_function):
             tf_asymmetry_parameters = self.get_parameters_for_tf_function_calculation(original_fit_function)
@@ -383,7 +258,7 @@ class FittingTabPresenter(object):
                 self.view.function_name = self.view.function_name.replace(',TFAsymmetry', '')
                 self.model.function_name = self.view.function_name
 
-        if self.view.fit_type != self.view.simultaneous_fit:
+        if not self.view.is_simul_fit():
             for index, fit_function in enumerate(self._fit_function):
                 fit_function = fit_function if fit_function else self.view.fit_object.clone()
                 new_function = calculate_tf_fit_function(fit_function)
@@ -397,22 +272,22 @@ class FittingTabPresenter(object):
         else:
             new_function = calculate_tf_fit_function(self.view.fit_object)
             self._fit_function = [new_function.clone()] * len(self.selected_data)
-            self.view.function_browser_multi.blockSignals(True)
-            self.view.function_browser_multi.clear()
-            self.view.function_browser_multi.setFunction(
+            self.view.function_browser.blockSignals(True)
+            self.view.function_browser.clear()
+            self.view.function_browser.setFunction(
                 str(self._fit_function[self.view.get_index_for_start_end_times()]))
-            self.view.function_browser_multi.setGlobalParameters(new_global_parameters)
-            self.view.function_browser_multi.blockSignals(False)
+            self.view.function_browser.setGlobalParameters(new_global_parameters)
+            self.view.function_browser.blockSignals(False)
 
         self.update_fit_status_information_in_view()
         self.handle_display_workspace_changed()
 
     def handle_function_parameter_changed(self):
-        if self.view.fit_type != self.view.simultaneous_fit:
+        if not self.view.is_simul_fit():
             index = self.view.get_index_for_start_end_times()
-            self._fit_function[index] = self.view.fit_object.clone()
+            self._fit_function[index] = self._get_fit_function()[index]
         else:
-            self._fit_function = [self.view.fit_object] * len(self.selected_data)
+            self._fit_function = self._get_fit_function()
 
     def handle_undo_fit_clicked(self):
         self._fit_function = self._fit_function_cache
@@ -436,6 +311,152 @@ class FittingTabPresenter(object):
     def handle_fit_specifier_changed(self):
         self.selected_data = self.get_workspace_selected_list()
 
+    def perform_standard_fit(self):
+        if not self.view.fit_object:
+            return
+
+        self._fit_function_cache = [func.clone() for func in self._fit_function]
+        fit_type = self.view.fit_type
+
+        try:
+            if fit_type == self.view.simultaneous_fit:
+                self._number_of_fits_cached = 1
+                simultaneous_fit_parameters = self.get_multi_domain_fit_parameters()
+                global_parameters = self.view.get_global_parameters()
+                calculation_function = functools.partial(
+                    self.model.do_simultaneous_fit,
+                    simultaneous_fit_parameters, global_parameters)
+                self.calculation_thread = self.create_thread(
+                    calculation_function)
+            else:
+                self._number_of_fits_cached = 1
+                single_fit_parameters = self.get_parameters_for_single_fit()
+                calculation_function = functools.partial(
+                    self.model.do_single_fit, single_fit_parameters)
+                self.calculation_thread = self.create_thread(
+                    calculation_function)
+
+            self.calculation_thread.threadWrapperSetUp(self.handle_started,
+                                                       self.handle_finished,
+                                                       self.handle_error)
+            self.calculation_thread.start()
+        except ValueError as error:
+            self.view.warning_popup(error)
+
+    def perform_tf_asymmetry_fit(self):
+        self._fit_function_cache = [item.clone() for item in self._fit_function if item]
+        fit_type = self.view.fit_type
+
+        try:
+            if fit_type == self.view.simultaneous_fit:
+                simultaneous_fit_parameters = self.get_multi_domain_tf_fit_parameters()
+                global_parameters = self.view.get_global_parameters()
+                calculation_function = functools.partial(
+                    self.model.do_simultaneous_tf_fit,
+                    simultaneous_fit_parameters, global_parameters)
+                self.calculation_thread = self.create_thread(calculation_function)
+            else:
+                single_fit_parameters = self.get_parameters_for_tf_single_fit_calculation()
+                calculation_function = functools.partial(
+                    self.model.do_single_tf_fit, single_fit_parameters)
+                self.calculation_thread = self.create_thread(calculation_function)
+
+            self.calculation_thread.threadWrapperSetUp(self.handle_started,
+                                                       self.handle_finished,
+                                                       self.handle_error)
+            self.calculation_thread.start()
+        except ValueError as error:
+            self.view.warning_popup(error)
+
+    def get_parameters_for_tf_single_fit_calculation(self):
+        workspace, workspace_directory = self.model.create_fitted_workspace_name(self.view.display_workspace,
+                                                                                 self.view.fit_object)
+
+        return {
+            'InputFunction': self.view.fit_object,
+            'ReNormalizedWorkspaceList': self.view.display_workspace,
+            'UnNormalizedWorkspaceList': self.context.group_pair_context.get_unormalisised_workspace_list(
+                [self.view.display_workspace])[0],
+            'OutputFitWorkspace': workspace,
+            'StartX': self.start_x[0],
+            'EndX': self.end_x[0],
+            'Minimizer': self.view.minimizer
+        }
+
+    def get_multi_domain_tf_fit_parameters(self):
+        workspace, workspace_directory = self.model.create_multi_domain_fitted_workspace_name(
+            self.view.display_workspace, self.view.fit_object)
+        return {
+            'InputFunction': self.view.fit_object,
+            'ReNormalizedWorkspaceList': self.selected_data,
+            'UnNormalizedWorkspaceList': self.context.group_pair_context.get_unormalisised_workspace_list(
+                self.selected_data),
+            'OutputFitWorkspace': workspace,
+            'StartX': self.start_x[self.view.get_index_for_start_end_times()],
+            'EndX': self.end_x[self.view.get_index_for_start_end_times()],
+            'Minimizer': self.view.minimizer
+        }
+
+    def clear_and_reset_gui_state(self):
+        self.view.set_datasets_in_function_browser(self.selected_data)
+
+        self._fit_status = [None] * len(
+            self.selected_data) if self.selected_data else [None]
+        self._fit_chi_squared = [0.0] * len(
+            self.selected_data) if self.selected_data else [0.0]
+        if self.view.fit_object:
+            self._fit_function = [func.clone() for func in self._get_fit_function()]
+        else:
+            self._fit_function = [None]
+
+        self.view.undo_fit_button.setEnabled(False)
+
+        self.reset_start_time_to_first_good_data_value()
+        self.view.update_displayed_data_combo_box(self.selected_data)
+        self.update_fit_status_information_in_view()
+
+    def clear_fit_information(self):
+        self._fit_status = [None] * len(
+            self.selected_data) if self.selected_data else [None]
+        self._fit_chi_squared = [0.0] * len(
+            self.selected_data) if self.selected_data else [0.0]
+        self.update_fit_status_information_in_view()
+        self.view.undo_fit_button.setEnabled(False)
+
+    def update_selected_workspace_guess(self):
+        if self.view.fit_type == self.view.simultaneous_fit:
+            self.update_fit_specifier_list()
+        if self.manual_selection_made:
+            guess_selection = self.selected_data
+            self.selected_data = guess_selection
+        else:
+            self.selected_data = self.get_workspace_selected_list()
+
+    def get_workspace_selected_list(self):
+        if self.context._frequency_context is not None:
+            freq = self.context._frequency_context.plot_type
+        else:
+            freq = 'None'
+
+        runs = 'All'
+        groups_and_pairs = self._get_selected_groups_and_pairs()
+        if self.view.fit_type == self.view.simultaneous_fit:
+            if self.view.simultaneous_fit_by == "Run":
+                runs = self.view.simultaneous_fit_by_specifier
+            elif self.view.simultaneous_fit_by == "Group/Pair":
+                groups_and_pairs = [self.view.simultaneous_fit_by_specifier]
+
+        guess_selection = []
+        for grppair in groups_and_pairs:
+            guess_selection += self.context.get_names_of_workspaces_to_fit(
+                runs=runs,
+                group_and_pair=grppair,
+                phasequad=False,
+                rebin=not self.view.fit_to_raw, freq=freq)
+
+        guess_selection = list(set(self._check_data_exists(guess_selection)))
+        return guess_selection
+
     def update_fit_specifier_list(self):
         if self.view.simultaneous_fit_by == "Run":
             flattened_run_list = [str(item) for sublist in self.context.data_context.current_runs for item in sublist]
@@ -449,7 +470,6 @@ class FittingTabPresenter(object):
 
     def get_parameters_for_single_fit(self):
         params = self._get_shared_parameters()
-
         params['InputWorkspace'] = self.view.display_workspace
         params['StartX'] = self.start_x[0]
         params['EndX'] = self.end_x[0]
@@ -481,55 +501,50 @@ class FittingTabPresenter(object):
         :return: The set of attributes common to all fit types
         """
         return {
-            'Function': self.view.fit_object,
+            'Function': self._current_fit_function(),
             'Minimizer': self.view.minimizer,
             'EvaluationType': self.view.evaluation_type
         }
 
-    @property
-    def selected_data(self):
-        return self._selected_data
-
-    @selected_data.setter
-    def selected_data(self, selected_data):
-        if self._selected_data == selected_data:
-            return
-
-        self._selected_data = selected_data
-        self.clear_and_reset_gui_state()
-
-    def clear_and_reset_gui_state(self):
-        single_data = [self.selected_data[0]] if self.selected_data else []
-        self.view.set_datasets_in_function_browser(single_data)
-        self.view.set_datasets_in_function_browser_multi(self.selected_data)
-
-        self._fit_status = [None] * len(
-            self.selected_data) if self.selected_data else [None]
-        self._fit_chi_squared = [0.0] * len(
-            self.selected_data) if self.selected_data else [0.0]
-        self._fit_function = [self.view.fit_object] * len(
-            self.selected_data) if self.selected_data else [self.view.fit_object]
-        self.view.undo_fit_button.setEnabled(False)
-
-        self.reset_start_time_to_first_good_data_value()
-        self.view.update_displayed_data_combo_box(self.selected_data)
-        self.update_fit_status_information_in_view()
+    def _update_stored_fit_functions(self):
+        if self.view.is_simul_fit():
+            if self.view.fit_object:  # make sure there is a fit function in the browser
+                self._fit_function = [self.view.fit_object.clone()]  # return the fit function stored in the browser
+            else:
+                self._fit_function = [None]
+        else:  # we need to convert stored function into equiv
+            if self.view.fit_object:  # make sure there is a fit function in the browser
+                if isinstance(self.view.fit_object, MultiDomainFunction):
+                    equiv_fit_function = self.view.fit_object.createEquivalentFunctions()
+                    single_domain_fit_functions = [func.clone() for func in equiv_fit_function]
+                else:
+                    single_domain_fit_functions = [self.view.fit_object.clone()]
+                self._fit_function = single_domain_fit_functions
+            else:
+                self._fit_function = [None] * len(self._start_x)
+
+    def _get_fit_function(self):
+        if self.view.is_simul_fit():
+            return [self.view.fit_object]  # return the fit function stored in the browser
+        else:  # we need to convert stored function into equiv
+            if self.view.fit_object:  # make sure thers a fit function in the browser
+                if isinstance(self.view.fit_object, MultiDomainFunction):
+                    equiv_fit_funtion = self.view.fit_object.createEquivalentFunctions()
+                    single_domain_fit_function = equiv_fit_funtion
+                else:
+                    single_domain_fit_function = [self.view.fit_object]
+                return single_domain_fit_function
+            else:
+                return [None] * len(self._start_x)
 
-    def clear_fit_information(self):
-        self._fit_status = [None] * len(
-            self.selected_data) if self.selected_data else [None]
-        self._fit_chi_squared = [0.0] * len(
-            self.selected_data) if self.selected_data else [0.0]
-        self.update_fit_status_information_in_view()
-        self.view.undo_fit_button.setEnabled(False)
+    def _current_fit_function(self):
+        return self._fit_function[self._fit_function_index()]
 
-    @property
-    def start_x(self):
-        return self._start_x
-
-    @property
-    def end_x(self):
-        return self._end_x
+    def _fit_function_index(self):
+        if self.view.is_simul_fit():
+            return 0  # if we are doing a single simulatenous fit, return index 0
+        else:
+            return self.view.get_index_for_start_end_times()  # else doing a single fit on displayed workspaces
 
     def update_start_x(self, index, value):
         self._start_x[index] = value
@@ -559,7 +574,8 @@ class FittingTabPresenter(object):
         self.view.end_time = self.end_x[0] if 0 < len(self.end_x) else 15.0
 
     def update_fit_status_information_in_view(self):
-        current_index = self.view.get_index_for_start_end_times()
+        current_index = self._fit_function_index()
+
         self.view.update_with_fit_outputs(self._fit_function[current_index],
                                           self._fit_status[current_index],
                                           self._fit_chi_squared[current_index])
@@ -572,10 +588,6 @@ class FittingTabPresenter(object):
         else:
             self.selected_data = []
 
-    def check_workspaces_are_tf_asymmetry_compliant(self, workspace_list):
-        non_compliant_workspaces = [item for item in workspace_list if 'Group' not in item]
-        return False if non_compliant_workspaces else True
-
     def get_parameters_for_tf_function_calculation(self, fit_function):
         mode = 'Construct' if self.view.tf_asymmetry_mode else 'Extract'
         workspace_list = self.selected_data if self.view.fit_type == self.view.simultaneous_fit else [
@@ -584,22 +596,9 @@ class FittingTabPresenter(object):
                 'WorkspaceList': workspace_list,
                 'Mode': mode}
 
-    def create_multi_domain_function(self, function_list):
-        if not any(function_list):
-            return None
-        multi_domain_function = MultiDomainFunction()
-        for index, func in enumerate(function_list):
-            multi_domain_function.add(func)
-            multi_domain_function.setDomainIndex(index, index)
-
-        return multi_domain_function
-
     def _get_selected_groups_and_pairs(self):
         return self.context.group_pair_context.selected_groups + self.context.group_pair_context.selected_pairs
 
-    def _check_data_exists(self, guess_selection):
-        return [item for item in guess_selection if AnalysisDataService.doesExist(item)]
-
     def _get_run_number_from_workspace(self, workspace_name):
         instrument = self.context.data_context.instrument
         run = re.findall(r'%s(\d+)' % instrument, workspace_name)
@@ -611,3 +610,13 @@ class FittingTabPresenter(object):
             grp = re.findall(r'%s' % grppair, workspace_name)
             if len(grp) > 0:
                 return grp[0]
+
+    @staticmethod
+    def check_workspaces_are_tf_asymmetry_compliant(workspace_list):
+        non_compliant_workspaces = [item for item in workspace_list if 'Group' not in item]
+        return False if non_compliant_workspaces else True
+
+    @staticmethod
+    def _check_data_exists(guess_selection):
+        return [item for item in guess_selection if AnalysisDataService.doesExist(item)]
+
diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
index 458beb77d34fc140fce9b6d5a9567e852a489312..df4e97c339596428fe3d36b1e5d355dc45f443e2 100644
--- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
+++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
@@ -27,13 +27,10 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         self.setup_simul_fit_combo_box()
         self.undo_fit_button.setEnabled(False)
 
-        self.function_browser = FunctionBrowser(self, False)
-        self.function_browser_multi = FunctionBrowser(self, True)
-        self.function_browser_multi.hide()
+        self.function_browser = FunctionBrowser(self, True)
         self.function_browser_layout.addWidget(self.function_browser)
-        self.function_browser_layout.addWidget(self.function_browser_multi)
         self.function_browser.setErrorsEnabled(True)
-        self.function_browser_multi.setErrorsEnabled(True)
+        self.function_browser.hideGlobalCheckbox()
 
         self.increment_parameter_display_button.clicked.connect(self.increment_display_combo_box)
         self.decrement_parameter_display_button.clicked.connect(self.decrement_display_combo_box)
@@ -85,12 +82,6 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         self.function_browser.removeDatasets(index_list)
         self.function_browser.addDatasets(data_set_name_list)
 
-    def set_datasets_in_function_browser_multi(self, data_set_name_list):
-        number_of_data_sets = self.function_browser_multi.getNumberOfDatasets()
-        index_list = range(number_of_data_sets)
-        self.function_browser_multi.removeDatasets(index_list)
-        self.function_browser_multi.addDatasets(data_set_name_list)
-
     def update_with_fit_outputs(self, fit_function, output_status, output_chi_squared):
         if not fit_function:
             self.fit_status_success_failure.setText('No Fit')
@@ -98,14 +89,14 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
             self.fit_status_chi_squared.setText('Chi squared: {}'.format(output_chi_squared))
             return
 
-        if self.fit_type != self.simultaneous_fit:
+        if self.is_simul_fit():
             self.function_browser.blockSignals(True)
             self.function_browser.updateMultiDatasetParameters(fit_function)
             self.function_browser.blockSignals(False)
         else:
-            self.function_browser_multi.blockSignals(True)
-            self.function_browser_multi.updateMultiDatasetParameters(fit_function)
-            self.function_browser_multi.blockSignals(False)
+            self.function_browser.blockSignals(True)
+            self.function_browser.updateParameters(fit_function)
+            self.function_browser.blockSignals(False)
 
         if output_status == 'success':
             self.fit_status_success_failure.setText('Success')
@@ -172,10 +163,7 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
 
     @property
     def fit_object(self):
-        if self.fit_type != self.simultaneous_fit:
-            return self.function_browser.getGlobalFunction()
-        else:
-            return self.function_browser_multi.getGlobalFunction()
+        return self.function_browser.getGlobalFunction()
 
     @property
     def minimizer(self):
@@ -263,15 +251,16 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         return current_index if current_index != -1 else 0
 
     def get_global_parameters(self):
-        return self.function_browser_multi.getGlobalParameters()
+        return self.function_browser.getGlobalParameters()
 
     def switch_to_simultaneous(self):
-        self.function_browser_multi.show()
-        self.function_browser.hide()
+        self.function_browser.showGlobalCheckbox()
 
     def switch_to_single(self):
-        self.function_browser_multi.hide()
-        self.function_browser.show()
+        self.function_browser.hideGlobalCheckbox()
+
+    def is_simul_fit(self):
+        return self.simul_fit_checkbox.isChecked()
 
     def setup_fit_by_specifier(self, choices):
         self.simul_fit_by_specifier.blockSignals(True)
diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py
index 630a529fbe1d5d594819501e892381a6d054c0c6..e93395cd2bea45dfb8895b764b4d72eb783ac319 100644
--- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py
+++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py
@@ -31,10 +31,6 @@ class FittingTabWidget(object):
             self.fitting_tab_presenter.handle_function_structure_changed)
         self.fitting_tab_view.function_browser.functionStructureChanged.connect(
             self.fitting_tab_presenter.handle_plot_guess_changed)
-        self.fitting_tab_view.function_browser_multi.functionStructureChanged.connect(
-            self.fitting_tab_presenter.handle_function_structure_changed)
-        self.fitting_tab_view.function_browser_multi.functionStructureChanged.connect(
-            self.fitting_tab_presenter.handle_plot_guess_changed)
         self.fitting_tab_view.function_name_line_edit.textChanged.connect(
             self.fitting_tab_presenter.handle_fit_name_changed_by_user)
         self.fitting_tab_view.undo_fit_button.clicked.connect(self.fitting_tab_presenter.handle_undo_fit_clicked)
@@ -43,6 +39,3 @@ class FittingTabWidget(object):
         self.fitting_tab_view.plot_guess_checkbox.stateChanged.connect(self.fitting_tab_presenter.handle_plot_guess_changed)
         self.fitting_tab_view.function_browser.parameterChanged.connect(self.fitting_tab_presenter.handle_function_parameter_changed)
         self.fitting_tab_view.function_browser.parameterChanged.connect(self.fitting_tab_presenter.handle_plot_guess_changed)
-        self.fitting_tab_view.function_browser_multi.parameterChanged.connect(self.fitting_tab_presenter.handle_function_parameter_changed)
-        self.fitting_tab_view.function_browser_multi.parameterChanged.connect(self.fitting_tab_presenter.handle_plot_guess_changed)
-        self.fitting_tab_view.simul_fit_checkbox.toggled.connect(self.fitting_tab_presenter.fitting_domain_type_changed)
diff --git a/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py b/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
index 6a58c69367f193a93ac40b7435ce08db8c6f8307..a6468caa5d7574951288d3164cdc37ca24e76737 100644
--- a/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
+++ b/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 import unittest
 
-from mantid.api import FunctionFactory
+from mantid.api import FunctionFactory, MultiDomainFunction
 from mantid.py3compat import mock
 from mantidqt.utils.qt.testing import start_qapplication
 from qtpy import QtWidgets
@@ -35,6 +35,17 @@ def wait_for_thread(thread_model):
         QtWidgets.QApplication.instance().processEvents()
 
 
+def create_multi_domain_function(function_list):
+    if not any(function_list):
+        return None
+    multi_domain_function = MultiDomainFunction()
+    for index, func in enumerate(function_list):
+        multi_domain_function.add(func)
+        multi_domain_function.setDomainIndex(index, index)
+
+    return multi_domain_function
+
+
 @start_qapplication
 class FittingTabPresenterTest(unittest.TestCase):
     def setUp(self):
@@ -73,7 +84,7 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.assertEqual(self.view.workspace_combo_box_label.text(), 'Display parameters for')
 
-    def test_that_changeing_fit_type_to_single_fit_updates_label(self):
+    def test_that_changing_fit_type_to_single_fit_updates_label(self):
         self.view.simul_fit_checkbox.setChecked(True)
         self.view.simul_fit_checkbox.setChecked(False)
 
@@ -99,6 +110,7 @@ class FittingTabPresenterTest(unittest.TestCase):
     def test_get_parameters_for_single_fit_returns_correctly(self):
         self.presenter.selected_data = ['Input Workspace Name']
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+
         result = self.presenter.get_parameters_for_single_fit()
 
         self.assertEqual(result, {'Function': mock.ANY,
@@ -107,6 +119,18 @@ class FittingTabPresenterTest(unittest.TestCase):
                                   'EvaluationType': 'CentrePoint'}
                          )
 
+    def test_get_parameters_for_simul_fit_returns_correctly(self):
+        self.presenter.selected_data = ['Input Workspace (1)', 'Input Workspace (2)']
+        self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+
+        result = self.presenter.get_multi_domain_fit_parameters()
+
+        self.assertEqual(result['InputWorkspace'], ['Input Workspace (1)', 'Input Workspace (2)'])
+        self.assertEqual(result['Minimizer'], 'Levenberg-Marquardt')
+        self.assertEqual(result['StartX'], [0.0, 0.0])
+        self.assertEqual(result['EndX'], [15.0, 15.0])
+        self.assertEqual(result['EvaluationType'], 'CentrePoint')
+
     def test_for_single_fit_mode_when_display_workspace_changes_updates_fitting_browser_with_new_name(self):
         self.presenter.selected_data = ['Input Workspace Name']
 
@@ -120,8 +144,7 @@ class FittingTabPresenterTest(unittest.TestCase):
             return_value=['Input Workspace Name_1', 'Input Workspace Name 2'])
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
         self.view.simul_fit_checkbox.setChecked(True)
-        print("FIT FUNCTION", self.view.function_browser_multi.getGlobalFunction())
-        self.presenter.model.do_simultaneous_fit.return_value = (self.view.function_browser_multi.getGlobalFunction(),
+        self.presenter.model.do_simultaneous_fit.return_value = (self.view.function_browser.getGlobalFunction(),
                                                                  'Fit Suceeded', 0.5)
 
         self.view.fit_button.clicked.emit(True)
@@ -144,8 +167,8 @@ class FittingTabPresenterTest(unittest.TestCase):
             return_value=['Input Workspace Name_1', 'Input Workspace Name 2'])
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
         self.view.simul_fit_checkbox.setChecked(True)
-        self.view.function_browser_multi.setGlobalParameters(['A'])
-        self.presenter.model.do_simultaneous_fit.return_value = (self.view.function_browser_multi.getGlobalFunction(),
+        self.view.function_browser.setGlobalParameters(['A'])
+        self.presenter.model.do_simultaneous_fit.return_value = (self.view.function_browser.getGlobalFunction(),
                                                                  'Fit Suceeded', 0.5)
 
         self.view.fit_button.clicked.emit(True)
@@ -162,113 +185,70 @@ class FittingTabPresenterTest(unittest.TestCase):
         call_args_globals = simultaneous_call_args[0][1]
         self.assertEqual(call_args_globals, ['A'])
 
-    def test_when_new_data_is_selected_clear_out_old_fits_and_information(self):
-        self.presenter._fit_status = ['success', 'success', 'success']
-        self.presenter._fit_chi_squared = [12.3, 3.4, 0.35]
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.presenter_fit_function = [fit_function, fit_function, fit_function]
-        self.presenter.manual_selection_made = True
-        self.presenter._start_x = [0.15, 0.45, 0.67]
-        self.presenter._end_x = [0.56, 0.78, 0.34]
-        self.view.end_time = 0.56
-        self.view.start_time = 0.15
-        self.presenter.retrieve_first_good_data_from_run_name = mock.MagicMock(return_value=0.15)
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
-
-        self.presenter.selected_data = new_workspace_list
-
-        self.assertEqual(self.presenter._fit_status, [None, None, None])
-        self.assertEqual(self.presenter._fit_chi_squared, [0.0, 0.0, 0.0])
-        self.assertEqual(self.presenter._fit_function, [None, None, None])
-        self.assertEqual(self.presenter._selected_data, new_workspace_list)
-        self.assertEqual(self.presenter.manual_selection_made, True)
-        self.assertEqual(self.presenter.start_x, [0.15, 0.15, 0.15])
-        self.assertEqual(self.presenter.end_x, [0.56, 0.56, 0.56])
-
-    def test_when_new_data_is_selected_updates_combo_box_on_view(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
+    def test_fit_function_updates_correctly_and_resets_gui_state_if_new_data_is_selected_and_single_fit(self):
+        self.presenter._fit_status = ['success']
+        self.presenter._fit_chi_squared = [12.3]
+        fit_function_string = 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'
+        self.view.function_browser.setFunction(fit_function_string)
+        self.view.simul_fit_checkbox.setChecked(False)
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry']
+        self.presenter.get_workspace_selected_list = mock.MagicMock(return_value=new_workspace_list)
 
-        self.presenter.selected_data = new_workspace_list
+        self.presenter.handle_selected_group_pair_changed()
 
-        self.assertEqual(retrieve_combobox_info(self.view.parameter_display_combo), new_workspace_list)
+        self.assertEqual(self.presenter._fit_status, [None, None])
+        self.assertEqual(self.presenter._fit_chi_squared, [0.0, 0.0])
+        self.assertEqual([str(item) for item in self.presenter._fit_function],
+                         [fit_function_string] * 2)
 
-    def test_when_new_data_is_selected_updates_fit_property_browser_appropriately_for_simultaneous(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
+    def test_stored_fit_function_updates_correctly_and_resets_gui_state_if_new_data_is_selected_and_simul_fit(self):
+        self.presenter._fit_status = ['success']
+        self.presenter._fit_chi_squared = [12.3]
+        fit_function_string = 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'
+        self.view.function_browser.setFunction(fit_function_string)
         self.view.simul_fit_checkbox.setChecked(True)
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry']
+        self.presenter.get_workspace_selected_list = mock.MagicMock(return_value=new_workspace_list)
 
-        self.presenter.selected_data = new_workspace_list
+        self.presenter.handle_selected_group_pair_changed()
 
-        self.assertEqual(self.view.function_browser_multi.getDatasetNames(), new_workspace_list)
-        self.assertEqual(self.view.function_browser_multi.getNumberOfDatasets(), 3)
+        self.assertEqual(self.presenter._fit_status, [None, None])
+        self.assertEqual(self.presenter._fit_chi_squared, [0.0, 0.0])
+        self.assertEqual(str(self.presenter._fit_function[0]),
+                         'composite=MultiDomainFunction,NumDeriv=true;name=GausOsc,'
+                         'A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i;name=GausOsc,'
+                         'A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i')
 
-    def test_when_switching_to_simultaneous_function_browser_setup_correctly(self):
+    def test_when_new_data_is_selected_updates_combo_box_on_view(self):
         new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                               'MUSR22725; Group; fwd; Asymmetry']
-        self.presenter.selected_data = new_workspace_list
-        self.presenter.context.get_names_of_workspaces_to_fit = mock.MagicMock(
-            return_value=['MUSR22725; Group; top; Asymmetry'])
-
-        self.view.simul_fit_checkbox.setChecked(True)
-
-        self.assertEqual(self.view.function_browser.getDatasetNames(), ['MUSR22725; Group; top; Asymmetry'])
-        self.assertEqual(self.view.function_browser.getNumberOfDatasets(), 1)
 
-    def test_when_switching_to_simultaneous_with_manual_selection_on_function_browser_setup_correctly(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
         self.presenter.selected_data = new_workspace_list
-        self.presenter.manual_selection_made = True
-
-        self.view.simul_fit_checkbox.setChecked(True)
 
-        self.assertEqual(self.view.function_browser_multi.getDatasetNames(), new_workspace_list)
-        self.assertEqual(self.view.function_browser_multi.getNumberOfDatasets(), 3)
+        self.assertEqual(retrieve_combobox_info(self.view.parameter_display_combo), new_workspace_list)
 
-    def test_when_switching_to_single_fit_from_simultaneous_with_manual_selection_on_function_browser_setup_correctly(
-            self):
+    def test_when_new_data_is_selected_updates_fit_property_browser_appropriately_for_simultaneous(self):
         new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                               'MUSR22725; Group; fwd; Asymmetry']
-        self.presenter.selected_data = new_workspace_list
-        self.presenter.manual_selection_made = True
-
         self.view.simul_fit_checkbox.setChecked(True)
-        self.view.simul_fit_checkbox.setChecked(False)
 
-        self.assertEqual(self.view.function_browser.getDatasetNames(), ['MUSR22725; Group; top; Asymmetry'])
-        self.assertEqual(self.view.function_browser.getNumberOfDatasets(), 1)
-
-    def test_display_workspace_changed_for_single_fit_updates_function_browser(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
         self.presenter.selected_data = new_workspace_list
-        self.presenter.manual_selection_made = True
-        self.presenter._start_x = [0.15, 0.45, 0.67]
-        self.presenter._end_x = [0.56, 0.78, 0.34]
 
-        self.view.parameter_display_combo.setCurrentIndex(1)
-
-        self.assertEqual(self.view.function_browser.getDatasetNames(), ['MUSR22725; Group; bottom; Asymmetry'])
-        self.assertEqual(self.view.function_browser.getNumberOfDatasets(), 1)
-        self.assertEqual(self.view.end_time, 0.78)
-        self.assertEqual(self.view.start_time, 0.45)
+        self.assertEqual(self.view.function_browser.getDatasetNames(), new_workspace_list)
+        self.assertEqual(self.view.function_browser.getNumberOfDatasets(), 3)
 
     def test_display_workspace_changed_for_simultaneous_fit_updates_function_browser(self):
         self.view.simul_fit_checkbox.setChecked(True)
         new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                               'MUSR22725; Group; fwd; Asymmetry']
         self.presenter.selected_data = new_workspace_list
-        self.presenter.manual_selection_made = True
         self.presenter._start_x = [0.15, 0.45, 0.67]
         self.presenter._end_x = [0.56, 0.78, 0.34]
 
         self.view.parameter_display_combo.setCurrentIndex(1)
 
-        self.assertEqual(self.view.function_browser_multi.getDatasetNames(), new_workspace_list)
-        self.assertEqual(self.view.function_browser_multi.getNumberOfDatasets(), 3)
-        # self.assertEqual(self.view.function_browser.getCurrentDataset(), 1) TODO FunctionBrowser seems to have an issue here
+        self.assertEqual(self.view.function_browser.getDatasetNames(), new_workspace_list)
+        self.assertEqual(self.view.function_browser.getNumberOfDatasets(), 3)
         self.assertEqual(self.view.end_time, 0.78)
         self.assertEqual(self.view.start_time, 0.45)
 
@@ -302,176 +282,65 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.assertEqual(self.view.function_name, 'test function')
 
-    def test_check_workspaces_are_tf_asymmetry_compliant(self):
-        list_of_lists_to_test = [
-            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-             'MUSR22725; Group; fwd; Asymmetry'],
-            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-             'MUSR22725; Pair; long; Asymmetry'],
-            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-             'MUSR22725; PhaseQuad']
-        ]
-
-        expected_results = [True, False, False]
-
-        for workspace_list, expected_result in zip(list_of_lists_to_test, expected_results):
-            result = self.presenter.check_workspaces_are_tf_asymmetry_compliant(workspace_list)
-            self.assertEqual(result, expected_result)
-
-    def test_get_parameters_for_tf_function_calculation_for_turning_mode_on(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
-        self.view.tf_asymmetry_mode_checkbox.blockSignals(True)
-        self.view.tf_asymmetry_mode = True
-        self.view.tf_asymmetry_mode_checkbox.blockSignals(False)
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.presenter.selected_data = new_workspace_list
-
-        result = self.presenter.get_parameters_for_tf_function_calculation(fit_function)
-
-        self.assertEqual(result, {'InputFunction': fit_function, 'WorkspaceList': [new_workspace_list[0]],
-                                  'Mode': 'Construct'})
-
-    def test_get_parameters_for_tf_function_calculation_for_turning_mode_off(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; Asymmetry']
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.presenter.selected_data = new_workspace_list
-
-        result = self.presenter.get_parameters_for_tf_function_calculation(fit_function)
-
-        self.assertEqual(result, {'InputFunction': fit_function, 'WorkspaceList': [new_workspace_list[0]],
-                                  'Mode': 'Extract'})
-
-    def test_handle_asymmetry_mode_changed_reverts_changed_and_shows_error_if_non_group_selected(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Pair; fwd; Long']
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.view.function_browser.setFunction(str(fit_function))
-        self.presenter.selected_data = new_workspace_list
-
-        self.view.tf_asymmetry_mode = True
-
-        self.view.warning_popup.assert_called_once_with(
-            'Can only fit groups in tf asymmetry mode and need a function defined')
-
-    def test_handle_asymmetry_mode_correctly_updates_function_for_a_single_fit(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry']
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        fit_function_2 = FunctionFactory.createInitialized('name=GausOsc,A=1.0,Sigma=2.5,Frequency=0.1,Phi=0')
-        self.view.function_browser.setFunction(str(fit_function))
-        self.presenter.selected_data = new_workspace_list
-        self.presenter.model.calculate_tf_function.return_value = fit_function_2
-
-        self.view.tf_asymmetry_mode = True
-
-        self.assertEqual(str(self.view.fit_object), str(fit_function_2))
-        self.assertEqual([str(item) for item in self.presenter._fit_function], [str(fit_function_2)])
-
-    def test_handle_asymmetry_mode_correctly_updates_function_for_sumultaneous_fit(self):
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.view.function_browser.setFunction(str(fit_function))
-        self.view.simul_fit_checkbox.setChecked(True)
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; fwd']
-        self.presenter.selected_data = new_workspace_list
-        fit_function_2 = self.view.fit_object.clone()
-        self.presenter.model.calculate_tf_function.return_value = fit_function_2
-
-        self.view.tf_asymmetry_mode = True
-
-        self.assertEqual([str(item) for item in self.presenter._fit_function], [str(fit_function_2)] * 3)
-        self.assertEqual(str(self.view.fit_object), str(fit_function_2))
-
-    def test_handle_asymmetry_mode_correctly_keeps_globals_for_simultaneous_fit(self):
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.view.function_browser.setFunction(str(fit_function))
-        self.view.simul_fit_checkbox.setChecked(True)
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; fwd']
-        self.presenter.selected_data = new_workspace_list
-        fit_function_2 = self.view.fit_object.clone()
-        self.presenter.model.calculate_tf_function.return_value = fit_function_2
-        self.view.function_browser_multi.setGlobalParameters(['A'])
-
-        self.view.tf_asymmetry_mode = True
-
-        self.assertEqual(self.view.get_global_parameters(), ['f0.f1.f1.A'])
-
-    def test_handle_tf_asymmetry_mode_succesfully_converts_back(self):
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
-                              'MUSR22725; Group; fwd; fwd']
-        self.presenter.selected_data = new_workspace_list
-        self.view.function_browser.setFunction(EXAMPLE_TF_ASYMMETRY_FUNCTION)
-        self.view.tf_asymmetry_mode_checkbox.blockSignals(True)
-        self.view.tf_asymmetry_mode = True
-        self.presenter._tf_asymmetry_mode = True
-        self.view.tf_asymmetry_mode_checkbox.blockSignals(False)
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        self.presenter.model.calculate_tf_function.return_value = fit_function
+    def test_on_function_structure_changed_updates_stored_fit_function_for_single_fit(self):
+        self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                                        'MUSR22725; Group; fwd; Asymmetry']
 
-        self.view.tf_asymmetry_mode = False
+        self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
 
         self.assertEqual([str(item) for item in self.presenter._fit_function],
-                         ['name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0',
-                          'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0',
-                          'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'])
-        self.assertEqual(str(self.view.fit_object), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-
-    def test_handle_fit_with_tf_asymmetry_mode_calls_CalculateMuonAsymmetry(self):
-        self.presenter.model.get_function_name.return_value = 'GausOsc'
-        self.presenter.model.create_fitted_workspace_name.return_value = ('workspace', 'workspace directory')
-        self.presenter.handle_finished = mock.MagicMock()
-        new_workspace_list = ['MUSR22725; Group; top; Asymmetry']
-        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
-        fit_function_2 = FunctionFactory.createInitialized(EXAMPLE_TF_ASYMMETRY_FUNCTION)
-        self.view.function_browser.setFunction(str(fit_function))
-        self.presenter.selected_data = new_workspace_list
-        self.presenter.model.calculate_tf_function.return_value = fit_function_2
-        self.view.tf_asymmetry_mode = True
-        self.presenter.handle_fit_clicked()
-        wait_for_thread(self.presenter.calculation_thread)
-
-        self.assertEqual(self.presenter.handle_finished.call_count, 1)
+                         ['name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'] * 3)
 
-    def test_get_parameters_for_tf_single_fit_calculation(self):
-        self.presenter.model.create_fitted_workspace_name.return_value = ('workspace', 'workspace directory')
-        self.context.group_pair_context.get_unormalisised_workspace_list = mock.MagicMock(return_value=
-        [
-            '__MUSR22725; Group; top; Asymmetry_unnorm',
-            '__MUSR22725; Group; bottom; Asymmetry_unnorm',
-            '__MUSR22725; Group; fwd; Asymmetry_unnorm'])
+    def test_on_function_structure_changed_updates_stored_fit_function_for_simultaneous_fit(self):
+        self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry']
 
-        result = self.presenter.get_parameters_for_tf_single_fit_calculation()
+        self.view.is_simul_fit = mock.MagicMock(return_value=True)
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        multi_domain_function = create_multi_domain_function([fit_function] * 2)
+        self.view.function_browser.setFunction(str(multi_domain_function))
 
-        self.assertEqual(result, {'EndX': 15.0,
-                                  'InputFunction': None,
-                                  'Minimizer': 'Levenberg-Marquardt',
-                                  'OutputFitWorkspace': 'workspace',
-                                  'ReNormalizedWorkspaceList': '',
-                                  'StartX': 0.0,
-                                  'UnNormalizedWorkspaceList': '__MUSR22725; Group; top; Asymmetry_unnorm'}
-                         )
+        self.assertEqual(str(self.presenter._fit_function[0]),
+                         'composite=MultiDomainFunction,NumDeriv=true;name=GausOsc,'
+                         'A=0.2,Sigma=0.2,'
+                         'Frequency=0.1,Phi=0,$domains=i;'
+                         'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i')
 
-    def test_on_function_structure_changed_stores_current_fit_state_in_relevant_presenter(self):
+    def test_updating_function_parameters_updates_relevant_stored_function_for_single_fit(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                                         'MUSR22725; Group; fwd; Asymmetry']
+        self.view.is_simul_fit = mock.MagicMock(return_value=False)
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        multi_domain_function = create_multi_domain_function([fit_function] * 3)
+        self.view.function_browser.setFunction(str(multi_domain_function))
+        self.view.parameter_display_combo.setCurrentIndex(2)
 
-        self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.view.function_browser.setParameter('A', 3)
 
-        self.assertEqual([str(item) for item in self.presenter._fit_function],
-                         ['name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'] * 3)
+        self.presenter.handle_function_parameter_changed()
+
+        self.assertEqual(str(self.presenter._fit_function[0]), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.assertEqual(str(self.presenter._fit_function[1]), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.assertEqual(str(self.presenter._fit_function[2]), 'name=GausOsc,A=3,Sigma=0.2,Frequency=0.1,Phi=0')
 
-    def test_updating_function_parameters_updates_relevant_stored_function(self):
+    def test_updating_function_parameters_updates_relevant_stored_function_for_simul_fit(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                                         'MUSR22725; Group; fwd; Asymmetry']
-        self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.view.is_simul_fit = mock.MagicMock(return_value=True)
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        multi_domain_function = create_multi_domain_function([fit_function] * 3)
+        self.view.function_browser.setFunction(str(multi_domain_function))
+        self.view.parameter_display_combo.setCurrentIndex(1)
+        self.view.function_browser.setParameter('A', 3)
 
-        self.view.function_browser.setParameter('A', 1.5)
+        self.presenter.handle_function_parameter_changed()
 
-        self.assertEqual(str(self.view.fit_object), 'name=GausOsc,A=1.5,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.assertEqual(str(self.presenter._fit_function[0]), 'composite=MultiDomainFunction,NumDeriv=true;'
+                                                               'name=GausOsc,''A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,'
+                                                               '$domains=i;name=GausOsc,A=3,Sigma=0.2,Frequency=0.1,'
+                                                               'Phi=0,$domains=i;name=GausOsc,A=0.2,Sigma=0.2,'
+                                                               'Frequency=0.1,Phi=0,$domains=i')
 
-    def test_handle_display_workspace_changed_updates_displayed_single_function(self):
+    def test_handle_display_workspace_changed_updates_displayed_function(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                                         'MUSR22725; Group; fwd; Asymmetry']
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
@@ -479,17 +348,16 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.view.parameter_display_combo.setCurrentIndex(1)
 
-        self.assertEqual(str(self.view.fit_object), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.assertEqual(str(self.presenter._fit_function[1]), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
 
     def test_setting_selected_data_resets_function_browser_datasets(self):
         self.assertEqual(self.view.function_browser.getDatasetNames(), [])
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                                         'MUSR22725; Group; fwd; Asymmetry']
 
-        self.assertEqual(self.view.function_browser.getDatasetNames(), ['MUSR22725; Group; top; Asymmetry'])
-        self.assertEqual(self.view.function_browser_multi.getDatasetNames(), ['MUSR22725; Group; top; Asymmetry',
-                                                                              'MUSR22725; Group; bottom; Asymmetry',
-                                                                              'MUSR22725; Group; fwd; Asymmetry'])
+        self.assertEqual(self.view.function_browser.getDatasetNames(), ['MUSR22725; Group; top; Asymmetry',
+                                                                        'MUSR22725; Group; bottom; Asymmetry',
+                                                                        'MUSR22725; Group; fwd; Asymmetry'])
 
     def test_switching_to_simultaneous_keeps_stored_fit_functions_same_length(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
@@ -501,10 +369,10 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.view.simul_fit_checkbox.setChecked(True)
 
-        self.assertEqual([str(item) for item in self.presenter._fit_function], [
-            'composite=MultiDomainFunction,NumDeriv=true;name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i'
-            ';name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i;'
-            'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i'] * 3)
+        self.assertEqual(str(self.view.fit_object),
+                         'composite=MultiDomainFunction,NumDeriv=true;name=GausOsc,A=0.2,Sigma=0.2,'
+                         'Frequency=0.1,Phi=0,$domains=i'';name=GausOsc,A=0.2,Sigma=0.2,'
+                         'Frequency=0.1,Phi=0,$domains=i;''name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0,$domains=i')
 
     def test_switching_from_simultaneous_to_single_fit_updates_fit_functions_appropriately(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
@@ -548,13 +416,17 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
         fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.5,Sigma=0.5,Frequency=1,Phi=0')
         self.presenter.fitting_calculation_model = mock.MagicMock()
-        self.presenter.model.do_single_fit.return_value = (fit_function, 'Fit Suceeded', 0.5)
+        self.presenter.model.do_single_fit.return_value = (fit_function, 'Fit Succeeded', 0.5)
         self.presenter.handle_fit_clicked()
         wait_for_thread(self.presenter.calculation_thread)
 
+        # test fit has updated the function
+        self.assertEqual(str(self.presenter._fit_function[0]), 'name=GausOsc,A=0.5,Sigma=0.5,Frequency=1,Phi=0')
+
         self.view.undo_fit_button.clicked.emit(True)
 
-        self.assertEqual(str(self.view.fit_object), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        # test undo fit has worked
+        self.assertEqual(str(self.presenter._fit_function[0]), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
 
     def test_removing_fit_and_then_switching_displayed_workspace_does_not_lead_to_error(self):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
@@ -578,7 +450,7 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.presenter.get_workspace_selected_list = mock.Mock()
         self.presenter.clear_and_reset_gui_state = mock.Mock()
         self.presenter.update_selected_workspace_guess()
-        self.assertEquals(self.presenter.get_workspace_selected_list.call_count, 1)
+        self.assertEqual(self.presenter.get_workspace_selected_list.call_count, 1)
 
     def test_update_selected_ws_guess_non(self):
         self.presenter.manual_selection_made = True
@@ -590,9 +462,9 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.presenter.selected_data = before
 
         self.presenter.update_selected_workspace_guess()
-        self.assertEquals(self.presenter.update_selected_frequency_workspace_guess.call_count, 0)
-        self.assertEquals(self.presenter.update_selected_time_workspace_guess.call_count, 0)
-        self.assertEquals(self.presenter.selected_data, before)
+        self.assertEqual(self.presenter.update_selected_frequency_workspace_guess.call_count, 0)
+        self.assertEqual(self.presenter.update_selected_time_workspace_guess.call_count, 0)
+        self.assertEqual(self.presenter.selected_data, before)
 
     def test_update_time_guess(self):
         self.context.get_names_of_workspaces_to_fit = mock.Mock(return_value="test")
@@ -606,8 +478,8 @@ class FittingTabPresenterTest(unittest.TestCase):
                                                                     rebin=False, freq='None')
         self.context.get_names_of_workspaces_to_fit.assert_any_call(runs="All", group_and_pair="fwd", phasequad=False,
                                                                     rebin=False, freq='None')
-        self.assertEquals(self.presenter.context.get_names_of_workspaces_to_fit.call_count, 2)
-        self.assertEquals(output, ["test"])
+        self.assertEqual(self.presenter.context.get_names_of_workspaces_to_fit.call_count, 2)
+        self.assertEqual(output, ["test"])
 
     def test_handle_plot_guess_changed_calls_correct_function(self):
         self.presenter.get_parameters_for_single_fit = mock.Mock(return_value={})
@@ -623,7 +495,6 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.assertEqual(-1, self.view.minimizer_combo.findText('FABADA'))
 
     def test_simul_fit_by_specifier_updates_correctly_when_fit_change_to_simultanenous(self):
-        # test simul fit by Run
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Run"])
 
         self.view.simul_fit_checkbox.setChecked(True)
@@ -675,8 +546,156 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.assertEqual(str(self.view.parameter_display_combo.itemText(0)), "WS1")
         self.assertEqual(str(self.view.parameter_display_combo.itemText(1)), "WS2")
 
-    def test_simul_fit_by_specifier_corretcly_selects_workspaces_for_fit(self):
-        pass
+    def test_check_workspaces_are_tf_asymmetry_compliant(self):
+        list_of_lists_to_test = [
+            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+             'MUSR22725; Group; fwd; Asymmetry'],
+            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+             'MUSR22725; Pair; long; Asymmetry'],
+            ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+             'MUSR22725; PhaseQuad']
+        ]
+
+        expected_results = [True, False, False]
+
+        for workspace_list, expected_result in zip(list_of_lists_to_test, expected_results):
+            result = self.presenter.check_workspaces_are_tf_asymmetry_compliant(workspace_list)
+            self.assertEqual(result, expected_result)
+
+    def test_get_parameters_for_tf_function_calculation_for_turning_mode_on(self):
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Group; fwd; Asymmetry']
+        self.view.tf_asymmetry_mode_checkbox.blockSignals(True)
+        self.view.tf_asymmetry_mode = True
+        self.view.tf_asymmetry_mode_checkbox.blockSignals(False)
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.presenter.selected_data = new_workspace_list
+
+        result = self.presenter.get_parameters_for_tf_function_calculation(fit_function)
+
+        self.assertEqual(result, {'InputFunction': fit_function, 'WorkspaceList': [new_workspace_list[0]],
+                                  'Mode': 'Construct'})
+
+    def test_get_parameters_for_tf_function_calculation_for_turning_mode_off(self):
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Group; fwd; Asymmetry']
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.presenter.selected_data = new_workspace_list
+
+        result = self.presenter.get_parameters_for_tf_function_calculation(fit_function)
+
+        self.assertEqual(result, {'InputFunction': fit_function, 'WorkspaceList': [new_workspace_list[0]],
+                                  'Mode': 'Extract'})
+
+    def test_handle_asymmetry_mode_changed_reverts_changed_and_shows_error_if_non_group_selected(self):
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Pair; fwd; Long']
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.view.function_browser.setFunction(str(fit_function))
+        self.presenter.selected_data = new_workspace_list
+
+        self.view.tf_asymmetry_mode = True
+
+        self.view.warning_popup.assert_called_once_with(
+            'Can only fit groups in tf asymmetry mode and need a function defined')
+
+    def test_handle_asymmetry_mode_correctly_updates_function_for_a_single_fit(self):
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry']
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        fit_function_2 = FunctionFactory.createInitialized('name=GausOsc,A=1.0,Sigma=2.5,Frequency=0.1,Phi=0')
+        self.view.function_browser.setFunction(str(fit_function))
+        self.presenter.selected_data = new_workspace_list
+        self.presenter.model.calculate_tf_function.return_value = fit_function_2
+
+        self.view.tf_asymmetry_mode = True
+
+        self.assertEqual(str(self.view.fit_object), str(fit_function_2))
+        self.assertEqual([str(item) for item in self.presenter._fit_function], [str(fit_function_2)])
+
+    def test_handle_asymmetry_mode_correctly_updates_function_for_simultaneous_fit(self):
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.view.function_browser.setFunction(str(fit_function))
+        self.view.simul_fit_checkbox.setChecked(True)
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Group; fwd; fwd']
+        self.presenter.selected_data = new_workspace_list
+        fit_function_2 = self.view.fit_object.clone()
+        self.presenter.model.calculate_tf_function.return_value = fit_function_2
+
+        self.view.tf_asymmetry_mode = True
+
+        self.assertEqual([str(item) for item in self.presenter._fit_function], [str(fit_function_2)] * 3)
+        self.assertEqual(str(self.view.fit_object), str(fit_function_2))
+
+    def test_handle_asymmetry_mode_correctly_keeps_globals_for_simultaneous_fit(self):
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.view.function_browser.setFunction(str(fit_function))
+        self.view.simul_fit_checkbox.setChecked(True)
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Group; fwd; fwd']
+        self.presenter.selected_data = new_workspace_list
+        fit_function_2 = self.view.fit_object.clone()
+        self.presenter.model.calculate_tf_function.return_value = fit_function_2
+        self.view.function_browser.setGlobalParameters(['A'])
+
+        self.view.tf_asymmetry_mode = True
+
+        self.assertEqual(self.view.get_global_parameters(), ['f0.f1.f1.A'])
+
+    def test_handle_tf_asymmetry_mode_succesfully_converts_back(self):
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                              'MUSR22725; Group; fwd; fwd']
+        self.presenter.selected_data = new_workspace_list
+        self.view.function_browser.setFunction(EXAMPLE_TF_ASYMMETRY_FUNCTION)
+        self.view.tf_asymmetry_mode_checkbox.blockSignals(True)
+        self.view.tf_asymmetry_mode = True
+        self.presenter._tf_asymmetry_mode = True
+        self.view.tf_asymmetry_mode_checkbox.blockSignals(False)
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.presenter.model.calculate_tf_function.return_value = fit_function
+
+        self.view.tf_asymmetry_mode = False
+
+        self.assertEqual([str(item) for item in self.presenter._fit_function],
+                         ['name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0',
+                          'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0',
+                          'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'])
+        self.assertEqual(str(self.view.fit_object), 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+
+    def test_handle_fit_with_tf_asymmetry_mode_calls_CalculateMuonAsymmetry(self):
+        self.presenter.model.get_function_name.return_value = 'GausOsc'
+        self.presenter.model.create_fitted_workspace_name.return_value = ('workspace', 'workspace directory')
+        self.presenter.handle_finished = mock.MagicMock()
+        new_workspace_list = ['MUSR22725; Group; top; Asymmetry']
+        fit_function = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        fit_function_2 = FunctionFactory.createInitialized(EXAMPLE_TF_ASYMMETRY_FUNCTION)
+        self.view.function_browser.setFunction(str(fit_function))
+        self.presenter.selected_data = new_workspace_list
+        self.presenter.model.calculate_tf_function.return_value = fit_function_2
+        self.view.tf_asymmetry_mode = True
+        self.presenter.handle_fit_clicked()
+        wait_for_thread(self.presenter.calculation_thread)
+
+        self.assertEqual(self.presenter.handle_finished.call_count, 1)
+
+    def test_get_parameters_for_tf_single_fit_calculation(self):
+        self.presenter.model.create_fitted_workspace_name.return_value = ('workspace', 'workspace directory')
+        self.context.group_pair_context.get_unormalisised_workspace_list = mock.MagicMock(return_value=
+        [
+            '__MUSR22725; Group; top; Asymmetry_unnorm',
+            '__MUSR22725; Group; bottom; Asymmetry_unnorm',
+            '__MUSR22725; Group; fwd; Asymmetry_unnorm'])
+
+        result = self.presenter.get_parameters_for_tf_single_fit_calculation()
+
+        self.assertEqual(result, {'EndX': 15.0,
+                                  'InputFunction': None,
+                                  'Minimizer': 'Levenberg-Marquardt',
+                                  'OutputFitWorkspace': 'workspace',
+                                  'ReNormalizedWorkspaceList': '',
+                                  'StartX': 0.0,
+                                  'UnNormalizedWorkspaceList': '__MUSR22725; Group; top; Asymmetry_unnorm'}
+                         )
 
 
 if __name__ == '__main__':