diff --git a/docs/source/interfaces/Engineering Diffraction.rst b/docs/source/interfaces/Engineering Diffraction.rst
index 89477dfa73a00c494143c17a0cd66574c8f5cce2..f25105692a3eb72adc7cf9b9449c3b47fb8a8c0f 100644
--- a/docs/source/interfaces/Engineering Diffraction.rst	
+++ b/docs/source/interfaces/Engineering Diffraction.rst	
@@ -121,7 +121,8 @@ a plot for each bank and cropped focusing generates a plot for the single bank o
 Clicking the focus button will begin the focusing algorithm for the selected run files. The button and plotting checkbox
 will be disabled until the fitting algorithm is complete.
 
-The focused output files are saved in NeXus, GSS, and raw XYE format to:
+The focused output files are saved in NeXus, GSS, and TOPAS format. The process will also output a CSV file containing
+all numerical sample logs. All of these files are saved to:
 
 `<CHOSEN_OUTPUT_DIRECTORY>/Focus/`
 
diff --git a/docs/source/release/v5.1.0/diffraction.rst b/docs/source/release/v5.1.0/diffraction.rst
index d4c0041287b2310f43156cddeda2fe8b045647e2..6a86de8a8e6c03acb1590156efefb2db30928069 100644
--- a/docs/source/release/v5.1.0/diffraction.rst
+++ b/docs/source/release/v5.1.0/diffraction.rst
@@ -14,6 +14,10 @@ Powder Diffraction
 
 Engineering Diffraction
 -----------------------
+Improvements
+^^^^^^^^^^^^
+- TOPAS files (`.abc`) have replaced the `.dat` files generated when focusing using the GUI.
+- Focusing with the GUI will now generate a CSV containing the averaged values of all numerical sample logs.
 
 Single Crystal Diffraction
 --------------------------
diff --git a/scripts/Engineering/gui/engineering_diffraction/tabs/focus/model.py b/scripts/Engineering/gui/engineering_diffraction/tabs/focus/model.py
index 3ca4cb3ffb10fc824d4978a3b71ede70f1e950fd..a744c6995cc5cd64365faa0d8388afde7cc35219 100644
--- a/scripts/Engineering/gui/engineering_diffraction/tabs/focus/model.py
+++ b/scripts/Engineering/gui/engineering_diffraction/tabs/focus/model.py
@@ -6,7 +6,8 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 from __future__ import (absolute_import, division, print_function)
 
-from os import path
+import csv
+from os import path, makedirs
 from matplotlib import gridspec
 import matplotlib.pyplot as plt
 
@@ -55,6 +56,7 @@ class FocusModel(object):
                     # Save the output to the file system.
                     self._save_output(instrument, sample_path, name, output_workspace_name, rb_num)
                 output_workspaces.append(workspaces_for_run)
+                self._output_sample_logs(instrument, run_no, sample_workspace, rb_num)
         else:
             for sample_path in sample_paths:
                 sample_workspace = path_handling.load_workspace(sample_path)
@@ -64,6 +66,7 @@ class FocusModel(object):
                                 curves_workspace, None, full_calib_workspace, spectrum_numbers)
                 output_workspaces.append([output_workspace_name])
                 self._save_output(instrument, sample_path, "cropped", output_workspace_name, rb_num)
+                self._output_sample_logs(instrument, run_no, sample_workspace, rb_num)
         # Plot the output
         if plot_output:
             for ws_names in output_workspaces:
@@ -126,8 +129,8 @@ class FocusModel(object):
                                                  rb_num)
         self._save_focused_output_files_as_gss(instrument, sample_path, bank, sample_workspace,
                                                rb_num)
-        self._save_focused_output_files_as_xye(instrument, sample_path, bank, sample_workspace,
-                                               rb_num)
+        self._save_focused_output_files_as_topas_xye(instrument, sample_path, bank, sample_workspace,
+                                                     rb_num)
 
     def _save_focused_output_files_as_gss(self, instrument, sample_path, bank, sample_workspace,
                                           rb_num):
@@ -153,19 +156,53 @@ class FocusModel(object):
                 self._generate_output_file_name(instrument, sample_path, bank, ".nxs"))
             SaveNexus(InputWorkspace=sample_workspace, Filename=nexus_output_path)
 
