From dd59d29ff701c25ac977e2f81af8a9a939ccadf7 Mon Sep 17 00:00:00 2001
From: Samuel Jackson <samueljackson@outlook.com>
Date: Mon, 28 Jan 2019 15:23:40 +0000
Subject: [PATCH] Add support for Jana format with modulated peaks

---
 .../plugins/algorithms/SaveReflections.py     | 45 ++++++++-----------
 .../plugins/algorithms/SaveReflectionsTest.py |  6 ++-
 .../UnitTest/jana_format_modulated.hkl.md5    |  1 +
 docs/source/release/v3.14.0/diffraction.rst   |  1 +
 4 files changed, 25 insertions(+), 28 deletions(-)
 create mode 100644 Testing/Data/UnitTest/jana_format_modulated.hkl.md5

diff --git a/Framework/PythonInterface/plugins/algorithms/SaveReflections.py b/Framework/PythonInterface/plugins/algorithms/SaveReflections.py
index d2b28cb2661..265743f523c 100644
--- a/Framework/PythonInterface/plugins/algorithms/SaveReflections.py
+++ b/Framework/PythonInterface/plugins/algorithms/SaveReflections.py
@@ -6,7 +6,7 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 import re
 import numpy as np
-from mantid.api import AlgorithmFactory, FileProperty, FileAction, PythonAlgorithm, ITableWorkspaceProperty
+from mantid.api import AlgorithmFactory, FileProperty, FileAction, PythonAlgorithm, ITableWorkspaceProperty, IPeaksWorkspace
 from mantid.kernel import StringListValidator, Direction
 from mantid.simpleapi import SaveHKL
 
@@ -168,9 +168,6 @@ class JanaFormat(object):
         :param file_name: the file name to output data to.
         :param workspace: the PeaksWorkspace to write to file.
         """
-        if has_modulated_indexing(workspace):
-            raise NotImplementedError("Cannot currently save modulated structures to Jana format")
-        self._cache_instrument_params(workspace)
         with open(file_name, 'w') as f_handle:
             self.write_header(f_handle, workspace)
             self.write_peaks(f_handle, workspace)
@@ -181,11 +178,13 @@ class JanaFormat(object):
         :param f_handle: handle to the file to write to.
         :param workspace: the PeaksWorkspace to save to file.
         """
-        sample = workspace.sample()
-        lattice = sample.getOrientedLattice()
-        lattice_params = [lattice.a(), lattice.b(), lattice.c(), lattice.alpha(), lattice.beta(), lattice.gamma()]
-        lattice_params = "".join(["{: >10.4f}".format(value) for value in lattice_params])
-        f_handle.write("# Lattice parameters   {}\n".format(lattice_params))
+        if isinstance(workspace, IPeaksWorkspace):
+            sample = workspace.sample()
+            lattice = sample.getOrientedLattice()
+            lattice_params = [lattice.a(), lattice.b(), lattice.c(), lattice.alpha(), lattice.beta(), lattice.gamma()]
+            lattice_params = "".join(["{: >10.4f}".format(value) for value in lattice_params])
+            f_handle.write("# Lattice parameters   {}\n".format(lattice_params))
+
         f_handle.write("(3i5,2f12.2,i5,4f10.4)\n")
 
     def write_peaks(self, f_handle, workspace):
@@ -219,29 +218,21 @@ class JanaFormat(object):
         f_handle.write("{SigInt: >12.2f}".format(**peak))
         f_handle.write("{: >5.0f}".format(1))
         f_handle.write("{Wavelength: >10.4f}".format(**peak))
-        f_handle.write("{: >10.4f}".format(self._get_two_theta(peak['DetID'])))
+        f_handle.write("{: >10.4f}".format(self._get_two_theta(peak)))
         f_handle.write("{: >10.4f}{: >10.4f}{: >10.4f}".format(1.0, 0.0, 0.0))
         f_handle.write("\n")
 
-    def _get_two_theta(self, det_id):
-        """Get the two theta value for this peak
-        :param det_id: the detector ID of this Peak
-        """
-        det = self._instrument.getDetector(det_id)
-        return np.degrees(det.getTwoTheta(self._instrument.getPos(), self._z_axis))
-
-    def _cache_instrument_params(self, workspace):
-        """Cache some parameters about the instrument
-
-        This stores some of the instrument parameters at the start
-        of execution so we can quickly access them later.
+    def _get_two_theta(self, peak):
+        """Get the two theta value for this peak.
 
-        :param workspace: the PeaksWorkspace to cache instrument data from.
+        This is just Bragg's law relating wavelength to scattering angle.
+        :param peak: peak object to get the scattering angle for.
+        :returns: the scattering angle for the peak.
         """
-        self._instrument = workspace.getInstrument()
-        frame = self._instrument.getReferenceFrame()
-        self._z_axis = frame.vecPointingAlongBeam()
-
+        d = peak['DSpacing']
+        wavelength = peak['Wavelength']
+        theta = 2.*np.arcsin(0.5*(wavelength / d))
+        return np.rad2deg(theta)
 
 # ------------------------------------------------------------------------------------------------------
 
diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/SaveReflectionsTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/SaveReflectionsTest.py
index 63af0dd771b..403019976a8 100644
--- a/Framework/PythonInterface/test/python/plugins/algorithms/SaveReflectionsTest.py
+++ b/Framework/PythonInterface/test/python/plugins/algorithms/SaveReflectionsTest.py
@@ -140,11 +140,15 @@ class SaveReflectionsTest(unittest.TestCase):
     def test_save_jana_format_modulated(self):
         # Arrange
         workspace = self._create_modulated_peak_table()
+        reference_result = self._get_reference_result("jana_format_modulated.hkl")
         file_name = os.path.join(self._test_dir, "test_jana_modulated.hkl")
         output_format = "Jana"
 
         # Act
-        self.assertRaises(RuntimeError, SaveReflections, InputWorkspace=workspace, Filename=file_name, Format=output_format)
+        SaveReflections(InputWorkspace=workspace, Filename=file_name, Format=output_format)
+
+        # Assert
+        self.assertTrue(compare_file(reference_result, file_name))
 
     def test_save_GSAS_format(self):
         # Arrange
diff --git a/Testing/Data/UnitTest/jana_format_modulated.hkl.md5 b/Testing/Data/UnitTest/jana_format_modulated.hkl.md5
new file mode 100644
index 00000000000..148d77dbf3f
--- /dev/null
+++ b/Testing/Data/UnitTest/jana_format_modulated.hkl.md5
@@ -0,0 +1 @@
+96c8aa0e0a91f2c537ec06f0170d7d25
diff --git a/docs/source/release/v3.14.0/diffraction.rst b/docs/source/release/v3.14.0/diffraction.rst
index 30df5d2faae..6e3734d6d24 100644
--- a/docs/source/release/v3.14.0/diffraction.rst
+++ b/docs/source/release/v3.14.0/diffraction.rst
@@ -77,6 +77,7 @@ Improvements
 - Focus on Pearl now saves out xye_tof files.
 - :ref:`PDLoadCharacterizations <algm-PDLoadCharacterizations>` now sets the same run numbers for all rows when using an ``exp.ini`` file.
 - Focus now checks if the vanadium for a run is already loaded before loading it in to prevent reloading the same vanadium multiple times.
+- :ref:`SaveReflections <algm-SaveReflections>` now supports saving indexed modulated peaks in the Jana format.
 
 
 Bugfixes
-- 
GitLab