예제 #1
0
 def test_get_input_variable(self, var_type):
     question = "How few are too few coffee cups?"
     answer = "1"
     with replace_stdin(io.StringIO(answer)):
         if isinstance(var_type, int):
             with pytest.raises(EOFError):
                 _ = _get_input_variable(question, var_type)
             return 0
         else:
             returns = _get_input_variable(question, var_type)
     assert returns == var_type(answer)
예제 #2
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()