示例#1
0
def test_touch_tip_default_args(loop, monkeypatch):
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    lw = ctx.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', 1)
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 3)
    instr = ctx.load_instrument('p300_single',
                                Mount.RIGHT,
                                tip_racks=[tiprack])

    instr.pick_up_tip()
    total_hw_moves = []

    async def fake_hw_move(mount,
                           abs_position,
                           speed=None,
                           critical_point=None,
                           max_speeds=None):
        nonlocal total_hw_moves
        total_hw_moves.append((abs_position, speed))

    instr.aspirate(10, lw.wells()[0])
    monkeypatch.setattr(ctx._hw_manager.hardware._api, 'move_to', fake_hw_move)
    instr.touch_tip()
    z_offset = Point(0, 0, 1)  # default z offset of 1mm
    speed = 60  # default speed
    edges = [
        lw.wells()[0]._from_center_cartesian(1, 0, 1) - z_offset,
        lw.wells()[0]._from_center_cartesian(-1, 0, 1) - z_offset,
        lw.wells()[0]._from_center_cartesian(0, 1, 1) - z_offset,
        lw.wells()[0]._from_center_cartesian(0, -1, 1) - z_offset
    ]
    for i in range(1, 5):
        assert total_hw_moves[i] == (edges[i - 1], speed)
示例#2
0
def test_blow_out(loop, monkeypatch):
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    lw = ctx.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', 1)
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 3)
    instr = ctx.load_instrument('p300_single',
                                Mount.RIGHT,
                                tip_racks=[tiprack])

    move_location = None
    instr.pick_up_tip()
    instr.aspirate(10, lw.wells()[0])

    def fake_move(loc):
        nonlocal move_location
        move_location = loc

    monkeypatch.setattr(instr, 'move_to', fake_move)

    instr.blow_out()
    # pipette should not move, if no location is passed
    assert move_location is None

    instr.aspirate(10)
    instr.blow_out(lw.wells()[0])
    # pipette should blow out at the top of the well as default
    assert move_location == lw.wells()[0].top()

    instr.aspirate(10)
    instr.blow_out(lw.wells()[0].bottom())
    # pipette should blow out at the location defined
    assert move_location == lw.wells()[0].bottom()
示例#3
0
def test_location_cache(loop, monkeypatch, get_labware_def):
    hardware = API.build_hardware_simulator(loop=loop)
    ctx = papi.ProtocolContext(loop)
    ctx.connect(hardware)
    right = ctx.load_instrument('p10_single', Mount.RIGHT)
    lw = ctx.load_labware('corning_96_wellplate_360ul_flat', 1)
    ctx.home()

    test_args = None

    def fake_plan_move(from_loc,
                       to_loc,
                       deck,
                       well_z_margin=None,
                       lw_z_margin=None,
                       force_direct=False,
                       minimum_z_height=None):
        nonlocal test_args
        test_args = (from_loc, to_loc, deck, well_z_margin, lw_z_margin)
        return [(Point(0, 1, 10), None), (Point(1, 2, 10), None),
                (Point(1, 2, 3), None)]

    monkeypatch.setattr(papi.geometry, 'plan_moves', fake_plan_move)
    # When we move without a cache, the from location should be the gantry
    # position
    right.move_to(lw.wells()[0].top())
    # The home position from hardware_control/simulator.py, taking into account
    # that the right pipette is a p10 single which is a different height than
    # the reference p300 single
    assert test_args[0].point == Point(418, 353, 205)
    assert test_args[0].labware is None

    # Once we have a location cache, that should be our from_loc
    right.move_to(lw.wells()[1].top())
    assert test_args[0].labware == lw.wells()[0]
示例#4
0
def test_return_tip(loop, get_labware_def):
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 1)
    mount = Mount.LEFT

    instr = ctx.load_instrument('p300_single', mount, tip_racks=[tiprack])

    with pytest.raises(TypeError):
        instr.return_tip()

    pipette: Pipette\
        = ctx._hw_manager.hardware._attached_instruments[mount]

    target_location = tiprack['A1'].top()
    instr.pick_up_tip(target_location)
    assert not tiprack.wells()[0].has_tip
    assert pipette.has_tip

    instr.return_tip()
    assert not pipette.has_tip
    assert not tiprack.wells()[0].has_tip

    instr.pick_up_tip()
    assert pipette.has_tip
    assert not tiprack.wells()[1].has_tip
