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(protocol engine): calculate volume and height for irregular well shapes #16299

Open
wants to merge 4 commits into
base: edge
Choose a base branch
from
Open
Changes from all commits
Commits
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
50 changes: 47 additions & 3 deletions api/src/opentrons/protocol_engine/state/frustum_helpers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Helper functions for liquid-level related calculations inside a given frustum."""
from typing import List, Tuple, Iterator, Sequence, Any
from typing import List, Tuple, Iterator, Sequence, Any, Union
from numpy import pi, iscomplex, roots, real
from math import sqrt

from ..errors.exceptions import InvalidLiquidHeightFound
from ..errors.exceptions import InvalidLiquidHeightFound, InvalidWellDefinitionError
from opentrons_shared_data.labware.types import (
is_circular_frusta_list,
is_rectangular_frusta_list,
CircularBoundedSection,
RectangularBoundedSection,
)
from opentrons_shared_data.labware.labware_definition import InnerWellGeometry

Expand All @@ -29,6 +32,39 @@ def reject_unacceptable_heights(
return valid_heights[0]


def get_cross_section_area(
bounded_section: Union[CircularBoundedSection, RectangularBoundedSection]
) -> 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"])
elif bounded_section["shape"] == "rectangular":
cross_section_area = cross_section_area_rectangular(
bounded_section["xDimension"],
bounded_section["yDimension"],
)
else:
raise InvalidWellDefinitionError(message="Invalid well volume components.")
return cross_section_area


def cross_section_area_circular(diameter: float) -> float:
"""Get the area of a circular cross-section."""
radius = diameter / 2
return pi * (radius**2)


def cross_section_area_rectangular(x_dimension: float, y_dimension: float) -> float:
"""Get the area of a rectangular cross-section."""
return x_dimension * y_dimension


def volume_from_frustum_formula(area_1: float, area_2: float, height: float) -> float:
"""Get the area of a section with differently shaped boundary cross-sections."""
area_term = area_1 + area_2 + sqrt(area_1 * area_2)
return (height / 3) * area_term


def rectangular_frustum_polynomial_roots(
bottom_length: float,
bottom_width: float,
Expand Down Expand Up @@ -239,5 +275,13 @@ def get_well_volumetric_capacity(
)

well_volume.append((next_f["topHeight"], frustum_volume))

else:
for f, next_f in get_boundary_cross_sections(sorted_frusta):
bottom_cross_section_area = get_cross_section_area(f)
top_cross_section_area = get_cross_section_area(next_f)
section_height = next_f["topHeight"] - f["topHeight"]
bounded_volume = volume_from_frustum_formula(
bottom_cross_section_area, top_cross_section_area, section_height
)
well_volume.append((next_f["topHeight"], bounded_volume))
return well_volume
Loading