Skip to content

Commit

Permalink
tip overlap conversion to source from physical definition tree
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyBatten committed May 8, 2024
1 parent af38b12 commit cd11ee0
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 64 deletions.
20 changes: 20 additions & 0 deletions api/src/opentrons/hardware_control/instruments/ot2/pipette.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,26 @@ def get_pick_up_current_by_configuration(
return config.configuration_by_nozzle_map[approved_map][
pip_types.PipetteTipType(self._liquid_class.max_volume).name
].current

def get_nominal_tip_overlap_by_configuration(
self,
config: PressFitPickUpTipConfiguration,
) -> float:
approved_map = None
for map_key in self._valid_nozzle_maps.maps.keys():
if (
self._valid_nozzle_maps.maps[map_key]
== list(self._nozzle_manager.current_configuration.map_store.keys())
):
approved_map = map_key
if approved_map is None:
raise ValueError(
"Nominal tip overlap request error. Nozzle Configuration does not match any approved map layout for the current pipette."
)

return config.configuration_by_nozzle_map[approved_map][
pip_types.PipetteTipType(self._liquid_class.max_volume).name
].tip_overlap

# Cache max is chosen somewhat arbitrarily. With a float is input we don't
# want this to unbounded.
Expand Down
115 changes: 66 additions & 49 deletions api/src/opentrons/hardware_control/instruments/ot3/pipette.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def __init__(
)
self._flow_acceleration = self._active_tip_settings.default_flow_acceleration

self._tip_overlap_lookup = self._liquid_class.tip_overlap_dictionary

self._tip_overlap = self.get_nominal_tip_overlap_by_configuration()

if use_old_aspiration_functions:
self._pipetting_function_version = PIPETTING_FUNCTION_FALLBACK_VERSION
Expand Down Expand Up @@ -166,8 +167,8 @@ def backlash_distance(self) -> float:
return self._backlash_distance

@property
def tip_overlap(self) -> Dict[str, float]:
return self._tip_overlap_lookup
def tip_overlap(self) -> float:
return self._tip_overlap

@property
def nozzle_offset(self) -> Point:
Expand Down Expand Up @@ -259,7 +260,7 @@ def reset_state(self) -> None:
)
self._flow_acceleration = self._active_tip_settings.default_flow_acceleration

self._tip_overlap_lookup = self.liquid_class.tip_overlap_dictionary
self._tip_overlap = self.get_nominal_tip_overlap_by_configuration()
self._nozzle_manager = (
nozzle_manager.NozzleConfigurationManager.build_from_config(self._config)
)
Expand Down Expand Up @@ -660,9 +661,20 @@ def set_tip_type(self, tip_type: pip_types.PipetteTipType) -> None:
self._flow_acceleration = self._active_tip_settings.default_flow_acceleration

self._fallback_tip_length = self._active_tip_settings.default_tip_length
self._tip_overlap_lookup = self.liquid_class.tip_overlap_dictionary
self._tip_overlap = self.get_nominal_tip_overlap_by_configuration()
self._working_volume = min(tip_type.value, self.liquid_class.max_volume)

def _get_matching_approved_nozzle_map(self) -> str:
for map_key in self._valid_nozzle_maps.maps.keys():
if (
self._valid_nozzle_maps.maps[map_key]
== list(self._nozzle_manager.current_configuration.map_store.keys())
):
return map_key
raise ValueError(
"Nozzle Configuration does not match any approved map layout for the current pipette."
)

def get_pick_up_configuration_for_tip_count(
self, count: int
) -> Union[CamActionPickUpTipConfiguration, PressFitPickUpTipConfiguration]:
Expand All @@ -672,19 +684,7 @@ def get_pick_up_configuration_for_tip_count(
):
if not config:
continue

approved_map = None
for map_key in self._valid_nozzle_maps.maps.keys():
if (
self._valid_nozzle_maps.maps[map_key]
== list(self._nozzle_manager.current_configuration.map_store.keys())
):
approved_map = map_key

if approved_map is None:
raise ValueError(
"Nozzle Configuration does not match any approved map layout for the current pipette."
)
approved_map = self._get_matching_approved_nozzle_map()

