Merge remote-tracking branch 'upstream/main' into 2024.01

This commit is contained in:
Jerome Kieffer 2024-01-15 17:11:57 +01:00
commit dde9ce1daf
8 changed files with 194 additions and 21 deletions

View File

@ -1,5 +1,5 @@
:Author: Jérôme Kieffer :Author: Jérôme Kieffer
:Date: 15/01/2024 :Date: 10/01/2024
:Keywords: changelog :Keywords: changelog
Change-log of versions Change-log of versions

View File

@ -44,6 +44,9 @@ Usage
**-d**, **--debug** **-d**, **--debug**
switch to verbose/debug mode switch to verbose/debug mode
**--no-proc**
do not benchmark using the central processor
**-c**, **--cpu** **-c**, **--cpu**
perform benchmark using OpenCL on the CPU perform benchmark using OpenCL on the CPU
@ -58,8 +61,7 @@ Usage
limited memory) limited memory)
**-n** NUMBER, **--number** NUMBER **-n** NUMBER, **--number** NUMBER
Runtime for each test, in seconds, by Perform the test for this amount of time, by default 10s/measurment
default 10
**-2d**, **--2dimention** **-2d**, **--2dimention**
Benchmark also algorithm for 2D-regrouping Benchmark also algorithm for 2D-regrouping
@ -71,7 +73,25 @@ Usage
Perfrom memory profiling (Linux only) Perfrom memory profiling (Linux only)
**-r** REPEAT, **--repeat** REPEAT **-r** REPEAT, **--repeat** REPEAT
Repeat each benchmark x times to take the best, by default only run once Repeat each measurement x times to take the best
**-ps** PIXELSPLIT [PIXELSPLIT ...], **--pixelsplit** PIXELSPLIT [PIXELSPLIT ...]
Benchmark using specific pixel splitting protocols: no, bbox, pseudo,
full, all
**-algo** ALGORITHM [ALGORITHM ...], **--algorithm** ALGORITHM [ALGORITHM ...]
Benchmark using specific algorithms: histogram, CSR, CSC, all
**-i** IMPLEMENTATION [IMPLEMENTATION ...], **--implementation** IMPLEMENTATION [IMPLEMENTATION ...]
Benchmark using specific algorithm implementations: python, cython,
opencl, all
**-f** FUNCTION, **--function** FUNCTION
Benchmark legacy (legacy), engine function (ng), or both (all)
**--all**
Benchmark using all available methods and devices
Results Results
------- -------

View File

@ -99,4 +99,4 @@ pyFAI-integrate = 'pyFAI.app.integrate:main'
[tool.cibuildwheel] [tool.cibuildwheel]
# Skip 32-bit builds and PyPy # Skip 32-bit builds and PyPy
skip = ["*-win32", "*-manylinux_i686", "pp*"] skip = ["*-win32", "*-manylinux_i686", "pp*", "*musllinux*"]

View File

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

View File

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

View File

@ -6,7 +6,8 @@ py.install_sources(
'nexus.py', 'nexus.py',
'ponifile.py', 'ponifile.py',
'sparse_frame.py', 'sparse_frame.py',
'spots.py'], 'spots.py',
'xrdml.py'],
pure: false, # Will be installed next to binaries pure: false, # Will be installed next to binaries
subdir: 'pyFAI/io' # Folder relative to site-packages to install to 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" __contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT" __license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "09/01/2024" __date__ = "10/01/2024"
import unittest import unittest
import os 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") 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(): def suite():
testsuite = unittest.TestSuite() testsuite = unittest.TestSuite()
loader = unittest.defaultTestLoader.loadTestsFromTestCase loader = unittest.defaultTestLoader.loadTestsFromTestCase
@ -304,6 +336,7 @@ def suite():
testsuite.addTest(loader(TestHDF5Writer)) testsuite.addTest(loader(TestHDF5Writer))
testsuite.addTest(loader(TestFabIOWriter)) testsuite.addTest(loader(TestFabIOWriter))
testsuite.addTest(loader(TestSpotWriter)) testsuite.addTest(loader(TestSpotWriter))
testsuite.addTest(loader(TestXrdmlWriter))
testsuite.addTest(loader(TestPoniFile)) testsuite.addTest(loader(TestPoniFile))
return testsuite return testsuite