Example #1
0
    def test_metadata_nodes(self):
        sem_node = "Acquisition_instrument.SEM"
        ebsd_node = sem_node + ".Detector.EBSD"

        assert metadata_nodes("sem") == sem_node
        assert metadata_nodes("ebsd") == ebsd_node
        assert metadata_nodes() == [sem_node, ebsd_node]
        assert metadata_nodes(["ebsd", "sem"]) == [ebsd_node, sem_node]
Example #2
0
def _get_metadata(omd: dict) -> dict:
    """Return metadata dictionary from original metadata dictionary.

    Parameters
    ----------
    omd
        Dictionary with original metadata.

    Returns
    -------
    md
        Dictionary with metadata.
    """
    md = ebsd_metadata()
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(f"{ebsd_node}.manufacturer", "EMsoft")
    mapping = {
        f"{ebsd_node}.version": ["EMheader", "EBSD", "Version"],
        f"{ebsd_node}.binning": ["NMLparameters", "EBSDNameList", "binning"],
        f"{ebsd_node}.elevation_angle":
        ["NMLparameters", "EBSDNameList", "thetac"],
        f"{ebsd_node}.exposure_time":
        ["NMLparameters", "EBSDNameList", "dwelltime"],
        f"{ebsd_node}.xpc": ["NMLparameters", "EBSDNameList", "xpc"],
        f"{ebsd_node}.ypc": ["NMLparameters", "EBSDNameList", "ypc"],
        f"{ebsd_node}.zpc": ["NMLparameters", "EBSDNameList", "L"],
        f"{sem_node}.beam_energy":
        ["NMLparameters", "EBSDNameList", "energymax"],
    }
    _set_metadata_from_mapping(omd, md, mapping)
    return md.as_dictionary()
Example #3
0
    def test_rebin(self, dummy_signal):
        ebsd_node = metadata_nodes("ebsd")

        # Passing new_shape, only scaling in signal space
        new_shape = (3, 3, 2, 1)
        new_binning = dummy_signal.axes_manager.shape[3] / new_shape[3]
        s2 = dummy_signal.rebin(new_shape=new_shape)
        assert s2.axes_manager.shape == new_shape
        assert s2.metadata.get_item(ebsd_node + ".binning") == new_binning

        # Passing scale, also scaling in navigation space
        scale = (3, 1, 3, 2)
        s2 = dummy_signal.rebin(scale=scale)
        expected_new_shape = [
            int(i / j) for i, j in zip(dummy_signal.axes_manager.shape, scale)
        ]
        assert s2.axes_manager.shape == tuple(expected_new_shape)
        assert s2.metadata.get_item(ebsd_node + ".binning") == float(scale[2])

        # Passing lazy signal to out parameter, only scaling in signal space but
        # upscaling
        scale = (1, 1, 1, 0.5)
        expected_new_shape = [
            int(i / j) for i, j in zip(dummy_signal.axes_manager.shape, scale)
        ]
        s2 = dummy_signal.copy().as_lazy()
        s3 = dummy_signal.rebin(scale=scale, out=s2)
        assert isinstance(s2, LazyEBSD)
        assert s2.axes_manager.shape == tuple(expected_new_shape)
        assert s2.metadata.get_item(ebsd_node + ".binning") == float(scale[3])
        assert s3 is None
Example #4
0
    def test_init(self):

        # Signal shape
        array0 = np.zeros(shape=(10, 10, 10, 10))
        s0 = EBSD(array0)
        assert array0.shape == s0.axes_manager.shape

        # Cannot initialise signal with one signal dimension
        with pytest.raises(ValueError):
            _ = EBSD(np.zeros(10))

        # Shape of one-image signal
        array1 = np.zeros(shape=(10, 10))
        s1 = EBSD(array1)
        assert array1.shape == s1.axes_manager.shape

        # SEM metadata
        kp_md = ebsd_metadata()
        sem_node = metadata_nodes("sem")
        assert_dictionary(
            kp_md.get_item(sem_node), s1.metadata.get_item(sem_node)
        )

        # Phases metadata
        assert s1.metadata.has_item("Sample.Phases")
Example #5
0
    def test_load_save_cycle(self, save_path_hdf5, remove_phases):
        s = load(KIKUCHIPY_FILE)

        # Check that metadata is read correctly
        assert s.metadata.Acquisition_instrument.SEM.Detector.EBSD.xpc == -5.64
        assert s.metadata.General.title == "patterns My awes0m4 ..."

        if remove_phases:
            del s.metadata.Sample.Phases
        s.save(save_path_hdf5, overwrite=True)
        s_reload = load(save_path_hdf5)
        np.testing.assert_equal(s.data, s_reload.data)

        # Change data set name and package version to make metadata equal, and
        # redo deleting of phases
        s_reload.metadata.General.title = s.metadata.General.title
        ebsd_node = metadata_nodes("ebsd")
        s_reload.metadata.set_item(
            ebsd_node + ".version", s.metadata.get_item(ebsd_node + ".version")
        )
        if remove_phases:
            s.metadata.Sample.set_item(
                "Phases", s_reload.metadata.Sample.Phases
            )
        np.testing.assert_equal(
            s_reload.metadata.as_dictionary(), s.metadata.as_dictionary()
        )
Example #6
0
 def test_set_experimental_parameters(self, dummy_signal):
     p = {
         "detector": "NORDIF UF-1100",
         "azimuth_angle": 1.0,
         "elevation_angle": 1.0,
         "sample_tilt": 70.0,
         "working_distance": 23.2,
         "binning": 8,
         "exposure_time": 0.01,
         "grid_type": "square",
         "gain": 10,
         "frame_number": 4,
         "frame_rate": 100,
         "scan_time": 60.0,
         "beam_energy": 20.0,
         "xpc": 0.5,
         "ypc": 0.5,
         "zpc": 15000.0,
         "static_background": np.ones(shape=(10, 10)),
         "manufacturer": "NORDIF",
         "version": "3.1.2",
         "microscope": "Hitachi SU-6600",
         "magnification": 500,
     }
     dummy_signal.set_experimental_parameters(**p)
     ebsd_node = metadata_nodes("ebsd")
     md_dict = dummy_signal.metadata.get_item(ebsd_node).as_dictionary()
     assert_dictionary(p, md_dict)
