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]
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()
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
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")
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() )
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]
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)
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
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, ]
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()
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
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"})
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, ]
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
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
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, )
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()
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
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
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