Skip to content

Commit

Permalink
Added support for y, x, energy, and energy EDS spectra, all content f…
Browse files Browse the repository at this point in the history
…rom FHI C. Rohner's example is parsed successfully, next steps: i) add Velox metadata schema version, ii) add microscope metadata, iii) merge PRs
  • Loading branch information
atomprobe-tc committed Jan 18, 2024
1 parent 020225d commit 1948a47
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
62 changes: 62 additions & 0 deletions pynxtools/dataconverter/readers/em/subparsers/rsciio_velox.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def tech_partner_to_nexus_normalization(self, template: dict) -> dict:
self.normalize_diff_content(obj, template) # diffraction images
elif content_type == "eds_map":
self.normalize_eds_map_content(obj, template) # ED(X)S in the TEM
elif content_type == "eds_spc":
self.normalize_eds_spc_content(obj, template) # EDS spectrum/(a)
elif content_type == "eels":
self.normalize_eels_content(obj, template) # electron energy loss spectroscopy
else: # == "n/a"
Expand Down Expand Up @@ -148,6 +150,7 @@ def content_resolver(self, obj: dict) -> str:
# all units indicating we are in real or complex i.e. reciprocal space
if meta["General/title"] in ("EDS"):
return "eds_spc"
# applies to multiple cases, sum spectrum, spectrum stack etc.

for symbol in chemical_symbols[1::]: # an eds_map
# TODO::does rosettasciio via hyperspy identify the symbol or is the
Expand Down Expand Up @@ -297,6 +300,65 @@ def normalize_diff_content(self, obj: dict, template: dict) -> dict:
self.id_mgn["event"] += 1
return template

def normalize_eds_spc_content(self, obj: dict, template: dict) -> dict:
"""Map relevant EDS spectrum/(a) to NeXus."""
meta = fd.FlatDict(obj["metadata"], "/")
dims = get_axes_dims(obj["axes"])
n_dims = None
if dims == [('Energy', 0)]:
n_dims = 1
elif dims == [('y', 0), ('x', 1), ('X-ray energy', 2)]:
n_dims = 3
else:
print(f"WARNING eds_spc for {dims} is not implemented!")
return
trg = f"/ENTRY[entry{self.entry_id}]/measurement/event_data_em_set/" \
f"EVENT_DATA_EM[event_data_em{self.id_mgn['event']}]/" \
f"SPECTRUM_SET[spectrum_set{self.id_mgn['event_spc']}]"
template[f"{trg}/source"] = meta["General/title"]
template[f"{trg}/PROCESS[process]/source/type"] = "file"
template[f"{trg}/PROCESS[process]/source/path"] = self.file_path
template[f"{trg}/PROCESS[process]/source/checksum"] = self.file_path_sha256
template[f"{trg}/PROCESS[process]/source/algorithm"] = "SHA256"
template[f"{trg}/PROCESS[process]/detector_identifier"] \
= f"Check carefully how rsciio/hyperspy knows this {meta['General/title']}!"
trg = f"/ENTRY[entry{self.entry_id}]/measurement/event_data_em_set/" \
f"EVENT_DATA_EM[event_data_em{self.id_mgn['event']}]/" \
f"SPECTRUM_SET[spectrum_set{self.id_mgn['event_spc']}]" \
f"DATA[spectrum_zerod]"
template[f"{trg}/@NX_class"] = "NXdata" # TODO::should be autodecorated
template[f"{trg}/@signal"] = "intensity"
if n_dims == 1:
template[f"{trg}/@axes"] = ["axis_energy"]
template[f"{trg}/@AXISNAME_indices[axis_energy_indices]"] = np.uint32(0)
support, unit = get_named_axis(obj["axes"], "Energy")
template[f"{trg}/AXISNAME[axis_energy]"] \
= {"compress": support, "strength": 1}
template[f"{trg}/AXISNAME[axis_energy]/@long_name"] \
= f"Energy ({unit})"
if n_dims == 3:
template[f"{trg}/@axes"] = ["axis_y", "axis_x", "axis_energy"]
template[f"{trg}/@AXISNAME_indices[axis_y_indices]"] = np.uint32(2)
template[f"{trg}/@AXISNAME_indices[axis_x_indices]"] = np.uint32(1)
template[f"{trg}/@AXISNAME_indices[axis_energy_indices]"] = np.uint32(0)
support, unit = get_named_axis(obj["axes"], "y")
template[f"{trg}/AXISNAME[axis_y]"] = {"compress": support, "strength": 1}
template[f"{trg}/AXISNAME[axis_y]/@long_name"] = f"y-axis position ({unit})"
support, unit = get_named_axis(obj["axes"], "x")
template[f"{trg}/AXISNAME[axis_x]"] = {"compress": support, "strength": 1}
template[f"{trg}/AXISNAME[axis_x]/@long_name"] = f"x-axis position ({unit})"
support, unit = get_named_axis(obj["axes"], "X-ray energy")
template[f"{trg}/AXISNAME[axis_energy]"] = {"compress": support, "strength": 1}
template[f"{trg}/AXISNAME[axis_energy]/@long_name"] = f"Energy ({unit})"
# template[f"{trg}/description"] = ""
template[f"{trg}/title"] = f"EDS spectrum {meta['General/title']}"
template[f"{trg}/intensity"] \
= {"compress": np.asarray(obj["data"]), "strength": 1}
# template[f"{trg}/intensity/@long_name"] = ""
self.id_mgn["event_spc"] += 1
self.id_mgn["event"] += 1
return template

def normalize_eds_map_content(self, obj: dict, template: dict) -> dict:
"""Map relevant EDS map to NeXus."""
meta = fd.FlatDict(obj["metadata"], "/")
Expand Down
15 changes: 12 additions & 3 deletions pynxtools/dataconverter/readers/em/utils/rsciio_hyperspy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def get_named_axis(axes_metadata, dim_name):
if isinstance(axis, dict):
if ("name" in axis):
if axis["name"] == dim_name:
reqs = ["index_in_array", "offset", "scale", "size", "units", "navigate"] # "name"
reqs = ["offset", "scale", "size", "units"]
# "index_in_array" and "navigate" are currently not required
# and ignored but might become important
for req in reqs:
if req not in axis:
raise ValueError(f"{req} not in {axis}!")
Expand All @@ -48,8 +50,15 @@ def get_axes_dims(axes_metadata):
if len(axes_metadata) >= 1:
for axis in axes_metadata:
if isinstance(axis, dict):
if ("name" in axis) and ("index_in_array" in axis):
retval.append((axis["name"], axis["index_in_array"]))
if ("name" in axis):
if "index_in_array" in axis:
retval.append((axis["name"], axis["index_in_array"]))
else:
if len(axes_metadata) == 1:
retval.append((axis["name"], 0))
else:
raise ValueError(f"get_axes_dims {axes_metadata} " \
f"is a case not implemented!")
# TODO::it seems that hyperspy sorts this by index_in_array
return retval

Expand Down

0 comments on commit 1948a47

Please sign in to comment.