def file_reader(filename: str, lazy: bool = False) -> List[dict]:
    """Reader electron backscatter patterns from .bmp files stored in a
    NORDIF project directory, their filenames listed in a text file.

    Parameters
    ----------
    filename
        File path to the NORDIF settings text file.
    lazy
        This parameter is not used in this reader.

    Returns
    -------
    scan : list of dicts
        Data, axes, metadata and original metadata.
    """
    # Get metadata from setting file
    ebsd_node = metadata_nodes("ebsd")
    md, omd, _ = get_settings_from_file(filename)
    dirname = os.path.dirname(filename)

    # Read static background image into metadata
    static_bg_file = os.path.join(dirname,
                                  "Background calibration pattern.bmp")
    try:
        md.set_item(ebsd_node + ".static_background", imread(static_bg_file))
    except FileNotFoundError:
        warnings.warn(
            f"Could not read static background pattern '{static_bg_file}', however it "
            "can be added using set_experimental_parameters().")

    # Set required and other parameters in metadata
    md.set_item("General.original_filename", filename)
    md.set_item("General.title", "Calibration patterns")
    md.set_item("Signal.signal_type", "EBSD")
    md.set_item("Signal.record_by", "image")

    scan = {}
    scan["metadata"] = md.as_dictionary()
    scan["original_metadata"] = omd.as_dictionary()

    coordinates = _get_coordinates(filename)

    data = _get_patterns(dirname=dirname, coordinates=coordinates)
    scan["data"] = data

    units = ["um"] * 3
    names = ["x", "dy", "dx"]
    scales = np.ones(3)
    scan["axes"] = [{
        "size": data.shape[i],
        "index_in_array": i,
        "name": names[i],
        "scale": scales[i],
        "offset": 0,
        "units": units[i],
    } for i in range(data.ndim)]

    return [scan]
Example #8
0
    def test_ebsd_masterpattern_metadata(self):
        ebsd_mp_node = metadata_nodes("ebsd_master_pattern")
        md = ebsd_master_pattern_metadata()

        assert md.get_item(ebsd_mp_node + ".BSE_simulation.mode") == ""
        assert (md.get_item(ebsd_mp_node +
                            ".Master_pattern.smallest_interplanar_spacing") ==
                -1.0)
Example #9
0
def h5ebsdheader2dicts(
    scan_group: h5py.Group,
    manufacturer: str,
    version: str,
    lazy: bool = False
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser,
           DictionaryTreeBrowser]:
    """Return three dictionaries in HyperSpy's
    :class:`hyperspy.misc.utils.DictionaryTreeBrowser` format, one
    with the h5ebsd scan header parameters as kikuchipy metadata,
    another with all datasets in the header as original metadata, and
    the last with info about scan size, image size and detector pixel
    size.

    Parameters
    ----------
    scan_group : h5py:Group
        HDF group of scan data and header.
    manufacturer
        Manufacturer of file. Options are
        "kikuchipy"/"EDAX"/"Bruker Nano"
    version
        Version of manufacturer software used to create file.
    lazy
        Read dataset lazily.

    Returns
    -------
    md
        kikuchipy ``metadata`` elements available in file.
    omd
        All metadata available in file.
    scan_size
        Scan, image, step and detector pixel size available in file.
    """
    md = ebsd_metadata()
    title = (scan_group.file.filename.split("/")[-1].split(".")[0] + " " +
             scan_group.name[1:].split("/")[0])
    if len(title) > 20:
        title = "{:.20}...".format(title)
    md.set_item("General.title", title)

    if "edax" in manufacturer.lower():
        md, omd, scan_size = edaxheader2dicts(scan_group, md)
    elif "bruker" in manufacturer.lower():
        md, omd, scan_size = brukerheader2dicts(scan_group, md)
    else:  # kikuchipy
        md, omd, scan_size = kikuchipyheader2dicts(scan_group, md)

    ebsd_node = metadata_nodes("ebsd")
    md.set_item(ebsd_node + ".manufacturer", manufacturer)
    md.set_item(ebsd_node + ".version", version)

    return md, omd, scan_size
Example #10
0
    def test_metadata_nodes(self):
        sem_node = "Acquisition_instrument.SEM"
        ebsd_node = sem_node + ".Detector.EBSD"
        simulation_node = "Simulation"
        ebsd_master_pattern_node = simulation_node + ".EBSD_master_pattern"

        assert metadata_nodes("sem") == sem_node
        assert metadata_nodes("ebsd") == ebsd_node
        assert metadata_nodes() == [
            sem_node,
            ebsd_node,
            ebsd_master_pattern_node,
        ]
        assert metadata_nodes(["ebsd", "sem"]) == [
            ebsd_node,
            sem_node,
        ]
        assert metadata_nodes(["sem", "ebsd_master_pattern"]) == [
            sem_node,
            ebsd_master_pattern_node,
        ]
Example #11
0
    def test_incorrect_static_background_pattern(
        self, dummy_signal, static_bg, error, match
    ):
        """Test for expected error messages when passing an incorrect
        static background pattern to `remove_static_background().`
        """

        ebsd_node = metadata_nodes("ebsd")
        dummy_signal.metadata.set_item(
            ebsd_node + ".static_background", static_bg
        )
        with pytest.raises(error, match=match):
            dummy_signal.remove_static_background()
    def __init__(self, *args, **kwargs):
        """Create an :class:`~kikuchipy.signals.EBSDMasterPattern`
        object from a :class:`hyperspy.signals.Signal2D` or a
        :class:`numpy.ndarray`.

        """

        Signal2D.__init__(self, *args, **kwargs)

        # Update metadata if object is initialized from numpy array or
        # with set_signal_type()
        if not self.metadata.has_item(metadata_nodes("ebsd_master_pattern")):
            md = self.metadata.as_dictionary()
            md.update(ebsd_master_pattern_metadata().as_dictionary())
            self.metadata = DictionaryTreeBrowser(md)
        if not self.metadata.has_item("Sample.Phases"):
            self.set_phase_parameters()
