Exemplo n.º 1
0
def verify_well_locations(well_list: List[EnhancedWellV1],
                          pipette: EnhancedPipetteV1):
    picked_tip = False
    if not pipette.tip_attached:
        pipette.pick_up_tip()
        picked_tip = True

    for well in well_list:
        move_to_loc = well.top()
        pipette.move_to(move_to_loc)
        #
        well_top_coords_absolute = well.top_coords_absolute()
        _, top_coords = well_vector(well.top())
        _, move_to_coords = well_vector(move_to_loc)
        intended_coords = well_top_coords_absolute + (move_to_coords -
                                                      top_coords)
        tip_coords = pipette.tip_coords_absolute()
        #
        robot.pause(
            pretty.format('verify location: {0} in {1} loc={2} tip={3}',
                          well.get_name(), well.parent.get_name(),
                          intended_coords, tip_coords))

    if picked_tip:
        pipette.return_tip(
        )  # we didn't dirty it, we can always re-use it todo: enhance return_tip() to adjust iterator so that next pick can pick up again
Exemplo n.º 2
0
def note_liquid(location, name=None, initially=None, initially_at_least=None, concentration=None, local_config=None):
    # Must keep in sync with Opentrons-Analyze analyze_liquid_name

    if local_config is None:
        local_config = rgatkinson.configuration.config

    well, __ = well_vector(location)
    if name is None:
        name = well.label
    else:
        well.label = name

    liquid = local_config.execution_context.liquids.get_liquid(name)

    d = {'name': name, 'location': get_location_path(well)}

    if initially is not None and initially_at_least is not None:
        raise ValueError  # can't use both at once

    initially = LiquidVolume.fix_initially(initially)

    if initially is None and initially_at_least is not None:
        initially = Interval([initially_at_least, well.geometry.well_capacity])

    if initially is not None:
        d['initially'] = initially
        well.liquid_volume.set_initially(initially)

    if concentration is not None:
        concentration = Concentration(concentration)
        liquid.concentration = concentration
        d['concentration'] = str(concentration)

    serialized = json.dumps(d).replace("{", "{{").replace("}", "}}")  # runtime calls comment.format(...) on our comment; avoid issues therewith
    robot.comment('Liquid: %s' % serialized)
Exemplo n.º 3
0
 def _shake_tip(self, location):
     # Modelled after Pipette._shake_off_tips()
     shake_off_distance = SHAKE_OFF_TIPS_DROP_DISTANCE / 2  # / 2 == less distance than shaking off tips
     if location:
         placeable, _ = well_vector(location)
         # ensure the distance is not >25% the diameter of placeable
         x = placeable.x_size()
         if x != 0:  # trash well has size zero
             shake_off_distance = max(min(shake_off_distance, x / 4), 1.0)
     self.robot.gantry.push_speed()
     self.robot.gantry.set_speed(
         SHAKE_OFF_TIPS_SPEED)  # tip is fully on, we can handle it
     self.robot.poses = self._jog(self.robot.poses, 'x',
                                  -shake_off_distance)  # move left
     self.robot.poses = self._jog(self.robot.poses, 'x',
                                  shake_off_distance * 2)  # move right
     self.robot.poses = self._jog(self.robot.poses, 'x',
                                  -shake_off_distance * 2)  # move left
     self.robot.poses = self._jog(self.robot.poses, 'x',
                                  shake_off_distance * 2)  # move right
     self.robot.poses = self._jog(self.robot.poses, 'x',
                                  -shake_off_distance)  # move left
     self.robot.gantry.pop_speed()