-    def _save_focused_output_files_as_xye(self, instrument, sample_path, bank, sample_workspace,
-                                          rb_num):
+    def _save_focused_output_files_as_topas_xye(self, instrument, sample_path, bank,
+                                                sample_workspace, rb_num):
         xye_output_path = path.join(
             path_handling.get_output_path(), "Focus",
-            self._generate_output_file_name(instrument, sample_path, bank, ".dat"))
-        SaveFocusedXYE(InputWorkspace=sample_workspace, Filename=xye_output_path, SplitFiles=False)
+            self._generate_output_file_name(instrument, sample_path, bank, ".abc"))
+        SaveFocusedXYE(InputWorkspace=sample_workspace,
+                       Filename=xye_output_path,
+                       SplitFiles=False,
+                       Format="TOPAS")
         if rb_num:
             xye_output_path = path.join(
                 path_handling.get_output_path(), "User", rb_num, "Focus",
-                self._generate_output_file_name(instrument, sample_path, bank, ".dat"))
+                self._generate_output_file_name(instrument, sample_path, bank, ".abc"))
             SaveFocusedXYE(InputWorkspace=sample_workspace,
                            Filename=xye_output_path,
-                           SplitFiles=False)
+                           SplitFiles=False,
+                           Format="TOPAS")
+
+    @staticmethod
+    def _output_sample_logs(instrument, run_number, workspace, rb_num):
+        def write_to_file():
+            with open(output_path, "w", newline="") as logfile:
+                writer = csv.writer(logfile, ["Sample Log", "Avg Value"])
+                for log in output_dict:
+                    writer.writerow([log, output_dict[log]])
+
+        output_dict = {}
+        sample_run = workspace.getRun()
+        log_names = sample_run.keys()
+        # Collect numerical sample logs.
+        for name in log_names:
+            try:
+                output_dict[name] = sample_run.getPropertyAsSingleValue(name)
+            except ValueError:
+                logger.information(f"Could not convert {name} to a numerical value. It will not be included in the "
+                                   f"sample logs output file.")
+        focus_dir = path.join(path_handling.get_output_path(), "Focus")
+        if not path.exists(focus_dir):
+            makedirs(focus_dir)
+        output_path = path.join(focus_dir, (instrument + "_" + run_number + "_sample_logs.csv"))
+        write_to_file()
+        if rb_num:
+            focus_user_dir = path.join(path_handling.get_output_path(), "User", rb_num, "Focus")
+            if not path.exists(focus_user_dir):
+                makedirs(focus_user_dir)
+            output_path = path.join(focus_user_dir, (instrument + "_" + run_number + "_sample_logs.csv"))
+            write_to_file()
 
     @staticmethod
     def _generate_output_file_name(instrument, sample_path, bank, suffix):
diff --git a/scripts/Engineering/gui/engineering_diffraction/tabs/focus/test/test_focus_model.py b/scripts/Engineering/gui/engineering_diffraction/tabs/focus/test/test_focus_model.py
index 7dd31c555c2d73a993dfb77e288a0c8bc403c432..d117784272767ff41946ec001cff9adf9a8efce7 100644
--- a/scripts/Engineering/gui/engineering_diffraction/tabs/focus/test/test_focus_model.py
+++ b/scripts/Engineering/gui/engineering_diffraction/tabs/focus/test/test_focus_model.py
@@ -7,9 +7,12 @@
 from __future__ import (absolute_import, division, print_function)
 
 import unittest
+import tempfile
+import shutil
 from os import path
 
-from mantid.py3compat.mock import patch
+from mantid.py3compat.mock import patch, MagicMock
+from mantid.simpleapi import CreateSampleWorkspace
 from Engineering.gui.engineering_diffraction.tabs.focus import model
 from Engineering.gui.engineering_diffraction.tabs.common import path_handling
 from Engineering.gui.engineering_diffraction.tabs.common.calibration_info import CalibrationInfo
@@ -19,11 +22,15 @@ file_path = "Engineering.gui.engineering_diffraction.tabs.focus.model"
 
 class FocusModelTest(unittest.TestCase):
     def setUp(self):
+        self.test_dir = tempfile.mkdtemp()
         self.model = model.FocusModel()
         self.current_calibration = CalibrationInfo(vanadium_path="/mocked/out/anyway",
                                                    sample_path="this_is_mocked_out_too",
                                                    instrument="ENGINX")
 
+    def tearDown(self):
+        shutil.rmtree(self.test_dir)
+
     @patch(file_path + ".path_handling.load_workspace")
     @patch(file_path + ".vanadium_corrections.Ads.doesExist")
     def test_focus_cancelled_if_van_wsp_missing(self, ads_exist, load):
@@ -31,11 +38,12 @@ class FocusModelTest(unittest.TestCase):
         self.model.focus_run("307593", ["1", "2"], False, "ENGINX", "0", None)
         self.assertEqual(load.call_count, 0)
 
+    @patch(file_path + ".FocusModel._output_sample_logs")
     @patch(file_path + ".Ads")
     @patch(file_path + ".FocusModel._save_output")
     @patch(file_path + ".FocusModel._run_focus")
     @patch(file_path + ".path_handling.load_workspace")