示例#5
0
def test_return_tip_old_version(loop, get_labware_def):
    # API version 2.2, a returned tip would be picked up by the
    # next pick up tip call
    ctx = papi.ProtocolContext(loop, api_version=APIVersion(2, 1))
    ctx.home()
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 1)
    mount = Mount.LEFT

    instr = ctx.load_instrument('p300_single', mount, tip_racks=[tiprack])

    with pytest.raises(TypeError):
        instr.return_tip()

    pipette: Pipette\
        = ctx._hw_manager.hardware._attached_instruments[mount]

    target_location = tiprack['A1'].top()
    instr.pick_up_tip(target_location)
    assert not tiprack.wells()[0].has_tip
    assert pipette.has_tip

    instr.return_tip()
    assert not pipette.has_tip
    assert tiprack.wells()[0].has_tip

    instr.pick_up_tip()
    assert pipette.has_tip
    assert not tiprack.wells()[0].has_tip
示例#6
0
def test_extra_labware(loop, get_labware_fixture):
    fixture_96_plate = get_labware_fixture('fixture_96_plate')
    bundled_labware = {'fixture/fixture_96_plate/1': fixture_96_plate}
    ctx = papi.ProtocolContext(loop, extra_labware=bundled_labware)
    ls1 = ctx.load_labware('fixture_96_plate', 3, namespace='fixture')
    assert ctx.loaded_labwares[3] == ls1
    assert ctx.loaded_labwares[3]._definition == fixture_96_plate
def test_load_module(loop):
    ctx = papi.ProtocolContext(loop)
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'tempdeck')
                                                           ]
    ctx.home()
    mod = ctx.load_module('tempdeck', 1)
    assert isinstance(mod, papi.TemperatureModuleContext)
示例#8
0
def test_build_edges_right_pipette(loop):
    ctx = papi.ProtocolContext(loop)
    test_lw = ctx.load_labware('corning_96_wellplate_360ul_flat', '2')
    test_lw2 = ctx.load_labware('corning_96_wellplate_360ul_flat', '6')
    mod = ctx.load_module('magnetic module', '1')
    mod.load_labware('corning_96_wellplate_360ul_flat')
    off = Point(0, 0, 1.0)
    right_pip_edges = [
        test_lw['A1']._from_center_cartesian(x=1.0, y=0, z=1) + off,
        test_lw['A1']._from_center_cartesian(x=0, y=0, z=1) + off,
        test_lw['A1']._from_center_cartesian(x=0, y=1.0, z=1) + off,
        test_lw['A1']._from_center_cartesian(x=0, y=-1.0, z=1) + off,
    ]
    # Test that module in slot 1 results in modified edge list
    res = build_edges(test_lw['A1'], 1.0, APIVersion(2, 4), Mount.RIGHT,
                      ctx._deck_layout)
    assert res == right_pip_edges

    right_pip_edges = [
        test_lw2['A12']._from_center_cartesian(x=1.0, y=0, z=1) + off,
        test_lw2['A12']._from_center_cartesian(x=-1.0, y=0, z=1) + off,
        test_lw2['A12']._from_center_cartesian(x=0, y=0, z=1) + off,
        test_lw2['A12']._from_center_cartesian(x=0, y=1.0, z=1) + off,
        test_lw2['A12']._from_center_cartesian(x=0, y=-1.0, z=1) + off,
    ]
    # Test that labware in slot 6 results in unmodified edge list
    res2 = build_edges(test_lw2['A12'], 1.0, APIVersion(2, 4), Mount.RIGHT,
                       ctx._deck_layout)
    assert res2 == right_pip_edges
示例#9
0
def test_bundled_labware_missing(loop, get_labware_fixture):
    bundled_labware = {}
    with pytest.raises(
        RuntimeError,
        match='No labware found in bundle with load name opentrons_1_trash_'
    ):
        papi.ProtocolContext(loop, bundled_labware=bundled_labware)