Example #13
0
    def test_file_reader(self):
        """Test correct data shape, axes properties and metadata."""
        s = load(EMSOFT_FILE)

        assert s.data.shape == (10, 10, 10)

        assert s.axes_manager["dx"].scale == 70
        assert s.axes_manager["dx"].units == "um"
        assert s.axes_manager["x"].units == "px"

        sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
        phase_node = "Sample.Phases.1"
        desired_dict = {
            f"{sem_node}.beam_energy": 20,
            f"{ebsd_node}.manufacturer": "EMsoft",
            f"{ebsd_node}.xpc": 4.86,
            f"{phase_node}.space_group": 140,
            f"{phase_node}.atom_coordinates.1.atom": 13,
        }
        for key, value in desired_dict.items():
            assert s.metadata.get_item(key) == value
Example #14
0
    def test_set_metadata_from_mapping(self):
        """Updating DictionaryTreeBrowser with values from a dictionary
        via a mapping.
        """
        sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
        md = ebsd_metadata()

        old_val1, new_val1 = (-1, 20)
        old_val2, new_val2 = (-1, 2)
        omd = {"V": new_val1, "xpc": {"xpc2": {"xpc3": new_val2}}}
        key1 = f"{sem_node}.beam_energy"
        key2 = f"{ebsd_node}.xpc"
        assert md.get_item(key1) == old_val1
        assert md.get_item(key2) == old_val2

        mapping = {key1: "V", key2: ["xpc", "xpc2", "xpc3"]}
        _set_metadata_from_mapping(omd, md, mapping)
        assert md.get_item(key1) == new_val1
        assert md.get_item(key2) == new_val2

        with pytest.warns(UserWarning, match="Could not read"):
            _ = _set_metadata_from_mapping(omd, md, {"a": "b"})
Example #15
0
def file_reader(
    filename: str,
    mmap_mode: Optional[str] = None,
    scan_size: Union[None, int, Tuple[int, ...]] = None,
    pattern_size: Optional[Tuple[int, ...]] = None,
    setting_file: Optional[str] = None,
    lazy: bool = False,
) -> List[Dict]:
    """Read electron backscatter patterns from a NORDIF data file.

    Parameters
    ----------
    filename
        File path to NORDIF data file.
    mmap_mode
    scan_size
        Scan size in number of patterns in width and height.
    pattern_size
        Pattern size in detector pixels in width and height.
    setting_file
        File path to NORDIF setting file (default is `Setting.txt` in
        same directory as ``filename``).
    lazy
        Open the data lazily without actually reading the data from disk
        until required. Allows opening arbitrary sized datasets. Default
        is False.

    Returns
    -------
    scan : list of dicts
        Data, axes, metadata and original metadata.
    """
    if mmap_mode is None:
        mmap_mode = "r" if lazy else "c"

    scan = {}

    # Make sure we open in correct mode
    if "+" in mmap_mode or (
        "write" in mmap_mode and "copyonwrite" != mmap_mode
    ):
        if lazy:
            raise ValueError("Lazy loading does not support in-place writing")
        f = open(filename, mode="r+b")
    else:
        f = open(filename, mode="rb")

    # Get metadata from setting file
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    folder, fname = os.path.split(filename)
    if setting_file is None:
        setting_file = os.path.join(folder, "Setting.txt")
    setting_file_exists = os.path.isfile(setting_file)
    if setting_file_exists:
        md, omd, scan_size_file = get_settings_from_file(setting_file)
        if not scan_size:
            scan_size = (scan_size_file.nx, scan_size_file.ny)
        if not pattern_size:
            pattern_size = (scan_size_file.sx, scan_size_file.sy)
    else:
        if scan_size is None and pattern_size is None:
            raise ValueError(
                "No setting file found and no scan_size or pattern_size "
                "detected in input arguments. These must be set if no setting "
                "file is provided."
            )
        md = ebsd_metadata()
        omd = DictionaryTreeBrowser()

    # Read static background image into metadata
    static_bg_file = os.path.join(folder, "Background acquisition pattern.bmp")
    try:
        md.set_item(ebsd_node + ".static_background", imread(static_bg_file))
    except FileNotFoundError:
        warnings.warn(
            f"Could not read static background pattern '{static_bg_file}', "
            "however it can be added using set_experimental_parameters()."
        )

    # Set required and other parameters in metadata
    md.set_item("General.original_filename", filename)
    md.set_item(
        "General.title", os.path.splitext(os.path.split(filename)[1])[0]
    )
    md.set_item("Signal.signal_type", "EBSD")
    md.set_item("Signal.record_by", "image")
    scan["metadata"] = md.as_dictionary()
    scan["original_metadata"] = omd.as_dictionary()

    # Set scan size and image size
    if isinstance(scan_size, int):
        nx = scan_size
        ny = 1
    else:
        nx, ny = scan_size
    sx, sy = pattern_size

    # Read data from file
    data_size = ny * nx * sy * sx
    if not lazy:
        f.seek(0)
        data = np.fromfile(f, dtype="uint8", count=data_size)
    else:
        data = np.memmap(f, mode=mmap_mode, dtype="uint8")

    try:
        data = data.reshape((ny, nx, sy, sx), order="C").squeeze()
    except ValueError:
        warnings.warn(
            "Pattern size and scan size larger than file size! Will attempt to "
            "load by zero padding incomplete frames."
        )
        # Data is stored image by image
        pw = [(0, ny * nx * sy * sx - data.size)]
        data = np.pad(data, pw, mode="constant")
        data = data.reshape((ny, nx, sy, sx))
    scan["data"] = data

    units = ["um"] * 4
    names = ["y", "x", "dy", "dx"]
    scales = np.ones(4)

    # Calibrate scan dimension
    try:
        scales[:2] = scales[:2] * scan_size_file.step_x
    except (TypeError, UnboundLocalError):
        warnings.warn(
            "Could not calibrate scan dimensions, this can be done using "
            "set_scan_calibration()"
        )

    # Create axis objects for each axis
    axes = [
        {
            "size": data.shape[i],
            "index_in_array": i,
            "name": names[i],
            "scale": scales[i],
            "offset": 0.0,
            "units": units[i],
        }
        for i in range(data.ndim)
    ]
    scan["axes"] = axes

    f.close()

    return [
        scan,
    ]
