Exemple #1
0
    def setUpClass(cls):
        if driver.get_backend_status() == driver.BACKEND_RUNNING:
            logging.info("A running backend is already found, skipping tests")
            cls.backend_was_running = True
            return

        # run the backend as a daemon
        # we cannot run it normally as the child would also think he's in a unittest
        cmd = ODEMISD_CMD + ODEMISD_ARG + [SPARC_CONFIG]
        ret = subprocess.call(cmd)
        if ret != 0:
            logging.error("Failed starting backend with '%s'", cmd)
        time.sleep(1) # time to start

        # Find CCD & SEM components
        cls.microscope = model.getMicroscope()
        for comp in model.getComponents():
            if comp.role == "ccd":
                cls.ccd = comp
            elif comp.role == "spectrometer":
                cls.spec = comp
            elif comp.role == "e-beam":
                cls.ebeam = comp
            elif comp.role == "se-detector":
                cls.sed = comp
            elif comp.role == "light":
                cls.light = comp
            elif comp.role == "filter":
                cls.light_filter = comp
Exemple #2
0
    def test_metadata(self):
        """
        Check if extra metadata are saved
        """
        settings_obs = SettingsObserver(model.getComponents())
        self.ccd.binning.value = (
            1, 1)  # make sure we don't save the right metadata by accident
        detvas = {'exposureTime', 'binning', 'gain'}
        s1 = stream.FluoStream("fluo2",
                               self.ccd,
                               self.ccd.data,
                               self.light,
                               self.light_filter,
                               detvas=detvas)
        s2 = stream.BrightfieldStream("bf",
                                      self.ccd,
                                      self.ccd.data,
                                      self.light,
                                      detvas=detvas)

        # Set different binning values for each stream
        s1.detBinning.value = (2, 2)
        s2.detBinning.value = (4, 4)
        st = stream.StreamTree(streams=[s1, s2])
        f = acqmng.acquire(st.getProjections(), settings_obs=settings_obs)
        data, e = f.result()
        for s in data:
            self.assertTrue(model.MD_EXTRA_SETTINGS in s.metadata,
                            "Stream %s didn't save extra metadata." % s)
        self.assertEqual(
            data[0].metadata[model.MD_EXTRA_SETTINGS][self.ccd.name]
            ['binning'], [(2, 2), 'px'])
        self.assertEqual(
            data[1].metadata[model.MD_EXTRA_SETTINGS][self.ccd.name]
            ['binning'], [(4, 4), 'px'])
Exemple #3
0
    def __init__(self):
        # the ebeam scanner
        self.escan = None
        # the secondary electron detector
        self.sed = None
        # the spectrometer
        self.spect = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                self.escan = c
            elif c.role == "se-detector":
                self.sed = c
            elif c.role == "spectrometer":
                self.spect = c
        if not all((self.escan, self.sed, self.spect)):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        self.set_roi(RECT_ROI)

        self.acq_left = 0  # how many points left to scan
        self.acq_spect_buf = [
        ]  # list that will receive the acquisition data (numpy arrays of shape (N,1))
        self.acq_complete = threading.Event()
Exemple #4
0
    def test_settings_observer_metadata_with_zstack(self):
        settings_observer = SettingsObserver(model.getComponents())
        vas = {"exposureTime"}
        s1 = stream.FluoStream("FM",
                               self.ccd,
                               self.ccd.data,
                               self.light,
                               self.light_filter,
                               detvas=vas,
                               focuser=self.fm_focuser)
        s1.detExposureTime.value = 0.023  # 23 ms

        zlevels_list = generate_zlevels(self.fm_focuser, [-2e-6, 2e-6], 1e-6)
        zlevels = {s1: list(zlevels_list)}

        f = acquireZStack([s1], zlevels, settings_observer)
        # get the data
        data, exp = f.result()
        self.assertIsNone(exp)
        for d in data:
            self.assertTrue(model.MD_EXTRA_SETTINGS in d.metadata)
            # if zstack, so the center has 3 components
            self.assertEqual(len(d.metadata[model.MD_POS]), 3)
            # if zstack, so the pixel size has 3 components
            self.assertEqual(len(d.metadata[model.MD_PIXEL_SIZE]), 3)
        self.assertEqual(
            data[0].metadata[model.MD_EXTRA_SETTINGS]["Camera"]
            ["exposureTime"], [0.023, "s"])
Exemple #5
0
    def setUpClass(cls):
        if driver.get_backend_status() == driver.BACKEND_RUNNING:
            logging.info("A running backend is already found, skipping tests")
            cls.backend_was_running = True
            return

        # run the backend as a daemon
        # we cannot run it normally as the child would also think he's in a unittest
        cmd = ODEMISD_CMD + ODEMISD_ARG + [SPARC_CONFIG]
        ret = subprocess.call(cmd)
        if ret != 0:
            logging.error("Failed starting backend with '%s'", cmd)
        time.sleep(1) # time to start

        # Find CCD & SEM components
        cls.microscope = model.getMicroscope()
        for comp in model.getComponents():
            if comp.role == "ccd":
                cls.ccd = comp
            elif comp.role == "spectrometer":
                cls.spec = comp
            elif comp.role == "e-beam":
                cls.ebeam = comp
            elif comp.role == "se-detector":
                cls.sed = comp
Exemple #6
0
def get_component(comp_name):
    """
    return the component with the given name
    comp_name (string): name of the component to find
    raises
        LookupError if the component doesn't exist
        other exception if there is an error while contacting the backend
    """
    return get_component_from_set(comp_name, model.getComponents())
Exemple #7
0
def affectsGraph(microscope):
    """
    Creates a graph based on the affects lists of the microscope components.
    returns (dict str->(set of str))
    """
    graph = {}
    for comp in model.getComponents():
        graph[comp.name] = set(comp.affects.value)
    return graph
Exemple #8
0
def get_component(comp_name):
    """
    return the component with the given name
    comp_name (string): name of the component to find
    raises
        LookupError if the component doesn't exist
        other exception if there is an error while contacting the backend
    """
    return get_component_from_set(comp_name, model.getComponents())
Exemple #9
0
def affectsGraph(microscope):
    """
    Creates a graph based on the affects lists of the microscope components.
    returns (dict str->(set of str))
    """
    graph = {}
    for comp in model.getComponents():
        graph[comp.name] = set(comp.affects.value)
    return graph
Exemple #10
0
def list_properties(comp_name, pretty=True):
    """
    print the data-flows and VAs of a component
    comp_name (string): name of the component or "*"
    pretty (bool): if True, display with pretty-printing
    """
    if comp_name == "*":
        for c in model.getComponents():
            print_attributes(c, pretty)
            print("")
    else:
        component = get_component(comp_name)
        print_attributes(component, pretty)
Exemple #11
0
def list_properties(comp_name, pretty=True):
    """
    print the data-flows and VAs of a component
    comp_name (string): name of the component or "*"
    pretty (bool): if True, display with pretty-printing
    """
    if comp_name == "*":
        for c in model.getComponents():
            print_attributes(c, pretty)
            print("")
    else:
        component = get_component(comp_name)
        print_attributes(component, pretty)
Exemple #12
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """
    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "focus":
                focus = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "sem-stage":
                sem_stage = c
            elif c.role == "ebeam-focus":
                ebeam_focus = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        logging.debug("Starting Phenom shift parameters calculation...")

        # Compute spot shift percentage
        f = delphi.SpotShiftFactor(ccd, detector, escan, focus)
        percentage = f.result()

        # Compute resolution-related values
        f = delphi.ResolutionShiftFactor(detector, escan, sem_stage,
                                         ebeam_focus)
        (a_x, a_y), (b_x, b_y) = f.result()

        # Compute HFW-related values
        f = delphi.HFWShiftFactor(detector, escan, sem_stage, ebeam_focus)
        c_x, c_y = f.result()

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info(
        "\n**Computed shift parameters**\n a_x: %f \n a_y: %f \n b_x: %f \n b_y: %f \n c_x: %f \n c_y: %f \n percentage: %s \n",
        a_x, a_y, b_x, b_y, c_x, c_y, percentage)
    return 0
Exemple #13
0
    def test_metadata(self):
        """
        Check if extra metadata are saved
        """
        settings_obs = SettingsObserver(model.getComponents())

        detvas = {"binning", "exposureTime"}
        sems = stream.SEMStream("test sem", self.sed, self.sed.data,
                                self.ebeam)
        specs = stream.SpectrumSettingsStream("test spec",
                                              self.spec,
                                              self.spec.data,
                                              self.ebeam,
                                              detvas=detvas)
        sps = stream.SEMSpectrumMDStream("test sem-spec", [sems, specs])

        specs.roi.value = (0, 0, 1, 1)
        specs.repetition.value = (2, 3)
        specs.detBinning.value = (2, specs.detBinning.value[1])
        specs.detExposureTime.value = 0.1

        specs2 = stream.SpectrumSettingsStream("test spec2",
                                               self.spec,
                                               self.spec.data,
                                               self.ebeam,
                                               detvas=detvas)
        sps2 = stream.SEMSpectrumMDStream("test sem-spec2", [sems, specs2])

        specs2.roi.value = (0, 0, 1, 1)
        specs2.repetition.value = (2, 3)
        specs2.detBinning.value = (4, specs2.detBinning.value[1])
        specs2.detExposureTime.value = 0.05

        f = acqmng.acquire([sps, sps2], settings_obs)
        data = f.result()

        spec1_data = data[0][1]
        spec2_data = data[0][3]
        self.assertEqual(
            spec1_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name]
            ['binning'], [(2, specs.detBinning.value[1]), 'px'])
        self.assertEqual(
            spec2_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name]
            ['binning'], [(4, specs2.detBinning.value[1]), 'px'])
        self.assertEqual(
            spec1_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name]
            ['exposureTime'], [0.1, 's'])
        self.assertEqual(
            spec2_data.metadata[model.MD_EXTRA_SETTINGS][self.spec.name]
            ['exposureTime'], [0.05, 's'])
Exemple #14
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(description="Automated focus procedure")

    parser.add_argument("--accuracy",
                        "-a",
                        dest="accuracy",
                        required=True,
                        help="Focus precision in meters")

    options = parser.parse_args(args[1:])
    accuracy = float(options.accuracy)

    try:
        ccd = None
        focus = None

        # find components by their role
        for c in model.getComponents():
            if c.role == "ccd":
                ccd = c
            elif c.role == "focus":
                focus = c
        if not all([ccd, focus]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # Measure current focus
        img = ccd.data.get()
        fm_cur = autofocus.MeasureFocus(img)
        logging.debug("Current focus level: %f", fm_cur)

        # Apply autofocus
        future_focus = align.AutoFocus(ccd, None, focus, accuracy)
        foc_pos, fm_final = future_focus.result()
        logging.debug("Focus level after applying autofocus: %f", fm_final)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #15
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """
    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "focus":
                focus = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "sem-stage":
                sem_stage = c
            elif c.role == "ebeam-focus":
                ebeam_focus = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        logging.debug("Starting Phenom shift parameters calculation...")
        
        # Compute spot shift percentage
        f = delphi.SpotShiftFactor(ccd, detector, escan, focus)
        percentage = f.result()

        # Compute resolution-related values
        f = delphi.ResolutionShiftFactor(detector, escan, sem_stage, ebeam_focus)
        (a_x, a_y), (b_x, b_y) = f.result()

        # Compute HFW-related values
        f = delphi.HFWShiftFactor(detector, escan, sem_stage, ebeam_focus)
        c_x, c_y = f.result()

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info("\n**Computed shift parameters**\n a_x: %f \n a_y: %f \n b_x: %f \n b_y: %f \n c_x: %f \n c_y: %f \n percentage: %s \n", a_x, a_y, b_x, b_y, c_x, c_y, percentage)
    return 0
Exemple #16
0
def acquire_timelapse(num, filename):

    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "ccd":
            ccd = c

    images = []
    for i in range(num):
        logging.info("Acquiring image %d", i + 1)
        images.append(ccd.data.get())

    # save the file
    exporter = dataio.find_fittest_converter(filename)
    exporter.export(filename, images)
Exemple #17
0
def updateMetadata(metadata, parent):
    """
    Update/fill the metadata with all the metadata from all the components
      affecting the given component
    metadata (dict str -> value): metadata
    parent (HwComponent): the component which created the data to which the metadata refers to. 
      Note that the metadata from this very component are not added.
    """
    # find every component which affects the parent
    for comp in model.getComponents():
        try:
            if parent in comp.affects:
                metadata.update(comp.getMetadata())
        except AttributeError:
            # no affects == empty set
            pass
Exemple #18
0
def acquire_timelapse(num, filename):

    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "ccd":
            ccd = c

    images = []
    for i in range(num):
        logging.info("Acquiring image %d", i + 1)
        images.append(ccd.data.get())
    
    # save the file
    exporter = dataio.find_fittest_converter(filename)
    exporter.export(filename, images)
Exemple #19
0
def print_component_tree(root, pretty=True, level=0):
    """
    Print all the components starting from the root.
    root (Component): the component at the root of the tree
    pretty (bool): if True, display with pretty-printing
    level (int > 0): hierarchy level (for pretty printing)
    """
    if pretty:
        # first print the root component
        print_component(root, pretty, level)

        # display all the children
        for comp in root.children.value:
            print_component_tree(comp, pretty, level + 1)
    else:
        for c in model.getComponents():
            print_component(c, pretty)
Exemple #20
0
def list_components(pretty=True):
    """
    pretty (bool): if True, display with pretty-printing
    """
    # Show the root first, and don't use it for the graph, because its "children"
    # are actually "dependencies", and it'd multiple parents in the graph.
    microscope = model.getMicroscope()
    subcomps = model.getComponents() - {microscope}

    print_component(microscope, pretty)
    if pretty:
        graph = build_graph_children(subcomps)
        print_component_graph(graph, pretty, 1)
    else:
        # The "pretty" code would do the same, but much slower
        for c in subcomps:
            print_component(c, pretty)
