Source code for nexusLIMS.extractors.plugins.profiles.fei_titan_tem_642

# ruff: noqa: ARG001
"""Instrument profile for FEI Titan TEM (642 microscope)."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from benedict import benedict

from nexusLIMS.extractors.base import InstrumentProfile
from nexusLIMS.extractors.profiles import get_profile_registry
from nexusLIMS.utils.dicts import (
    set_nested_dict_value,
    try_getting_dict_value,
)

if TYPE_CHECKING:
    from nexusLIMS.extractors.base import ExtractionContext

_logger = logging.getLogger(__name__)


[docs] def parse_tecnai_metadata( metadata: dict[str, Any], context: ExtractionContext, ) -> dict[str, Any]: """ Parse Tecnai-specific metadata from ImageTags.Tecnai.Microscope_Info. The 642 Titan TEM stores extensive microscope parameters in a delimited string format that needs special parsing. Parameters ---------- metadata Metadata dictionary with 'nx_meta' key context Extraction context (unused but required by profile signature) Returns ------- dict Modified metadata dictionary with parsed Tecnai metadata """ # Import the processing function from the DM3 extractor from nexusLIMS.extractors.plugins.digital_micrograph import ( # noqa: PLC0415 process_tecnai_microscope_info, ) # Check if Tecnai metadata exists using benedict's keypaths method b = benedict(metadata) keypaths_list = b.keypaths() # Find the keypath that ends with "Tecnai" path_to_tecnai = None for keypath in keypaths_list: if keypath.endswith(".Tecnai") or keypath == "Tecnai": path_to_tecnai = keypath.split(".") break if path_to_tecnai is None: # For whatever reason, the expected Tecnai Tag is not present, # so return to prevent errors below return metadata tecnai_value = b[".".join(path_to_tecnai)] microscope_info = tecnai_value["Microscope Info"] tecnai_value["Microscope Info"] = process_tecnai_microscope_info(microscope_info) set_nested_dict_value(metadata, path_to_tecnai, tecnai_value) # Map Tecnai metadata fields to nx_meta fields term_mapping = { "Gun_Name": "Gun Name", "Extractor_Voltage": "Extractor Voltage (V)", "Camera_Length": "Camera Length (m)", "Gun_Lens_No": "Gun Lens #", "Emission_Current": "Emission Current (μA)", "Spot": "Spot", "Mode": "Tecnai Mode", "Defocus": "Defocus", "C2_Strength": "C2 Lens Strength (%)", "C3_Strength": "C3 Lens Strength (%)", "Obj_Strength": "Objective Lens Strength (%)", "Dif_Strength": "Diffraction Lens Strength (%)", "Microscope_Name": "Tecnai Microscope Name", "User": "Tecnai User", "Image_Shift_x": "Image Shift X (μm)", "Image_Shift_y": "Image Shift Y (μm)", "Stage_Position_x": ["Stage Position", "X (μm)"], "Stage_Position_y": ["Stage Position", "Y (μm)"], "Stage_Position_z": ["Stage Position", "Z (μm)"], "Stage_Position_theta": ["Stage Position", "θ (°)"], "Stage_Position_phi": ["Stage Position", "φ (°)"], "C1_Aperture": "C1 Aperture (μm)", "C2_Aperture": "C2 Aperture (μm)", "Obj_Aperture": "Objective Aperture (μm)", "SA_Aperture": "Selected Area Aperture (μm)", ("Filter_Settings", "Mode"): ["Tecnai Filter", "Mode"], ("Filter_Settings", "Dispersion"): ["Tecnai Filter", "Dispersion (eV/channel)"], ("Filter_Settings", "Aperture"): ["Tecnai Filter", "Aperture (mm)"], ("Filter_Settings", "Prism_Shift"): ["Tecnai Filter", "Prism Shift (eV)"], ("Filter_Settings", "Drift_Tube"): ["Tecnai Filter", "Drift Tube (eV)"], ("Filter_Settings", "Total_Energy_Loss"): [ "Tecnai Filter", "Total Energy Loss (eV)", ], } for in_term, out_term in term_mapping.items(): base = [*list(path_to_tecnai), "Microscope Info"] if isinstance(in_term, str): in_term = [in_term] # noqa: PLW2901 elif isinstance(in_term, tuple): in_term = list(in_term) # noqa: PLW2901 if isinstance(out_term, str): out_term = [out_term] # noqa: PLW2901 val = try_getting_dict_value(metadata, base + in_term) # only add the value to this list if we found it if val is not None and val not in ["DO NOT EDIT", "DO NOT ENTER"]: set_nested_dict_value(metadata, ["nx_meta", *out_term], val) # Parse specimen info path = [*list(path_to_tecnai), "Specimen Info"] val = try_getting_dict_value(metadata, path) if val is not None and val != "Specimen information is not available yet": set_nested_dict_value(metadata, ["nx_meta", "Specimen"], val) return metadata
[docs] def detect_diffraction_mode( metadata: dict[str, Any], context: ExtractionContext, ) -> dict[str, Any]: """ Detect diffraction patterns by Mode or Operation Mode values. The 642 TEM indicates diffraction via specific Mode strings. Parameters ---------- metadata Metadata dictionary with 'nx_meta' key context Extraction context (unused but required by profile signature) Returns ------- dict Modified metadata dictionary with updated dataset type if applicable """ # Check Tecnai Mode if ( "Tecnai Mode" in metadata["nx_meta"] and metadata["nx_meta"]["Tecnai Mode"] == "STEM nP SA Zoom Diffraction" ): _logger.info( 'Detected file as Diffraction type based on "Tecnai ' 'Mode" == "STEM nP SA Zoom Diffraction"', ) metadata["nx_meta"]["DatasetType"] = "Diffraction" metadata["nx_meta"]["Data Type"] = "STEM_Diffraction" # Check Operation Mode elif ( "Operation Mode" in metadata["nx_meta"] and metadata["nx_meta"]["Operation Mode"] == "DIFFRACTION" ): _logger.info( 'Detected file as Diffraction type based on "Operation ' 'Mode" == "DIFFRACTION"', ) metadata["nx_meta"]["DatasetType"] = "Diffraction" metadata["nx_meta"]["Data Type"] = "TEM_Diffraction" return metadata
# Register the profile fei_titan_tem_642_profile = InstrumentProfile( instrument_id="FEI-Titan-TEM", parsers={ "tecnai_metadata": parse_tecnai_metadata, "diffraction_detection": detect_diffraction_mode, }, transformations={}, extension_fields={}, ) """An instrument profile for the FEI Titan TEM""" get_profile_registry().register(fei_titan_tem_642_profile) _logger.debug("Registered FEI Titan TEM (642) instrument profile")