Example #16
0
def get_settings_from_file(
    filename: str,
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser, DictionaryTreeBrowser]:
    """Return metadata with parameters from NORDIF setting file.

    Parameters
    ----------
    filename
        File path of NORDIF setting file.

    Returns
    -------
    md
        Metadata complying with HyperSpy's metadata structure.
    omd
        Metadata that does not fit into HyperSpy's metadata structure.
    scan_size
        Information on image size, scan size and scan steps.
    """
    f = open(filename, "r", encoding="latin-1")  # Avoid byte strings
    content = f.read().splitlines()

    # Get line numbers of setting blocks
    blocks = {
        "[NORDIF]": -1,
        "[Microscope]": -1,
        "[EBSD detector]": -1,
        "[Detector angles]": -1,
        "[Acquisition settings]": -1,
        "[Area]": -1,
        "[Specimen]": -1,
    }
    for i, line in enumerate(content):
        for block in blocks:
            if block in line:
                blocks[block] = i
    l_nordif = blocks["[NORDIF]"]
    l_mic = blocks["[Microscope]"]
    l_det = blocks["[EBSD detector]"]
    l_ang = blocks["[Detector angles]"]
    l_acq = blocks["[Acquisition settings]"]
    l_area = blocks["[Area]"]
    l_specimen = blocks["[Specimen]"]

    # Create metadata and original metadata structures
    md = ebsd_metadata()
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    omd = DictionaryTreeBrowser()
    omd.set_item("nordif_header", content)

    # Get metadata values from settings file using regular expressions
    azimuth_angle = get_string(content, "Azimuthal\t(.*)\t", l_ang + 4, f)
    md.set_item(ebsd_node + ".azimuth_angle", float(azimuth_angle))
    beam_energy = get_string(
        content, "Accelerating voltage\t(.*)\tkV", l_mic + 5, f
    )
    md.set_item(sem_node + ".beam_energy", float(beam_energy))
    detector = get_string(content, "Model\t(.*)\t", l_det + 1, f)
    detector = re.sub("[^a-zA-Z0-9]", repl="", string=detector)
    md.set_item(ebsd_node + ".detector", "NORDIF " + detector)
    elevation_angle = get_string(content, "Elevation\t(.*)\t", l_ang + 5, f)
    md.set_item(ebsd_node + ".elevation_angle", float(elevation_angle))
    exposure_time = get_string(content, "Exposure time\t(.*)\t", l_acq + 3, f)
    md.set_item(ebsd_node + ".exposure_time", float(exposure_time) / 1e6)
    frame_rate = get_string(content, "Frame rate\t(.*)\tfps", l_acq + 1, f)
    md.set_item(ebsd_node + ".frame_rate", int(frame_rate))
    gain = get_string(content, "Gain\t(.*)\t", l_acq + 4, f)
    md.set_item(ebsd_node + ".gain", float(gain))
    magnification = get_string(content, "Magnification\t(.*)\t#", l_mic + 3, f)
    md.set_item(sem_node + ".magnification", int(magnification))
    mic_manufacturer = get_string(content, "Manufacturer\t(.*)\t", l_mic + 1, f)
    mic_model = get_string(content, "Model\t(.*)\t", l_mic + 2, f)
    md.set_item(sem_node + ".microscope", mic_manufacturer + " " + mic_model)
    sample_tilt = get_string(content, "Tilt angle\t(.*)\t", l_mic + 7, f)
    md.set_item(ebsd_node + ".sample_tilt", float(sample_tilt))
    scan_time = get_string(content, "Scan time\t(.*)\t", l_area + 7, f)
    scan_time = time.strptime(scan_time, "%H:%M:%S")
    scan_time = datetime.timedelta(
        hours=scan_time.tm_hour,
        minutes=scan_time.tm_min,
        seconds=scan_time.tm_sec,
    ).total_seconds()
    md.set_item(ebsd_node + ".scan_time", int(scan_time))
    version = get_string(content, "Software version\t(.*)\t", l_nordif + 1, f)
    md.set_item(ebsd_node + ".version", version)
    working_distance = get_string(
        content, "Working distance\t(.*)\tmm", l_mic + 6, f
    )
    md.set_item(sem_node + ".working_distance", float(working_distance))
    md.set_item(ebsd_node + ".grid_type", "square")
    md.set_item(ebsd_node + ".manufacturer", "NORDIF")
    specimen = get_string(content, "Name\t(.*)\t", l_specimen + 1, f)
    pmd = _phase_metadata()
    pmd["material_name"] = specimen
    md.set_item("Sample.Phases.1", pmd)

    # Get scan size values
    scan_size = DictionaryTreeBrowser()
    num_samp = get_string(content, "Number of samples\t(.*)\t#", l_area + 6, f)
    ny, nx = [int(i) for i in num_samp.split("x")]
    scan_size.set_item("nx", int(nx))
    scan_size.set_item("ny", int(ny))
    pattern_size = get_string(content, "Resolution\t(.*)\tpx", l_acq + 2, f)
    sx, sy = [int(i) for i in pattern_size.split("x")]
    scan_size.set_item("sx", int(sx))
    scan_size.set_item("sy", int(sy))
    step_size = get_string(content, "Step size\t(.*)\t", l_area + 5, f)
    scan_size.set_item("step_x", float(step_size))
    scan_size.set_item("step_y", float(step_size))

    return md, omd, scan_size