示例#10
0
def test_incorrect_module_error(loop):
    ctx = papi.ProtocolContext(loop)
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'tempdeck')
                                                           ]
    ctx.home()
    with pytest.raises(ValueError):
        assert ctx.load_module('the cool module', 1)
示例#11
0
def test_load_simulating_module(loop):
    # Check that a known module will not throw an error if
    # in simulation mode
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    mod = ctx.load_module('tempdeck', 1)
    assert isinstance(mod, papi.TemperatureModuleContext)
示例#12
0
def test_invalid_slot_module_error(loop):
    ctx = papi.ProtocolContext(loop)
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'tempdeck')
                                                           ]
    ctx.home()
    with pytest.raises(AssertionError):
        assert ctx.load_module('thermocycler', 1)
示例#13
0
def test_magdeck_labware_props(loop):
    ctx = papi.ProtocolContext(loop)
    # TODO Ian 2019-05-29 load fixtures, not real defs
    labware_name = 'biorad_96_wellplate_200ul_pcr'
    labware_def = json.loads(
        pkgutil.get_data(
            'opentrons',
            f'shared_data/labware/definitions/2/{labware_name}/1.json'))
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'magdeck')]
    mod = ctx.load_module('magdeck', 1)
    assert mod.labware is None
    mod.load_labware(labware_name)
    mod.engage()
    lw_offset = labware_def['parameters']['magneticModuleEngageHeight']
    assert mod._module._driver.plate_height == lw_offset
    mod.disengage()
    mod.engage(offset=2)
    assert mod._module._driver.plate_height == lw_offset + 2
    mod.disengage()
    mod.engage(height=3)
    assert mod._module._driver.plate_height == 3
    mod._geometry.reset_labware()
    labware_name = 'corning_96_wellplate_360ul_flat'
    mod.load_labware(labware_name)
    with pytest.raises(ValueError):
        mod.engage()
    with pytest.raises(ValueError):
        mod.engage(offset=1)
    mod.engage(height=2)
    assert mod._module._driver.plate_height == 2
示例#14
0
def test_load_module_default_slot(loop):
    ctx = papi.ProtocolContext(loop)
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'tempdeck')
                                                           ]
    ctx.home()
    mod = ctx.load_module('thermocycler')
    assert isinstance(mod, papi.ThermocyclerContext)
示例#15
0
def test_thermocycler_lid(loop):
    ctx = papi.ProtocolContext(loop)
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0',
                                                            'thermocycler')]
    mod = ctx.load_module('thermocycler')
    assert ctx.deck[7] == mod._geometry

    assert mod.lid_position == 'open'

    # Open should work if the lid is open (no status change)
    mod.open_lid()
    assert mod.lid_position == 'open'
    assert 'opening thermocycler lid' in ','.join(
        [cmd.lower() for cmd in ctx.commands()])

    # Close should work if the lid is open
    mod.close_lid()
    assert mod.lid_position == 'closed'
    assert 'closing thermocycler lid' in ','.join(
        [cmd.lower() for cmd in ctx.commands()])

    # Close should work if the lid is closed (no status change)
    mod.close_lid()
    assert mod.lid_position == 'closed'
    assert mod._geometry.lid_status == 'closed'
    assert mod._geometry.highest_z == (98.0)  # ignore 37.7mm lid for now

    # Open should work if the lid is closed
    mod.open_lid()
    assert mod.lid_position == 'open'
    assert mod._geometry.lid_status == 'open'
    assert mod._geometry.highest_z == 98.0
