Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bruker XRF fix #326

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions rsciio/bruker/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from ._api import file_reader
from ._utils import export_metadata

__all__ = [
"file_reader",
"export_metadata",
]


Expand Down
43 changes: 25 additions & 18 deletions rsciio/bruker/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@
x2d = XmlToDict(dub_attr_pre_str="XmlClass", tags_to_flatten="ClassInstance")


class Container(object):
class Container:
pass


class SFSTreeItem(object):
class SFSTreeItem:
"""Class to manage one internal sfs file.

Reading, reading in chunks, reading and extracting, reading without
Expand Down Expand Up @@ -285,7 +285,7 @@
return data


class SFS_reader(object):
class SFS_reader:
"""Class to read sfs file.
SFS is AidAim software's(tm) single file system.
The class provides basic reading capabilities of such container.
Expand Down Expand Up @@ -450,7 +450,7 @@
return item


class EDXSpectrum(object):
class EDXSpectrum:
def __init__(self, spectrum):
"""
Wrap the objectified bruker EDS spectrum xml part
Expand Down Expand Up @@ -499,7 +499,9 @@
"ElevationAngle": xrf_header_dict["ExcitationAngle"],
}
# USED:
self.hv = self.esma_metadata["PrimaryEnergy"]
self.hv = self.esma_metadata.get("PrimaryEnergy", None)
if self.hv is None:
_logger.warning("The beam energy couldn't be found.")

Check warning on line 504 in rsciio/bruker/_api.py

View check run for this annotation

Codecov / codecov/patch

rsciio/bruker/_api.py#L504

Added line #L504 was not covered by tests
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting for a smaller file from #323 (comment).

self.elev_angle = self.esma_metadata["ElevationAngle"]
date_time = gen_iso_date_time(spectrum_header)
if date_time is not None:
Expand Down Expand Up @@ -527,7 +529,7 @@
return int(round((en_temp - self.offset) / self.scale))


class HyperHeader(object):
class HyperHeader:
"""Wrap Bruker HyperMaping xml header into python object.

Arguments:
Expand Down Expand Up @@ -585,7 +587,7 @@
semData = root.find("./ClassInstance[@Type='TRTSEMData']")
self.sem_metadata = x2d.dictionarize(semData)
# parse values for use in hspy metadata:
self.hv = self.sem_metadata.get("HV", 0.0) # in kV
self.hv = self.sem_metadata.get("HV", None) # in kV
# image/hypermap resolution in um/pixel:
if "DX" in self.sem_metadata:
self.units = "µm"
Expand All @@ -610,7 +612,9 @@
"""return python dictionary with aquisition instrument
mandatory data
"""
acq_inst = {"beam_energy": self.hv}
acq_inst = {}
if self.hv is not None:
acq_inst["beam_energy"] = self.hv
if "Mag" in self.sem_metadata:
acq_inst["magnification"] = self.sem_metadata["Mag"]
if detector:
Expand All @@ -620,7 +624,8 @@
acq_inst["Detector"] = det
# In case of XRF, the primary energy is only defined in
# the spectrum metadata
acq_inst["beam_energy"] = eds_metadata.hv
if eds_metadata.hv is not None:
acq_inst["beam_energy"] = eds_metadata.hv

return acq_inst

Expand Down Expand Up @@ -744,9 +749,9 @@
optimal channel number
"""
eds_max_energy = self.spectra_data[index].amplification / 1000 # in kV
if hasattr(self, "hv") and (self.hv > 0) and (self.hv < eds_max_energy):
if self.hv and self.hv > 0 and self.hv < eds_max_energy:
return self.spectra_data[index].energy_to_channel(self.hv)
if (not hasattr(self, "hv")) or (self.hv == 0):
if self.hv is None:
logging.warn(
"bcf header contains no node for electron beam "
"voltage or such node is absent.\n"
Expand Down Expand Up @@ -1542,16 +1547,18 @@
was used from metadata: TEM or SEM.
However simple guess can be made using the acceleration
voltage, assuming that SEM is <= 30kV or TEM is >30kV"""
if hv > 30.0:
if hv is not None and hv > 30.0:
mode = "TEM"
else:
mode = "SEM"
_logger.info(
"Guessing that the acquisition instrument is %s " % mode
+ "because the beam energy is %i keV. If this is wrong, " % hv
+ "please provide the right instrument using the 'instrument' "
+ "keyword."
)

if hv is not None:
_logger.info(
"Guessing that the acquisition instrument is %s " % mode
+ "because the beam energy is %i keV. If this is wrong, " % hv
+ "please provide the right instrument using the 'instrument' "
+ "keyword."
)
return mode


Expand Down
51 changes: 51 additions & 0 deletions rsciio/bruker/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
#
# Copyright 2007-2024 The HyperSpy developers
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with any project and source this library is coupled.
# If not, see <https://www.gnu.org/licenses/#GPL>.

import xml.etree.ElementTree as ET

from rsciio._docstrings import FILENAME_DOC
from rsciio.utils.tools import sanitize_msxml_float

from ._api import SFS_reader


def export_metadata(filename, output_filename=None):
"""
Export the metadata from a bcf file to a xml file.

Parameters
----------
%s
output_filename : str, pathlib.Path or None
The filename of the exported xml file.
If ``None``, use "header.xml" as default.

"""
sfs = SFS_reader(filename)
# all file items in this singlefilesystem class instance is held inside
# dictionary hierarchy, we fetch the header:
header = sfs.vfs["EDSDatabase"]["HeaderData"]
xml_str = sanitize_msxml_float(header.get_as_BytesIO_string().getvalue())
xml = ET.ElementTree(ET.fromstring(xml_str))

if output_filename is None: # pragma: no cover
output_filename = "header.xml"
xml.write(output_filename)


export_metadata.__doc__ %= FILENAME_DOC
8 changes: 7 additions & 1 deletion rsciio/tests/test_bruker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np
import pytest

from rsciio.bruker import file_reader
from rsciio.bruker import export_metadata, file_reader
from rsciio.utils.tests import assert_deep_almost_equal

hs = pytest.importorskip("hyperspy.api", reason="hyperspy not installed")
Expand Down Expand Up @@ -324,3 +324,9 @@ def test_bruker_XRF():
def test_unsupported_extension():
with pytest.raises(ValueError):
file_reader("fname.unsupported_extension")


def test_export_xml(tmp_path):
export_metadata(
TEST_DATA_DIR / test_files[0], output_filename=tmp_path / "header.xml"
)
5 changes: 4 additions & 1 deletion rsciio/tests/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ def test_dir_plugins(plugin):
pytest.importorskip("h5py")
plugin_module = importlib.import_module(plugin_string)

if plugin["name"] == "MSA":
if plugin["name"] == "Bruker":
assert dir(plugin_module) == ["export_metadata", "file_reader"]
elif plugin["name"] == "MSA":
assert dir(plugin_module) == [
"file_reader",
"file_writer",
Expand All @@ -142,6 +144,7 @@ def test_dir_plugins(plugin):
]
elif plugin["name"] == "DigitalSurf":
assert dir(plugin_module) == ["file_reader", "file_writer", "parse_metadata"]

elif plugin["writes"] is False:
assert dir(plugin_module) == ["file_reader"]
else:
Expand Down
1 change: 1 addition & 0 deletions upcoming_changes/326.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Raise a warning instead of an error when the beam energy can't be found in :ref:`bruker-format` xrf files.
1 change: 1 addition & 0 deletions upcoming_changes/326.enhancements.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`~.bruker.export_metadata` utility function for exporting metadata from :ref:`bruker-format` file.