Exemplo n.º 4
0
    def dispense(self,
                 volume=None,
                 location=None,
                 rate=1.0,
                 full_dispense: bool = False,
                 top_clearance=None,
                 bottom_clearance=None,
                 manual_liquid_volume_allowance=None):
        if not helpers.is_number(volume):  # recapitulate super
            if volume and not location:
                location = volume
            volume = self._working_volume - self.current_volume
        location = location if location else self.previous_placeable
        well, _ = well_vector(location)

        if top_clearance is None:
            if tls.dispense_params_transfer:
                top_clearance = tls.dispense_params_transfer.top_clearance_transfer
            if top_clearance is None:
                top_clearance = self.config.dispense.top_clearance
        if bottom_clearance is None:
            if tls.dispense_params_transfer:
                bottom_clearance = tls.dispense_params_transfer.bottom_clearance_transfer
            if bottom_clearance is None:
                bottom_clearance = self.config.dispense.bottom_clearance
        if manual_liquid_volume_allowance is None:
            if tls.dispense_params_transfer:
                manual_liquid_volume_allowance = tls.dispense_params_transfer.manual_manufacture_tolerance_transfer
            if manual_liquid_volume_allowance is None:
                manual_liquid_volume_allowance = self.config.dispense.manual_liquid_volume_allowance

        if is_close(volume, self.current_volume
                    ):  # avoid finicky floating-point precision issues
            volume = self.current_volume
        location = self._adjust_location_to_liquid_top(
            location=location,
            aspirate_volume=None,
            top_clearance=top_clearance,
            bottom_clearance=bottom_clearance,
            manual_liquid_volume_allowance=manual_liquid_volume_allowance)

        with DispenseParams():
            tls.dispense_params.full_dispense_from_dispense = full_dispense

            def call_super():
                super(EnhancedPipette, self).dispense(volume=volume,
                                                      location=location,
                                                      rate=rate)

            self._update_pose_tree_in_place(call_super)

            if tls.dispense_params.fully_dispensed:
                assert self.current_volume == 0
                if self.current_volume == 0:
                    pass  # nothing to do: the next self._position_for_aspirate will reposition for us: 'if pipette is currently empty, ensure the plunger is at "bottom"'
                else:
                    raise NotImplementedError

            # track volume
            well, __ = well_vector(location)
            well.liquid_volume.dispense(volume)
Exemplo n.º 5
0
    def aspirate(self,
                 volume=None,
                 location=None,
                 rate: float = 1.0,
                 pre_wet: bool = None,
                 ms_pause: float = None,
                 top_clearance=None,
                 bottom_clearance=None,
                 manual_liquid_volume_allowance=None):
        if not helpers.is_number(volume):  # recapitulate super
            if volume and not location:
                location = volume
            volume = self._working_volume - self.current_volume
        location = location if location else self.previous_placeable
        well, _ = well_vector(location)

        if top_clearance is None:
            if tls.aspirate_params_transfer:
                top_clearance = tls.aspirate_params_transfer.top_clearance_transfer
            if top_clearance is None:
                top_clearance = self.config.aspirate.top_clearance
        if bottom_clearance is None:
            if tls.aspirate_params_transfer:
                bottom_clearance = tls.aspirate_params_transfer.bottom_clearance_transfer
            if bottom_clearance is None:
                bottom_clearance = self.config.aspirate.bottom_clearance
        if manual_liquid_volume_allowance is None:
            if tls.aspirate_params_transfer:
                manual_liquid_volume_allowance = tls.aspirate_params_transfer.manual_manufacture_tolerance_transfer
            if manual_liquid_volume_allowance is None:
                manual_liquid_volume_allowance = self.config.aspirate.manual_liquid_volume_allowance

        current_liquid_volume = well.liquid_volume.current_volume_min
        needed_liquid_volume = well.geometry.min_aspiratable_volume + volume
        if current_liquid_volume < needed_liquid_volume:
            msg = pretty.format(
                'aspirating too much from well={0} have={1:n} need={2:n}',
                well.get_name(), current_liquid_volume, needed_liquid_volume)
            warn(msg)

        self._pre_wet(well, volume, location, rate, pre_wet)
        location = self._adjust_location_to_liquid_top(
            location=location,
            aspirate_volume=volume,
            top_clearance=top_clearance,
            bottom_clearance=bottom_clearance,
            manual_liquid_volume_allowance=manual_liquid_volume_allowance)

        def call_super():
            super(EnhancedPipette, self).aspirate(volume=volume,
                                                  location=location,
                                                  rate=rate)

        self._update_pose_tree_in_place(call_super)

        self.pause_after_aspirate(ms_pause)

        # finish up todo: what if we're doing an air gap
        well, __ = well_vector(location)
        well.liquid_volume.aspirate(volume)
        if volume != 0:
            self.prev_aspirated_well = well