示例#16
0
def test_starting_tip_and_reset_tipracks(loop, get_labware_def, monkeypatch):
    ctx = papi.ProtocolContext(loop)
    ctx.home()

    tr = ctx.load_labware('opentrons_96_tiprack_300ul', 1)
    tr_2 = ctx.load_labware('opentrons_96_tiprack_300ul', 2)
    pipL = ctx.load_instrument('p300_single', Mount.LEFT, tip_racks=[tr, tr_2])
    pipR = ctx.load_instrument('p300_single',
                               Mount.RIGHT,
                               tip_racks=[tr, tr_2])

    pipL.starting_tip = tr.wells()[2]
    pipL.pick_up_tip()
    assert pipL._last_tip_picked_up_from is tr.wells()[2]
    pipL.drop_tip()

    pipR.starting_tip = tr.wells()[2]
    pipR.pick_up_tip()
    assert pipR._last_tip_picked_up_from is tr.wells()[3]
    pipR.drop_tip()

    tr.wells()[95].has_tip = False
    pipL.starting_tip = tr.wells()[95]
    pipL.pick_up_tip()
    assert pipL._last_tip_picked_up_from is tr_2.wells()[0]

    pipL.reset_tipracks()
    assert tr.wells()[2].has_tip
    assert tr.wells()[3].has_tip
示例#17
0
def test_load_instrument(loop):
    ctx = papi.ProtocolContext(loop=loop)
    for model in config_models:
        loaded = ctx.load_instrument(model, Mount.LEFT, replace=True)
        assert loaded.model == model
        instr_name = name_for_model(model)
        loaded = ctx.load_instrument(instr_name, Mount.RIGHT, replace=True)
        assert loaded.name == instr_name
示例#18
0
def test_tip_length_for(loop, monkeypatch):
    ctx = papi.ProtocolContext(loop)
    instr = ctx.load_instrument('p20_single_gen2', 'left')
    tiprack = ctx.load_labware('geb_96_tiprack_10ul', '1')
    assert instr._tip_length_for(tiprack)\
        == (tiprack._definition['parameters']['tipLength']
            - instr.hw_pipette['tip_overlap']
            ['opentrons/geb_96_tiprack_10ul/1'])
示例#19
0
def test_module_load_labware_with_label(loop):
    ctx = papi.ProtocolContext(loop)
    labware_name = 'corning_96_wellplate_360ul_flat'
    ctx._hw_manager.hardware._backend._attached_modules = [('mod0', 'tempdeck')
                                                           ]
    mod = ctx.load_module('Temperature Module', 1)
    lw = mod.load_labware(labware_name, label='my cool labware')
    assert lw.name == 'my cool labware'
示例#20
0
def test_loaded_modules(loop, monkeypatch):
    ctx = papi.ProtocolContext(loop)
    assert ctx.loaded_modules == {}
    mod1 = ctx.load_module('tempdeck', 4)
    mod1.load_labware('biorad_96_wellplate_200ul_pcr')
    mod2 = ctx.load_module('thermocycler')
    assert ctx.loaded_modules[4] == mod1
    assert ctx.loaded_modules[7] == mod2
示例#21
0
def test_load_instrument(loop):
    ctx = papi.ProtocolContext(loop=loop)
    for config in config_models:
        loaded = ctx.load_instrument(config, Mount.LEFT, replace=True)
        assert loaded.model == config
        prefix = config.split('_v')[0]
        loaded = ctx.load_instrument(prefix, Mount.RIGHT, replace=True)
        assert loaded.name.startswith(prefix)
示例#22
0
def test_magdeck_gen2_labware_props(loop):
    ctx = papi.ProtocolContext(loop)
    mod = ctx.load_module('magnetic module gen2', 1)
    mod.engage(height=25)
    assert mod._module.current_height == 25
    with pytest.raises(ValueError):
        mod.engage(height=25.1)  # max engage height for gen2 is 25 mm
    mod.engage(height=0)
    assert mod._module.current_height == 0
示例#23
0
def test_load_instrument(loop, name):
    ctx = papi.ProtocolContext(loop=loop)
    assert ctx.loaded_instruments == {}
    loaded = ctx.load_instrument(name, Mount.LEFT, replace=True)
    assert ctx.loaded_instruments[Mount.LEFT.name.lower()] == loaded
    assert loaded.name == name
    loaded = ctx.load_instrument(name, Mount.RIGHT, replace=True)
    assert ctx.loaded_instruments[Mount.RIGHT.name.lower()] == loaded
    assert loaded.name == name
