Merge pull request #2030 from kif/1648_support_xrdml

Support Xrdml
This commit is contained in:
Edgar Gutierrez 2024-01-15 13:11:27 +01:00 committed by GitHub
commit 685e67d8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 17 deletions

View File

@ -8,7 +8,7 @@ Change-log of versions
2024.1 UNRELEASED
-----------------
- Support XRDML formt (compatibility with MAUD software)
- Support pathlib for reading PONI files
- Support pathlib for reading PONI files
- Refactor `pyFAI-benchmark` tool (Thanks Edgar)
- Possibility to define the detector orientation:
+ It is the position of the origin of the detector at any of the 4 corner of the image

View File

@ -30,7 +30,7 @@ __author__ = "Jérôme Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "21/11/2023"
__date__ = "11/01/2024"
__status__ = "stable"
__docformat__ = 'restructuredtext'
@ -47,7 +47,7 @@ from . import units
from .utils import EPS32, deg2rad, crc32
from .utils.decorators import deprecated, deprecated_warning
from .containers import Integrate1dResult, Integrate2dResult, SeparateResult, ErrorModel
from .io import DefaultAiWriter
from .io import DefaultAiWriter, save_integrate_result
from .io.ponifile import PoniFile
error = None
from .method_registry import IntegrationMethod
@ -1099,8 +1099,7 @@ class AzimuthalIntegrator(Geometry):
result._set_metadata(metadata)
if filename is not None:
writer = DefaultAiWriter(filename, self)
writer.write(result)
save_integrate_result(filename, result)
return result
@ -1600,9 +1599,7 @@ class AzimuthalIntegrator(Geometry):
result._set_has_solidangle_correction(correctSolidAngle)
if filename is not None:
writer = DefaultAiWriter(filename, self)
writer.write(result)
save_integrate_result(filename, result)
return result
_integrate1d_ng = integrate1d_ng
@ -2102,8 +2099,7 @@ class AzimuthalIntegrator(Geometry):
result._set_metadata(metadata)
if filename is not None:
writer = DefaultAiWriter(filename, self)
writer.write(result)
save_integrate_result(filename, result)
return result
@ -2617,8 +2613,7 @@ class AzimuthalIntegrator(Geometry):
result._set_std(sem)
if filename is not None:
writer = DefaultAiWriter(filename, self)
writer.write(result)
save_integrate_result(filename, result)
return result

View File