Example #17
0
 def test_ebsd_metadata(self):
     sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
     md = ebsd_metadata()
     assert md.get_item(sem_node + ".microscope") == ""
     assert md.get_item(ebsd_node + ".xpc") == -1.0
Example #18
0
def brukerheader2dicts(
    scan_group: h5py.Group, md: DictionaryTreeBrowser
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser,
           DictionaryTreeBrowser]:
    """Return scan metadata dictionaries from a Bruker h5ebsd file.

    Parameters
    ----------
    scan_group : h5py:Group
        HDF group of scan data and header.
    md : hyperspy.misc.utils.DictionaryTreeBrowser
        Dictionary with empty fields from kikuchipy's metadata.

    Returns
    -------
    md
        kikuchipy ``metadata`` elements available in Bruker file.
    omd
        All metadata available in Bruker file.
    scan_size
        Scan, image, step and detector pixel size available in Bruker
        file.
    """
    # Get header group and data group as dictionaries
    pattern_dset_names = list(manufacturer_pattern_names().values())
    hd = hdf5group2dict(
        group=scan_group["EBSD/Header"],
        data_dset_names=pattern_dset_names,
        recursive=True,
    )
    dd = hdf5group2dict(group=scan_group["EBSD/Data"],
                        data_dset_names=pattern_dset_names)

    # Populate metadata dictionary
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(ebsd_node + ".elevation_angle", hd["CameraTilt"])
    grid_type = hd["Grid Type"]
    if grid_type == "isometric":
        md.set_item(ebsd_node + ".grid_type", "square")
    else:
        raise IOError(
            f"Only square grids are supported, however a {grid_type} grid was passed"
        )
    # Values: data set name, data group, function to apply, node
    dset_mapping = {
        "sample_tilt": ["SampleTilt", hd, None, ebsd_node],
        "xpz": ["PCX", dd, np.nanmean, ebsd_node],
        "ypc": ["PCY", dd, np.nanmean, ebsd_node],
        "zpc": ["DD", dd, np.nanmean, ebsd_node],
        "static_background": ["StaticBackground", hd, None, ebsd_node],
        "working_distance": ["WD", hd, None, sem_node],
        "beam_energy": ["KV", hd, None, sem_node],
        "magnification": ["Magnification", hd, None, sem_node],
    }
    for k, v in dset_mapping.items():
        dset_name, dset_group, func, node = v
        try:
            dset = dset_group[dset_name]
            if func:
                dset = func(dset)
        except KeyError:
            dset = None
        md.set_item(node + f".{k}", dset)
    # Loop over phases
    for phase_no, phase in hd["Phases"].items():
        phase["material_name"] = phase["Name"]
        phase["space_group"] = phase["IT"]
        phase["atom_coordinates"] = {}
        for key, val in phase["AtomPositions"].items():
            atom = val.split(",")
            atom[1:] = list(map(float, atom[1:]))
            phase["atom_coordinates"][key] = {
                "atom": atom[0],
                "coordinates": atom[1:4],
                "site_occupation": atom[4],
                "debye_waller_factor": atom[5],
            }
        md = _update_phase_info(md, phase, phase_no)

    # Populate original metadata dictionary
    omd = DictionaryTreeBrowser({"bruker_header": hd})

    # Initialize scan size dictionary
    scan_size = DictionaryTreeBrowser()

    # Get region of interest (ROI, only rectangular shape supported)
    try:
        sd = hdf5group2dict(group=scan_group["EBSD/SEM"])
        ir = sd["IY"][()]
        ic = sd["IX"][()]
        roi = True
    except KeyError:
        roi = False
        nr = hd["NROWS"]
        nc = hd["NCOLS"]
    if roi:
        nr_roi, nc_roi, is_rectangular = _bruker_roi_is_rectangular(ir, ic)
        if is_rectangular:
            nr = nr_roi
            nc = nc_roi
            # Get indices of patterns in the 2D map
            idx = np.array([ir - np.min(ir), ic - np.min(ic)])
            scan_size["indices"] = np.ravel_multi_index(idx,
                                                        (nr, nc)).argsort()
        else:
            raise ValueError(
                "Only a rectangular region of interest is supported")

    # Populate scan size dictionary
    scan_size.set_item("sx", hd["PatternWidth"])
    scan_size.set_item("sy", hd["PatternHeight"])
    scan_size.set_item("nx", nc)
    scan_size.set_item("ny", nr)
    scan_size.set_item("step_x", hd["XSTEP"])
    scan_size.set_item("step_y", hd["YSTEP"])
    scan_size.set_item(
        "delta",
        hd["DetectorFullHeightMicrons"] / hd["UnClippedPatternHeight"])

    return md, omd, scan_size
    def set_simulation_parameters(
        self,
        complete_cutoff: Union[None, int, float] = None,
        depth_step: Union[None, int, float] = None,
        energy_step: Union[None, int, float] = None,
        hemisphere: Union[None, str] = None,
        incident_beam_energy: Union[None, int, float] = None,
        max_depth: Union[None, int, float] = None,
        min_beam_energy: Union[None, int, float] = None,
        mode: Optional[str] = None,
        number_of_electrons: Optional[int] = None,
        pixels_along_x: Optional[int] = None,
        projection: Union[None, str] = None,
        sample_tilt: Union[None, int, float] = None,
        smallest_interplanar_spacing: Union[None, int, float] = None,
        strong_beam_cutoff: Union[None, int, float] = None,
        weak_beam_cutoff: Union[None, int, float] = None,
    ):
        """Set simulated parameters in signal metadata.

        Parameters
        ----------
        complete_cutoff
            Bethe parameter c3.
        depth_step
            Material penetration depth step size, in nm.
        energy_step
            Energy bin size, in keV.
        hemisphere
            Which hemisphere(s) the data contains.
        incident_beam_energy
            Incident beam energy, in keV.
        max_depth
            Maximum material penetration depth, in nm.
        min_beam_energy
            Minimum electron energy to consider, in keV.
        mode
            Simulation mode, e.g. Continuous slowing down
            approximation (CSDA) used by EMsoft.
        number_of_electrons
            Total number of incident electrons.
        pixels_along_x
            Pixels along horizontal direction.
        projection
            Which projection the pattern is in.
        sample_tilt
            Sample tilte angle from horizontal, in degrees.
        smallest_interplanar_spacing
            Smallest interplanar spacing, d-spacing, taken into account
            in the computation of the electrostatic lattice potential,
            in nm.
        strong_beam_cutoff
            Bethe parameter c1.
        weak_beam_cutoff
            Bethe parameter c2.

        See Also
        --------
        set_phase_parameters

        Examples
        --------
        >>> import kikuchipy as kp
        >>> ebsd_mp_node = kp.signals.util.metadata_nodes(
        ...     "ebsd_master_pattern")
        >>> s.metadata.get_item(ebsd_mp_node + '.incident_beam_energy')
        15.0
        >>> s.set_simulated_parameters(incident_beam_energy=20.5)
        >>> s.metadata.get_item(ebsd_mp_node + '.incident_beam_energy')
        20.5
        """
        md = self.metadata
        ebsd_mp_node = metadata_nodes("ebsd_master_pattern")
        _write_parameters_to_dictionary(
            {
                "BSE_simulation": {
                    "depth_step": depth_step,
                    "energy_step": energy_step,
                    "incident_beam_energy": incident_beam_energy,
                    "max_depth": max_depth,
                    "min_beam_energy": min_beam_energy,
                    "mode": mode,
                    "number_of_electrons": number_of_electrons,
                    "pixels_along_x": pixels_along_x,
                    "sample_tilt": sample_tilt,
                },
                "Master_pattern": {
                    "Bethe_parameters": {
                        "complete_cutoff": complete_cutoff,
                        "strong_beam_cutoff": strong_beam_cutoff,
                        "weak_beam_cutoff": weak_beam_cutoff,
                    },
                    "smallest_interplanar_spacing":
                    smallest_interplanar_spacing,
                    "projection": projection,
                    "hemisphere": hemisphere,
                },
            },
            md,
            ebsd_mp_node,
        )
