Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add WellVolumeOffset to WellLocation #16302

Merged
merged 56 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
dd74c58
initial implementation
pmoegenburg Sep 19, 2024
e210544
update
pmoegenburg Sep 19, 2024
0deed9a
updated get_well_position
pmoegenburg Sep 19, 2024
8f713df
updated implementation
pmoegenburg Sep 19, 2024
64fc6e2
update schema 9
pmoegenburg Sep 19, 2024
1bcc578
updated docstring
pmoegenburg Sep 20, 2024
33e6108
updated schema
pmoegenburg Sep 20, 2024
fd93d0e
updated docstring
pmoegenburg Sep 20, 2024
a40b66b
updated schema and formatted
pmoegenburg Sep 20, 2024
add6638
fleshed out height-to-volume-to-height
pmoegenburg Sep 23, 2024
f2b6bc7
created command-specific WellLocations and move_to_well() subfunctions
pmoegenburg Sep 23, 2024
087caaf
added LiquidHandlingWellLocation and PickUpTipWellLocation
pmoegenburg Sep 24, 2024
3d53137
split get_well_position into submethods
pmoegenburg Sep 24, 2024
68981a3
test fixes
pmoegenburg Sep 25, 2024
a9b6dfb
updated typescript
pmoegenburg Sep 25, 2024
8c0f5ce
schema and test fixes
pmoegenburg Sep 25, 2024
2bc1d66
revert typescript work
pmoegenburg Sep 25, 2024
8731a19
cleaned up comments
pmoegenburg Sep 25, 2024
32760c3
updated command schema and formatted
pmoegenburg Sep 25, 2024
957c573
removed comment
pmoegenburg Sep 25, 2024
df07616
added validate_well_position()
pmoegenburg Sep 26, 2024
843a5b5
Merge branch 'edge' into EXEC-643-add-WellVolumeOffset
pmoegenburg Sep 26, 2024
da7f479
added TODO
pmoegenburg Sep 26, 2024
a9ca313
removed TODO and fixed robot-server tavern tests
pmoegenburg Sep 27, 2024
22d2c39
added test, need to fix. added well_location guardrails
pmoegenburg Sep 30, 2024
17ce9b1
refactored geometry methods and added helper function in labware
pmoegenburg Sep 30, 2024
9d559a6
refactored geometry and added validate_dispense_volume_into_well
pmoegenburg Sep 30, 2024
54205b3
updated long doc strings
pmoegenburg Sep 30, 2024
d2af54e
cleaned up comments and doc strings
pmoegenburg Sep 30, 2024
c2a8271
Merge branch 'edge' into EXEC-643-add-WellVolumeOffset
pmoegenburg Sep 30, 2024
ad334cc
updated command schema 9
pmoegenburg Sep 30, 2024
a671aad
refactored geometry and test
pmoegenburg Oct 2, 2024
726ffc1
Merge branch 'edge' into EXEC-643-add-WellVolumeOffset
pmoegenburg Oct 2, 2024
910d940
updated tests
pmoegenburg Oct 9, 2024
a3ac146
Merge branch 'edge' into EXEC-643-add-WellVolumeOffset
pmoegenburg Oct 9, 2024
38afbc3
update needed after merging in edge
pmoegenburg Oct 9, 2024
b688aac
follow-up format and lint
pmoegenburg Oct 9, 2024
f03bcb4
fixed up frustum_helpers and tests
pmoegenburg Oct 10, 2024
67d53a4
Merge branch 'edge' into EXEC-643-add-WellVolumeOffset
pmoegenburg Oct 10, 2024
053a47c
updated validate_well_position and command schema 10
pmoegenburg Oct 10, 2024
05a075c
added tiprack check to move_to_well
pmoegenburg Oct 10, 2024
97858d8
updates to relax WellLocation contraints based on PR review
pmoegenburg Oct 11, 2024
4e6a6ba
reverted command schema 9
pmoegenburg Oct 11, 2024
f364bc9
linted, removed test, reverted protocol/models/ work
pmoegenburg Oct 11, 2024
029ecea
eliminated test that doesn't do anything
pmoegenburg Oct 11, 2024
38235fe
Created PickUpTipWellOrigin without MENISCUS enum and updated BlowOut…
pmoegenburg Oct 14, 2024
cedc8b4
added Papi support for Well.meniscus Location
pmoegenburg Oct 15, 2024
7fe0451
reduce PE constraints
pmoegenburg Oct 16, 2024
fc0572f
Papi refactor to eliminate calculations prior to PE execution
pmoegenburg Oct 16, 2024
81da6d0
fixed Papi to not calculate meniscus location prior to PE execution
pmoegenburg Oct 17, 2024
b9aba1f
fixed simulation/analysis
pmoegenburg Oct 17, 2024
618e92f
Hid Location.is_meniscus from docs
pmoegenburg Oct 17, 2024
fb8a20c
added geometry test
pmoegenburg Oct 17, 2024
6feaff0
tidied up
pmoegenburg Oct 17, 2024
936b91c
addressed TODO to more appropriately name exceptions
pmoegenburg Oct 17, 2024
665e815
eliminated unneeded method
pmoegenburg Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/src/opentrons/protocol_engine/commands/aspirate.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ async def execute(self, params: AspirateParams) -> _ExecuteReturn:
pipette_id = params.pipetteId
labware_id = params.labwareId
well_name = params.wellName
volume = params.volume
pmoegenburg marked this conversation as resolved.
Show resolved Hide resolved

ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
pipette_id=pipette_id
Expand Down Expand Up @@ -118,6 +119,7 @@ async def execute(self, params: AspirateParams) -> _ExecuteReturn:
well_name=well_name,
well_location=params.wellLocation,
current_well=current_well,
operation_volume=-volume,
)
deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
state_update.set_pipette_location(
Expand All @@ -130,7 +132,7 @@ async def execute(self, params: AspirateParams) -> _ExecuteReturn:
try:
volume_aspirated = await self._pipetting.aspirate_in_place(
pipette_id=pipette_id,
volume=params.volume,
volume=volume,
flow_rate=params.flowRate,
command_note_adder=self._command_note_adder,
)
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_engine/execution/movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async def move_to_well(
force_direct: bool = False,
minimum_z_height: Optional[float] = None,
speed: Optional[float] = None,
operation_volume: Optional[float] = None,
) -> Point:
"""Move to a specific well."""
self._state_store.labware.raise_if_labware_inaccessible_by_pipette(
Expand Down Expand Up @@ -129,6 +130,7 @@ async def move_to_well(
current_well=current_well,
force_direct=force_direct,
minimum_z_height=minimum_z_height,
operation_volume=operation_volume,
)

speed = self._state_store.pipettes.get_movement_speed(
Expand Down
17 changes: 14 additions & 3 deletions api/src/opentrons/protocol_engine/state/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ def get_well_position(
labware_id: str,
well_name: str,
well_location: Optional[WellLocation] = None,
operation_volume: Optional[float] = None,
) -> Point:
"""Given relative well location in a labware, get absolute position."""
labware_pos = self.get_labware_position(labware_id)
Expand All @@ -434,11 +435,21 @@ def get_well_position(
elif well_location.origin == WellOrigin.CENTER:
offset = offset.copy(update={"z": offset.z + well_depth / 2.0})
elif well_location.origin == WellOrigin.MENISCUS:
liquid_height = self._wells.get_last_measured_liquid_height(
starting_liquid_height = self._wells.get_last_measured_liquid_height(
labware_id, well_name
)
if liquid_height is not None:
offset = offset.copy(update={"z": offset.z + liquid_height})
if starting_liquid_height is not None:
sfoster1 marked this conversation as resolved.
Show resolved Hide resolved
if well_location.volumeOffset.volumeOffset == "operationVolume":
volume = operation_volume or 0.0
else:
volume = well_location.volumeOffset.volumeOffset
# height_after_operation = self.get_ending_height(labware_id, well_name, starting_liquid_height, volume)
height_after_operation = starting_liquid_height # delete, use above method once implemented
if volume: # delete
pass # delete
offset = offset.copy(
update={"z": offset.z + height_after_operation}
)
else:
raise errors.LiquidHeightUnknownError(
"Must liquid probe before specifying WellOrigin.MENISCUS."
Expand Down
8 changes: 5 additions & 3 deletions api/src/opentrons/protocol_engine/state/motion.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def get_movement_waypoints_to_well(
current_well: Optional[CurrentWell] = None,
force_direct: bool = False,
minimum_z_height: Optional[float] = None,
operation_volume: Optional[float] = None,
) -> List[motion_planning.Waypoint]:
"""Calculate waypoints to a destination that's specified as a well."""
location = current_well or self._pipettes.get_current_location()
Expand All @@ -107,9 +108,10 @@ def get_movement_waypoints_to_well(
destination_cp = CriticalPoint.XY_CENTER

destination = self._geometry.get_well_position(
labware_id,
well_name,
well_location,
labware_id=labware_id,
well_name=well_name,
well_location=well_location,
operation_volume=operation_volume,
)

move_type = _move_types.get_move_type_to_well(
Expand Down
7 changes: 7 additions & 0 deletions api/src/opentrons/protocol_engine/types.py
pmoegenburg marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,18 @@ class WellOffset(BaseModel):
z: float = 0


class WellVolumeOffset(BaseModel):
"""A z-offset to account for volume in an operation."""

volumeOffset: Union[float, Literal["operationVolume"]] = 0.0
SyntaxColoring marked this conversation as resolved.
Show resolved Hide resolved


class WellLocation(BaseModel):
"""A relative location in reference to a well's location."""

origin: WellOrigin = WellOrigin.TOP
offset: WellOffset = Field(default_factory=WellOffset)
volumeOffset: WellVolumeOffset = Field(default_factory=WellVolumeOffset)
SyntaxColoring marked this conversation as resolved.
Show resolved Hide resolved
pmoegenburg marked this conversation as resolved.
Show resolved Hide resolved


class DropTipWellLocation(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ async def test_aspirate_implementation_no_prep(
well_name="A3",
well_location=location,
current_well=None,
operation_volume=-50,
),
).then_return(Point(x=1, y=2, z=3))

Expand Down Expand Up @@ -145,6 +146,7 @@ async def test_aspirate_implementation_with_prep(
labware_id="123",
well_name="A3",
),
operation_volume=-50,
),
).then_return(Point(x=1, y=2, z=3))

Expand Down Expand Up @@ -210,6 +212,7 @@ async def test_aspirate_raises_volume_error(
well_name="A3",
well_location=location,
current_well=None,
operation_volume=-50,
),
).then_return(Point(1, 2, 3))

Expand Down Expand Up @@ -267,6 +270,7 @@ async def test_overpressure_error(
well_name=well_name,
well_location=well_location,
current_well=None,
operation_volume=-50,
),
).then_return(position)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ async def test_move_to_well(
current_well=None,
force_direct=True,
minimum_z_height=12.3,
operation_volume=None,
)
).then_return(
[Waypoint(Point(1, 2, 3), CriticalPoint.XY_CENTER), Waypoint(Point(4, 5, 6))]
Expand Down Expand Up @@ -257,6 +258,7 @@ async def test_move_to_well_from_starting_location(
well_location=well_location,
force_direct=False,
minimum_z_height=None,
operation_volume=None,
)
).then_return([Waypoint(Point(1, 2, 3), CriticalPoint.XY_CENTER)])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,7 @@ def test_get_well_position_with_meniscus_offset(
origin=WellOrigin.MENISCUS,
offset=WellOffset(x=2, y=3, z=4),
),
operation_volume=0.0,
)

assert result == Point(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def test_get_movement_waypoints_to_well_for_y_center(
).then_return(False)

decoy.when(
geometry_view.get_well_position("labware-id", "well-name", WellLocation())
geometry_view.get_well_position("labware-id", "well-name", WellLocation(), None)
).then_return(Point(x=4, y=5, z=6))

decoy.when(
Expand Down Expand Up @@ -391,7 +391,7 @@ def test_get_movement_waypoints_to_well_for_xy_center(
).then_return(True)

decoy.when(
geometry_view.get_well_position("labware-id", "well-name", WellLocation())
geometry_view.get_well_position("labware-id", "well-name", WellLocation(), None)
).then_return(Point(x=4, y=5, z=6))

decoy.when(
Expand Down Expand Up @@ -460,6 +460,7 @@ def test_get_movement_waypoints_to_well_raises(
labware_id="labware-id",
well_name="A1",
well_location=None,
operation_volume=None,
)
).then_return(Point(x=4, y=5, z=6))
decoy.when(pipette_view.get_current_location()).then_return(None)
Expand Down
23 changes: 23 additions & 0 deletions shared-data/command/schemas/9.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,26 @@
}
}
},
"WellVolumeOffset": {
"title": "WellVolumeOffset",
"description": "A z-offset to account for volume in an operation.",
"type": "object",
"properties": {
"volumeOffset": {
"title": "Volumeoffset",
"default": 0.0,
"anyOf": [
{
"type": "number"
},
{
"enum": ["operationVolume"],
"type": "string"
}
]
}
}
},
"WellLocation": {
"title": "WellLocation",
"description": "A relative location in reference to a well's location.",
Expand All @@ -333,6 +353,9 @@
},
"offset": {
"$ref": "#/definitions/WellOffset"
},
"volumeOffset": {
"$ref": "#/definitions/WellVolumeOffset"
}
}
},
Expand Down
Loading