@ -42,7 +42,7 @@ __author__ = "Jerome Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "25/09/2023"
__date__ = "11/01/2024"
__status__ = "production"
__docformat__ = 'restructuredtext'
@ -999,6 +999,9 @@ def save_integrate_result(filename, result, title="title", sample="sample", inst
"""
if filename.endswith(".nxs"):
raise RuntimeError("Implement Nexus writer")
elif filename.endswith(".xrdml"):
from .xrdml import save_xrdml
save_xrdml(filename, result)
else:
writer = DefaultAiWriter(filename, result.poni)
writer.write(result)
writer = DefaultAiWriter(filename, result.poni)
writer.write(result)

View File

@ -6,7 +6,8 @@ py.install_sources(
'nexus.py',
'ponifile.py',
'sparse_frame.py',
'spots.py'],
'spots.py',
'xrdml.py'],
pure: false, # Will be installed next to binaries
subdir: 'pyFAI/io' # Folder relative to site-packages to install to
)

121
src/pyFAI/io/xrdml.py Normal file
View File

@ -0,0 +1,121 @@
# coding: utf-8
#
# Project: Azimuthal integration
# https://github.com/silx-kit/pyFAI
#
# Copyright (C) 2024-2024 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer (Jerome.Kieffer@ESRF.eu)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Module for exporting XRDML powder diffraction files
Inspiration from
"""
__author__ = "Jérôme Kieffer"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "11/01/2024"
__docformat__ = 'restructuredtext'
import os
from xml.etree import ElementTree as et
from .nexus import get_isotime
from .. import version as pyFAI_version
def save_xrdml(filename, result):
"""
https://www.researchgate.net/profile/Mohamed-Ali-392/post/XRD_Refinement_for_TiO2_Anatase_using_MAUD/attachment/60fa1d85647f3906fc8af2f3/AS%3A1048546157539329%401627004293526/download/sample.xrdml
"""
now = get_isotime()
dirname = os.path.dirname(os.path.abspath(filename))
if not os.path.isdir(dirname):
os.makedirs(dirname, exist_ok=True)
xrdml = et.Element("xrdMeasurements", {"xmlns":"http://www.xrdml.com/XRDMeasurement/1.0",
"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
"xsi:schemaLocation": "http://www.xrdml.com/XRDMeasurement/1.3 http://www.xrdml.com/XRDMeasurement/1.3/XRDMeasurement.xsd",
"status": "Completed"})
sample = et.Element("sample", type="To be analyzed")
xid = et.Element("id")
xid.text = "000000-0000"
sample.append(xid)
name = et.Element("name")
name.text = "sample"
sample.append(name)
xrdml.append(sample)
measurement = et.Element("xrdMeasurement", {"measurementType":"Scan", "status":"Completed"})
wavelength = result.poni.wavelength
if wavelength:
txtwavelength = str(wavelength*1e10)
usedWavelength = et.Element("usedWavelength", intended="K-Alpha")
k_alpha1 = et.Element("kAlpha1", unit="Angstrom")
k_alpha1.text = txtwavelength
usedWavelength.append(k_alpha1)
k_alpha2 = et.Element("kAlpha2", unit="Angstrom")
k_alpha2.text = txtwavelength
usedWavelength.append(k_alpha2)
k_beta = et.Element("kBeta", unit="Angstrom")
k_beta.text = txtwavelength
usedWavelength.append(k_beta)
ratio = et.Element("ratioKAlpha2KAlpha1")
ratio.text = "0"
usedWavelength.append(ratio)
measurement.append(usedWavelength)
scan = et.Element("scan", {"appendNumber":"0",
"mode":"Pre-set time",
"measurementType": "Area measurement",
"scanAxis":"Gonio",
"status":"Completed"})
header = et.Element("header")
for stamp in ("startTimeStamp", "endTimeStamp"):
estamp = et.Element(stamp)
estamp.text = now
header.append(estamp)
author = et.Element("author")
name = et.Element("name")
name.text = "pyFAI"
author.append(name)
header.append(author)
source = et.Element("source")
sw = et.Element("applicationSoftware", version=pyFAI_version)
sw.text='pyFAI'
source.append(sw)
header.append(source)
scan.append(header)
datapoints = et.Element("dataPoints")
positions = et.Element("positions", axis=result.unit.short_name, unit=result.unit.unit_symbol)
for pos, idx in {"startPosition": 0, "endPosition":-1}.items():
position = et.Element(pos)
position.text = str(result.radial[idx])
positions.append(position)
datapoints.append(positions)
ct = et.Element("commonCountingTime", unit="seconds")
ct.text = "1.00"
datapoints.append(ct)
intensities = et.Element("intensities", unit="counts")
intensities.text = " ".join(str(i) for i in result.intensity)
datapoints.append(intensities)
scan.append(datapoints)
measurement.append(scan)
xrdml.append(measurement)
with open(filename, "wb") as w:
w.write(et.tostring(xrdml))

View File

@ -32,7 +32,7 @@ __author__ = "Jérôme Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "09/01/2024"
__date__ = "10/01/2024"
import unittest
import os
@ -296,6 +296,38 @@ class TestSpotWriter(unittest.TestCase):
self.assertGreater(size.st_size, sum(i.size for i in self.spots), "file is large enough")
class TestXrdmlWriter(unittest.TestCase):
@classmethod
def setUpClass(cls)->None:
super(TestXrdmlWriter, cls).setUpClass()
cls.img = fabio.open(UtilsTest.getimage("Pilatus1M.edf"))
cls.ai = pyFAI.load(UtilsTest.getimage("Pilatus1M.poni"))
cls.result = cls.ai.integrate1d(cls.img.data, 200, method=("no", "histogram", "cython"), unit="2th_deg")
@classmethod
def tearDownClass(cls)->None:
super(TestXrdmlWriter, cls).tearDownClass()
cls.ai = cls.img = cls.result=None
def test_xrdml(self):
from ..io.xrdml import save_xrdml
fd, tmpfile = UtilsTest.tempfile(".xrdml")
os.close(fd)
save_xrdml(tmpfile, self.result)
self.assertGreater(os.path.getsize(tmpfile), 3000)
def test_integration(self):
fd, tmpfile = UtilsTest.tempfile(".xrdml")
os.close(fd)
self.ai.integrate1d(self.img.data, 200, method=("no", "histogram", "cython"), unit="2th_deg",
filename=tmpfile)
self.assertGreater(os.path.getsize(tmpfile), 3000)
from xml.etree import ElementTree as et
with open(tmpfile, "rb") as f:
xml = et.fromstring(f.read())
def suite():
testsuite = unittest.TestSuite()
loader = unittest.defaultTestLoader.loadTestsFromTestCase
@ -304,6 +336,7 @@ def suite():
testsuite.addTest(loader(TestHDF5Writer))
testsuite.addTest(loader(TestFabIOWriter))
testsuite.addTest(loader(TestSpotWriter))
testsuite.addTest(loader(TestXrdmlWriter))
testsuite.addTest(loader(TestPoniFile))
return testsuite