if isinstance(config, PressFitPickUpTipConfiguration) and all(
[
Expand Down Expand Up @@ -728,49 +728,66 @@ def get_pick_up_speed_by_configuration(
"Pick up tip speed request error. Nozzle Configuration does not match any approved map layout for the current pipette."
)

return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].speed
try:
return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].speed
except:
default = config.configuration_by_nozzle_map[approved_map].get("default")
if default is not None:
return default.speed
raise KeyError(f"Default tip type configuration values do not exist for Nozzle Map {approved_map}.")

def get_pick_up_distance_by_configuration(
self,
config: Union[CamActionPickUpTipConfiguration, PressFitPickUpTipConfiguration],
) -> float:
approved_map = None
for map_key in self._valid_nozzle_maps.maps.keys():
if (
self._valid_nozzle_maps.maps[map_key]
== list(self._nozzle_manager.current_configuration.map_store.keys())
):
approved_map = map_key
if approved_map is None:
raise ValueError(
"Pick up tip distance request error. Nozzle Configuration does not match any approved map layout for the current pipette."
)
approved_map = self._get_matching_approved_nozzle_map()

return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].distance
try:
return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].distance
except:
default = config.configuration_by_nozzle_map[approved_map].get("default")
if default is not None:
return default.distance
raise KeyError(f"Default tip type configuration values do not exist for Nozzle Map {approved_map}.")

def get_pick_up_current_by_configuration(
self,
config: Union[CamActionPickUpTipConfiguration, PressFitPickUpTipConfiguration],
) -> float:
approved_map = None
for map_key in self._valid_nozzle_maps.maps.keys():
if (
self._valid_nozzle_maps.maps[map_key]
== list(self._nozzle_manager.current_configuration.map_store.keys())
):
approved_map = map_key
if approved_map is None:
raise ValueError(
"Pick up tip current request error. Nozzle Configuration does not match any approved map layout for the current pipette."
)
approved_map = self._get_matching_approved_nozzle_map()

return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].current
try:
return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].current
except:
default = config.configuration_by_nozzle_map[approved_map].get("default")
if default is not None:
return default.current
raise KeyError(f"Default tip type configuration values do not exist for Nozzle Map {approved_map}.")

def get_nominal_tip_overlap_by_configuration(self) -> float:
for config in (
self._config.pick_up_tip_configurations.press_fit,
self._config.pick_up_tip_configurations.cam_action,
):
if not config:
continue
approved_map = self._get_matching_approved_nozzle_map()

try:
return config.configuration_by_nozzle_map[approved_map][
self._active_tip_setting_name.name
].tip_overlap
except:
default = config.configuration_by_nozzle_map[approved_map].get("default")
if default is not None:
return default.tip_overlap
raise KeyError(f"Default tip type configuration values do not exist for Nozzle Map {approved_map}.")


