Skip to content

feat(shared-data): change schema 2 to include user-defined volumes#18815

Merged
caila-marashaj merged 27 commits intoedgefrom
EXEC-762-user-defined-volumes
Jul 21, 2025
Merged

feat(shared-data): change schema 2 to include user-defined volumes#18815
caila-marashaj merged 27 commits intoedgefrom
EXEC-762-user-defined-volumes

Conversation

@caila-marashaj
Copy link
Copy Markdown
Contributor

@caila-marashaj caila-marashaj commented Jul 2, 2025

Overview

Liquid-level detection is out now, and its main blind-spot is labware that don't already have InnerLabwareGeometry definitions in our shared-data repo. This is difficult for users to fill in on their own, as it comes from some pretty intensive measuring and modeling from our hardware department.

As an alternative, we can give users/support people the option to add UserDefinedVolumes to their labware's shared-data file, which will be a list of small dictionaries, each containing a height entry and a volume entry, like this:
"innerLabwareGeometry": { "userVolumes": { "heightToVolumeMap": [ { "height": 0.5, "volume": 3 }, { "height": 2.0, "volume": 10 }, { "height": 10.4, "volume": 70 }, { "height": 30.8, "volume": 100 }, { "height": 59.3, "volume": 300 } ] }

This pr enables this type of data to be used as input for the same liquid-level detection functionality that InnerWellGeometry definitions use, like labware.height_from_volume and labware.volume_from_height.

InnerWellGeometry will use the same geometric calculations we've been using before, and estimations using UserDefinedVolumes will use linear interpolations of the user-specified volumes and heights. This obviously won't be as accurate, but for a lot of labware, especially small labware, it won't matter a ton, so this is a good jumping off point I think.

Changelog

  • change labware schema 2 to allow InnerLabwareGeometry to be either InnerWellGeometry or UserDefinedVolumes
  • add this ^ to labware_definitions as well
  • create linear interpolation functions for UserDefinedVolumes in frustum_helpers
  • have geometry.py determine the type of InnerLabwareGeometry and call the right kind of calculation
  • add a fixture in there
  • some tests

Testing

Hardware testing is coming up with a protocol that will allow users to automatically generate these UserDefinedVolumes data by dispensing known volumes into a well, and probing the liquid height in the well after dispensing. The math itself hasn't been rigorously tested yet, but once we get some more data to work with, we're going to use this protocol to come up with UserDefinedVolumes data for labware with known InnerWellGeometry data, and compare the results of height_from_volume and volume_from_height a bunch of times to evaluate the efficacy.

@codecov
Copy link
Copy Markdown

codecov bot commented Jul 3, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 25.23%. Comparing base (c7c6032) to head (a1d2a12).
Report is 2 commits behind head on edge.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             edge   #18815      +/-   ##
==========================================
+ Coverage   23.96%   25.23%   +1.26%     
==========================================
  Files        3358     3357       -1     
  Lines      294546   294581      +35     
  Branches    31020    31053      +33     
==========================================
+ Hits        70588    74324    +3736     
+ Misses     223937   220230    -3707     
- Partials       21       27       +6     
Flag Coverage Δ
app 3.14% <ø> (+2.14%) ⬆️
protocol-designer 18.96% <ø> (-0.01%) ⬇️
shared-data 72.91% <100.00%> (+0.14%) ⬆️
step-generation 5.31% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...pentrons_shared_data/labware/labware_definition.py 79.34% <100.00%> (+2.08%) ⬆️

... and 89 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@caila-marashaj caila-marashaj force-pushed the EXEC-762-user-defined-volumes branch from 8e03737 to 2eb5ef7 Compare July 14, 2025 18:56
@caila-marashaj caila-marashaj marked this pull request as ready for review July 14, 2025 21:34
@caila-marashaj caila-marashaj requested review from a team as code owners July 14, 2025 21:34
@caila-marashaj caila-marashaj requested review from smb2268 and removed request for a team July 14, 2025 21:34
Copy link
Copy Markdown
Contributor

@SyntaxColoring SyntaxColoring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This looks fundamentally right.

Just lots of small things, sorry for the wall of text. Only some of these are blocking change requests. Blocking change requests are prefixed with ⚠️.

Comment thread shared-data/labware/schemas/2.json Outdated
Comment thread shared-data/labware/schemas/2.json Outdated
Comment thread shared-data/js/__tests__/labwareDefSchemaV2.test.ts
Comment thread shared-data/js/__tests__/labwareDefSchemaV2.test.ts Outdated
Comment thread shared-data/js/__tests__/labwareDefSchemaV2.test.ts
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/src/opentrons/protocol_engine/state/frustum_helpers.py
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
@caila-marashaj caila-marashaj force-pushed the EXEC-762-user-defined-volumes branch 2 times, most recently from 7a03bc8 to 75b42fc Compare July 15, 2025 18:41
Comment thread api/tests/opentrons/protocols/geometry/test_frustum_helpers.py
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
@caila-marashaj caila-marashaj force-pushed the EXEC-762-user-defined-volumes branch from 6860105 to 67362ef Compare July 17, 2025 20:40
from opentrons.protocol_engine.errors.exceptions import InvalidLiquidHeightFound


@pytest.fixture
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed for this PR, just a Hot Pytest Tip.

I think you might be falling into a common Pytest trap of overusing fixtures. I see a lot of people do this. Something in the Pytest docs must be pushing fixtures too hard, or something.

The way I see it, fixtures are helpful when you want to do any of the following:

  • Separate the fixture's setup and teardown code from the test code
    • for better error reporting in the CLI output
    • for better timing statistics in the CLI output
    • to save a level of indentation inside the test function
  • Share an expensive object across multiple tests, for performance
  • Automatically run test functions with multiple values of the fixture

But for something like this, the fanciness of fixtures isn't doing anything for us. it may as well be a regular module-level constant, like USER_DEFINED_VOLUMES_PARAMS = {...}.

The downside to fixtures is that they break type checking and IDE features like "find references." There have been pushes for Pytest to change the fixture syntax to fix that (pytest-dev/pytest#5981, pytest-dev/pytest#9946), but it doesn't seem like they're budging.

Comment on lines +4526 to +4530
decoy.when(
mock_well_math_utils[target_measurement + "_" + well_def_type](
sentinel.arbitrary_height_volume, geometry_def
)
).then_return(sentinel.arbitrary_return_val)
Copy link
Copy Markdown
Contributor

@SyntaxColoring SyntaxColoring Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the effort to deduplicate between the find_height_at_well_volume() test and the find_volume_at_well_height() test, which are basically structurally identical. But dynamically constructing the function name to mock out feels a bit too sneaky.

I would do it the "dumb" way and have one decoy.when() call inside the if target_measurement == "height" block and one decoy.when() call inside the elif target_measurement == "volume" block.

Feel free to defer if you want to focus on getting the JS side passing and getting this merged.

Comment thread api/tests/opentrons/protocol_engine/state/test_geometry_view.py Outdated
@caila-marashaj caila-marashaj force-pushed the EXEC-762-user-defined-volumes branch 3 times, most recently from 1af4b24 to 0ca35de Compare July 21, 2025 15:26
Comment thread shared-data/labware/fixtures/2/user_volumes_prototype.json
Comment thread shared-data/js/__tests__/labwareDefSchemaV2.test.ts Outdated
@caila-marashaj caila-marashaj force-pushed the EXEC-762-user-defined-volumes branch from 068fdf2 to 6fed787 Compare July 21, 2025 18:59
Copy link
Copy Markdown
Contributor

@SyntaxColoring SyntaxColoring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! 🧪

@caila-marashaj caila-marashaj merged commit 517ab7f into edge Jul 21, 2025
98 of 99 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants