UniteLabs
Reference

Shapes

Shape sections that model wells, tubes, troughs, and tips, and how to query their width, depth, and area at any height.

Every container in the labware library, whether a plate well, a tube, a trough, or a tip, is built from one or more shape sections: Cylinder, Cuboid, Cone, ConicalFrustum, Pyramid, PyramidalFrustum, SphericalCap, HalfSphere, and SphericalSegment. A Container stacks them from top to bottom, and the library uses that model to turn a volume into a liquid level and back.

from unitelabs.labware import Container, ConicalFrustum, Cylinder

# The container of a 50 mL conical tube: a cylinder on top of a conical bottom
container = Container(
    max_volume=50_000,
    sections=[
        Cylinder(radius=13.89, height=98.77),  # top section first
        ConicalFrustum(radius_lower=3.6, radius_upper=13.89, height=14.88),
    ],
)

Dimensions at Height

When an operation needs the width of a container at a specific height, for example to move a pipetting channel to the side of a well, the answer depends on the shape. Three methods give the same answer for every concrete shape class, so your code doesn't have to handle each shape type separately:

  • width_at_height(height) returns the x-axis extent of the cross-section at a given height.
  • depth_at_height(height) returns the y-axis extent of the cross-section at a given height. For circular shapes this equals the width.
  • area_at_height(height) returns the cross-sectional area at a given height.

For stacked containers (any Container with multiple sections), one more method is available:

  • section_at_height(height) returns a tuple[Shape, Decimal] containing the shape section and the local height within that section. Container delegates width_at_height, depth_at_height, and area_at_height through this method.

Usage Example

Given the Standard50mLTube defined in Tubes and Tube Racks, a tube with a cylindrical upper section and a conical frustum at the bottom, you can query the width at any height without caring which section you are in:

from unitelabs.labware import Decimal

tube = Standard50mLTube()

# Width at the middle of the cylindrical section (e.g. 50 mm from bottom)
width_mid = tube.container.width_at_height(Decimal("50"))
print(f"Width at 50 mm: {width_mid} mm")  # 27.78 mm (2 × 13.89)

# Width near the tip of the conical frustum (e.g. 5 mm from bottom)
width_tip = tube.container.width_at_height(Decimal("5"))
print(f"Width at 5 mm: {width_tip} mm")  # interpolated between 7.2 mm and 27.78 mm

# Cross-sectional area at 50 mm
area = tube.container.area_at_height(Decimal("50"))
print(f"Area at 50 mm: {round(area, 3)} mm²")  # π × 13.89²

# Inspect which section and local offset a given height falls into
section, local_height = tube.container.section_at_height(Decimal("5"))
print(f"Section: {section}")          # ConicalFrustum(...)
print(f"Local height: {local_height} mm")

Shape Reference

The table below shows how each concrete shape implements the three methods, where h is the queried height and H is the total height of the shape:

Shapewidth_at_height(h)depth_at_height(h)area_at_height(h)
Cuboidwidth (constant)depth (constant)width × depth
Pyramid(h/H) × width(h/H) × depth(h/H)² × width × depth
PyramidalFrustumlinear interpolation between width_lower and width_upperlinear interpolation between depth_lower and depth_upperw(h) × d(h)
Cylinder2 × radius (constant)2 × radius (constant)π × r²
Cone2 × (h/H) × radiussame as widthπ × ((h/H) × r)²
ConicalFrustum2 × r(h), linear interpolation between radius_lower and radius_uppersame as widthπ × r(h)²
SphericalCap2 × √(2Rh − h²)same as widthπ × (2Rh − h²)
HalfSphereinherits from SphericalCapinherits from SphericalCapinherits from SphericalCap
SphericalSegment2 × √(2R·h' − h'²) where h' is height from sphere bottomsame as widthπ × (2R·h' − h'²)
Stacked containers: When a Container has multiple sections, width_at_height, depth_at_height, and area_at_height automatically resolve the correct section via section_at_height. The height passed is always measured from the bottom of the container, not from the bottom of the individual section.

Last updated