Skip to content

Commit

Permalink
created two needed tests for my PR and updated frustum_helper.py
Browse files Browse the repository at this point in the history
  • Loading branch information
pmoegenburg committed Oct 2, 2024
1 parent a5f4f04 commit 250af76
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 28 deletions.
66 changes: 39 additions & 27 deletions api/src/opentrons/protocol_engine/state/frustum_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
from opentrons_shared_data.labware.labware_definition import InnerWellGeometry


SectionsType = Union[CircularBoundedSection, RectangularBoundedSection]


def reject_unacceptable_heights(
potential_heights: List[float], max_height: float
) -> float:
Expand All @@ -33,9 +36,7 @@ def reject_unacceptable_heights(
return valid_heights[0]


def get_cross_section_area(
bounded_section: Union[CircularBoundedSection, RectangularBoundedSection]
) -> float:
def get_cross_section_area(bounded_section: SectionsType) -> float:
"""Find the shape of a cross-section and calculate the area appropriately."""
if bounded_section["shape"] == "circular":
cross_section_area = cross_section_area_circular(bounded_section["diameter"])
Expand Down Expand Up @@ -242,11 +243,11 @@ def get_well_volumetric_capacity(

if is_rectangular_frusta_list(sorted_frusta):
for f, next_f in get_boundary_pairs(sorted_frusta):
top_cross_section_width = next_f["xDimension"]
top_cross_section_length = next_f["yDimension"]
bottom_cross_section_width = f["xDimension"]
bottom_cross_section_length = f["yDimension"]
frustum_height = next_f["topHeight"] - f["topHeight"]
top_cross_section_width = next_f.xDimension
top_cross_section_length = next_f.yDimension
bottom_cross_section_width = f.xDimension
bottom_cross_section_length = f.yDimension
frustum_height = next_f.topHeight - f.topHeight
frustum_volume = volume_from_height_rectangular(
target_height=frustum_height,
total_frustum_height=frustum_height,
Expand All @@ -256,20 +257,20 @@ def get_well_volumetric_capacity(
top_width=top_cross_section_width,
)

well_volume.append((next_f["topHeight"], frustum_volume))
well_volume.append((next_f.topHeight, frustum_volume))
elif is_circular_frusta_list(sorted_frusta):
for f, next_f in get_boundary_pairs(sorted_frusta):
top_cross_section_radius = next_f["diameter"] / 2.0
bottom_cross_section_radius = f["diameter"] / 2.0
frustum_height = next_f["topHeight"] - f["topHeight"]
top_cross_section_radius = next_f.diameter / 2.0
bottom_cross_section_radius = f.diameter / 2.0
frustum_height = next_f.topHeight - f.topHeight
frustum_volume = volume_from_height_circular(
target_height=frustum_height,
total_frustum_height=frustum_height,
bottom_radius=bottom_cross_section_radius,
top_radius=top_cross_section_radius,
)

well_volume.append((next_f["topHeight"], frustum_volume))
well_volume.append((next_f.topHeight, frustum_volume))
else:
raise NotImplementedError(
"Well section with differing boundary shapes not yet implemented."
Expand All @@ -278,20 +279,26 @@ def get_well_volumetric_capacity(


def height_at_volume_within_section(
top_cross_section: Union[CircularBoundedSection, RectangularBoundedSection],
bottom_cross_section: Union[CircularBoundedSection, RectangularBoundedSection],
top_cross_section: SectionsType,
bottom_cross_section: SectionsType,
target_volume_relative: float,
frustum_height: float,
) -> float:
"""Calculate a height within a bounded section according to geometry."""
if top_cross_section["shape"] == bottom_cross_section["shape"] == "circular":
if (
top_cross_section["shape"] == "circular"
and bottom_cross_section["shape"] == "circular"
):
frustum_height = height_from_volume_circular(
volume=target_volume_relative,
top_radius=(top_cross_section["diameter"] / 2),
bottom_radius=(bottom_cross_section["diameter"] / 2),
total_frustum_height=frustum_height,
)
elif top_cross_section["shape"] == bottom_cross_section["shape"] == "rectangular":
elif (
top_cross_section["shape"] == "rectangular"
and bottom_cross_section["shape"] == "rectangular"
):
frustum_height = height_from_volume_rectangular(
volume=target_volume_relative,
total_frustum_height=frustum_height,
Expand All @@ -308,20 +315,26 @@ def height_at_volume_within_section(


def volume_at_height_within_section(
top_cross_section: Union[CircularBoundedSection, RectangularBoundedSection],
bottom_cross_section: Union[CircularBoundedSection, RectangularBoundedSection],
top_cross_section: SectionsType,
bottom_cross_section: SectionsType,
target_height_relative: float,
frustum_height: float,
) -> float:
"""Calculate a volume within a bounded section according to geometry."""
if top_cross_section["shape"] == bottom_cross_section["shape"] == "circular":
if (
top_cross_section["shape"] == "circular"
and bottom_cross_section["shape"] == "circular"
):
frustum_volume = volume_from_height_circular(
target_height=target_height_relative,
total_frustum_height=frustum_height,
bottom_radius=(bottom_cross_section["diameter"] / 2),
top_radius=(top_cross_section["diameter"] / 2),
)
elif top_cross_section["shape"] == bottom_cross_section["shape"] == "rectangular":
elif (
top_cross_section["shape"] == "rectangular"
and bottom_cross_section["shape"] == "rectangular"
):
frustum_volume = volume_from_height_rectangular(
target_height=target_height_relative,
total_frustum_height=frustum_height,
Expand All @@ -347,13 +360,12 @@ def _find_volume_in_partial_frustum(
partial_volume: Optional[float] = None
for bottom_cross_section, top_cross_section in get_boundary_pairs(sorted_frusta):
if (
bottom_cross_section["topHeight"]
< target_height
< top_cross_section["targetHeight"]
bottom_cross_section.topHeight < target_height
and target_height < top_cross_section.topHeight
):
relative_target_height = target_height - bottom_cross_section["topHeight"]
relative_target_height = target_height - bottom_cross_section.topHeight
frustum_height = (
top_cross_section["topHeight"] - bottom_cross_section["topHeight"]
top_cross_section.topHeight - bottom_cross_section.topHeight
)
partial_volume = volume_at_height_within_section(
top_cross_section=top_cross_section,
Expand Down Expand Up @@ -420,7 +432,7 @@ def _find_height_in_partial_frustum(
bottom_cross_section, top_cross_section = cross_sections
(bottom_height, bottom_volume), (top_height, top_volume) = capacity

if bottom_volume < target_volume < top_volume:
if bottom_volume < target_volume and target_volume < top_volume:
relative_target_volume = target_volume - bottom_volume
frustum_height = top_height - bottom_height
partial_height = height_at_volume_within_section(
Expand Down
28 changes: 27 additions & 1 deletion api/src/opentrons/protocol_engine/state/labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
from opentrons.protocol_engine.state import update_types
from opentrons_shared_data.deck.types import DeckDefinitionV5
from opentrons_shared_data.gripper.constants import LABWARE_GRIP_FORCE
from opentrons_shared_data.labware.labware_definition import LabwareRole
from opentrons_shared_data.labware.labware_definition import (
LabwareRole,
InnerWellGeometry,
)
from opentrons_shared_data.pipette.types import LabwareUri

from opentrons.types import DeckSlotName, StagingSlotName, MountType
Expand Down Expand Up @@ -462,6 +465,29 @@ def get_well_definition(
f"{well_name} does not exist in {labware_id}."
) from e

def get_well_geometry(
self, labware_id: str, well_name: Optional[str] = None
) -> InnerWellGeometry:
"""Get a well's inner geometry by labware and well name."""
labware_def = self.get_definition(labware_id)
if labware_def.innerLabwareGeometry is None:
raise errors.InvalidWellDefinitionError(
message=f"No innerLabwareGeometry found for labware_id: {labware_id}."
)
well_def = self.get_well_definition(labware_id, well_name)
well_id = well_def.geometryDefinitionId
if well_id is None:
raise errors.InvalidWellDefinitionError(
message=f"No geometryDefinitionId found for well: {well_name} in labware_id: {labware_id}"
)
else:
well_geometry = labware_def.innerLabwareGeometry.get(well_id)
if well_geometry is None:
raise errors.InvalidWellDefinitionError(
message=f"No innerLabwareGeometry found for well_id: {well_id} in labware_id: {labware_id}"
)
return well_geometry

def get_well_size(
self, labware_id: str, well_name: str
) -> Tuple[float, float, float]:
Expand Down
24 changes: 24 additions & 0 deletions api/tests/opentrons/protocol_engine/state/test_frustum_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Test methods that translate well heights and volumes for GeometryView."""
from opentrons.protocol_engine.state.frustum_helpers import (
find_volume_at_well_height,
find_height_at_well_volume,
)
from ...protocol_runner.test_json_translator import _load_labware_definition_data


def test_find_volume_at_well_height() -> None:
"""Test find_volume_at_well_height."""
labware_def = _load_labware_definition_data()
assert labware_def.innerLabwareGeometry is not None
inner_well_def = labware_def.innerLabwareGeometry["welldefinition1111"]
result = find_volume_at_well_height(40.0, inner_well_def)
assert result == 1245.833 # use isclose() or something


def test_find_height_at_well_volume() -> None:
"""Test find_height_at_well_volume."""
labware_def = _load_labware_definition_data()
assert labware_def.innerLabwareGeometry is not None
inner_well_def = labware_def.innerLabwareGeometry["welldefinition1111"]
result = find_height_at_well_volume(1245.833, inner_well_def)
assert result == 40.0 # use isclose() or something

0 comments on commit 250af76

Please sign in to comment.