Example #20
0
def file_writer(
    filename: str,
    signal,
    add_scan: Optional[bool] = None,
    scan_number: int = 1,
    **kwargs,
):
    """Write an :class:`~kikuchipy.signals.EBSD` or
    :class:`~kikuchipy.signals.LazyEBSD` signal to an existing,
    but not open, or new h5ebsd file.

    Only writing to kikuchipy's h5ebsd format is supported.

    Parameters
    ----------
    filename
        Full path of HDF file.
    signal : kikuchipy.signals.EBSD or kikuchipy.signals.LazyEBSD
        Signal instance.
    add_scan
        Add signal to an existing, but not open, h5ebsd file. If it does
        not exist it is created and the signal is written to it.
    scan_number
        Scan number in name of HDF dataset when writing to an existing,
        but not open, h5ebsd file.
    kwargs
        Keyword arguments passed to :meth:`h5py:Group.require_dataset`.
    """
    # Set manufacturer and version to use in file
    from kikuchipy.release import version as ver_signal

    man_ver_dict = {"manufacturer": "kikuchipy", "version": ver_signal}

    # Open file in correct mode
    mode = "w"
    if os.path.isfile(filename) and add_scan:
        mode = "r+"
    try:
        f = h5py.File(filename, mode=mode)
    except OSError:
        raise OSError("Cannot write to an already open file.")

    if os.path.isfile(filename) and add_scan:
        check_h5ebsd(f)
        man_file, ver_file = manufacturer_version(f)
        if man_ver_dict["manufacturer"].lower() != man_file.lower():
            f.close()
            raise IOError(
                f"Only writing to kikuchipy's (and not {man_file}'s) h5ebsd "
                "format is supported."
            )
        man_ver_dict["version"] = ver_file

        # Get valid scan number
        scans_file = [f[k] for k in f["/"].keys() if "Scan" in k]
        scan_nos = [int(i.name.split()[-1]) for i in scans_file]
        for i in scan_nos:
            if i == scan_number:
                q = f"Scan {i} already in file, enter another scan number:\n"
                scan_number = _get_input_variable(q, int)
                if scan_number is None:
                    raise IOError("Invalid scan number.")
    else:  # File did not exist
        dict2h5ebsdgroup(man_ver_dict, f["/"], **kwargs)

    scan_group = f.create_group("Scan " + str(scan_number))

    # Create scan dictionary with EBSD and SEM metadata
    # Add scan size, image size and detector pixel size to dictionary to write
    data_shape = [1] * 4  # (ny, nx, sy, sx)
    data_scales = [1] * 4  # (y, x, dy, dx)
    nav_extent = [0, 1, 0, 1]  # (x0, x1, y0, y1)
    am = signal.axes_manager
    nav_axes = am.navigation_axes
    nav_dim = am.navigation_dimension
    if nav_dim == 1:
        nav_axis = nav_axes[0]
        if nav_axis.name == "y":
            data_shape[0] = nav_axis.size
            data_scales[0] = nav_axis.scale
            nav_extent[2:] = am.navigation_extent
        else:  # nav_axis.name == "x" or something else
            data_shape[1] = nav_axis.size
            data_scales[1] = nav_axis.scale
            nav_extent[:2] = am.navigation_extent
    elif nav_dim == 2:
        data_shape[:2] = [i.size for i in nav_axes][::-1]
        data_scales[:2] = [i.scale for i in nav_axes][::-1]
        nav_extent = am.navigation_extent
    data_shape[2:] = am.signal_shape
    data_scales[2:] = [i.scale for i in am.signal_axes]
    ny, nx, sy, sx = data_shape
    scale_ny, scale_nx, scale_sy, _ = data_scales
    md = signal.metadata.deepcopy()
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(ebsd_node + ".pattern_width", sx)
    md.set_item(ebsd_node + ".pattern_height", sy)
    md.set_item(ebsd_node + ".n_columns", nx)
    md.set_item(ebsd_node + ".n_rows", ny)
    md.set_item(ebsd_node + ".step_x", scale_nx)
    md.set_item(ebsd_node + ".step_y", scale_ny)
    md.set_item(ebsd_node + ".detector_pixel_size", scale_sy)
    # Separate EBSD and SEM metadata
    det_str, ebsd_str = ebsd_node.split(".")[-2:]  # Detector and EBSD nodes
    md_sem = md.get_item(sem_node).copy().as_dictionary()  # SEM node as dict
    md_det = md_sem.pop(det_str)  # Remove/assign detector node from SEM node
    md_ebsd = md_det.pop(ebsd_str)
    # Phases
    if md.get_item("Sample.Phases") is None:
        md = _update_phase_info(md, _phase_metadata())  # Add default phase
    md_ebsd["Phases"] = md.Sample.Phases.as_dictionary()
    for phase in md_ebsd["Phases"].keys():  # Ensure coordinates are arrays
        atom_coordinates = md_ebsd["Phases"][phase]["atom_coordinates"]
        for atom in atom_coordinates.keys():
            atom_coordinates[atom]["coordinates"] = np.array(
                atom_coordinates[atom]["coordinates"]
            )
    scan = {"EBSD": {"Header": md_ebsd}, "SEM": {"Header": md_sem}}

    # Write scan dictionary to HDF groups
    dict2h5ebsdgroup(scan, scan_group)

    # Write signal to file
    man_pats = manufacturer_pattern_names()
    dset_pattern_name = man_pats["kikuchipy"]
    overwrite_dataset(
        scan_group.create_group("EBSD/Data"),
        signal.data.reshape(nx * ny, sy, sx),
        dset_pattern_name,
        signal_axes=(2, 1),
        **kwargs,
    )
    nx_start, nx_stop, ny_start, ny_stop = nav_extent
    sample_pos = {
        "y_sample": np.tile(np.linspace(ny_start, ny_stop, ny), nx),
        "x_sample": np.tile(np.linspace(nx_start, nx_stop, nx), ny),
    }
    dict2h5ebsdgroup(sample_pos, scan_group["EBSD/Data"])

    f.close()