def _reload_and_check_skip(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class LoadedStaticPipetteData:
tip_configuration_lookup_table: Dict[
float, pipette_definition.SupportedTipsDefinition
]
nominal_tip_overlap: Dict[str, float]
nominal_tip_overlap: float
nozzle_map: NozzleMap
back_left_corner_offset: Point
front_right_corner_offset: Point
Expand Down Expand Up @@ -150,8 +150,48 @@ def _get_virtual_pipette_static_config_by_model(
tip_configuration = config.liquid_properties[liquid_class].supported_tips[
tip_type
]
valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
pipette_model.pipette_type,
pipette_model.pipette_channels,
pipette_model.pipette_version,
)

nozzle_manager = NozzleConfigurationManager.build_from_config(config)

tip_overlap_for_tip_type = None
for configuration in (
config.pick_up_tip_configurations.press_fit,
config.pick_up_tip_configurations.cam_action,
):
if not config:
continue

approved_map = None
for map_key in valid_nozzle_maps.maps.keys():
if (
valid_nozzle_maps.maps[map_key]
== list(nozzle_manager.current_configuration.map_store.keys())
):
approved_map = map_key
if approved_map is None:
raise ValueError(
"Virtual Static Nozzle Configuration does not match any approved map layout for the current pipette."
)
try:
tip_overlap_for_tip_type = configuration.configuration_by_nozzle_map[approved_map][
tip_type.name
].tip_overlap
break
except:
default = configuration.configuration_by_nozzle_map[approved_map].get("default")
if default is not None:
tip_overlap_for_tip_type = default.tip_overlap
break
if tip_overlap_for_tip_type is None:
raise ValueError(
"Virtual Static Nozzle Configuration does not have a valid pick up tip configuration."
)

pip_back_left = config.pipette_bounding_box_offsets.back_left_corner
pip_front_right = config.pipette_bounding_box_offsets.front_right_corner
return LoadedStaticPipetteData(
Expand All @@ -173,9 +213,7 @@ def _get_virtual_pipette_static_config_by_model(
default_aspirate=tip_configuration.default_aspirate_flowrate.values_by_api_level,
default_dispense=tip_configuration.default_dispense_flowrate.values_by_api_level,
),
nominal_tip_overlap=config.liquid_properties[
liquid_class
].tip_overlap_dictionary,
nominal_tip_overlap=tip_overlap_for_tip_type,
nozzle_map=nozzle_manager.current_configuration,
back_left_corner_offset=Point(
pip_back_left[0], pip_back_left[1], pip_back_left[2]
Expand Down Expand Up @@ -214,6 +252,7 @@ def get_pipette_static_config(pipette_dict: PipetteDict) -> LoadedStaticPipetteD
k.value: v for k, v in pipette_dict["supported_tips"].items()
},
nominal_tip_overlap=pipette_dict["tip_overlap"],

# TODO(mc, 2023-02-28): these two values are not present in PipetteDict
# https://opentrons.atlassian.net/browse/RCORE-655
home_position=0,
Expand Down
37 changes: 30 additions & 7 deletions api/src/opentrons/protocol_engine/state/pipettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class StaticPipetteConfig:
tip_configuration_lookup_table: Dict[
float, pipette_definition.SupportedTipsDefinition
]
nominal_tip_overlap: Dict[str, float]
nominal_tip_overlap: float
home_position: float
nozzle_offset_z: float
pipette_bounding_box_offsets: PipetteBoundingBoxOffsets
Expand Down Expand Up @@ -651,12 +651,35 @@ def get_flow_rates(self, pipette_id: str) -> FlowRates:

def get_nominal_tip_overlap(self, pipette_id: str, labware_uri: str) -> float:
"""Get the nominal tip overlap for a given labware from config."""
tip_overlaps_by_uri = self.get_config(pipette_id).nominal_tip_overlap

try:
return tip_overlaps_by_uri[labware_uri]
except KeyError:
return tip_overlaps_by_uri.get("default", 0)
nominal_tip_overlap = self.get_config(pipette_id).nominal_tip_overlap
return nominal_tip_overlap

#OKAY LOTS OF NOTES:
# first we will be calling self.get_nominal_tip_overlap_by_configuration(tip_type_uri???) from here
#in that we will resolve to a float
#the float will return there
#IF the active tip does not match the one resolved from the labware uri then we will return "default"
#if nothing matches at all (keyerror) we will return the default

#translate the uri here into a tip type

#look up labware definitions_by_uri
#then use that to find tip volume
#then look up the tip types and find a type that matches that volume

#new notes:
#maybe instead since this is only used in the geometry view we can just remove the labware uri references
#instead we can pass down tip type from here
#or we can infer it at the bottom like we do the others

#near as I can tell there would be minimal harm in that plan then removing references to labware uri ^

#get_definition_by_uri()

# try:
# return tip_overlaps_by_uri[labware_uri]
# except KeyError:
# return tip_overlaps_by_uri.get("default", 0)

def get_z_axis(self, pipette_id: str) -> MotorAxis:
"""Get the MotorAxis representing this pipette's Z stage."""
Expand Down
9 changes: 5 additions & 4 deletions api/src/opentrons/protocols/api_support/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ def tip_length_for(
pipette["pipette_id"], tip_rack_definition
).tip_length
except TipLengthCalNotFound:
tip_overlap = pipette["tip_overlap"].get(
uri_from_definition(tip_rack_definition),
pipette["tip_overlap"]["default"],
)
# tip_overlap = pipette["tip_overlap"].get(
# uri_from_definition(tip_rack_definition),
# pipette["tip_overlap"]["default"],
# )
tip_overlap = pipette["tip_overlap"]
tip_length = tip_rack_definition["parameters"]["tipLength"]
return tip_length - tip_overlap

Expand Down
Loading

0 comments on commit cd11ee0

Please sign in to comment.