Exemple #21
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(description=
                     "Automated focus procedure")

    parser.add_argument("--accuracy", "-a", dest="accuracy", required=True,
                        help="Focus precision in meters")

    options = parser.parse_args(args[1:])
    accuracy = float(options.accuracy)

    try:
        ccd = None
        focus = None

        # find components by their role
        for c in model.getComponents():
            if c.role == "ccd":
                ccd = c
            elif c.role == "focus":
                focus = c
        if not all([ccd, focus]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")
    
        # Measure current focus
        img = ccd.data.get()
        fm_cur = autofocus.MeasureFocus(img)
        logging.debug("Current focus level: %f", fm_cur)

        # Apply autofocus
        future_focus = align.AutoFocus(ccd, None, focus, accuracy)
        foc_pos, fm_final = future_focus.result()
        logging.debug("Focus level after applying autofocus: %f", fm_final)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #22
0
def print_component_tree(root, pretty=True, level=0):
    """
    Print all the components starting from the root.
    root (Component): the component at the root of the tree
    pretty (bool): if True, display with pretty-printing
    level (int > 0): hierarchy level (for pretty printing)
    """
    if pretty:
        # first print the root component
        print_component(root, pretty, level)

        # display all the children
        for comp in root.children.value:
            print_component_tree(comp, pretty, level + 1)
    else:
        for c in model.getComponents():
            print_component(c, pretty)
Exemple #23
0
def acquire_grid(fn_prefix, zpos):
    """
    returns (int): number of positions acquired
    """

    escan = None
    sed = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "se-detector":
            sed = c
        elif c.role == "ccd":
            ccd = c
    if not all([escan, sed, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")


#    xyps = calc_xy_pos(escan)
#    xyps = predefined_xy_pos_0(escan)
    xyps = predefined_xy_pos_not0(escan)
    logging.debug("Will scan on X/Y positions %s", xyps)

    n_pos = 0
    for x, y in xyps:
        xnm, ynm = convert_xy_pos_to_nm(escan, x, y)
        logging.info("Acquiring at position (%+f, %+f)", xnm, ynm)

        startt = time.time()
        d = acquire_ar(escan, sed, ccd, x, y, N_IMAGES)
        endt = time.time()
        logging.debug("Took %g s (expected = %g s)", endt - startt,
                      EXP_TIME * N_IMAGES)

        save_data(d,
                  prefix=fn_prefix,
                  zpos=zpos,
                  ypos=round(ynm),
                  xpos=round(xnm))
        n_pos += 1

    return n_pos
Exemple #24
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(description=
                     "Automated spot alignment procedure")

    try:
        escan = None
        ccd = None
        focus = None
        stage = None

        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "focus":
                focus = c
            elif c.role == "align":
                stage = c
        if not all([escan, ccd, focus, stage]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # Apply spot alignment
        future_spot = align.AlignSpot(ccd, stage, escan, focus)
        t = future_spot.result()
        logging.debug("Final distance to the center (in meters): %f", t)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #25
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(
        description="Automated spot alignment procedure")

    try:
        escan = None
        ccd = None
        focus = None
        stage = None

        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "focus":
                focus = c
            elif c.role == "align":
                stage = c
        if not all([escan, ccd, focus, stage]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # Apply spot alignment
        future_spot = align.AlignSpot(ccd, stage, escan, focus)
        t = future_spot.result()
        logging.debug("Final distance to the center (in meters): %f", t)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #26
0
def acquire_grid(fn_prefix, zpos):
    """
    returns (int): number of positions acquired
    """

    escan = None
    sed = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "se-detector":
            sed = c
        elif c.role == "ccd":
            ccd = c
    if not all([escan, sed, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")

#    xyps = calc_xy_pos(escan)
#    xyps = predefined_xy_pos_0(escan)
    xyps = predefined_xy_pos_not0(escan)
    logging.debug("Will scan on X/Y positions %s", xyps)

    n_pos = 0
    for x, y in xyps:
        xnm, ynm = convert_xy_pos_to_nm(escan, x, y)
        logging.info("Acquiring at position (%+f, %+f)", xnm, ynm)

        startt = time.time()
        d = acquire_ar(escan, sed, ccd, x, y, N_IMAGES)
        endt = time.time()
        logging.debug("Took %g s (expected = %g s)",
                     endt - startt, EXP_TIME * N_IMAGES)

        save_data(d, prefix=fn_prefix, zpos=zpos, ypos=round(ynm), xpos=round(xnm))
        n_pos += 1

    return n_pos
Exemple #27
0
def stop_move():
    """
    stop the move of every axis of every actuators
    """
    # Take all the components and skip the ones that don't look like an actuator
    try:
        comps = model.getComponents()
    except Exception:
        raise IOError("Failed to contact the back-end")

    error = False
    for c in comps:
        if not isinstance(c.axes, collections.Mapping):
            continue
        try:
            c.stop()
        except Exception:
            logging.exception("Failed to stop actuator %s", c.name)
            error = True

    if error:
        raise IOError("Failed to stop all the actuators")
Exemple #28
0
def print_component_tree(root, pretty=True, level=0):
    """
    Print all the components starting from the root.
    root (Component): the component at the root of the tree
    pretty (bool): if True, display with pretty-printing
    level (int > 0): hierarchy level (for pretty printing)
    """
    if pretty:
        # first print the root component
        print_component(root, pretty, level)

        children = set(root.children)
        # For microscope, it doesn't have anything in children
        if isinstance(root.detectors, collections.Set):
            children = root.detectors | root.emitters | root.actuators

        # display all the children
        for comp in children:
            print_component_tree(comp, pretty, level + 1)
    else:
        for c in model.getComponents():
            print_component(c, pretty)
Exemple #29
0
def stop_move():
    """
    stop the move of every axis of every actuators
    """
    # Take all the components and skip the ones that don't look like an actuator
    try:
        comps = model.getComponents()
    except Exception:
        raise IOError("Failed to contact the back-end")

    error = False
    for c in comps:
        if not isinstance(c.axes, collections.Mapping):
            continue
        try:
            c.stop()
        except Exception:
            logging.exception("Failed to stop actuator %s", c.name)
            error = True

    if error:
        raise IOError("Failed to stop all the actuators")
Exemple #30
0
def print_component_tree(root, pretty=True, level=0):
    """
    Print all the components starting from the root.
    root (Component): the component at the root of the tree
    pretty (bool): if True, display with pretty-printing
    level (int > 0): hierarchy level (for pretty printing)
    """
    if pretty:
        # first print the root component
        print_component(root, pretty, level)

        children = set(root.children)
        # For microscope, it doesn't have anything in children
        if isinstance(root.detectors, collections.Set):
            children = root.detectors | root.emitters | root.actuators

        # display all the children
        for comp in children:
            print_component_tree(comp, pretty, level + 1)
    else:
        for c in model.getComponents():
            print_component(c, pretty)
Exemple #31
0
    def setUpClass(cls):

        if driver.get_backend_status() == driver.BACKEND_RUNNING:
            logging.info("A running backend is already found, skipping tests")
            cls.backend_was_running = True
            return

        # run the backend as a daemon
        # we cannot run it normally as the child would also think he's in a unittest
        cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_CONFIG]
        ret = subprocess.call(cmd)
        if ret != 0:
            logging.error("Failed starting backend with '%s'", cmd)
        time.sleep(1) # time to start

        # create some streams connected to the backend
        cls.microscope = model.getMicroscope()
        for comp in model.getComponents():
            if comp.role == "ccd":
                cls.ccd = comp
            elif comp.role == "spectrometer":
                cls.spec = comp
            elif comp.role == "e-beam":
                cls.ebeam = comp
            elif comp.role == "se-detector":
                cls.sed = comp
            elif comp.role == "light":
                cls.light = comp
            elif comp.role == "filter":
                cls.light_filter = comp

        s1 = stream.FluoStream("fluo1", cls.ccd, cls.ccd.data,
                               cls.light, cls.light_filter)
        s2 = stream.FluoStream("fluo2", cls.ccd, cls.ccd.data,
                               cls.light, cls.light_filter)
        s2.excitation.value = s2.excitation.range[1]
        s3 = stream.BrightfieldStream("bf", cls.ccd, cls.ccd.data, cls.light)
        cls.streams = [s1, s2, s3]
Exemple #32
0
 def __init__(self):
     # the ebeam scanner
     self.escan = None
     # the secondary electron detector 
     self.sed = None
     # the spectrometer
     self.spect = None
     # find components by their role
     for c in model.getComponents():
         if c.role == "e-beam":
             self.escan = c
         elif c.role == "se-detector":
             self.sed = c
         elif c.role == "spectrometer":
             self.spect = c
     if not all((self.escan, self.sed, self.spect)):
         logging.error("Failed to find all the components")
         raise KeyError("Not all components found")
     
     self.set_roi(RECT_ROI)
     
     self.acq_left = 0 # how many points left to scan
     self.acq_spect_buf = [] # list that will receive the acquisition data (numpy arrays of shape (N,1))
     self.acq_complete = threading.Event()
Exemple #33
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser()

    parser.add_argument("--repetitions_x", "-x", dest="repetitions_x",
                        type=int, default=4,
                        help="repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument("--repetitions_y", "-y", dest="repetitions_y",
                        type=int, default=4,
                        help="repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument("--dwell_time", "-t", dest="dwell_time", required=True,
                        type=float,
                        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument("--max_allowed_diff", "-d", dest="max_allowed_diff", required=True,
                        type=float,
                        help="max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)")

    options = parser.parse_args(args[1:])
    repetitions = (options.repetitions_x, options.repetitions_y)
    dwell_time = options.dwell_time
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            # elif c.role == "light":
            #    light = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # f_acq = SEMCCDAcquisition(escan, ccd, detector, light)

        # optical_image_1, optical_image_2, optical_image_3, electron_image = f_acq.result()

        f = find_overlay.FindOverlay(repetitions, dwell_time, max_allowed_diff, escan, ccd, detector,
                                     skew=True)
        trans_val, cor_md = f.result()
        trans_md, skew_md = cor_md
        iscale = trans_md[model.MD_PIXEL_SIZE_COR]
        irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
        ishear = -skew_md[model.MD_SHEAR_COR]
        iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
        logging.debug("iscale: %s irot: %s ishear: %s iscale_xy: %s", iscale, irot, ishear, iscale_xy)

        # md_1 = img.mergeMetadata(optical_image_1.metadata, correction_md)
        # md_2 = img.mergeMetadata(optical_image_2.metadata, correction_md)
        # md_3 = img.mergeMetadata(optical_image_3.metadata, correction_md)
        # optical_image_1.metadata.update(md_1)
        # optical_image_2.metadata.update(md_2)
        # optical_image_3.metadata.update(md_3)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #34
0
def man_calib(logpath):
    escan = None
    detector = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "bs-detector":
            detector = c
        elif c.role == "ccd":
            ccd = c
        elif c.role == "sem-stage":
            sem_stage = c
        elif c.role == "align":
            opt_stage = c
        elif c.role == "ebeam-focus":
            ebeam_focus = c
        elif c.role == "overview-focus":
            navcam_focus = c
        elif c.role == "focus":
            focus = c
        elif c.role == "overview-ccd":
            overview_ccd = c
        elif c.role == "chamber":
            chamber = c
    if not all([escan, detector, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")

    hw_settings = aligndelphi.list_hw_settings(escan, ccd)

    try:
        # Get pressure values
        pressures = chamber.axes["pressure"].choices
        vacuum_pressure = min(pressures.keys())
        vented_pressure = max(pressures.keys())
        if overview_ccd:
            for p, pn in pressures.items():
                if pn == "overview":
                    overview_pressure = p
                    break
            else:
                raise IOError("Failed to find the overview pressure in %s" % (pressures,))

        calibconf = get_calib_conf()
        shid, sht = chamber.sampleHolder.value
        calib_values = calibconf.get_sh_calib(shid)
        if calib_values is None:
            first_hole = second_hole = offset = resa = resb = hfwa = spotshift = (0, 0)
            scaling = iscale = iscale_xy = (1, 1)
            rotation = irot = ishear = 0
            hole_focus = aligndelphi.SEM_KNOWN_FOCUS
            opt_focus = aligndelphi.OPTICAL_KNOWN_FOCUS
            print "\033[1;31mCalibration values missing! All the steps will be performed anyway...\033[1;m"
            force_calib = True
        else:
            first_hole, second_hole, hole_focus, opt_focus, offset, scaling, rotation, iscale, irot, iscale_xy, ishear, resa, resb, hfwa, spotshift = calib_values
            force_calib = False
        print "\033[1;36m"
        print "**Delphi Manual Calibration steps**"
        print "1.Sample holder hole detection"
        print "    Current values: 1st hole: " + str(first_hole)
        print "                    2st hole: " + str(second_hole)
        print "                    hole focus: " + str(hole_focus)
        print "2.Twin stage calibration"
        print "    Current values: offset: " + str(offset)
        print "                    scaling: " + str(scaling)
        print "                    rotation: " + str(rotation)
        print "                    optical focus: " + str(opt_focus)
        print "3.Fine alignment"
        print "    Current values: scale: " + str(iscale)
        print "                    rotation: " + str(irot)
        print "                    scale-xy: " + str(iscale_xy)
        print "                    shear: " + str(ishear)
        print "4.SEM image calibration"
        print "    Current values: resolution-a: " + str(resa)
        print "                    resolution-b: " + str(resb)
        print "                    hfw-a: " + str(hfwa)
        print "                    spot shift: " + str(spotshift)
        print '\033[1;m'
        print "\033[1;33mNote that you should not perform any stage move during the process. \nInstead, you may zoom in/out while focusing.\033[1;m"
        print "\033[1;30mNow initializing, please wait...\033[1;m"

        # Move to the overview position first
        f = chamber.moveAbs({"pressure": overview_pressure})
        f.result()

        # Reference the (optical) stage
        f = opt_stage.reference({"x", "y"})
        f.result()

        f = focus.reference({"z"})
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            f = aligndelphi.LensAlignment(overview_ccd, sem_stage, logpath)
            position = f.result()
        except IOError as ex:
            if not force_calib:
                position = (offset[0] * scaling[0], offset[1] * scaling[1])
            else:
                position = (0, 0)
            logging.warning("Failed to locate the optical lens (%s), will used previous value %s",
                            ex, position)

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": vacuum_pressure})
        f.result()

        # Set basic e-beam settings
        escan.spotSize.value = 2.7
        escan.accelVoltage.value = 5300  # V

        # Detect the holes/markers of the sample holder
        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the sample holder hole detection? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Move Phenom sample stage next to expected hole position
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)
                ebeam_focus.moveAbsSync({"z": hole_focus})
                # Set the FoV to almost 2mm
                escan.horizontalFoV.value = escan.horizontalFoV.range[1]
                msg = "\033[1;34mPlease turn on the SEM stream and focus the SEM image. Then turn off the stream and press Enter ...\033[1;m"
                raw_input(msg)
                print "\033[1;30mTrying to detect the holes/markers, please wait...\033[1;m"
                try:
                    hole_detectionf = aligndelphi.HoleDetection(detector, escan, sem_stage,
                                                                ebeam_focus, manual=True, logpath=logpath)
                    new_first_hole, new_second_hole, new_hole_focus = hole_detectionf.result()
                    print '\033[1;36m'
                    print "Values computed: 1st hole: " + str(new_first_hole)
                    print "                 2st hole: " + str(new_second_hole)
                    print "                 hole focus: " + str(new_hole_focus)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        first_hole, second_hole, hole_focus = new_first_hole, new_second_hole, new_hole_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mSample holder hole detection failed.\033[1;m"
            else:
                break

        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        f = opt_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        if hole_focus is not None:
            good_focus = hole_focus - aligndelphi.GOOD_FOCUS_OFFSET
        else:
            good_focus = aligndelphi.SEM_KNOWN_FOCUS - aligndelphi.GOOD_FOCUS_OFFSET
        f = ebeam_focus.moveAbs({"z": good_focus})
        f.result()

        # Set min fov
        # We want to be as close as possible to the center when we are zoomed in
        escan.horizontalFoV.value = escan.horizontalFoV.range[0]
        pure_offset = None

        # Start with the best optical focus known so far
        f = focus.moveAbs({"z": opt_focus})
        f.result()

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the twin stage calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                if not force_calib:
                    print "\033[1;33mIf you cannot see the whole source background (bright circle) you may try to move to the already known offset position. \nTo do this press the R key at any moment.\033[1;m"
                    rollback_pos = (offset[0] * scaling[0], offset[1] * scaling[1])
                else:
                    rollback_pos = None
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, escan.depthOfField.value)
                ar.focusByArrow(rollback_pos)
                detector.data.unsubscribe(_discard_data)
                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                try:
                    # TODO: the first point (at 0,0) isn't different from the next 4 points,
                    # excepted it might be a little harder to focus.
                    # => use the same code for all of them
                    align_offsetf = aligndelphi.AlignAndOffset(ccd, detector, escan, sem_stage,
                                                               opt_stage, focus, logpath)
                    align_offset = align_offsetf.result()
                    new_opt_focus = focus.position.value.get('z')

                    def ask_user_to_focus(n):
                        detector.data.subscribe(_discard_data)
                        msg = ("\033[1;34mAbout to calculate rotation and scaling (%d/4). "
                               "Please turn on the Optical stream, "
                               "set Power to 0 Watt and focus the image using the mouse "
                               "so you have a clearly visible spot. \n"
                               "If you do not see a spot nor the source background, "
                               "move the sem-stage from the command line by steps of 200um "
                               "in x and y until you can see the source background at the center. \n"
                               "Then turn off the stream and press Enter ...\033[1;m" %
                               (n + 1,))
                        raw_input(msg)  # TODO: use ArrowFocus() too?
                        print "\033[1;30mCalculating rotation and scaling (%d/4), please wait...\033[1;m" % (n + 1,)
                        detector.data.unsubscribe(_discard_data)

                    f = aligndelphi.RotationAndScaling(ccd, detector, escan, sem_stage,
                                                       opt_stage, focus, align_offset,
                                                       manual=ask_user_to_focus, logpath=logpath)
                    acc_offset, new_rotation, new_scaling = f.result()

                    # Offset is divided by scaling, since Convert Stage applies scaling
                    # also in the given offset
                    pure_offset = acc_offset
                    new_offset = ((acc_offset[0] / new_scaling[0]), (acc_offset[1] / new_scaling[1]))

                    print '\033[1;36m'
                    print "Values computed: offset: " + str(new_offset)
                    print "                 scaling: " + str(new_scaling)
                    print "                 rotation: " + str(new_rotation)
                    print "                 optical focus: " + str(new_opt_focus)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        offset, scaling, rotation, opt_focus = new_offset, new_scaling, new_rotation, new_opt_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mTwin stage calibration failed.\033[1;m"
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the fine alignment? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Return to the center so fine alignment can be executed just after calibration
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({"x":pure_offset[0], "y":pure_offset[1]})
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({"x":offset[0] * scaling[0], "y":offset[1] * scaling[1]})
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
                    f.result()

                f = focus.moveAbs({"z": opt_focus})
                f.result()
                f = ebeam_focus.moveAbs({"z": good_focus})
                f.result()

                # Run the optical fine alignment
                # TODO: reuse the exposure time
                # Configure e-beam to write CL spots
                escan.horizontalFoV.value = escan.horizontalFoV.range[0]
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, escan.depthOfField.value)
                ar.focusByArrow()
                detector.data.unsubscribe(_discard_data)

                # restore CCD settings (as the GUI/user might have changed them)
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                # Center (roughly) the spot on the CCD
                f = spot.CenterSpot(ccd, sem_stage, escan, spot.ROUGH_MOVE, spot.STAGE_MOVE, detector.data)
                dist, vect = f.result()
                if dist is None:
                    logging.warning("Failed to find a spot, twin stage calibration might have failed")

                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                try:
                    escan.horizontalFoV.value = 80e-06
                    f = align.FindOverlay((4, 4),
                                          0.5,  # s, dwell time
                                          10e-06,  # m, maximum difference allowed
                                          escan,
                                          ccd,
                                          detector,
                                          skew=True,
                                          bgsub=True)
                    trans_val, cor_md = f.result()
                    trans_md, skew_md = cor_md
                    new_iscale = trans_md[model.MD_PIXEL_SIZE_COR]
                    new_irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
                    new_ishear = skew_md[model.MD_SHEAR_COR]
                    new_iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
                    print '\033[1;36m'
                    print "Values computed: scale: " + str(new_iscale)
                    print "                 rotation: " + str(new_irot)
                    print "                 scale-xy: " + str(new_iscale_xy)
                    print "                 shear: " + str(new_ishear)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        iscale, irot, iscale_xy, ishear = new_iscale, new_irot, new_iscale_xy, new_ishear
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except ValueError:
                    print "\033[1;31mFine alignment failed.\033[1;m"
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the SEM image calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Resetting shift parameters, to not take them into account during calib
                blank_md = dict.fromkeys(aligndelphi.MD_CALIB_SEM, (0, 0))
                escan.updateMetadata(blank_md)

                # We measure the shift in the area just behind the hole where there
                # are always some features plus the edge of the sample carrier. For
                # that reason we use the focus measured in the hole detection step
                f = sem_stage.moveAbs(aligndelphi.SHIFT_DETECTION)
                f.result()

                f = ebeam_focus.moveAbs({"z": hole_focus})
                f.result()
                try:
                    # Compute spot shift percentage
                    print "\033[1;30mSpot shift measurement in progress, please wait...\033[1;m"
                    f = aligndelphi.ScaleShiftFactor(detector, escan, logpath)
                    new_spotshift = f.result()

                    # Compute resolution-related values.
                    print "\033[1;30mCalculating resolution shift, please wait...\033[1;m"
                    resolution_shiftf = aligndelphi.ResolutionShiftFactor(detector, escan, logpath)
                    new_resa, new_resb = resolution_shiftf.result()

                    # Compute HFW-related values
                    print "\033[1;30mCalculating HFW shift, please wait...\033[1;m"
                    hfw_shiftf = aligndelphi.HFWShiftFactor(detector, escan, logpath)
                    new_hfwa = hfw_shiftf.result()

                    print '\033[1;36m'
                    print "Values computed: resolution-a: " + str(new_resa)
                    print "                 resolution-b: " + str(new_resb)
                    print "                 hfw-a: " + str(new_hfwa)
                    print "                 spot shift: " + str(new_spotshift)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        resa, resb, hfwa, spotshift = new_resa, new_resb, new_hfwa, new_spotshift
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mSEM image calibration failed.\033[1;m"
            else:
                break

        # Update calibration file
        print "\033[1;30mUpdating calibration file is done\033[1;m"

    finally:
        print "\033[1;30mCalibration ended, now ejecting sample, please wait...\033[1;m"
        # Eject the sample holder
        f = chamber.moveAbs({"pressure": vented_pressure})

        aligndelphi.restore_hw_settings(escan, ccd, hw_settings)

        # Store the final version of the calibration file
        try:
            shutil.copy(calibconf.file_path, logpath)
        except Exception:
            logging.info("Failed to log calibration file", exc_info=True)

        f.result()
Exemple #35
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--repetitions_x",
        "-x",
        dest="repetitions_x",
        type=int,
        default=4,
        help=
        "repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument(
        "--repetitions_y",
        "-y",
        dest="repetitions_y",
        type=int,
        default=4,
        help=
        "repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument(
        "--dwell_time",
        "-t",
        dest="dwell_time",
        required=True,
        type=float,
        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument(
        "--max_allowed_diff",
        "-d",
        dest="max_allowed_diff",
        required=True,
        type=float,
        help=
        "max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)"
    )

    options = parser.parse_args(args[1:])
    repetitions = (options.repetitions_x, options.repetitions_y)
    dwell_time = options.dwell_time
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            # elif c.role == "light":
            #    light = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # f_acq = SEMCCDAcquisition(escan, ccd, detector, light)

        # optical_image_1, optical_image_2, optical_image_3, electron_image = f_acq.result()

        f = find_overlay.FindOverlay(repetitions,
                                     dwell_time,
                                     max_allowed_diff,
                                     escan,
                                     ccd,
                                     detector,
                                     skew=True)
        trans_val, cor_md = f.result()
        trans_md, skew_md = cor_md
        iscale = trans_md[model.MD_PIXEL_SIZE_COR]
        irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
        ishear = -skew_md[model.MD_SHEAR_COR]
        iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
        logging.debug("iscale: %s irot: %s ishear: %s iscale_xy: %s", iscale,
                      irot, ishear, iscale_xy)

        # md_1 = img.mergeMetadata(optical_image_1.metadata, correction_md)
        # md_2 = img.mergeMetadata(optical_image_2.metadata, correction_md)
        # md_3 = img.mergeMetadata(optical_image_3.metadata, correction_md)
        # optical_image_1.metadata.update(md_1)
        # optical_image_2.metadata.update(md_2)
        # optical_image_3.metadata.update(md_3)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #36
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(description=
                     "Automated AR acquisition at multiple spot locations")

    parser.add_argument("--repetitions_x", "-x", dest="repetitions_x", required=True,
                        help="repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument("--repetitions_y", "-y", dest="repetitions_y", required=True,
                        help="repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument("--dwell_time", "-t", dest="dwell_time", required=True,
                        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument("--max_allowed_diff", "-d", dest="max_allowed_diff", required=True,
                        help="max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)")

    options = parser.parse_args(args[1:])
    repetitions = (int(options.repetitions_x), int(options.repetitions_y))
    dwell_time = float(options.dwell_time)
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "se-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "light":
                light = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")
        
        f_acq = SEMCCDAcquisition(escan, ccd, detector, light)

        optical_image_1, optical_image_2, optical_image_3, electron_image = f_acq.result()
        
        f = find_overlay.FindOverlay(repetitions, dwell_time, max_allowed_diff, escan, ccd, detector)
        trans_val, correction_md = f.result()

        md_1 = img.mergeMetadata(optical_image_1.metadata, correction_md)
        md_2 = img.mergeMetadata(optical_image_2.metadata, correction_md)
        md_3 = img.mergeMetadata(optical_image_3.metadata, correction_md)
        optical_image_1.metadata.update(md_1)
        optical_image_2.metadata.update(md_2)
        optical_image_3.metadata.update(md_3)

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    return 0
Exemple #37
0
def man_calib(logpath):
    escan = None
    detector = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "bs-detector":
            detector = c
        elif c.role == "ccd":
            ccd = c
        elif c.role == "sem-stage":
            sem_stage = c
        elif c.role == "align":
            opt_stage = c
        elif c.role == "ebeam-focus":
            ebeam_focus = c
        elif c.role == "overview-focus":
            navcam_focus = c
        elif c.role == "focus":
            focus = c
        elif c.role == "overview-ccd":
            overview_ccd = c
        elif c.role == "chamber":
            chamber = c
    if not all([escan, detector, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")

    hw_settings = aligndelphi.list_hw_settings(escan, ccd)

    try:
        # Get pressure values
        pressures = chamber.axes["pressure"].choices
        vacuum_pressure = min(pressures.keys())
        vented_pressure = max(pressures.keys())
        if overview_ccd:
            for p, pn in pressures.items():
                if pn == "overview":
                    overview_pressure = p
                    break
            else:
                raise IOError("Failed to find the overview pressure in %s" % (pressures,))

        calibconf = get_calib_conf()
        shid, sht = chamber.sampleHolder.value
        calib_values = calibconf.get_sh_calib(shid)
        if calib_values is None:
            first_hole = second_hole = offset = resa = resb = hfwa = scaleshift = (0, 0)
            scaling = iscale = iscale_xy = (1, 1)
            rotation = irot = ishear = 0
            hole_focus = aligndelphi.SEM_KNOWN_FOCUS
            opt_focus = aligndelphi.OPTICAL_KNOWN_FOCUS
            print_col(ANSI_RED, "Calibration values missing! All the steps will be performed anyway...")
            force_calib = True
        else:
            first_hole, second_hole, hole_focus, opt_focus, offset, scaling, rotation, iscale, irot, iscale_xy, ishear, resa, resb, hfwa, scaleshift = calib_values
            force_calib = False
        print_col(ANSI_CYAN,
                  "**Delphi Manual Calibration steps**\n"
                  "1.Sample holder hole detection\n"
                  "    Current values: 1st hole: " + str(first_hole) + "\n"
                  "                    2st hole: " + str(second_hole) + "\n"
                  "                    hole focus: " + str(hole_focus) + "\n"
                  "2.SEM image calibration\n"
                  "    Current values: resolution-a: " + str(resa) + "\n"
                  "                    resolution-b: " + str(resb) + "\n"
                  "                    hfw-a: " + str(hfwa) + "\n"
                  "                    spot shift: " + str(scaleshift) + "\n"
                  "3.Twin stage calibration\n"
                  "    Current values: offset: " + str(offset) + "\n"
                  "                    scaling: " + str(scaling) + "\n"
                  "                    rotation: " + str(rotation) + "\n"
                  "                    optical focus: " + str(opt_focus) + "\n"
                  "4.Fine alignment\n"
                  "    Current values: scale: " + str(iscale) + "\n"
                  "                    rotation: " + str(irot) + "\n"
                  "                    scale-xy: " + str(iscale_xy) + "\n"
                  "                    shear: " + str(ishear))
        print_col(ANSI_YELLOW,
                  "Note that you should not perform any stage move during the process.\n"
                  "Instead, you may zoom in/out while focusing.")
        print_col(ANSI_BLACK, "Now initializing, please wait...")

        # Move to the overview position first
        f = chamber.moveAbs({"pressure": overview_pressure})
        f.result()

        # Reference the (optical) stage
        f = opt_stage.reference({"x", "y"})
        f.result()

        f = focus.reference({"z"})
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            f = aligndelphi.LensAlignment(overview_ccd, sem_stage, logpath)
            position = f.result()
        except IOError as ex:
            if not force_calib:
                position = (offset[0] * scaling[0], offset[1] * scaling[1])
            else:
                position = (0, 0)
            logging.warning("Failed to locate the optical lens (%s), will used previous value %s",
                            ex, position)

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": vacuum_pressure})
        f.result()

        # Set basic e-beam settings
        escan.spotSize.value = 2.7
        escan.accelVoltage.value = 5300  # V
        # Without automatic blanker, the background subtraction doesn't work
        if (model.hasVA(escan, "blanker") and  # For simulator
            None in escan.blanker.choices and
            escan.blanker.value is not None):
            logging.warning("Blanker set back to automatic")
            escan.blanker.value = None

        # Detect the holes/markers of the sample holder
        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the sample holder hole detection? [Y/n]")
            if ans in YES_CHARS:
                # Move Phenom sample stage next to expected hole position
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)
                ebeam_focus.moveAbsSync({"z": hole_focus})
                # Set the FoV to almost 2mm
                escan.horizontalFoV.value = escan.horizontalFoV.range[1]
                input_col(ANSI_BLUE,
                          "Please turn on the SEM stream and focus the SEM image. Then turn off the stream and press Enter...")
                print_col(ANSI_BLACK,"Trying to detect the holes/markers, please wait...")
                try:
                    hole_detectionf = aligndelphi.HoleDetection(detector, escan, sem_stage,
                                                                ebeam_focus, manual=True, logpath=logpath)
                    new_first_hole, new_second_hole, new_hole_focus = hole_detectionf.result()
                    print_col(ANSI_CYAN,
                              "Values computed: 1st hole: " + str(new_first_hole) + "\n"
                              "                 2st hole: " + str(new_second_hole) + "\n"
                              "                 hole focus: " + str(new_hole_focus))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        first_hole, second_hole, hole_focus = new_first_hole, new_second_hole, new_hole_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "Sample holder hole detection failed.")
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the SEM image calibration? [Y/n]")
            if ans in YES_CHARS:
                # Resetting shift parameters, to not take them into account during calib
                blank_md = dict.fromkeys(aligndelphi.MD_CALIB_SEM, (0, 0))
                escan.updateMetadata(blank_md)

                # We measure the shift in the area just behind the hole where there
                # are always some features plus the edge of the sample carrier. For
                # that reason we use the focus measured in the hole detection step
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)

                ebeam_focus.moveAbsSync({"z": hole_focus})
                try:
                    # Compute spot shift percentage
                    print_col(ANSI_BLACK, "Spot shift measurement in progress, please wait...")
                    f = aligndelphi.ScaleShiftFactor(detector, escan, logpath)
                    new_scaleshift = f.result()

                    # Compute resolution-related values.
                    print_col(ANSI_BLACK, "Calculating resolution shift, please wait...")
                    resolution_shiftf = aligndelphi.ResolutionShiftFactor(detector, escan, logpath)
                    new_resa, new_resb = resolution_shiftf.result()

                    # Compute HFW-related values
                    print_col(ANSI_BLACK, "Calculating HFW shift, please wait...")
                    hfw_shiftf = aligndelphi.HFWShiftFactor(detector, escan, logpath)
                    new_hfwa = hfw_shiftf.result()

                    print_col(ANSI_CYAN,
                              "Values computed: resolution-a: " + str(new_resa) + "\n"
                              "                 resolution-b: " + str(new_resb) + "\n"
                              "                 hfw-a: " + str(new_hfwa) + "\n"
                              "                 spot shift: " + str(new_scaleshift))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        resa, resb, hfwa, scaleshift = new_resa, new_resb, new_hfwa, new_scaleshift
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "SEM image calibration failed.")
            else:
                break

        # Update the SEM metadata to have the spots already at corrected place
        escan.updateMetadata({
            model.MD_RESOLUTION_SLOPE: resa,
            model.MD_RESOLUTION_INTERCEPT: resb,
            model.MD_HFW_SLOPE: hfwa,
            model.MD_SPOT_SHIFT: scaleshift
        })

        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        f = opt_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        if hole_focus is not None:
            good_focus = hole_focus - aligndelphi.GOOD_FOCUS_OFFSET
        else:
            good_focus = aligndelphi.SEM_KNOWN_FOCUS - aligndelphi.GOOD_FOCUS_OFFSET
        f = ebeam_focus.moveAbs({"z": good_focus})
        f.result()

        # Set min fov
        # We want to be as close as possible to the center when we are zoomed in
        escan.horizontalFoV.value = escan.horizontalFoV.range[0]
        pure_offset = None

        # Start with the best optical focus known so far
        f = focus.moveAbs({"z": opt_focus})
        f.result()

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the twin stage calibration? [Y/n]")
            if ans in YES_CHARS:
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = ccd.binning.clip((4, 4))
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print_col(ANSI_BLUE,
                          "Please turn on the Optical stream, set Power to 0 Watt "
                          "and focus the image so you have a clearly visible spot.\n"
                          "Use the up and down arrows or the mouse to move the "
                          "optical focus and right and left arrows to move the SEM focus. "
                          "Then turn off the stream and press Enter...")
                if not force_calib:
                    print_col(ANSI_YELLOW,
                              "If you cannot see the whole source background (bright circle) "
                              "you may try to move to the already known offset position. \n"
                              "To do this press the R key at any moment and use I to go back "
                              "to the initial position.")
                    rollback_pos = (offset[0] * scaling[0], offset[1] * scaling[1])
                else:
                    rollback_pos = None
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, 10e-6)
                ar.focusByArrow(rollback_pos)
                detector.data.unsubscribe(_discard_data)
                print_col(ANSI_BLACK, "Twin stage calibration starting, please wait...")
                try:
                    # TODO: the first point (at 0,0) isn't different from the next 4 points,
                    # excepted it might be a little harder to focus.
                    # => use the same code for all of them
                    align_offsetf = aligndelphi.AlignAndOffset(ccd, detector, escan, sem_stage,
                                                               opt_stage, focus, logpath)
                    align_offset = align_offsetf.result()
                    new_opt_focus = focus.position.value.get('z')

                    # If the offset is large, it can prevent the SEM stage to follow
                    # the optical stage. If it's really large (eg > 1mm) it will
                    # even prevent from going to the calibration locations.
                    # So warn about this, as soon as we detect it. It could be
                    # caused either due to a mistake in the offset detection, or
                    # because the reference switch of the optical axis is not
                    # centered (enough) on the axis. In such case, a technician
                    # should open the sample holder and move the reference switch.
                    # Alternatively, we could try to be more clever and support
                    # the range of the tmcm axes to be defined per sample
                    # holder, and set some asymmetric range (to reflect the fact
                    # that 0 is not at the center).
                    for a, trans in zip(("x", "y"), align_offset):
                        # SEM pos = Opt pos + offset
                        rng_sem = sem_stage.axes[a].range
                        rng_opt = opt_stage.axes[a].range
                        if (rng_opt[0] + trans < rng_sem[0] or
                            rng_opt[1] + trans > rng_sem[1]):
                            logging.info("Stage align offset = %s, which could cause "
                                         "moves on the SEM stage out of range (on axis %s)",
                                         align_offset, a)
                            input_col(ANSI_RED,
                                      "Twin stage offset on axis %s is %g mm, which could cause moves out of range.\n"
                                      "Check that the reference switch in the sample holder is properly at the center." %
                                      (a, trans * 1e3))

                    def ask_user_to_focus(n):
                        detector.data.subscribe(_discard_data)
                        input_col(ANSI_BLUE,
                                  "About to calculate rotation and scaling (%d/4). " % (n + 1,) +
                                  "Please turn on the Optical stream, "
                                  "set Power to 0 Watt and focus the image using the mouse "
                                  "so you have a clearly visible spot. \n"
                                  "If you do not see a spot nor the source background, "
                                  "move the sem-stage from the command line by steps of 200um "
                                  "in x and y until you can see the source background at the center. \n"
                                  "Then turn off the stream and press Enter...")
                        # TODO: use ArrowFocus() too?
                        print_col(ANSI_BLACK, "Calculating rotation and scaling (%d/4), please wait..." % (n + 1,))
                        detector.data.unsubscribe(_discard_data)

                    f = aligndelphi.RotationAndScaling(ccd, detector, escan, sem_stage,
                                                       opt_stage, focus, align_offset,
                                                       manual=ask_user_to_focus, logpath=logpath)
                    acc_offset, new_rotation, new_scaling = f.result()

                    # Offset is divided by scaling, since Convert Stage applies scaling
                    # also in the given offset
                    pure_offset = acc_offset
                    new_offset = ((acc_offset[0] / new_scaling[0]), (acc_offset[1] / new_scaling[1]))

                    print_col(ANSI_CYAN,
                              "Values computed: offset: " + str(new_offset) + "\n"
                              "                 scaling: " + str(new_scaling) + "\n"
                              "                 rotation: " + str(new_rotation) + "\n"
                              "                 optical focus: " + str(new_opt_focus))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        offset, scaling, rotation, opt_focus = new_offset, new_scaling, new_rotation, new_opt_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "Twin stage calibration failed.")
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the fine alignment? [Y/n]")
            if ans in YES_CHARS:
                # Return to the center so fine alignment can be executed just after calibration
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({"x":pure_offset[0], "y":pure_offset[1]})
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({"x":offset[0] * scaling[0], "y":offset[1] * scaling[1]})
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
                    f.result()

                f = focus.moveAbs({"z": opt_focus})
                f.result()
                f = ebeam_focus.moveAbs({"z": good_focus})
                f.result()

                # Run the optical fine alignment
                # TODO: reuse the exposure time
                # Configure e-beam to write CL spots
                escan.horizontalFoV.value = escan.horizontalFoV.range[0]
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print_col(ANSI_BLUE,
                          "Please turn on the Optical stream, set Power to 0 Watt "
                          "and focus the image so you have a clearly visible spot.\n"
                          "Use the up and down arrows or the mouse to move the "
                          "optical focus and right and left arrows to move the SEM focus. "
                          "Then turn off the stream and press Enter...")
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, 10e-6)
                ar.focusByArrow()
                detector.data.unsubscribe(_discard_data)

                print_col(ANSI_BLACK, "Fine alignment in progress, please wait...")

                # restore CCD settings (as the GUI/user might have changed them)
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                # Center (roughly) the spot on the CCD
                f = spot.CenterSpot(ccd, sem_stage, escan, spot.ROUGH_MOVE, spot.STAGE_MOVE, detector.data)
                dist, vect = f.result()
                if dist is None:
                    logging.warning("Failed to find a spot, twin stage calibration might have failed")

                try:
                    escan.horizontalFoV.value = 80e-06
                    f = align.FindOverlay((4, 4),
                                          0.5,  # s, dwell time
                                          10e-06,  # m, maximum difference allowed
                                          escan,
                                          ccd,
                                          detector,
                                          skew=True,
                                          bgsub=True)
                    trans_val, cor_md = f.result()
                    trans_md, skew_md = cor_md
                    new_iscale = trans_md[model.MD_PIXEL_SIZE_COR]
                    new_irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
                    new_ishear = skew_md[model.MD_SHEAR_COR]
                    new_iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
                    print_col(ANSI_CYAN,
                              "Values computed: scale: " + str(new_iscale) + "\n"
                              "                 rotation: " + str(new_irot) + "\n"
                              "                 scale-xy: " + str(new_iscale_xy) + "\n"
                              "                 shear: " + str(new_ishear))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        iscale, irot, iscale_xy, ishear = new_iscale, new_irot, new_iscale_xy, new_ishear
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except ValueError:
                    print_col(ANSI_RED, "Fine alignment failed.")
            else:
                break
    except Exception:
        logging.exception("Unexpected failure during calibration")
    finally:
        print_col(ANSI_BLACK, "Calibration ended, now ejecting sample, please wait...")
        # Eject the sample holder
        f = chamber.moveAbs({"pressure": vented_pressure})

        aligndelphi.restore_hw_settings(escan, ccd, hw_settings)

        # Store the final version of the calibration file in the log folder
        try:
            shutil.copy(calibconf.file_path, logpath)
        except Exception:
            logging.info("Failed to log calibration file", exc_info=True)

        ans = input_col(ANSI_MAGENTA, "Press Enter to close")
        f.result()
Exemple #38
0
    def __init__(self, microscope):
        """
        microscope (Microscope): the whole microscope component, thus it can
            handle all the components needed
        """
        self.microscope = microscope
        self._graph = affectsGraph(self.microscope)

        # Use subset for modes guessed
        if microscope.role == "sparc2":
            self._modes = copy.deepcopy(SPARC2_MODES)
        elif microscope.role in ("sparc-simplex", "sparc"):
            self._modes = copy.deepcopy(SPARC_MODES)
        else:
            raise NotImplementedError("Microscope role '%s' unsupported" %
                                      (microscope.role, ))

        # keep list of already accessed components, to avoid creating new proxys
        # every time the mode changes
        self._known_comps = dict()  # str (role) -> component

        # All the actuators in the microscope, to cache proxy's to them
        self._actuators = []
        for comp in model.getComponents():
            if hasattr(comp, 'axes') and isinstance(comp.axes, dict):
                self._actuators.append(comp)

        # last known axes position
        self._stored = {}
        self._last_mode = None  # previous mode that was set
        # Removes modes which are not supported by the current microscope
        for m, (det, conf) in self._modes.items():
            try:
                comp = self._getComponent(det)
            except LookupError:
                logging.debug("Removing mode %s, which is not supported", m)
                del self._modes[m]

        # Create the guess information out of the mode
        # TODO: just make it a dict comprole -> mode
        self.guessed = self._modes.copy()
        # No stream should ever imply alignment mode
        for m in ALIGN_MODES:
            try:
                del self.guessed[m]
            except KeyError:
                pass  # Mode to delete is just not there

        # Handle different focus for chamber-view (in SPARCv2)
        self._chamber_view_own_focus = False
        if "chamber-view" in self._modes:
            self._focus_in_chamber_view = None
            self._focus_out_chamber_view = None
            # Check whether the focus affects the chamber view
            try:
                chamb_det = self._getComponent(self._modes["chamber-view"][0])
                focus = self._getComponent("focus")
                if self.affects(focus.name, chamb_det.name):
                    self._chamber_view_own_focus = True
            except LookupError:
                pass
            if not self._chamber_view_own_focus:
                logging.debug("No focus component affecting chamber")

        try:
            spec = self._getComponent("spectrometer")
        except LookupError:
            spec = None
        # TODO: do this also for the sparc
        if self.microscope.role == "sparc2":
            # Remove the moves that don't affects the detector
            for mode in self._modes:
                det_role = self._modes[mode][0]
                det = self._getComponent(det_role)
                modeconf = self._modes[mode][1]
                for act_role in modeconf.keys():
                    try:
                        act = self._getComponent(act_role)
                    except LookupError:
                        # TODO: just remove that move too?
                        logging.debug(
                            "Failed to find component %s, skipping it",
                            act_role)
                        continue
                    if not self.affects(act.name, det.name):
                        logging.debug(
                            "Actuator %s doesn't affect %s, so removing it from mode %s",
                            act_role, det_role, mode)
                        del modeconf[act_role]

        # will take care of executing setPath asynchronously
        self._executor = ThreadPoolExecutor(max_workers=1)
Exemple #39
0
def man_calib(logpath):
    escan = None
    detector = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "bs-detector":
            detector = c
        elif c.role == "ccd":
            ccd = c
        elif c.role == "sem-stage":
            sem_stage = c
        elif c.role == "align":
            opt_stage = c
        elif c.role == "ebeam-focus":
            ebeam_focus = c
        elif c.role == "overview-focus":
            navcam_focus = c
        elif c.role == "focus":
            focus = c
        elif c.role == "overview-ccd":
            overview_ccd = c
        elif c.role == "chamber":
            chamber = c
    if not all([escan, detector, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")

    hw_settings = aligndelphi.list_hw_settings(escan, ccd)

    try:
        # Get pressure values
        pressures = chamber.axes["pressure"].choices
        vacuum_pressure = min(pressures.keys())
        vented_pressure = max(pressures.keys())
        if overview_ccd:
            for p, pn in pressures.items():
                if pn == "overview":
                    overview_pressure = p
                    break
            else:
                raise IOError("Failed to find the overview pressure in %s" %
                              (pressures, ))

        calibconf = get_calib_conf()
        shid, sht = chamber.sampleHolder.value
        calib_values = calibconf.get_sh_calib(shid)
        if calib_values is None:
            first_hole = second_hole = offset = resa = resb = hfwa = scaleshift = (
                0, 0)
            scaling = iscale = iscale_xy = (1, 1)
            rotation = irot = ishear = 0
            hole_focus = aligndelphi.SEM_KNOWN_FOCUS
            opt_focus = aligndelphi.OPTICAL_KNOWN_FOCUS
            print "\033[1;31mCalibration values missing! All the steps will be performed anyway...\033[1;m"
            force_calib = True
        else:
            first_hole, second_hole, hole_focus, opt_focus, offset, scaling, rotation, iscale, irot, iscale_xy, ishear, resa, resb, hfwa, scaleshift = calib_values
            force_calib = False
        print "\033[1;36m"
        print "**Delphi Manual Calibration steps**"
        print "1.Sample holder hole detection"
        print "    Current values: 1st hole: " + str(first_hole)
        print "                    2st hole: " + str(second_hole)
        print "                    hole focus: " + str(hole_focus)
        print "2.Twin stage calibration"
        print "    Current values: offset: " + str(offset)
        print "                    scaling: " + str(scaling)
        print "                    rotation: " + str(rotation)
        print "                    optical focus: " + str(opt_focus)
        print "3.Fine alignment"
        print "    Current values: scale: " + str(iscale)
        print "                    rotation: " + str(irot)
        print "                    scale-xy: " + str(iscale_xy)
        print "                    shear: " + str(ishear)
        print "4.SEM image calibration"
        print "    Current values: resolution-a: " + str(resa)
        print "                    resolution-b: " + str(resb)
        print "                    hfw-a: " + str(hfwa)
        print "                    spot shift: " + str(scaleshift)
        print '\033[1;m'
        print "\033[1;33mNote that you should not perform any stage move during the process. \nInstead, you may zoom in/out while focusing.\033[1;m"
        print "\033[1;30mNow initializing, please wait...\033[1;m"

        # Move to the overview position first
        f = chamber.moveAbs({"pressure": overview_pressure})
        f.result()

        # Reference the (optical) stage
        f = opt_stage.reference({"x", "y"})
        f.result()

        f = focus.reference({"z"})
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            f = aligndelphi.LensAlignment(overview_ccd, sem_stage, logpath)
            position = f.result()
        except IOError as ex:
            if not force_calib:
                position = (offset[0] * scaling[0], offset[1] * scaling[1])
            else:
                position = (0, 0)
            logging.warning(
                "Failed to locate the optical lens (%s), will used previous value %s",
                ex, position)

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": vacuum_pressure})
        f.result()

        # Set basic e-beam settings
        escan.spotSize.value = 2.7
        escan.accelVoltage.value = 5300  # V

        # Detect the holes/markers of the sample holder
        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the sample holder hole detection? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Move Phenom sample stage next to expected hole position
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)
                ebeam_focus.moveAbsSync({"z": hole_focus})
                # Set the FoV to almost 2mm
                escan.horizontalFoV.value = escan.horizontalFoV.range[1]
                msg = "\033[1;34mPlease turn on the SEM stream and focus the SEM image. Then turn off the stream and press Enter ...\033[1;m"
                raw_input(msg)
                print "\033[1;30mTrying to detect the holes/markers, please wait...\033[1;m"
                try:
                    hole_detectionf = aligndelphi.HoleDetection(
                        detector,
                        escan,
                        sem_stage,
                        ebeam_focus,
                        manual=True,
                        logpath=logpath)
                    new_first_hole, new_second_hole, new_hole_focus = hole_detectionf.result(
                    )
                    print '\033[1;36m'
                    print "Values computed: 1st hole: " + str(new_first_hole)
                    print "                 2st hole: " + str(new_second_hole)
                    print "                 hole focus: " + str(new_hole_focus)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        first_hole, second_hole, hole_focus = new_first_hole, new_second_hole, new_hole_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole,
                                               hole_focus, opt_focus, offset,
                                               scaling, rotation, iscale, irot,
                                               iscale_xy, ishear, resa, resb,
                                               hfwa, scaleshift)
                    break
                except IOError:
                    print "\033[1;31mSample holder hole detection failed.\033[1;m"
            else:
                break

        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        f = opt_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        if hole_focus is not None:
            good_focus = hole_focus - aligndelphi.GOOD_FOCUS_OFFSET
        else:
            good_focus = aligndelphi.SEM_KNOWN_FOCUS - aligndelphi.GOOD_FOCUS_OFFSET
        f = ebeam_focus.moveAbs({"z": good_focus})
        f.result()

        # Set min fov
        # We want to be as close as possible to the center when we are zoomed in
        escan.horizontalFoV.value = escan.horizontalFoV.range[0]
        pure_offset = None

        # Start with the best optical focus known so far
        f = focus.moveAbs({"z": opt_focus})
        f.result()

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the twin stage calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                if not force_calib:
                    print "\033[1;33mIf you cannot see the whole source background (bright circle) you may try to move to the already known offset position. \nTo do this press the R key at any moment.\033[1;m"
                    rollback_pos = (offset[0] * scaling[0],
                                    offset[1] * scaling[1])
                else:
                    rollback_pos = None
                ar = ArrowFocus(sem_stage, focus, ebeam_focus,
                                ccd.depthOfField.value,
                                escan.depthOfField.value)
                ar.focusByArrow(rollback_pos)
                detector.data.unsubscribe(_discard_data)
                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                try:
                    # TODO: the first point (at 0,0) isn't different from the next 4 points,
                    # excepted it might be a little harder to focus.
                    # => use the same code for all of them
                    align_offsetf = aligndelphi.AlignAndOffset(
                        ccd, detector, escan, sem_stage, opt_stage, focus,
                        logpath)
                    align_offset = align_offsetf.result()
                    new_opt_focus = focus.position.value.get('z')

                    def ask_user_to_focus(n):
                        detector.data.subscribe(_discard_data)
                        msg = (
                            "\033[1;34mAbout to calculate rotation and scaling (%d/4). "
                            "Please turn on the Optical stream, "
                            "set Power to 0 Watt and focus the image using the mouse "
                            "so you have a clearly visible spot. \n"
                            "If you do not see a spot nor the source background, "
                            "move the sem-stage from the command line by steps of 200um "
                            "in x and y until you can see the source background at the center. \n"
                            "Then turn off the stream and press Enter ...\033[1;m"
                            % (n + 1, ))
                        raw_input(msg)  # TODO: use ArrowFocus() too?
                        print "\033[1;30mCalculating rotation and scaling (%d/4), please wait...\033[1;m" % (
                            n + 1, )
                        detector.data.unsubscribe(_discard_data)

                    f = aligndelphi.RotationAndScaling(
                        ccd,
                        detector,
                        escan,
                        sem_stage,
                        opt_stage,
                        focus,
                        align_offset,
                        manual=ask_user_to_focus,
                        logpath=logpath)
                    acc_offset, new_rotation, new_scaling = f.result()

                    # Offset is divided by scaling, since Convert Stage applies scaling
                    # also in the given offset
                    pure_offset = acc_offset
                    new_offset = ((acc_offset[0] / new_scaling[0]),
                                  (acc_offset[1] / new_scaling[1]))

                    print '\033[1;36m'
                    print "Values computed: offset: " + str(new_offset)
                    print "                 scaling: " + str(new_scaling)
                    print "                 rotation: " + str(new_rotation)
                    print "                 optical focus: " + str(
                        new_opt_focus)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        offset, scaling, rotation, opt_focus = new_offset, new_scaling, new_rotation, new_opt_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole,
                                               hole_focus, opt_focus, offset,
                                               scaling, rotation, iscale, irot,
                                               iscale_xy, ishear, resa, resb,
                                               hfwa, scaleshift)
                    break
                except IOError:
                    print "\033[1;31mTwin stage calibration failed.\033[1;m"
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the fine alignment? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Return to the center so fine alignment can be executed just after calibration
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({
                        "x": pure_offset[0],
                        "y": pure_offset[1]
                    })
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({
                        "x": offset[0] * scaling[0],
                        "y": offset[1] * scaling[1]
                    })
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
                    f.result()

                f = focus.moveAbs({"z": opt_focus})
                f.result()
                f = ebeam_focus.moveAbs({"z": good_focus})
                f.result()

                # Run the optical fine alignment
                # TODO: reuse the exposure time
                # Configure e-beam to write CL spots
                escan.horizontalFoV.value = escan.horizontalFoV.range[0]
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                ar = ArrowFocus(sem_stage, focus, ebeam_focus,
                                ccd.depthOfField.value,
                                escan.depthOfField.value)
                ar.focusByArrow()
                detector.data.unsubscribe(_discard_data)

                # restore CCD settings (as the GUI/user might have changed them)
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                # Center (roughly) the spot on the CCD
                f = spot.CenterSpot(ccd, sem_stage, escan, spot.ROUGH_MOVE,
                                    spot.STAGE_MOVE, detector.data)
                dist, vect = f.result()
                if dist is None:
                    logging.warning(
                        "Failed to find a spot, twin stage calibration might have failed"
                    )

                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                try:
                    escan.horizontalFoV.value = 80e-06
                    f = align.FindOverlay(
                        (4, 4),
                        0.5,  # s, dwell time
                        10e-06,  # m, maximum difference allowed
                        escan,
                        ccd,
                        detector,
                        skew=True,
                        bgsub=True)
                    trans_val, cor_md = f.result()
                    trans_md, skew_md = cor_md
                    new_iscale = trans_md[model.MD_PIXEL_SIZE_COR]
                    new_irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
                    new_ishear = skew_md[model.MD_SHEAR_COR]
                    new_iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
                    print '\033[1;36m'
                    print "Values computed: scale: " + str(new_iscale)
                    print "                 rotation: " + str(new_irot)
                    print "                 scale-xy: " + str(new_iscale_xy)
                    print "                 shear: " + str(new_ishear)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        iscale, irot, iscale_xy, ishear = new_iscale, new_irot, new_iscale_xy, new_ishear
                        calibconf.set_sh_calib(shid, first_hole, second_hole,
                                               hole_focus, opt_focus, offset,
                                               scaling, rotation, iscale, irot,
                                               iscale_xy, ishear, resa, resb,
                                               hfwa, scaleshift)
                    break
                except ValueError:
                    print "\033[1;31mFine alignment failed.\033[1;m"
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the SEM image calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS:
                # Resetting shift parameters, to not take them into account during calib
                blank_md = dict.fromkeys(aligndelphi.MD_CALIB_SEM, (0, 0))
                escan.updateMetadata(blank_md)

                # We measure the shift in the area just behind the hole where there
                # are always some features plus the edge of the sample carrier. For
                # that reason we use the focus measured in the hole detection step
                f = sem_stage.moveAbs(aligndelphi.SHIFT_DETECTION)
                f.result()

                f = ebeam_focus.moveAbs({"z": hole_focus})
                f.result()
                try:
                    # Compute spot shift percentage
                    print "\033[1;30mSpot shift measurement in progress, please wait...\033[1;m"
                    f = aligndelphi.ScaleShiftFactor(detector, escan, logpath)
                    new_scaleshift = f.result()

                    # Compute resolution-related values.
                    print "\033[1;30mCalculating resolution shift, please wait...\033[1;m"
                    resolution_shiftf = aligndelphi.ResolutionShiftFactor(
                        detector, escan, logpath)
                    new_resa, new_resb = resolution_shiftf.result()

                    # Compute HFW-related values
                    print "\033[1;30mCalculating HFW shift, please wait...\033[1;m"
                    hfw_shiftf = aligndelphi.HFWShiftFactor(
                        detector, escan, logpath)
                    new_hfwa = hfw_shiftf.result()

                    print '\033[1;36m'
                    print "Values computed: resolution-a: " + str(new_resa)
                    print "                 resolution-b: " + str(new_resb)
                    print "                 hfw-a: " + str(new_hfwa)
                    print "                 spot shift: " + str(new_scaleshift)
                    print '\033[1;m'
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS:
                        resa, resb, hfwa, scaleshift = new_resa, new_resb, new_hfwa, new_scaleshift
                        calibconf.set_sh_calib(shid, first_hole, second_hole,
                                               hole_focus, opt_focus, offset,
                                               scaling, rotation, iscale, irot,
                                               iscale_xy, ishear, resa, resb,
                                               hfwa, scaleshift)
                    break
                except IOError:
                    print "\033[1;31mSEM image calibration failed.\033[1;m"
            else:
                break

        # Update calibration file
        print "\033[1;30mUpdating calibration file is done\033[1;m"

    finally:
        print "\033[1;30mCalibration ended, now ejecting sample, please wait...\033[1;m"
        # Eject the sample holder
        f = chamber.moveAbs({"pressure": vented_pressure})

        aligndelphi.restore_hw_settings(escan, ccd, hw_settings)

        # Store the final version of the calibration file
        try:
            shutil.copy(calibconf.file_path, logpath)
        except Exception:
            logging.info("Failed to log calibration file", exc_info=True)

        f.result()
Exemple #40
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(
        description="Automated AR acquisition at multiple spot locations")

    parser.add_argument(
        "--repetitions_x",
        "-x",
        dest="repetitions_x",
        required=True,
        help=
        "repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument(
        "--repetitions_y",
        "-y",
        dest="repetitions_y",
        required=True,
        help=
        "repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument(
        "--dwell_time",
        "-t",
        dest="dwell_time",
        required=True,
        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument(
        "--max_allowed_diff",
        "-d",
        dest="max_allowed_diff",
        required=True,
        help=
        "max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)"
    )

    options = parser.parse_args(args[1:])
    repetitions = (int(options.repetitions_x), int(options.repetitions_y))
    dwell_time = float(options.dwell_time)
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "se-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # ccd.data.get()
        gscanner = GridScanner(repetitions, dwell_time, escan, ccd, detector)

        # Wait for ScanGrid to finish
        optical_image, electron_coordinates, electron_scale = gscanner.DoAcquisition(
        )
        hdf5.export("scanned_image.h5", optical_image)
        logging.debug("electron coord = %s", electron_coordinates)

        ############## TO BE REMOVED ON TESTING##############
        #        grid_data = hdf5.read_data("scanned_image.h5")
        #        C, T, Z, Y, X = grid_data[0].shape
        #        grid_data[0].shape = Y, X
        #        optical_image = grid_data[0]
        #####################################################

        logging.debug("Isolating spots...")
        opxs = optical_image.metadata[model.MD_PIXEL_SIZE]
        optical_dist = escan.pixelSize.value[0] * electron_scale[0] / opxs[0]
        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(
            optical_image, repetitions, optical_dist)
        logging.debug("Number of spots found: %d", len(subimages))

        hdf5.export("spot_found.h5", subimages, thumbnail=None)
        logging.debug("Finding spot centers...")
        spot_coordinates = spot.FindCenterCoordinates(subimages)
        logging.debug("center coord = %s", spot_coordinates)
        optical_coordinates = coordinates.ReconstructCoordinates(
            subimage_coordinates, spot_coordinates)
        logging.debug(optical_coordinates)
        rgb_optical = img.DataArray2RGB(optical_image)

        for ta in optical_coordinates:
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 0] = 255
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 1] *= 0.5
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 2] *= 0.5

        misc.imsave('spots_image.png', rgb_optical)

        # TODO: Make function for scale calculation
        sorted_coordinates = sorted(optical_coordinates,
                                    key=lambda tup: tup[1])
        tab = tuple(
            map(operator.sub, sorted_coordinates[0], sorted_coordinates[1]))
        optical_scale = math.hypot(tab[0], tab[1])
        scale = electron_scale[0] / optical_scale
        print(scale)

        # max_allowed_diff in pixels
        max_allowed_diff_px = max_allowed_diff / escan.pixelSize.value[0]

        logging.debug("Matching coordinates...")
        known_electron_coordinates, known_optical_coordinates, max_diff = coordinates.MatchCoordinates(
            optical_coordinates, electron_coordinates, scale,
            max_allowed_diff_px)

        logging.debug("Calculating transformation...")
        (calc_translation_x, calc_translation_y), (
            calc_scaling_x,
            calc_scaling_y), calc_rotation = transform.CalculateTransform(
                known_electron_coordinates, known_optical_coordinates)
        logging.debug("Electron->Optical: ")
        print(calc_translation_x, calc_translation_y, calc_scaling_x,
              calc_scaling_y, calc_rotation)
        final_electron = coordinates._TransformCoordinates(
            known_optical_coordinates,
            (calc_translation_x, calc_translation_y), calc_rotation,
            (calc_scaling_x, calc_scaling_y))

        logging.debug("Overlay done.")

        # Calculate distance between the expected and found electron coordinates
        coord_diff = []
        for ta, tb in zip(final_electron, known_electron_coordinates):
            tab = tuple(map(operator.sub, ta, tb))
            coord_diff.append(math.hypot(tab[0], tab[1]))

        mean_difference = numpy.mean(coord_diff) * escan.pixelSize.value[0]

        variance_sum = 0
        for i in range(0, len(coord_diff)):
            variance_sum += (mean_difference - coord_diff[i])**2
        variance = (variance_sum / len(coord_diff)) * escan.pixelSize.value[0]

        not_found_spots = len(electron_coordinates) - len(final_electron)

        # Generate overlay image
        logging.debug("Generating images...")
        (calc_translation_x, calc_translation_y), (
            calc_scaling_x,
            calc_scaling_y), calc_rotation = transform.CalculateTransform(
                known_optical_coordinates, known_electron_coordinates)
        logging.debug("Optical->Electron: ")
        print(calc_translation_x, calc_translation_y, calc_scaling_x,
              calc_scaling_y, calc_rotation)
        overlay_coordinates = coordinates._TransformCoordinates(
            known_electron_coordinates,
            (calc_translation_y, calc_translation_x), -calc_rotation,
            (calc_scaling_x, calc_scaling_y))

        for ta in overlay_coordinates:
            rgb_optical[ta[0] - 1:ta[0] + 1, ta[1] - 1:ta[1] + 1, 1] = 255

        misc.imsave('overlay_image.png', rgb_optical)
        misc.imsave('optical_image.png', optical_image)
        logging.debug(
            "Done. Check electron_image.png, optical_image.png and overlay_image.png."
        )

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info(
        "\n**Overlay precision stats (Resulted to expected electron coordinates comparison)**\n Mean distance: %f (unit: m)\n Variance: %f (unit: m)\n Not found spots: %d",
        mean_difference, variance, not_found_spots)
    return 0
Exemple #41
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """
    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "sem-stage":
                sem_stage = c
            elif c.role == "align":
                opt_stage = c
            elif c.role == "ebeam-focus":
                ebeam_focus = c
            elif c.role == "overview-focus":
                navcam_focus = c
            elif c.role == "focus":
                focus = c
            elif c.role == "stage":
                comb_stage = c
            elif c.role == "overview-ccd":
                navcam = c
            elif c.role == "chamber":
                chamber = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        logging.debug("Starting initial calibration procedure...")

        # Move to NavCam
        f = chamber.moveAbs({"pressure": 1e04})
        f.result()

        # Optical stage reference
        axes = set(opt_stage.referenced.value.keys())
        f = opt_stage.reference(axes)
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            future_lens = delphi.LensAlignment(navcam, sem_stage)
            position = future_lens.result()
            logging.debug("\nSEM position after lens alignment: %s \n",
                          position)
        except IOError:
            raise IOError("Lens alignment failed.")

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": 1e-02})
        f.result()

        # Lens to a good focus position
        f = focus.moveAbs(LENS_KNOWN_FOCUS)
        f.result()

        # Compute calibration values
        f = delphi.UpdateConversion(ccd,
                                    detector,
                                    escan,
                                    sem_stage,
                                    opt_stage,
                                    ebeam_focus,
                                    focus,
                                    comb_stage,
                                    True,
                                    sem_position=position)
        first_hole, second_hole, hole_focus, offset, rotation, scaling, resa, resb, hfwa, spotshift = f.result(
        )

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info(
        "\n**Computed calibration values**\n first hole: %s (unit: m,m)\n second hole: %s (unit: m,m)\n hole focus: %f (unit: m)\n offset: %s (unit: m,m)\n rotation: %f (unit: radians)\n scaling: %s \n",
        first_hole, second_hole, hole_focus, offset, rotation, scaling)
    logging.info(
        "\n**Computed SEM shift parameters**\n resa: %s \n resb: %s \n hfwa: %s \n spotshift: %s \n",
        resa, resb, hfwa, spotshift)
    return 0
Exemple #42
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(description=
                     "Automated AR acquisition at multiple spot locations")

    parser.add_argument("--repetitions_x", "-x", dest="repetitions_x", required=True,
                        help="repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument("--repetitions_y", "-y", dest="repetitions_y", required=True,
                        help="repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument("--dwell_time", "-t", dest="dwell_time", required=True,
                        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument("--max_allowed_diff", "-d", dest="max_allowed_diff", required=True,
                        help="max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)")

    options = parser.parse_args(args[1:])
    repetitions = (int(options.repetitions_x), int(options.repetitions_y))
    dwell_time = float(options.dwell_time)
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "se-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")
    
        # ccd.data.get()
        future_scan = images.ScanGrid(repetitions, dwell_time, escan, ccd, detector)

        # Wait for ScanGrid to finish
        optical_image, electron_coordinates, electron_scale = future_scan.result()
        hdf5.export("scanned_image.h5", optical_image)
        logging.debug("electron coord = %s", electron_coordinates)

        ############## TO BE REMOVED ON TESTING##############
#        grid_data = hdf5.read_data("scanned_image.h5")
#        C, T, Z, Y, X = grid_data[0].shape
#        grid_data[0].shape = Y, X
#        optical_image = grid_data[0]
        #####################################################
    
        logging.debug("Isolating spots...")
        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(optical_image, repetitions, optical_scale)
        logging.debug("Number of spots found: %d", len(subimages))

        hdf5.export("spot_found.h5", subimages,thumbnail=None)
        logging.debug("Finding spot centers...")
        spot_coordinates = coordinates.FindCenterCoordinates(subimages)
        logging.debug("center coord = %s", spot_coordinates)
        optical_coordinates = coordinates.ReconstructCoordinates(subimage_coordinates, spot_coordinates)
        logging.debug(optical_coordinates)
        rgb_optical = img.DataArray2RGB(optical_image)
        
        for ta in optical_coordinates:
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 0] = 255
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 1] *= 0.5
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 2] *= 0.5
        
        misc.imsave('spots_image.png', rgb_optical)

        # TODO: Make function for scale calculation
        sorted_coordinates = sorted(optical_coordinates, key=lambda tup: tup[1])
        tab = tuple(map(operator.sub, sorted_coordinates[0], sorted_coordinates[1]))
        optical_scale = math.hypot(tab[0], tab[1])
        scale = electron_scale[0] / optical_scale
        print scale

        # max_allowed_diff in pixels
        max_allowed_diff_px = max_allowed_diff / escan.pixelSize.value[0]

        logging.debug("Matching coordinates...")
        known_electron_coordinates, known_optical_coordinates = coordinates.MatchCoordinates(optical_coordinates, electron_coordinates, scale, max_allowed_diff_px)
    
        logging.debug("Calculating transformation...")
        (calc_translation_x, calc_translation_y), (calc_scaling_x, calc_scaling_y), calc_rotation = transform.CalculateTransform(known_electron_coordinates, known_optical_coordinates)
        logging.debug("Electron->Optical: ")
        print calc_translation_x, calc_translation_y, calc_scaling_x, calc_scaling_y, calc_rotation
        final_electron = coordinates._TransformCoordinates(known_optical_coordinates, (calc_translation_x, calc_translation_y), calc_rotation, (calc_scaling_x, calc_scaling_y))

        logging.debug("Overlay done.")
        
        # Calculate distance between the expected and found electron coordinates
        coord_diff = []
        for ta, tb in zip(final_electron, known_electron_coordinates):
            tab = tuple(map(operator.sub, ta, tb))
            coord_diff.append(math.hypot(tab[0], tab[1]))

        mean_difference = numpy.mean(coord_diff) * escan.pixelSize.value[0]

        variance_sum = 0
        for i in range(0, len(coord_diff)):
            variance_sum += (mean_difference - coord_diff[i]) ** 2
        variance = (variance_sum / len(coord_diff)) * escan.pixelSize.value[0]
        
        not_found_spots = len(electron_coordinates) - len(final_electron)

        # Generate overlay image
        logging.debug("Generating images...")
        (calc_translation_x, calc_translation_y), (calc_scaling_x, calc_scaling_y), calc_rotation = transform.CalculateTransform(known_optical_coordinates, known_electron_coordinates)
        logging.debug("Optical->Electron: ")
        print calc_translation_x, calc_translation_y, calc_scaling_x, calc_scaling_y, calc_rotation
        overlay_coordinates = coordinates._TransformCoordinates(known_electron_coordinates, (calc_translation_y, calc_translation_x), -calc_rotation, (calc_scaling_x, calc_scaling_y))

        for ta in overlay_coordinates:
            rgb_optical[ta[0] - 1:ta[0] + 1, ta[1] - 1:ta[1] + 1, 1] = 255
            
        misc.imsave('overlay_image.png', rgb_optical)
        misc.imsave('optical_image.png', optical_image)
        logging.debug("Done. Check electron_image.png, optical_image.png and overlay_image.png.")

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info("\n**Overlay precision stats (Resulted to expected electron coordinates comparison)**\n Mean distance: %f (unit: m)\n Variance: %f (unit: m)\n Not found spots: %d", mean_difference, variance, not_found_spots)
    return 0
Exemple #43
0
def man_calib(logpath):
    escan = None
    detector = None
    ccd = None
    # find components by their role
    for c in model.getComponents():
        if c.role == "e-beam":
            escan = c
        elif c.role == "bs-detector":
            detector = c
        elif c.role == "ccd":
            ccd = c
        elif c.role == "sem-stage":
            sem_stage = c
        elif c.role == "align":
            opt_stage = c
        elif c.role == "ebeam-focus":
            ebeam_focus = c
        elif c.role == "overview-focus":
            navcam_focus = c
        elif c.role == "focus":
            focus = c
        elif c.role == "overview-ccd":
            overview_ccd = c
        elif c.role == "chamber":
            chamber = c
    if not all([escan, detector, ccd]):
        logging.error("Failed to find all the components")
        raise KeyError("Not all components found")

    hw_settings = aligndelphi.list_hw_settings(escan, ccd)

    try:
        # Get pressure values
        pressures = chamber.axes["pressure"].choices
        vacuum_pressure = min(pressures.keys())
        vented_pressure = max(pressures.keys())
        if overview_ccd:
            for p, pn in pressures.items():
                if pn == "overview":
                    overview_pressure = p
                    break
            else:
                raise IOError("Failed to find the overview pressure in %s" % (pressures,))

        calibconf = get_calib_conf()
        shid, sht = chamber.sampleHolder.value
        calib_values = calibconf.get_sh_calib(shid)
        if calib_values is None:
            first_hole = second_hole = offset = resa = resb = hfwa = scaleshift = (0, 0)
            scaling = iscale = iscale_xy = (1, 1)
            rotation = irot = ishear = 0
            hole_focus = aligndelphi.SEM_KNOWN_FOCUS
            opt_focus = aligndelphi.OPTICAL_KNOWN_FOCUS
            print_col(ANSI_RED, "Calibration values missing! All the steps will be performed anyway...")
            force_calib = True
        else:
            first_hole, second_hole, hole_focus, opt_focus, offset, scaling, rotation, iscale, irot, iscale_xy, ishear, resa, resb, hfwa, scaleshift = calib_values
            force_calib = False
        print_col(ANSI_CYAN,
                  "**Delphi Manual Calibration steps**\n"
                  "1.Sample holder hole detection\n"
                  "    Current values: 1st hole: " + str(first_hole) + "\n"
                  "                    2st hole: " + str(second_hole) + "\n"
                  "                    hole focus: " + str(hole_focus) + "\n"
                  "2.SEM image calibration\n"
                  "    Current values: resolution-a: " + str(resa) + "\n"
                  "                    resolution-b: " + str(resb) + "\n"
                  "                    hfw-a: " + str(hfwa) + "\n"
                  "                    spot shift: " + str(scaleshift) + "\n"
                  "3.Twin stage calibration\n"
                  "    Current values: offset: " + str(offset) + "\n"
                  "                    scaling: " + str(scaling) + "\n"
                  "                    rotation: " + str(rotation) + "\n"
                  "                    optical focus: " + str(opt_focus) + "\n"
                  "4.Fine alignment\n"
                  "    Current values: scale: " + str(iscale) + "\n"
                  "                    rotation: " + str(irot) + "\n"
                  "                    scale-xy: " + str(iscale_xy) + "\n"
                  "                    shear: " + str(ishear))
        print_col(ANSI_YELLOW,
                  "Note that you should not perform any stage move during the process.\n"
                  "Instead, you may zoom in/out while focusing.")
        print_col(ANSI_BLACK, "Now initializing, please wait...")

        # Move to the overview position first
        f = chamber.moveAbs({"pressure": overview_pressure})
        f.result()

        # Reference the (optical) stage
        f = opt_stage.reference({"x", "y"})
        f.result()

        f = focus.reference({"z"})
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            f = aligndelphi.LensAlignment(overview_ccd, sem_stage, logpath)
            position = f.result()
        except IOError as ex:
            if not force_calib:
                position = (offset[0] * scaling[0], offset[1] * scaling[1])
            else:
                position = (0, 0)
            logging.warning("Failed to locate the optical lens (%s), will used previous value %s",
                            ex, position)

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": vacuum_pressure})
        f.result()

        # Set basic e-beam settings
        escan.spotSize.value = 2.7
        escan.accelVoltage.value = 5300  # V
        # Without automatic blanker, the background subtraction doesn't work
        if (model.hasVA(escan, "blanker") and  # For simulator
            None in escan.blanker.choices and
            escan.blanker.value is not None):
            logging.warning("Blanker set back to automatic")
            escan.blanker.value = None

        # Detect the holes/markers of the sample holder
        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the sample holder hole detection? [Y/n]")
            if ans in YES_CHARS:
                # Move Phenom sample stage next to expected hole position
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)
                ebeam_focus.moveAbsSync({"z": hole_focus})
                # Set the FoV to almost 2mm
                escan.horizontalFoV.value = escan.horizontalFoV.range[1]
                input_col(ANSI_BLUE,
                          "Please turn on the SEM stream and focus the SEM image. Then turn off the stream and press Enter...")
                print_col(ANSI_BLACK,"Trying to detect the holes/markers, please wait...")
                try:
                    hole_detectionf = aligndelphi.HoleDetection(detector, escan, sem_stage,
                                                                ebeam_focus, manual=True, logpath=logpath)
                    new_first_hole, new_second_hole, new_hole_focus = hole_detectionf.result()
                    print_col(ANSI_CYAN,
                              "Values computed: 1st hole: " + str(new_first_hole) + "\n"
                              "                 2st hole: " + str(new_second_hole) + "\n"
                              "                 hole focus: " + str(new_hole_focus))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        first_hole, second_hole, hole_focus = new_first_hole, new_second_hole, new_hole_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "Sample holder hole detection failed.")
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the SEM image calibration? [Y/n]")
            if ans in YES_CHARS:
                # Resetting shift parameters, to not take them into account during calib
                blank_md = dict.fromkeys(aligndelphi.MD_CALIB_SEM, (0, 0))
                escan.updateMetadata(blank_md)

                # We measure the shift in the area just behind the hole where there
                # are always some features plus the edge of the sample carrier. For
                # that reason we use the focus measured in the hole detection step
                sem_stage.moveAbsSync(aligndelphi.SHIFT_DETECTION)

                ebeam_focus.moveAbsSync({"z": hole_focus})
                try:
                    # Compute spot shift percentage
                    print_col(ANSI_BLACK, "Spot shift measurement in progress, please wait...")
                    f = aligndelphi.ScaleShiftFactor(detector, escan, logpath)
                    new_scaleshift = f.result()

                    # Compute resolution-related values.
                    print_col(ANSI_BLACK, "Calculating resolution shift, please wait...")
                    resolution_shiftf = aligndelphi.ResolutionShiftFactor(detector, escan, logpath)
                    new_resa, new_resb = resolution_shiftf.result()

                    # Compute HFW-related values
                    print_col(ANSI_BLACK, "Calculating HFW shift, please wait...")
                    hfw_shiftf = aligndelphi.HFWShiftFactor(detector, escan, logpath)
                    new_hfwa = hfw_shiftf.result()

                    print_col(ANSI_CYAN,
                              "Values computed: resolution-a: " + str(new_resa) + "\n"
                              "                 resolution-b: " + str(new_resb) + "\n"
                              "                 hfw-a: " + str(new_hfwa) + "\n"
                              "                 spot shift: " + str(new_scaleshift))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        resa, resb, hfwa, scaleshift = new_resa, new_resb, new_hfwa, new_scaleshift
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "SEM image calibration failed.")
            else:
                break

        # Update the SEM metadata to have the spots already at corrected place
        escan.updateMetadata({
            model.MD_RESOLUTION_SLOPE: resa,
            model.MD_RESOLUTION_INTERCEPT: resb,
            model.MD_HFW_SLOPE: hfwa,
            model.MD_SPOT_SHIFT: scaleshift
        })

        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        f = opt_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        if hole_focus is not None:
            good_focus = hole_focus - aligndelphi.GOOD_FOCUS_OFFSET
        else:
            good_focus = aligndelphi.SEM_KNOWN_FOCUS - aligndelphi.GOOD_FOCUS_OFFSET
        f = ebeam_focus.moveAbs({"z": good_focus})
        f.result()

        # Set min fov
        # We want to be as close as possible to the center when we are zoomed in
        escan.horizontalFoV.value = escan.horizontalFoV.range[0]
        pure_offset = None

        # Start with the best optical focus known so far
        f = focus.moveAbs({"z": opt_focus})
        f.result()

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the twin stage calibration? [Y/n]")
            if ans in YES_CHARS:
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = ccd.binning.clip((4, 4))
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print_col(ANSI_BLUE,
                          "Please turn on the Optical stream, set Power to 0 Watt "
                          "and focus the image so you have a clearly visible spot.\n"
                          "Use the up and down arrows or the mouse to move the "
                          "optical focus and right and left arrows to move the SEM focus. "
                          "Then turn off the stream and press Enter...")
                if not force_calib:
                    print_col(ANSI_YELLOW,
                              "If you cannot see the whole source background (bright circle) "
                              "you may try to move to the already known offset position. \n"
                              "To do this press the R key at any moment and use I to go back "
                              "to the initial position.")
                    rollback_pos = (offset[0] * scaling[0], offset[1] * scaling[1])
                else:
                    rollback_pos = None
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, 10e-6)
                ar.focusByArrow(rollback_pos)
                detector.data.unsubscribe(_discard_data)
                print_col(ANSI_BLACK, "Twin stage calibration starting, please wait...")
                try:
                    # TODO: the first point (at 0,0) isn't different from the next 4 points,
                    # excepted it might be a little harder to focus.
                    # => use the same code for all of them
                    align_offsetf = aligndelphi.AlignAndOffset(ccd, detector, escan, sem_stage,
                                                               opt_stage, focus, logpath)
                    align_offset = align_offsetf.result()
                    new_opt_focus = focus.position.value.get('z')

                    # If the offset is large, it can prevent the SEM stage to follow
                    # the optical stage. If it's really large (eg > 1mm) it will
                    # even prevent from going to the calibration locations.
                    # So warn about this, as soon as we detect it. It could be
                    # caused either due to a mistake in the offset detection, or
                    # because the reference switch of the optical axis is not
                    # centered (enough) on the axis. In such case, a technician
                    # should open the sample holder and move the reference switch.
                    # Alternatively, we could try to be more clever and support
                    # the range of the tmcm axes to be defined per sample
                    # holder, and set some asymmetric range (to reflect the fact
                    # that 0 is not at the center).
                    for a, trans in zip(("x", "y"), align_offset):
                        # SEM pos = Opt pos + offset
                        rng_sem = sem_stage.axes[a].range
                        rng_opt = opt_stage.axes[a].range
                        if (rng_opt[0] + trans < rng_sem[0] or
                            rng_opt[1] + trans > rng_sem[1]):
                            logging.info("Stage align offset = %s, which could cause "
                                         "moves on the SEM stage out of range (on axis %s)",
                                         align_offset, a)
                            input_col(ANSI_RED,
                                      "Twin stage offset on axis %s is %g mm, which could cause moves out of range.\n"
                                      "Check that the reference switch in the sample holder is properly at the center." %
                                      (a, trans * 1e3))

                    def ask_user_to_focus(n):
                        detector.data.subscribe(_discard_data)
                        input_col(ANSI_BLUE,
                                  "About to calculate rotation and scaling (%d/4). " % (n + 1,) +
                                  "Please turn on the Optical stream, "
                                  "set Power to 0 Watt and focus the image using the mouse "
                                  "so you have a clearly visible spot. \n"
                                  "If you do not see a spot nor the source background, "
                                  "move the sem-stage from the command line by steps of 200um "
                                  "in x and y until you can see the source background at the center. \n"
                                  "Then turn off the stream and press Enter...")
                        # TODO: use ArrowFocus() too?
                        print_col(ANSI_BLACK, "Calculating rotation and scaling (%d/4), please wait..." % (n + 1,))
                        detector.data.unsubscribe(_discard_data)

                    f = aligndelphi.RotationAndScaling(ccd, detector, escan, sem_stage,
                                                       opt_stage, focus, align_offset,
                                                       manual=ask_user_to_focus, logpath=logpath)
                    acc_offset, new_rotation, new_scaling = f.result()

                    # Offset is divided by scaling, since Convert Stage applies scaling
                    # also in the given offset
                    pure_offset = acc_offset
                    new_offset = ((acc_offset[0] / new_scaling[0]), (acc_offset[1] / new_scaling[1]))

                    print_col(ANSI_CYAN,
                              "Values computed: offset: " + str(new_offset) + "\n"
                              "                 scaling: " + str(new_scaling) + "\n"
                              "                 rotation: " + str(new_rotation) + "\n"
                              "                 optical focus: " + str(new_opt_focus))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        offset, scaling, rotation, opt_focus = new_offset, new_scaling, new_rotation, new_opt_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except IOError:
                    print_col(ANSI_RED, "Twin stage calibration failed.")
            else:
                break

        while True:
            ans = "Y" if force_calib else None
            while ans not in YES_NO_CHARS:
                ans = input_col(ANSI_MAGENTA,
                                "Do you want to execute the fine alignment? [Y/n]")
            if ans in YES_CHARS:
                # Return to the center so fine alignment can be executed just after calibration
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({"x":pure_offset[0], "y":pure_offset[1]})
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({"x":offset[0] * scaling[0], "y":offset[1] * scaling[1]})
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
                    f.result()

                f = focus.moveAbs({"z": opt_focus})
                f.result()
                f = ebeam_focus.moveAbs({"z": good_focus})
                f.result()

                # Run the optical fine alignment
                # TODO: reuse the exposure time
                # Configure e-beam to write CL spots
                escan.horizontalFoV.value = escan.horizontalFoV.range[0]
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                if not escan.rotation.readonly:
                    escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print_col(ANSI_BLUE,
                          "Please turn on the Optical stream, set Power to 0 Watt "
                          "and focus the image so you have a clearly visible spot.\n"
                          "Use the up and down arrows or the mouse to move the "
                          "optical focus and right and left arrows to move the SEM focus. "
                          "Then turn off the stream and press Enter...")
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, 10e-6)
                ar.focusByArrow()
                detector.data.unsubscribe(_discard_data)

                print_col(ANSI_BLACK, "Fine alignment in progress, please wait...")

                # restore CCD settings (as the GUI/user might have changed them)
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                # Center (roughly) the spot on the CCD
                f = spot.CenterSpot(ccd, sem_stage, escan, spot.ROUGH_MOVE, spot.STAGE_MOVE, detector.data)
                dist, vect = f.result()
                if dist is None:
                    logging.warning("Failed to find a spot, twin stage calibration might have failed")

                try:
                    escan.horizontalFoV.value = 80e-06
                    f = align.FindOverlay((4, 4),
                                          0.5,  # s, dwell time
                                          10e-06,  # m, maximum difference allowed
                                          escan,
                                          ccd,
                                          detector,
                                          skew=True,
                                          bgsub=True)
                    trans_val, cor_md = f.result()
                    trans_md, skew_md = cor_md
                    new_iscale = trans_md[model.MD_PIXEL_SIZE_COR]
                    new_irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
                    new_ishear = skew_md[model.MD_SHEAR_COR]
                    new_iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
                    print_col(ANSI_CYAN,
                              "Values computed: scale: " + str(new_iscale) + "\n"
                              "                 rotation: " + str(new_irot) + "\n"
                              "                 scale-xy: " + str(new_iscale_xy) + "\n"
                              "                 shear: " + str(new_ishear))
                    ans = "Y" if force_calib else None
                    while ans not in YES_NO_CHARS:
                        ans = input_col(ANSI_MAGENTA,
                                        "Do you want to update the calibration file with these values? [Y/n]")
                    if ans in YES_CHARS:
                        iscale, irot, iscale_xy, ishear = new_iscale, new_irot, new_iscale_xy, new_ishear
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, opt_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, scaleshift)
                        print_col(ANSI_BLACK, "Calibration file is updated.")
                    break
                except ValueError:
                    print_col(ANSI_RED, "Fine alignment failed.")
            else:
                break
    except Exception:
        logging.exception("Unexpected failure during calibration")
    finally:
        print_col(ANSI_BLACK, "Calibration ended, now ejecting sample, please wait...")
        # Eject the sample holder
        f = chamber.moveAbs({"pressure": vented_pressure})

        aligndelphi.restore_hw_settings(escan, ccd, hw_settings)

        # Store the final version of the calibration file in the log folder
        try:
            shutil.copy(calibconf.file_path, logpath)
        except Exception:
            logging.info("Failed to log calibration file", exc_info=True)

        ans = input_col(ANSI_MAGENTA, "Press Enter to close")
        f.result()
Exemple #44
0
    def __init__(self, microscope):
        """
        microscope (Microscope): the whole microscope component, thus it can
            handle all the components needed
        """
        self.microscope = microscope
        self._graph = affectsGraph(self.microscope)
        self._chamber_view_own_focus = False

        # Use subset for modes guessed
        if microscope.role == "sparc2":
            self._modes = copy.deepcopy(SPARC2_MODES)
        elif microscope.role in ("sparc-simplex", "sparc"):
            self._modes = copy.deepcopy(SPARC_MODES)
        elif microscope.role in ("secom", "delphi"):
            self._modes = copy.deepcopy(SECOM_MODES)
        else:
            raise NotImplementedError("Microscope role '%s' unsupported" %
                                      (microscope.role, ))

        # Currently only used with the SECOM/DELPHI
        self.quality = ACQ_QUALITY_FAST

        # keep list of all components, to avoid creating new proxies
        # every time the mode changes
        self._cached_components = model.getComponents()

        # All the actuators in the microscope, to cache proxy's to them
        self._actuators = []
        for comp in self._cached_components:
            if hasattr(comp, 'axes') and isinstance(comp.axes, dict):
                self._actuators.append(comp)

        # last known axes position (before going to an alignment mode)
        self._stored = {
        }  # (str, str) -> pos: (comp role, axis name) -> position
        self._last_mode = None  # previous mode that was set

        # Removes modes which are not supported by the current microscope
        for m, (det, conf) in list(self._modes.items()):
            try:
                comp = self._getComponent(det)
            except LookupError:
                logging.debug("Removing mode %s, which is not supported", m)
                del self._modes[m]

        # Create the guess information out of the mode
        # TODO: just make it a dict comprole -> mode
        self.guessed = self._modes.copy()
        # No stream should ever imply alignment mode
        for m in ALIGN_MODES:
            try:
                del self.guessed[m]
            except KeyError:
                pass  # Mode to delete is just not there

        if self.microscope.role in ("secom", "delphi"):
            # To record the fan settings when in "fast" acq quality
            try:
                ccd = self._getComponent("ccd")
            except LookupError:
                ccd = None
                # Check that at least it's a confocal microscope
                try:
                    lm = self._getComponent("laser-mirror")
                except LookupError:
                    logging.warning("Couldn't find a CCD on a SECOM/DELPHI")

            self._has_fan_speed = model.hasVA(ccd, "fanSpeed")
            self._has_fan_temp = (model.hasVA(ccd, "targetTemperature")
                                  and not ccd.targetTemperature.readonly)
            # Consider that by default we are in "fast" acquisition, with the fan
            # active (if it ought to be active)
            self._fan_enabled = True
            # Settings of the fan when the fan is in "active cooling" mode
            self._enabled_fan_speed = None
            self._enabled_fan_temp = None

        # Handle different focus for chamber-view (in SPARCv2)
        if "chamber-view" in self._modes:
            self._focus_in_chamber_view = None
            self._focus_out_chamber_view = None
            # Check whether the focus affects the chamber view
            try:
                chamb_det = self._getComponent(self._modes["chamber-view"][0])
                focus = self._getComponent("focus")
                if self.affects(focus.name, chamb_det.name):
                    self._chamber_view_own_focus = True
            except LookupError:
                pass
            if not self._chamber_view_own_focus:
                logging.debug("No focus component affecting chamber")

        # will take care of executing setPath asynchronously
        self._executor = OneTaskExecutor()
Exemple #45
0
    def __init__(self, microscope):
        """
        microscope (Microscope): the whole microscope component, thus it can
            handle all the components needed
        """
        self.microscope = microscope
        self._graph = affectsGraph(self.microscope)

        # Use subset for modes guessed
        if microscope.role == "sparc2":
            self._modes = copy.deepcopy(SPARC2_MODES)
        elif microscope.role in ("sparc-simplex", "sparc"):
            self._modes = copy.deepcopy(SPARC_MODES)
        else:
            raise NotImplementedError("Microscope role '%s' unsupported" % (microscope.role,))

        # keep list of already accessed components, to avoid creating new proxys
        # every time the mode changes
        self._known_comps = dict()  # str (role) -> component

        # All the actuators in the microscope, to cache proxy's to them
        self._actuators = []
        for comp in model.getComponents():
            if hasattr(comp, 'axes') and isinstance(comp.axes, dict):
                self._actuators.append(comp)

        # last known axes position
        self._stored = {}
        self._last_mode = None  # previous mode that was set
        # Removes modes which are not supported by the current microscope
        for m, (det, conf) in self._modes.items():
            try:
                comp = self._getComponent(det)
            except LookupError:
                logging.debug("Removing mode %s, which is not supported", m)
                del self._modes[m]

        # Create the guess information out of the mode
        # TODO: just make it a dict comprole -> mode
        self.guessed = self._modes.copy()
        # No stream should ever imply alignment mode
        for m in ALIGN_MODES:
            try:
                del self.guessed[m]
            except KeyError:
                pass  # Mode to delete is just not there

        # Handle different focus for chamber-view (in SPARCv2)
        if "chamber-view" in self._modes:
            self._focus_in_chamber_view = None
            self._focus_out_chamber_view = None
            # Check whether the focus affects the chamber view
            self._chamber_view_own_focus = False
            try:
                chamb_det = self._getComponent(self._modes["chamber-view"][0])
                focus = self._getComponent("focus")
                if self.affects(focus.name, chamb_det.name):
                    self._chamber_view_own_focus = True
            except LookupError:
                pass
            if not self._chamber_view_own_focus:
                logging.debug("No focus component affecting chamber")

        try:
            spec = self._getComponent("spectrometer")
        except LookupError:
            spec = None
        if self.microscope.role == "sparc2" and spec:
            # Remove the moves that don't affects the detector
            # TODO: do this for _all_ modes
            for mode in ('spectral', 'monochromator'):
                if mode in self._modes:
                    det_role = self._modes[mode][0]
                    det = self._getComponent(det_role)
                    modeconf = self._modes[mode][1]
                    for act_role in modeconf.keys():
                        try:
                            act = self._getComponent(act_role)
                        except LookupError:
                            # TODO: just remove that move too?
                            logging.debug("Failed to find component %s, skipping it", act_role)
                            continue
                        if not self.affects(act.name, det.name):
                            logging.debug("Actuator %s doesn't affect %s, so removing it from mode %s",
                                          act_role, det_role, mode)
                            del modeconf[act_role]

        # will take care of executing setPath asynchronously
        self._executor = ThreadPoolExecutor(max_workers=1)
Exemple #46
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """
    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "sem-stage":
                sem_stage = c
            elif c.role == "align":
                opt_stage = c
            elif c.role == "ebeam-focus":
                ebeam_focus = c
            elif c.role == "overview-focus":
                navcam_focus = c
            elif c.role == "focus":
                focus = c
            elif c.role == "stage":
                comb_stage = c
            elif c.role == "overview-ccd":
                navcam = c
            elif c.role == "chamber":
                chamber = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        logging.debug("Starting initial calibration procedure...")

        # Move to NavCam
        f = chamber.moveAbs({"pressure":1e04})
        f.result()

        # Optical stage reference
        axes = set(opt_stage.referenced.value.keys())
        f = opt_stage.reference(axes)
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x":0, "y":0})
        f.result()
        
        # Calculate offset approximation
        try:
            future_lens = delphi.LensAlignment(navcam, sem_stage)
            position = future_lens.result()
            logging.debug("\nSEM position after lens alignment: %s \n", position)
        except IOError:
            raise IOError("Lens alignment failed.")

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure":1e-02})
        f.result()

        # Compute calibration values
        f = delphi.UpdateConversion(ccd, detector, escan, sem_stage, opt_stage, ebeam_focus,
                                    focus, comb_stage, True, sem_position=position)
        first_hole, second_hole, hole_focus, offset, rotation, scaling, resa, resb, hfwa, spotshift = f.result()

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info("\n**Computed calibration values**\n first hole: %s (unit: m,m)\n second hole: %s (unit: m,m)\n hole focus: %f (unit: m)\n offset: %s (unit: m,m)\n rotation: %f (unit: radians)\n scaling: %s \n", first_hole, second_hole, hole_focus, offset, rotation, scaling)
    logging.info("\n**Computed SEM shift parameters**\n resa: %s \n resb: %s \n hfwa: %s \n spotshift: %s \n", resa, resb, hfwa, spotshift)
    return 0
Exemple #47
0
    def __init__(self, microscope):
        """
        microscope (Microscope): the whole microscope component, thus it can
            handle all the components needed
        """
        self.microscope = microscope
        self._graph = affectsGraph(self.microscope)
        self._chamber_view_own_focus = False

        # Use subset for modes guessed
        if microscope.role == "sparc2":
            self._modes = copy.deepcopy(SPARC2_MODES)
        elif microscope.role in ("sparc-simplex", "sparc"):
            self._modes = copy.deepcopy(SPARC_MODES)
        elif microscope.role in ("secom", "delphi"):
            self._modes = copy.deepcopy(SECOM_MODES)
        else:
            raise NotImplementedError("Microscope role '%s' unsupported" % (microscope.role,))

        # Currently only used with the SECOM/DELPHI
        self.quality = ACQ_QUALITY_FAST

        # keep list of all components, to avoid creating new proxies
        # every time the mode changes
        self._cached_components = model.getComponents()

        # All the actuators in the microscope, to cache proxy's to them
        self._actuators = []
        for comp in self._cached_components:
            if hasattr(comp, 'axes') and isinstance(comp.axes, dict):
                self._actuators.append(comp)

        # last known axes position (before going to an alignment mode)
        self._stored = {}  # (str, str) -> pos: (comp role, axis name) -> position
        self._last_mode = None  # previous mode that was set

        # Removes modes which are not supported by the current microscope
        for m, (det, conf) in self._modes.items():
            try:
                comp = self._getComponent(det)
            except LookupError:
                logging.debug("Removing mode %s, which is not supported", m)
                del self._modes[m]

        # Create the guess information out of the mode
        # TODO: just make it a dict comprole -> mode
        self.guessed = self._modes.copy()
        # No stream should ever imply alignment mode
        for m in ALIGN_MODES:
            try:
                del self.guessed[m]
            except KeyError:
                pass  # Mode to delete is just not there

        if self.microscope.role in ("secom", "delphi"):
            # To record the fan settings when in "fast" acq quality
            try:
                ccd = self._getComponent("ccd")
            except LookupError:
                ccd = None
                # Check that at least it's a confocal microscope
                try:
                    lm = self._getComponent("laser-mirror")
                except LookupError:
                    logging.warning("Couldn't find a CCD on a SECOM/DELPHI")

            self._has_fan_speed = model.hasVA(ccd, "fanSpeed")
            self._has_fan_temp = (model.hasVA(ccd, "targetTemperature") and
                                  not ccd.targetTemperature.readonly)
            # Consider that by default we are in "fast" acquisition, with the fan
            # active (if it ought to be active)
            self._fan_enabled = True
            # Settings of the fan when the fan is in "active cooling" mode
            self._enabled_fan_speed = None
            self._enabled_fan_temp = None

        # Handle different focus for chamber-view (in SPARCv2)
        if "chamber-view" in self._modes:
            self._focus_in_chamber_view = None
            self._focus_out_chamber_view = None
            # Check whether the focus affects the chamber view
            try:
                chamb_det = self._getComponent(self._modes["chamber-view"][0])
                focus = self._getComponent("focus")
                if self.affects(focus.name, chamb_det.name):
                    self._chamber_view_own_focus = True
            except LookupError:
                pass
            if not self._chamber_view_own_focus:
                logging.debug("No focus component affecting chamber")

        # will take care of executing setPath asynchronously
        self._executor = ThreadPoolExecutor(max_workers=1)
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """
    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "bs-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
            elif c.role == "sem-stage":
                sem_stage = c
            elif c.role == "align":
                opt_stage = c
            elif c.role == "ebeam-focus":
                ebeam_focus = c
            elif c.role == "overview-focus":
                navcam_focus = c
            elif c.role == "focus":
                focus = c
            elif c.role == "overview-ccd":
                overview_ccd = c
            elif c.role == "chamber":
                chamber = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # Get pressure values
        pressures = chamber.axes["pressure"].choices
        vacuum_pressure = min(pressures.keys())
        vented_pressure = max(pressures.keys())
        if overview_ccd:
            for p, pn in pressures.items():
                if pn == "overview":
                    overview_pressure = p
                    break
            else:
                raise IOError("Failed to find the overview pressure in %s" % (pressures,))

        calibconf = get_calib_conf()
        shid, sht = chamber.sampleHolder.value
        calib_values = calibconf.get_sh_calib(shid)
        if calib_values is None:
            first_hole = second_hole = offset = resa = resb = hfwa = spotshift = (0, 0)
            scaling = iscale = iscale_xy = (1, 1)
            hole_focus = rotation = irot = ishear = 0
            print "\033[1;31mCalibration values missing! All the steps will be performed anyway...\033[1;m"
            force_calib = True
        else:
            first_hole, second_hole, hole_focus, offset, scaling, rotation, iscale, irot, iscale_xy, ishear, resa, resb, hfwa, spotshift = calib_values
            force_calib = False
        print '\033[1;36m'
        print "**Delphi Manual Calibration steps**"
        print "1.Sample holder hole detection"
        print "    Current values: 1st hole: " + str(first_hole)
        print "                    2st hole: " + str(second_hole)
        print "                    hole focus: " + str(hole_focus)
        print "2.Twin stage calibration"
        print "    Current values: offset: " + str(offset)
        print "                    scaling: " + str(scaling)
        print "                    rotation: " + str(rotation)
        print "3.SEM image calibration"
        print "    Current values: resolution-a: " + str(resa)
        print "                    resolution-b: " + str(resb)
        print "                    hfw-a: " + str(hfwa)
        print "                    spot shift: " + str(spotshift)
        print "4.Fine alignment"
        print "    Current values: scale: " + str(iscale)
        print "                    rotation: " + str(irot)
        print "                    scale-xy: " + str(iscale_xy)
        print "                    shear: " + str(ishear)
        print '\033[1;m'
        print "\033[1;33mNote that you should not perform any stage move during the process. \nInstead, you may zoom in/out while focusing.\033[1;m"
        print "\033[1;30mNow initializing, please wait...\033[1;m"

        # Move to the overview position first
        f = chamber.moveAbs({"pressure": overview_pressure})
        f.result()

        # Reference the (optical) stage
        f = opt_stage.reference({"x", "y"})
        f.result()

        f = focus.reference({"z"})
        f.result()

        # SEM stage to (0,0)
        f = sem_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        # Calculate offset approximation
        try:
            f = aligndelphi.LensAlignment(overview_ccd, sem_stage)
            position = f.result()
        except Exception:
            raise IOError("Lens alignment failed.")

        # Just to check if move makes sense
        f = sem_stage.moveAbs({"x": position[0], "y": position[1]})
        f.result()

        # Move to SEM
        f = chamber.moveAbs({"pressure": vacuum_pressure})
        f.result()

        while True:
            ans = None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the sample holder hole detection? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS or force_calib:
                # Compute stage calibration values
                # Detect the holes/markers of the sample holder
                # Move Phenom sample stage to expected hole position
                f = sem_stage.moveAbs(aligndelphi.EXPECTED_HOLES[0])
                f.result()
                # Set the FoV to almost 2mm
                escan.horizontalFoV.value = escan.horizontalFoV.range[1]
                msg = "\033[1;34mPlease turn on the SEM stream and focus the SEM image. Then turn off the stream and press Enter ...\033[1;m"
                raw_input(msg)
                print "\033[1;30mTrying to detect the holes/markers, please wait...\033[1;m"
                try:
                    hole_detectionf = aligndelphi.HoleDetection(detector, escan, sem_stage,
                                                                ebeam_focus, known_focus=None, manual=True)
                    new_first_hole, new_second_hole, new_hole_focus = hole_detectionf.result()
                    new_hole_focus = ebeam_focus.position.value.get('z')
                    print '\033[1;36m'
                    print "Values computed: 1st hole: " + str(new_first_hole)
                    print "                 2st hole: " + str(new_second_hole)
                    print "                 hole focus: " + str(new_hole_focus)
                    print '\033[1;m'
                    ans = None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS or force_calib:
                        first_hole, second_hole, hole_focus = new_first_hole, new_second_hole, new_hole_focus
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mSample holder hole detection failed.\033[1;m"
            else:
                break

        f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
        f.result()

        f = opt_stage.moveAbs({"x": 0, "y": 0})
        f.result()

        if hole_focus is not None:
            f = ebeam_focus.moveAbs({"z": hole_focus})
            f.result()
        # Set min fov
        # We want to be as close as possible to the center when we are zoomed in
        escan.horizontalFoV.value = escan.horizontalFoV.range[0]
        pure_offset = None
        center_focus = None

        while True:
            ans = None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the twin stage calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS or force_calib:
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                if not force_calib:
                    print "\033[1;33mIf you cannot see the whole source background (bright circle) you may try to move to the already known offset position. \nTo do this press the R key at any moment.\033[1;m"
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, escan.depthOfField.value)
                ar.focusByArrow(rollback_position=(offset[0] * scaling[0], offset[1] * scaling[1]))
                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                detector.data.unsubscribe(_discard_data)
                try:
                    align_offsetf = aligndelphi.AlignAndOffset(ccd, detector, escan, sem_stage,
                                                               opt_stage, focus)
                    align_offset = align_offsetf.result()
                    center_focus = focus.position.value.get('z')

                    rotation_scalingf = aligndelphi.RotationAndScaling(ccd, detector, escan, sem_stage,
                                                                       opt_stage, focus, align_offset, manual=True)
                    acc_offset, new_rotation, new_scaling = rotation_scalingf.result()

                    # Offset is divided by scaling, since Convert Stage applies scaling
                    # also in the given offset
                    pure_offset = acc_offset
                    new_offset = ((acc_offset[0] / new_scaling[0]), (acc_offset[1] / new_scaling[1]))

                    print '\033[1;36m'
                    print "Values computed: offset: " + str(new_offset)
                    print "                 scaling: " + str(new_scaling)
                    print "                 rotation: " + str(new_rotation)
                    print '\033[1;m'
                    ans = None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS or force_calib:
                        offset, scaling, rotation = new_offset, new_scaling, new_rotation
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mTwin stage calibration failed.\033[1;m"
            else:
                break

        while True:
            ans = None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the SEM image calibration? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS or force_calib:
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({"x":pure_offset[0], "y":pure_offset[1]})
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({"x":offset[0] * scaling[0], "y":offset[1] * scaling[1]})
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
                    f.result()
                if center_focus is not None:
                    f = focus.moveAbs({"z": center_focus})
                    f.result()
                try:
                    # Compute spot shift percentage
                    # Configure CCD and e-beam to write CL spots
                    ccd.binning.value = (1, 1)
                    ccd.resolution.value = ccd.resolution.range[1]
                    ccd.exposureTime.value = 900e-03
                    escan.scale.value = (1, 1)
                    escan.resolution.value = (1, 1)
                    escan.translation.value = (0, 0)
                    escan.rotation.value = 0
                    escan.shift.value = (0, 0)
                    escan.dwellTime.value = 5e-06
                    detector.data.subscribe(_discard_data)
                    print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                    print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                    ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, escan.depthOfField.value)
                    ar.focusByArrow()
                    print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                    detector.data.unsubscribe(_discard_data)
                    spot_shiftf = aligndelphi.SpotShiftFactor(ccd, detector, escan, focus)
                    new_spotshift = spot_shiftf.result()

                    print "\033[1;30mCalculating resolution and HFW shift, please wait...\033[1;m"
                    # Compute resolution-related values
                    resolution_shiftf = aligndelphi.ResolutionShiftFactor(detector, escan, sem_stage, ebeam_focus, hole_focus)
                    new_resa, new_resb = resolution_shiftf.result()

                    # Compute HFW-related values
                    hfw_shiftf = aligndelphi.HFWShiftFactor(detector, escan, sem_stage, ebeam_focus, hole_focus)
                    new_hfwa = hfw_shiftf.result()

                    print '\033[1;36m'
                    print "Values computed: resolution-a: " + str(new_resa)
                    print "                 resolution-b: " + str(new_resb)
                    print "                 hfw-a: " + str(new_hfwa)
                    print "                 spot shift: " + str(new_spotshift)
                    print '\033[1;m'
                    ans = None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS or force_calib:
                        resa, resb, hfwa, spotshift = new_resa, new_resb, new_hfwa, new_spotshift
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except IOError:
                    print "\033[1;31mSEM image calibration failed.\033[1;m"
            else:
                break

        while True:
            ans = None
            while ans not in YES_NO_CHARS:
                msg = "\033[1;35mDo you want to execute the fine alignment? [Y/n]\033[1;m"
                ans = raw_input(msg)
            if ans in YES_CHARS or force_calib:
                # Return to the center so fine alignment can be executed just after calibration
                f = opt_stage.moveAbs({"x": 0, "y": 0})
                f.result()
                if pure_offset is not None:
                    f = sem_stage.moveAbs({"x":pure_offset[0], "y":pure_offset[1]})
                    f.result()
                elif offset is not None:
                    f = sem_stage.moveAbs({"x":offset[0] * scaling[0], "y":offset[1] * scaling[1]})
                    f.result()
                else:
                    f = sem_stage.moveAbs({"x":position[0], "y":position[1]})
                    f.result()
                if center_focus is not None:
                    f = focus.moveAbs({"z": center_focus})
                    f.result()

                # Run the optical fine alignment
                # TODO: reuse the exposure time
                # Configure CCD and e-beam to write CL spots
                ccd.binning.value = (1, 1)
                ccd.resolution.value = ccd.resolution.range[1]
                ccd.exposureTime.value = 900e-03
                escan.horizontalFoV.value = escan.horizontalFoV.range[0]
                escan.scale.value = (1, 1)
                escan.resolution.value = (1, 1)
                escan.translation.value = (0, 0)
                escan.rotation.value = 0
                escan.shift.value = (0, 0)
                escan.dwellTime.value = 5e-06
                detector.data.subscribe(_discard_data)
                print "\033[1;34mPlease turn on the Optical stream, set Power to 0 Watt and focus the image so you have a clearly visible spot.\033[1;m"
                print "\033[1;34mUse the up and down arrows or the mouse to move the optical focus and right and left arrows to move the SEM focus. Then turn off the stream and press Enter ...\033[1;m"
                ar = ArrowFocus(sem_stage, focus, ebeam_focus, ccd.depthOfField.value, escan.depthOfField.value)
                ar.focusByArrow()
                print "\033[1;30mFine alignment in progress, please wait...\033[1;m"
                detector.data.unsubscribe(_discard_data)
                try:
                    escan.horizontalFoV.value = 80e-06
                    f = align.FindOverlay((4, 4),
                                          0.5,  # s, dwell time
                                          10e-06,  # m, maximum difference allowed
                                          escan,
                                          ccd,
                                          detector,
                                          skew=True,
                                          bgsub=True)
                    trans_val, cor_md = f.result()
                    trans_md, skew_md = cor_md
                    new_iscale = trans_md[model.MD_PIXEL_SIZE_COR]
                    new_irot = -trans_md[model.MD_ROTATION_COR] % (2 * math.pi)
                    new_ishear = skew_md[model.MD_SHEAR_COR]
                    new_iscale_xy = skew_md[model.MD_PIXEL_SIZE_COR]
                    print '\033[1;36m'
                    print "Values computed: scale: " + str(new_iscale)
                    print "                 rotation: " + str(new_irot)
                    print "                 scale-xy: " + str(new_iscale_xy)
                    print "                 shear: " + str(new_ishear)
                    print '\033[1;m'
                    ans = None
                    while ans not in YES_NO_CHARS:
                        msg = "\033[1;35mDo you want to update the calibration file with these values? [Y/n]\033[1;m"
                        ans = raw_input(msg)
                    if ans in YES_CHARS or force_calib:
                        iscale, irot, iscale_xy, ishear = new_iscale, new_irot, new_iscale_xy, new_ishear
                        calibconf.set_sh_calib(shid, first_hole, second_hole, hole_focus, offset,
                               scaling, rotation, iscale, irot, iscale_xy, ishear,
                               resa, resb, hfwa, spotshift)
                    break
                except ValueError:
                    print "\033[1;31mFine alignment failed.\033[1;m"
            else:
                break

        # Update calibration file
        print "\033[1;30mUpdating calibration file is done, now ejecting, please wait...\033[1;m"
    except KeyboardInterrupt:
        logging.warning("Manual calibration procedure was cancelled.")
    except:
        logging.exception("Unexpected error while performing action.")
        return 127
    finally:
        # Eject the sample holder
        f = chamber.moveAbs({"pressure": vented_pressure})
        f.result()

    return 0