Example #21
0
def brukerheader2dicts(
    scan_group: h5py.Group, md: DictionaryTreeBrowser
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser, DictionaryTreeBrowser]:
    """Return scan metadata dictionaries from a Bruker h5ebsd file.

    Parameters
    ----------
    scan_group : h5py:Group
        HDF group of scan data and header.
    md : hyperspy.misc.utils.DictionaryTreeBrowser
        Dictionary with empty fields from kikuchipy's metadata.

    Returns
    -------
    md
        kikuchipy ``metadata`` elements available in Bruker file.
    omd
        All metadata available in Bruker file.
    scan_size
        Scan, image, step and detector pixel size available in Bruker
        file.
    """
    # Get header group and data group as dictionaries
    pattern_dset_names = list(manufacturer_pattern_names().values())
    hd = hdf5group2dict(
        group=scan_group["EBSD/Header"],
        data_dset_names=pattern_dset_names,
        recursive=True,
    )
    dd = hdf5group2dict(
        group=scan_group["EBSD/Data"], data_dset_names=pattern_dset_names,
    )

    # Populate metadata dictionary
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(ebsd_node + ".elevation_angle", hd["CameraTilt"])
    grid_type = hd["Grid Type"]
    if grid_type == "isometric":
        md.set_item(ebsd_node + ".grid_type", "square")
    else:
        raise IOError(
            f"Only square grids are supported, however a {grid_type} grid was "
            "passed."
        )
    md.set_item(ebsd_node + ".sample_tilt", hd["SampleTilt"])
    md.set_item(ebsd_node + ".xpc", np.mean(dd["PCX"]))
    md.set_item(ebsd_node + ".ypc", np.mean(dd["PCY"]))
    md.set_item(ebsd_node + ".zpc", np.mean(dd["DD"]))
    md.set_item(ebsd_node + ".static_background", hd["StaticBackground"])
    md.set_item(sem_node + ".working_distance", hd["WD"])
    md.set_item(sem_node + ".beam_energy", hd["KV"])
    md.set_item(sem_node + ".magnification", hd["Magnification"])
    # Loop over phases
    for phase_no, phase in hd["Phases"].items():
        phase["material_name"] = phase["Name"]
        phase["space_group"] = phase["IT"]
        phase["atom_coordinates"] = {}
        for key, val in phase["AtomPositions"].items():
            atom = val.split(",")
            atom[1:] = list(map(float, atom[1:]))
            phase["atom_coordinates"][key] = {
                "atom": atom[0],
                "coordinates": atom[1:4],
                "site_occupation": atom[4],
                "debye_waller_factor": atom[5],
            }
        md = _update_phase_info(md, phase, phase_no)

    # Populate original metadata dictionary
    omd = DictionaryTreeBrowser({"bruker_header": hd})

    # Populate scan size dictionary
    scan_size = DictionaryTreeBrowser()
    scan_size.set_item("sx", hd["PatternWidth"])
    scan_size.set_item("sy", hd["PatternHeight"])
    scan_size.set_item("nx", hd["NCOLS"])
    scan_size.set_item("ny", hd["NROWS"])
    scan_size.set_item("step_x", hd["XSTEP"])
    scan_size.set_item("step_y", hd["YSTEP"])
    scan_size.set_item(
        "delta", hd["DetectorFullHeightMicrons"] / hd["UnClippedPatternHeight"]
    )

    return md, omd, scan_size