示例#24
0
def test_load_simulating_module(loop, loadname, klass, model):
    # Check that a known module will not throw an error if
    # in simulation mode
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    mod = ctx.load_module(loadname, 7)
    assert isinstance(mod, klass)
    assert mod.geometry.model.value == model
    assert mod._module.model() == model
def run(protocol: protocol_api.ProtocolContext(api_version=api_version)):

    # define deck positions and labware

    # tips
    tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul', 6)

    tipracks_10f = [
        protocol.load_labware('opentrons_96_filtertiprack_10ul', x)
        for x in [1, 4, 7, 10]
    ]

    # plates
    reagents = protocol.load_labware('usascientific_12_reservoir_22ml', 3,
                                     'reagents')
    assay = protocol.load_labware('corning_384_wellplate_112ul_flat', 9,
                                  'assay')

    samples = [
        protocol.load_labware('biorad_96_wellplate_200ul_pcr', x, 'samples')
        for x in [2, 5, 8, 11]
    ]

    # initialize pipettes
    pipette_left = protocol.load_instrument('p300_multi',
                                            'left',
                                            tip_racks=[tiprack_300])

    pipette_right = protocol.load_instrument('p10_multi',
                                             'right',
                                             tip_racks=tipracks_10f)

    # distribute 38 µL of quantification reagent into each well of the assay
    # plate. Use the same tip for the entirety of these transfers, then
    # replace it in the rack.

    add_buffer(pipette_left, [reagents[x] for x in ['A1', 'A2']],
               assay,
               cols,
               38,
               13000 / 8,
               tip=None,
               tip_vol=300,
               remaining=None,
               drop_tip=False)

    # add 2 µL of each sample to each of the wells. Mix after dispensing.
    # Dispose of these tips.
    for i, plate in enumerate(samples):
        start = i + 1
        assay_wells = get_96_from_384_wells(method='interleaved', start=start)
        pipette_right.transfer(2,
                               plate.wells(), [assay[x] for x in assay_wells],
                               mix_after=(1, 10),
                               touch_tip=True,
                               trash=True,
                               new_tip='always')
示例#26
0
def get_protocol_api(
        bundled_labware: Dict[str, Dict[str, Any]] = None,
        bundled_data: Dict[str, bytes] = None) -> protocol_api.ProtocolContext:
    """
    Build and return a :py:class:`ProtocolContext` connected to the robot.

    This can be used to run protocols from interactive Python sessions
    such as Jupyter or an interpreter on the command line:

    .. code-block:: python

        >>> from opentrons.execute import get_protocol_api
        >>> protocol = get_protocol_api()
        >>> instr = protocol.load_instrument('p300_single', 'right')
        >>> instr.home()

    When this function is called, modules and instruments will be recached.

    :param bundled_labware: If specified, a mapping from labware names to
                            labware definitions for labware to consider in the
                            protocol. Note that if you specify this, _only_
                            labware in this argument will be allowed in the
                            protocol. This is preparation for a beta feature
                            and is best not used.
    :param bundled_data: If specified, a mapping from filenames to contents
                         for data to be available in the protocol from
                         :py:attr:`.ProtocolContext.bundled_data`.

    :returns opentrons.protocol_api.ProtocolContext: The protocol context.
    """
    if not _HWCONTROL:
        # Build a hardware controller in a worker thread, which is necessary
        # because ipython runs its notebook in asyncio but the notebook
        # is at script/repl scope not function scope and is synchronous so
        # you can't control the loop from inside. If we update to
        # IPython 7 we can avoid this, but for now we can't
        def _build_hwcontroller():
            global _HWCONTROL
            try:
                loop = asyncio.get_event_loop()
            except RuntimeError:
                loop = asyncio.new_event_loop()
            _HWCONTROL = loop.run_until_complete(
                API.build_hardware_controller())

        thread = threading.Thread(target=_build_hwcontroller,
                                  name='Hardware-controller-builder')
        thread.start()
        thread.join()

    context = protocol_api.ProtocolContext(hardware=_HWCONTROL,
                                           bundled_labware=bundled_labware,
                                           bundled_data=bundled_data)
    context._hw_manager.hardware.cache_instruments()
    context._hw_manager.hardware.discover_modules()
    return context