-    def test_focus_run_for_each_bank(self, load_focus, run_focus, output, ads):
+    def test_focus_run_for_each_bank(self, load_focus, run_focus, output, ads, logs):
         ads.retrieve.return_value = "test_wsp"
         banks = ["1", "2"]
         load_focus.return_value = "mocked_sample"
@@ -47,11 +55,12 @@ class FocusModelTest(unittest.TestCase):
                                      "305761_" + model.FOCUSED_OUTPUT_WORKSPACE_NAME + banks[-1],
                                      "test_wsp", "test_wsp", banks[-1], None)
 
+    @patch(file_path + ".FocusModel._output_sample_logs")
     @patch(file_path + ".Ads")
     @patch(file_path + ".FocusModel._save_output")
     @patch(file_path + ".FocusModel._run_focus")
     @patch(file_path + ".path_handling.load_workspace")
-    def test_focus_run_for_custom_spectra(self, load_focus, run_focus, output, ads):
+    def test_focus_run_for_custom_spectra(self, load_focus, run_focus, output, ads, logs):
         ads.retrieve.return_value = "test_wsp"
         spectra = "20-50"
         load_focus.return_value = "mocked_sample"
@@ -63,6 +72,7 @@ class FocusModelTest(unittest.TestCase):
                                      "305761_" + model.FOCUSED_OUTPUT_WORKSPACE_NAME + "cropped",
                                      "test_wsp", "test_wsp", None, None, spectra)
 
+    @patch(file_path + ".FocusModel._output_sample_logs")
     @patch(file_path + ".Ads")
     @patch(file_path + ".FocusModel._save_output")
     @patch(file_path + ".FocusModel._plot_focused_workspaces")
@@ -70,7 +80,7 @@ class FocusModelTest(unittest.TestCase):
     @patch(file_path + ".path_handling.load_workspace")
     @patch(file_path + ".vanadium_corrections.fetch_correction_workspaces")
     def test_focus_plotted_when_checked(self, fetch_van, load_focus, run_focus, plot_focus, output,
-                                        ads):
+                                        ads, logs):
         ads.doesExist.return_value = True
         fetch_van.return_value = ("mocked_integ", "mocked_curves")
         banks = ["1", "2"]
@@ -80,6 +90,7 @@ class FocusModelTest(unittest.TestCase):
 
         self.assertEqual(1, plot_focus.call_count)
 
+    @patch(file_path + ".FocusModel._output_sample_logs")
     @patch(file_path + ".Ads")
     @patch(file_path + ".FocusModel._save_output")
     @patch(file_path + ".FocusModel._plot_focused_workspaces")
@@ -87,7 +98,7 @@ class FocusModelTest(unittest.TestCase):
     @patch(file_path + ".path_handling.load_workspace")
     @patch(file_path + ".vanadium_corrections.fetch_correction_workspaces")
     def test_focus_not_plotted_when_not_checked(self, fetch_van, load_focus, run_focus, plot_focus,
-                                                output, ads):
+                                                output, ads, logs):
         fetch_van.return_value = ("mocked_integ", "mocked_curves")
         banks = ["1", "2"]
         load_focus.return_value = "mocked_sample"
@@ -121,6 +132,36 @@ class FocusModelTest(unittest.TestCase):
         self.assertEqual(gss.call_count, 2)
         self.assertEqual(xye.call_count, 2)
 
+    @patch(file_path + ".logger")
+    @patch(file_path + ".path_handling.get_output_path")
+    @patch(file_path + ".csv")
+    def test_output_sample_logs_with_rb_number(self, mock_csv, mock_path, mock_logger):
+        mock_writer = MagicMock()
+        mock_csv.writer.return_value = mock_writer
+        mock_path.return_value = self.test_dir
+        ws = CreateSampleWorkspace()
+
+        self.model._output_sample_logs("ENGINX", "00000", ws, "0")
+
+        self.assertEqual(5, len(ws.getRun().keys()))
+        self.assertEqual(1, mock_logger.information.call_count)
+        self.assertEqual(8, mock_writer.writerow.call_count)
+
+    @patch(file_path + ".logger")
+    @patch(file_path + ".path_handling.get_output_path")
+    @patch(file_path + ".csv")
+    def test_output_sample_logs_without_rb_number(self, mock_csv, mock_path, mock_logger):
+        mock_writer = MagicMock()
+        mock_csv.writer.return_value = mock_writer
+        mock_path.return_value = self.test_dir
+        ws = CreateSampleWorkspace()
+
+        self.model._output_sample_logs("ENGINX", "00000", ws, None)
+
+        self.assertEqual(5, len(ws.getRun().keys()))
+        self.assertEqual(1, mock_logger.information.call_count)
+        self.assertEqual(4, mock_writer.writerow.call_count)
+
 
 if __name__ == '__main__':
     unittest.main()