Example #22
0
def edaxheader2dicts(
    scan_group: h5py.Group, md: DictionaryTreeBrowser
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser, DictionaryTreeBrowser]:
    """Return scan metadata dictionaries from an EDAX TSL h5ebsd file.

    Parameters
    ----------
    scan_group : h5py:Group
        HDF group of scan data and header.
    md
        Dictionary with empty fields from kikuchipy's metadata.

    Returns
    -------
    md
        kikuchipy ``metadata`` elements available in EDAX file.
    omd
        All metadata available in EDAX file.
    scan_size
        Scan, image, step and detector pixel size available in EDAX
        file.
    """
    # Get header group as dictionary
    pattern_dset_names = list(manufacturer_pattern_names().values())
    hd = hdf5group2dict(
        group=scan_group["EBSD/Header"],
        data_dset_names=pattern_dset_names,
        recursive=True,
    )

    # Populate metadata dictionary
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(ebsd_node + ".azimuth_angle", hd["Camera Azimuthal Angle"])
    md.set_item(ebsd_node + ".elevation_angle", hd["Camera Elevation Angle"])
    grid_type = hd["Grid Type"]
    if grid_type == "SqrGrid":
        md.set_item(ebsd_node + ".grid_type", "square")
    else:
        raise IOError(
            f"Only square grids are supported, however a {grid_type} grid was "
            "passed."
        )
    md.set_item(ebsd_node + ".sample_tilt", hd["Sample Tilt"])
    md.set_item("General.authors", hd["Operator"])
    md.set_item("General.notes", hd["Notes"])
    md.set_item(ebsd_node + ".xpc", hd["Pattern Center Calibration"]["x-star"])
    md.set_item(ebsd_node + ".ypc", hd["Pattern Center Calibration"]["y-star"])
    md.set_item(ebsd_node + ".zpc", hd["Pattern Center Calibration"]["z-star"])
    md.set_item(sem_node + ".working_distance", hd["Working Distance"])
    if "SEM-PRIAS Images" in scan_group.keys():
        md.set_item(
            sem_node + ".magnification",
            scan_group["SEM-PRIAS Images/Header/Mag"][0],
        )
    # Loop over phases in group and add to metadata
    for phase_no, phase in hd["Phase"].items():
        phase["material_name"] = phase["MaterialName"]
        phase["lattice_constants"] = [
            phase["Lattice Constant a"],
            phase["Lattice Constant b"],
            phase["Lattice Constant c"],
            phase["Lattice Constant alpha"],
            phase["Lattice Constant beta"],
            phase["Lattice Constant gamma"],
        ]
        md = _update_phase_info(md, phase, phase_no)

    # Populate original metadata dictionary
    omd = DictionaryTreeBrowser({"edax_header": hd})

    # Populate scan size dictionary
    scan_size = DictionaryTreeBrowser()
    scan_size.set_item("sx", hd["Pattern Width"])
    scan_size.set_item("sy", hd["Pattern Height"])
    scan_size.set_item("nx", hd["nColumns"])
    scan_size.set_item("ny", hd["nRows"])
    scan_size.set_item("step_x", hd["Step X"])
    scan_size.set_item("step_y", hd["Step Y"])
    scan_size.set_item("delta", 1.0)

    return md, omd, scan_size
Example #23
0
def kikuchipyheader2dicts(
    scan_group: h5py.Group, md: DictionaryTreeBrowser
) -> Tuple[DictionaryTreeBrowser, DictionaryTreeBrowser, DictionaryTreeBrowser]:
    """Return scan metadata dictionaries from a kikuchipy h5ebsd file.

    Parameters
    ----------
    scan_group : h5py:Group
        HDF group of scan data and header.
    md
        Dictionary with empty fields from kikuchipy's metadata.

    Returns
    -------
    md
        kikuchipy ``metadata`` elements available in kikuchipy file.
    omd
        All metadata available in kikuchipy file.
    scan_size
        Scan, image, step and detector pixel size available in
        kikuchipy file.
    """
    # Data sets to not read via hdf5group2dict
    pattern_dset_names = list(manufacturer_pattern_names().values())

    omd = DictionaryTreeBrowser()
    sem_node, ebsd_node = metadata_nodes(["sem", "ebsd"])
    md.set_item(
        ebsd_node,
        hdf5group2dict(
            group=scan_group["EBSD/Header"], data_dset_names=pattern_dset_names,
        ),
    )
    md = _delete_from_nested_dictionary(md, "Phases")
    phase_node = "Sample.Phases"
    md.set_item(
        sem_node,
        hdf5group2dict(
            group=scan_group["SEM/Header"], data_dset_names=pattern_dset_names,
        ),
    )
    md.set_item(
        phase_node,
        hdf5group2dict(
            group=scan_group["EBSD/Header/Phases"],
            data_dset_names=pattern_dset_names,
            recursive=True,
        ),
    )

    # Get and remove scan info values from metadata
    mapping = {
        "sx": "pattern_width",
        "sy": "pattern_height",
        "nx": "n_columns",
        "ny": "n_rows",
        "step_x": "step_x",
        "step_y": "step_y",
        "delta": "detector_pixel_size",
    }
    scan_size = DictionaryTreeBrowser()
    for k, v in mapping.items():
        scan_size.set_item(k, _get_nested_dictionary(md, ebsd_node + "." + v))
    md = _delete_from_nested_dictionary(md, mapping.values())

    return md, omd, scan_size