示例#27
0
def test_bundled_labware_missing(loop, get_labware_fixture):
    bundled_labware = {}
    with pytest.raises(
            RuntimeError,
            match='No labware found in bundle with load name fixture_96_plate'
    ):
        ctx = papi.ProtocolContext(loop, bundled_labware=bundled_labware)
        ctx.load_labware('fixture_96_plate', 3, namespace='fixture')

    fixture_96_plate = get_labware_fixture('fixture_96_plate')
    bundled_labware = {'fixture/fixture_96_plate/1': fixture_96_plate}
    with pytest.raises(
            RuntimeError,
            match='No labware found in bundle with load name fixture_96_plate'
    ):
        ctx = papi.ProtocolContext(loop,
                                   bundled_labware={},
                                   extra_labware=bundled_labware)
        ctx.load_labware('fixture_96_plate', 3, namespace='fixture')
示例#28
0
def _instr_labware(loop):
    ctx = papi.ProtocolContext(loop)
    lw1 = ctx.load_labware('biorad_96_wellplate_200ul_pcr', 1)
    lw2 = ctx.load_labware('corning_96_wellplate_360ul_flat', 2)
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 3)
    instr = ctx.load_instrument('p300_single', Mount.RIGHT,
                                tip_racks=[tiprack])

    return {'ctx': ctx, 'instr': instr, 'lw1': lw1, 'lw2': lw2,
            'tiprack': tiprack}
示例#29
0
def set_up_paired_instrument(loop):
    ctx = papi.ProtocolContext(loop)
    ctx.home()
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 1)
    tiprack2 = ctx.load_labware('opentrons_96_tiprack_300ul', 2)
    tiprack3 = ctx.load_labware('opentrons_96_tiprack_300ul', 3)
    right = ctx.load_instrument(
        'p300_multi', Mount.RIGHT, tip_racks=[tiprack, tiprack2])
    left = ctx.load_instrument(
        'p300_multi', Mount.LEFT, tip_racks=[tiprack2, tiprack3])

    return right.pair_with(left), [tiprack, tiprack2, tiprack3]
示例#30
0
def test_multichannel_transfer_old_version(loop):
    # for API version below 2.2, multichannel pipette can only
    # reach row A of 384-well plates
    ctx = papi.ProtocolContext(loop, api_version=APIVersion(2, 1))
    lw1 = ctx.load_labware('biorad_96_wellplate_200ul_pcr', 1)
    lw2 = ctx.load_labware('corning_384_wellplate_112ul_flat', 2)
    tiprack = ctx.load_labware('opentrons_96_tiprack_300ul', 3)
    instr_multi = ctx.load_instrument('p300_multi',
                                      Mount.LEFT,
                                      tip_racks=[tiprack])

    xfer_plan = tx.TransferPlan(
        100,
        lw1.rows()[0][0],
        [lw2.rows()[0][1], lw2.rows()[1][1]],
        instr_multi,
        max_volume=instr_multi.hw_pipette['working_volume'],
        api_version=ctx.api_version)
    xfer_plan_list = []
    for step in xfer_plan:
        xfer_plan_list.append(step)
    exp1 = [{
        'method': 'pick_up_tip',
        'args': [],
        'kwargs': {}
    }, {
        'method': 'aspirate',
        'args': [100, lw1.wells_by_name()['A1'], 1.0],
        'kwargs': {}
    }, {
        'method': 'dispense',
        'args': [100, lw2.wells_by_index()['A2'], 1.0],
        'kwargs': {}
    }, {
        'method': 'drop_tip',
        'args': [],
        'kwargs': {}
    }]
    assert xfer_plan_list == exp1

    # target without row limit
    with pytest.raises(IndexError):
        xfer_plan = tx.TransferPlan(
            100,
            lw1.rows()[0][1],
            lw2.rows()[1][1],
            instr_multi,
            max_volume=instr_multi.hw_pipette['working_volume'],
            api_version=ctx.api_version)
        xfer_plan_list = []
        for step in xfer_plan:
            xfer_plan_list.append(step)