Exemplo n.º 6
0
    def _run_transfer_plan(self, tips, plan, **kwargs):
        air_gap = kwargs.get('air_gap', 0)
        touch_tip = kwargs.get('touch_tip', False)
        is_distribute = kwargs.get('mode', 'transfer') == 'distribute'

        total_transfers = len(plan)
        seen_aspirate = False
        assert len(plan) == 0 or plan[0].get(
            'aspirate')  # first step must be an aspirate

        step_info_map = dict()

        with AspirateParamsTransfer(self.config.aspirate):
            with DispenseParamsTransfer(self.config.dispense):

                for i, step in enumerate(plan):
                    aspirate = step.get('aspirate')
                    dispense = step.get('dispense')

                    if aspirate:
                        # *always* record on aspirates so we can test has_disposal_vol on subsequent dispenses
                        have_disposal_vol = self.has_disposal_vol(
                            plan, i, step_info_map, **kwargs)

                        # we might have overspill from a previous transfer.
                        if self.current_volume > 0:
                            info(
                                pretty.format(
                                    'carried over {0:n} uL from prev operation',
                                    self.current_volume))

                        if not seen_aspirate:
                            assert i == 0

                            if kwargs.get('pre_wet', None) and kwargs.get(
                                    'mix_before', None):
                                warn(
                                    "simultaneous use of 'pre_wet' and 'mix_before' is not tested"
                                )

                            if (kwargs.get('allow_overspill',
                                           self.config.allow_overspill_default)
                                    and self.config.enable_enhancements
                                ) and zeroify(self.current_volume) > 0:
                                this_aspirated_well, __ = well_vector(
                                    aspirate['location'])
                                if self.prev_aspirated_well is this_aspirated_well:
                                    if have_disposal_vol:
                                        # try to remove current volume from this aspirate
                                        new_aspirate_vol = zeroify(
                                            aspirate.get('volume') -
                                            self.current_volume)
                                        if new_aspirate_vol == 0 or new_aspirate_vol >= self.min_volume:
                                            aspirate[
                                                'volume'] = new_aspirate_vol
                                            info(
                                                pretty.format(
                                                    'reduced this aspirate by {0:n} uL',
                                                    self.current_volume))
                                            extra = 0  # can't blow out since we're relying on its presence in pipette
                                        else:
                                            extra = self.current_volume - aspirate[
                                                'volume']
                                            assert zeroify(extra) > 0
                                    else:
                                        info(
                                            pretty.format(
                                                "overspill of {0:n} uL isn't for disposal",
                                                self.current_volume))
                                        extra = self.current_volume
                                else:
                                    # different locations; can't re-use
                                    info(
                                        'this aspirate is from location different than current pipette contents'
                                    )
                                    extra = self.current_volume
                                if zeroify(extra) > 0:
                                    # quiet_log('blowing out overspill of %s uL' % format_number(self.current_volume))
                                    self._blowout_during_transfer(
                                        loc=None,
                                        **kwargs)  # loc isn't actually used

                            elif zeroify(self.current_volume) > 0:
                                info(
                                    pretty.format(
                                        'blowing out unexpected overspill of {0:n} uL',
                                        self.current_volume))
                                self._blowout_during_transfer(
                                    loc=None,
                                    **kwargs)  # loc isn't actually used

                        seen_aspirate = True

                        self._add_tip_during_transfer(tips, **kwargs)
                        # Previous blow-out elisions that don't reduce their adjacent aspirates (because they're not
                        # carrying disposal_vols) might eventually catch up with us in the form of blowing the capacity
                        # of the pipette. When they do, we give in, and carry out the blow-out. This still can be a net
                        # win, in that we reduce the overall number of blow-outs. We might be tempted here to reduce
                        # the capacity of the overflowing aspirate, but that would reduce precision (we still *could*
                        # do that if it has disposal_vol, but that doesn't seem worth it).
                        if self.current_volume + aspirate[
                                'volume'] > self._working_volume:
                            info(
                                pretty.format(
                                    'current {0:n} uL with aspirate(has_disposal={1}) of {2:n} uL would overflow capacity',
                                    self.current_volume,
                                    self.has_disposal_vol(
                                        plan, i, step_info_map,
                                        **kwargs), aspirate['volume']))
                            self._blowout_during_transfer(
                                loc=None, **kwargs)  # loc isn't actually used

                        tls.aspirate_params_transfer.sequester(kwargs)
                        self._aspirate_during_transfer(aspirate['volume'],
                                                       aspirate['location'],
                                                       **kwargs)

                    if dispense:
                        if self.current_volume < dispense['volume']:
                            warn(
                                pretty.format(
                                    'current {0:n} uL will truncate dispense of {1:n} uL',
                                    self.current_volume, dispense['volume']))

                        can_full_dispense = self.current_volume - dispense[
                            'volume'] <= 0

                        tls.dispense_params_transfer.sequester(
                            kwargs, can_full_dispense)
                        self._dispense_during_transfer(dispense['volume'],
                                                       dispense['location'],
                                                       **kwargs)

                        do_touch = touch_tip or touch_tip is 0
                        is_last_step = step is plan[-1]
                        if is_last_step or plan[i + 1].get('aspirate'):
                            do_drop = not is_last_step or not (
                                kwargs.get('keep_last_tip', False)
                                and self.config.enable_enhancements)
                            # original always blew here. there are several reasons we could still be forced to blow
                            do_blow = not is_distribute  # other modes (are there any?) we're not sure about
                            do_blow = do_blow or kwargs.get(
                                'blow_out', False)  # for compatibility
                            do_blow = do_blow or do_touch  # for compatibility
                            do_blow = do_blow or not (
                                kwargs.get(
                                    'allow_blow_elision',
                                    self.config.allow_blow_elision_default)
                                and self.config.enable_enhancements)
                            if not do_blow:
                                if is_last_step:
                                    if self.current_volume > 0:
                                        if not (kwargs.get(
                                                'allow_overspill', self.config.
                                                allow_overspill_default
                                        ) and self.config.enable_enhancements):
                                            do_blow = True
                                        elif self.current_volume > kwargs.get(
                                                'disposal_vol', 0):
                                            warn(
                                                pretty.format(
                                                    'carried over {0:n} uL to next operation',
                                                    self.current_volume))
                                        else:
                                            info(
                                                pretty.format(
                                                    'carried over {0:n} uL to next operation',
                                                    self.current_volume))
                                else:
                                    # if we can, account for any overspill in the next aspirate
                                    if self.current_volume > 0:
                                        if self.has_disposal_vol(
                                                plan, i + 1, step_info_map,
                                                **kwargs):
                                            next_aspirate = plan[i + 1].get(
                                                'aspirate')
                                            assert next_aspirate
                                            next_aspirated_well, __ = well_vector(
                                                next_aspirate['location'])
                                            if self.prev_aspirated_well is next_aspirated_well:
                                                new_aspirate_vol = zeroify(
                                                    next_aspirate.get('volume')
                                                    - self.current_volume)
                                                if new_aspirate_vol == 0 or new_aspirate_vol >= self.min_volume:
                                                    next_aspirate[
                                                        'volume'] = new_aspirate_vol
                                                    info(
                                                        pretty.format(
                                                            'reduced next aspirate by {0:n} uL',
                                                            self.current_volume
                                                        ))
                                                else:
                                                    do_blow = True
                                            else:
                                                do_blow = True  # different aspirate locations
                                        else:
                                            # Next aspirate doesn't *want* our overspill, so we don't reduce his
                                            # volume. But it's harmless to just leave the overspill present; might
                                            # be useful down the line
                                            pass
                                    else:
                                        pass  # currently empty
                            if do_touch:
                                self.touch_tip(touch_tip)
                            if do_blow:
                                self._blowout_during_transfer(
                                    dispense['location'], **kwargs)
                            if do_drop:
                                tips = self._drop_tip_during_transfer(
                                    tips, i, total_transfers, **kwargs)
                        else:
                            if air_gap:
                                self.air_gap(air_gap)
                            if do_touch:
                                self.touch_tip(touch_tip)