opentrons 8.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (601) hide show
  1. opentrons/__init__.py +150 -0
  2. opentrons/_version.py +34 -0
  3. opentrons/calibration_storage/__init__.py +54 -0
  4. opentrons/calibration_storage/deck_configuration.py +62 -0
  5. opentrons/calibration_storage/encoder_decoder.py +31 -0
  6. opentrons/calibration_storage/file_operators.py +142 -0
  7. opentrons/calibration_storage/helpers.py +103 -0
  8. opentrons/calibration_storage/ot2/__init__.py +34 -0
  9. opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
  10. opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
  11. opentrons/calibration_storage/ot2/models/__init__.py +0 -0
  12. opentrons/calibration_storage/ot2/models/v1.py +149 -0
  13. opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
  14. opentrons/calibration_storage/ot2/tip_length.py +281 -0
  15. opentrons/calibration_storage/ot3/__init__.py +31 -0
  16. opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
  17. opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
  18. opentrons/calibration_storage/ot3/models/__init__.py +0 -0
  19. opentrons/calibration_storage/ot3/models/v1.py +122 -0
  20. opentrons/calibration_storage/ot3/module_offset.py +138 -0
  21. opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
  22. opentrons/calibration_storage/types.py +45 -0
  23. opentrons/cli/__init__.py +21 -0
  24. opentrons/cli/__main__.py +5 -0
  25. opentrons/cli/analyze.py +557 -0
  26. opentrons/config/__init__.py +631 -0
  27. opentrons/config/advanced_settings.py +871 -0
  28. opentrons/config/defaults_ot2.py +214 -0
  29. opentrons/config/defaults_ot3.py +499 -0
  30. opentrons/config/feature_flags.py +86 -0
  31. opentrons/config/gripper_config.py +55 -0
  32. opentrons/config/reset.py +203 -0
  33. opentrons/config/robot_configs.py +187 -0
  34. opentrons/config/types.py +183 -0
  35. opentrons/drivers/__init__.py +0 -0
  36. opentrons/drivers/absorbance_reader/__init__.py +11 -0
  37. opentrons/drivers/absorbance_reader/abstract.py +72 -0
  38. opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
  39. opentrons/drivers/absorbance_reader/driver.py +81 -0
  40. opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
  41. opentrons/drivers/absorbance_reader/simulator.py +84 -0
  42. opentrons/drivers/asyncio/__init__.py +0 -0
  43. opentrons/drivers/asyncio/communication/__init__.py +22 -0
  44. opentrons/drivers/asyncio/communication/async_serial.py +187 -0
  45. opentrons/drivers/asyncio/communication/errors.py +88 -0
  46. opentrons/drivers/asyncio/communication/serial_connection.py +557 -0
  47. opentrons/drivers/command_builder.py +102 -0
  48. opentrons/drivers/flex_stacker/__init__.py +13 -0
  49. opentrons/drivers/flex_stacker/abstract.py +214 -0
  50. opentrons/drivers/flex_stacker/driver.py +768 -0
  51. opentrons/drivers/flex_stacker/errors.py +68 -0
  52. opentrons/drivers/flex_stacker/simulator.py +309 -0
  53. opentrons/drivers/flex_stacker/types.py +367 -0
  54. opentrons/drivers/flex_stacker/utils.py +19 -0
  55. opentrons/drivers/heater_shaker/__init__.py +5 -0
  56. opentrons/drivers/heater_shaker/abstract.py +76 -0
  57. opentrons/drivers/heater_shaker/driver.py +204 -0
  58. opentrons/drivers/heater_shaker/simulator.py +94 -0
  59. opentrons/drivers/mag_deck/__init__.py +6 -0
  60. opentrons/drivers/mag_deck/abstract.py +44 -0
  61. opentrons/drivers/mag_deck/driver.py +208 -0
  62. opentrons/drivers/mag_deck/simulator.py +63 -0
  63. opentrons/drivers/rpi_drivers/__init__.py +33 -0
  64. opentrons/drivers/rpi_drivers/dev_types.py +94 -0
  65. opentrons/drivers/rpi_drivers/gpio.py +282 -0
  66. opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
  67. opentrons/drivers/rpi_drivers/interfaces.py +15 -0
  68. opentrons/drivers/rpi_drivers/types.py +364 -0
  69. opentrons/drivers/rpi_drivers/usb.py +102 -0
  70. opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
  71. opentrons/drivers/serial_communication.py +151 -0
  72. opentrons/drivers/smoothie_drivers/__init__.py +4 -0
  73. opentrons/drivers/smoothie_drivers/connection.py +51 -0
  74. opentrons/drivers/smoothie_drivers/constants.py +121 -0
  75. opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
  76. opentrons/drivers/smoothie_drivers/errors.py +49 -0
  77. opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
  78. opentrons/drivers/smoothie_drivers/simulator.py +99 -0
  79. opentrons/drivers/smoothie_drivers/types.py +16 -0
  80. opentrons/drivers/temp_deck/__init__.py +10 -0
  81. opentrons/drivers/temp_deck/abstract.py +54 -0
  82. opentrons/drivers/temp_deck/driver.py +197 -0
  83. opentrons/drivers/temp_deck/simulator.py +57 -0
  84. opentrons/drivers/thermocycler/__init__.py +12 -0
  85. opentrons/drivers/thermocycler/abstract.py +99 -0
  86. opentrons/drivers/thermocycler/driver.py +395 -0
  87. opentrons/drivers/thermocycler/simulator.py +126 -0
  88. opentrons/drivers/types.py +107 -0
  89. opentrons/drivers/utils.py +222 -0
  90. opentrons/execute.py +742 -0
  91. opentrons/hardware_control/__init__.py +65 -0
  92. opentrons/hardware_control/__main__.py +77 -0
  93. opentrons/hardware_control/adapters.py +98 -0
  94. opentrons/hardware_control/api.py +1347 -0
  95. opentrons/hardware_control/backends/__init__.py +7 -0
  96. opentrons/hardware_control/backends/controller.py +400 -0
  97. opentrons/hardware_control/backends/errors.py +9 -0
  98. opentrons/hardware_control/backends/estop_state.py +164 -0
  99. opentrons/hardware_control/backends/flex_protocol.py +497 -0
  100. opentrons/hardware_control/backends/ot3controller.py +1930 -0
  101. opentrons/hardware_control/backends/ot3simulator.py +900 -0
  102. opentrons/hardware_control/backends/ot3utils.py +664 -0
  103. opentrons/hardware_control/backends/simulator.py +442 -0
  104. opentrons/hardware_control/backends/status_bar_state.py +240 -0
  105. opentrons/hardware_control/backends/subsystem_manager.py +431 -0
  106. opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
  107. opentrons/hardware_control/backends/types.py +14 -0
  108. opentrons/hardware_control/constants.py +6 -0
  109. opentrons/hardware_control/dev_types.py +125 -0
  110. opentrons/hardware_control/emulation/__init__.py +0 -0
  111. opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
  112. opentrons/hardware_control/emulation/app.py +56 -0
  113. opentrons/hardware_control/emulation/connection_handler.py +38 -0
  114. opentrons/hardware_control/emulation/heater_shaker.py +150 -0
  115. opentrons/hardware_control/emulation/magdeck.py +60 -0
  116. opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
  117. opentrons/hardware_control/emulation/module_server/client.py +78 -0
  118. opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
  119. opentrons/hardware_control/emulation/module_server/models.py +31 -0
  120. opentrons/hardware_control/emulation/module_server/server.py +110 -0
  121. opentrons/hardware_control/emulation/parser.py +74 -0
  122. opentrons/hardware_control/emulation/proxy.py +241 -0
  123. opentrons/hardware_control/emulation/run_emulator.py +68 -0
  124. opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
  125. opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
  126. opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
  127. opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
  128. opentrons/hardware_control/emulation/settings.py +119 -0
  129. opentrons/hardware_control/emulation/simulations.py +133 -0
  130. opentrons/hardware_control/emulation/smoothie.py +192 -0
  131. opentrons/hardware_control/emulation/tempdeck.py +69 -0
  132. opentrons/hardware_control/emulation/thermocycler.py +128 -0
  133. opentrons/hardware_control/emulation/types.py +10 -0
  134. opentrons/hardware_control/emulation/util.py +38 -0
  135. opentrons/hardware_control/errors.py +43 -0
  136. opentrons/hardware_control/execution_manager.py +164 -0
  137. opentrons/hardware_control/instruments/__init__.py +5 -0
  138. opentrons/hardware_control/instruments/instrument_abc.py +39 -0
  139. opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
  140. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
  141. opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
  142. opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
  143. opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
  144. opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
  145. opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
  146. opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
  147. opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
  148. opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
  149. opentrons/hardware_control/module_control.py +332 -0
  150. opentrons/hardware_control/modules/__init__.py +69 -0
  151. opentrons/hardware_control/modules/absorbance_reader.py +373 -0
  152. opentrons/hardware_control/modules/errors.py +7 -0
  153. opentrons/hardware_control/modules/flex_stacker.py +948 -0
  154. opentrons/hardware_control/modules/heater_shaker.py +426 -0
  155. opentrons/hardware_control/modules/lid_temp_status.py +35 -0
  156. opentrons/hardware_control/modules/magdeck.py +233 -0
  157. opentrons/hardware_control/modules/mod_abc.py +245 -0
  158. opentrons/hardware_control/modules/module_calibration.py +93 -0
  159. opentrons/hardware_control/modules/plate_temp_status.py +61 -0
  160. opentrons/hardware_control/modules/tempdeck.py +299 -0
  161. opentrons/hardware_control/modules/thermocycler.py +731 -0
  162. opentrons/hardware_control/modules/types.py +417 -0
  163. opentrons/hardware_control/modules/update.py +255 -0
  164. opentrons/hardware_control/modules/utils.py +73 -0
  165. opentrons/hardware_control/motion_utilities.py +318 -0
  166. opentrons/hardware_control/nozzle_manager.py +422 -0
  167. opentrons/hardware_control/ot3_calibration.py +1171 -0
  168. opentrons/hardware_control/ot3api.py +3227 -0
  169. opentrons/hardware_control/pause_manager.py +31 -0
  170. opentrons/hardware_control/poller.py +112 -0
  171. opentrons/hardware_control/protocols/__init__.py +106 -0
  172. opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
  173. opentrons/hardware_control/protocols/calibratable.py +45 -0
  174. opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
  175. opentrons/hardware_control/protocols/configurable.py +48 -0
  176. opentrons/hardware_control/protocols/event_sourcer.py +18 -0
  177. opentrons/hardware_control/protocols/execution_controllable.py +33 -0
  178. opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
  179. opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
  180. opentrons/hardware_control/protocols/gripper_controller.py +55 -0
  181. opentrons/hardware_control/protocols/hardware_manager.py +51 -0
  182. opentrons/hardware_control/protocols/identifiable.py +16 -0
  183. opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
  184. opentrons/hardware_control/protocols/liquid_handler.py +266 -0
  185. opentrons/hardware_control/protocols/module_provider.py +16 -0
  186. opentrons/hardware_control/protocols/motion_controller.py +243 -0
  187. opentrons/hardware_control/protocols/position_estimator.py +45 -0
  188. opentrons/hardware_control/protocols/simulatable.py +10 -0
  189. opentrons/hardware_control/protocols/stoppable.py +9 -0
  190. opentrons/hardware_control/protocols/types.py +27 -0
  191. opentrons/hardware_control/robot_calibration.py +224 -0
  192. opentrons/hardware_control/scripts/README.md +28 -0
  193. opentrons/hardware_control/scripts/__init__.py +1 -0
  194. opentrons/hardware_control/scripts/gripper_control.py +208 -0
  195. opentrons/hardware_control/scripts/ot3gripper +7 -0
  196. opentrons/hardware_control/scripts/ot3repl +7 -0
  197. opentrons/hardware_control/scripts/repl.py +187 -0
  198. opentrons/hardware_control/scripts/tc_control.py +97 -0
  199. opentrons/hardware_control/scripts/update_module_fw.py +274 -0
  200. opentrons/hardware_control/simulator_setup.py +260 -0
  201. opentrons/hardware_control/thread_manager.py +431 -0
  202. opentrons/hardware_control/threaded_async_lock.py +97 -0
  203. opentrons/hardware_control/types.py +792 -0
  204. opentrons/hardware_control/util.py +234 -0
  205. opentrons/legacy_broker.py +53 -0
  206. opentrons/legacy_commands/__init__.py +1 -0
  207. opentrons/legacy_commands/commands.py +483 -0
  208. opentrons/legacy_commands/helpers.py +153 -0
  209. opentrons/legacy_commands/module_commands.py +276 -0
  210. opentrons/legacy_commands/protocol_commands.py +54 -0
  211. opentrons/legacy_commands/publisher.py +155 -0
  212. opentrons/legacy_commands/robot_commands.py +51 -0
  213. opentrons/legacy_commands/types.py +1186 -0
  214. opentrons/motion_planning/__init__.py +32 -0
  215. opentrons/motion_planning/adjacent_slots_getters.py +168 -0
  216. opentrons/motion_planning/deck_conflict.py +501 -0
  217. opentrons/motion_planning/errors.py +35 -0
  218. opentrons/motion_planning/types.py +42 -0
  219. opentrons/motion_planning/waypoints.py +218 -0
  220. opentrons/ordered_set.py +138 -0
  221. opentrons/protocol_api/__init__.py +105 -0
  222. opentrons/protocol_api/_liquid.py +157 -0
  223. opentrons/protocol_api/_liquid_properties.py +814 -0
  224. opentrons/protocol_api/_nozzle_layout.py +31 -0
  225. opentrons/protocol_api/_parameter_context.py +300 -0
  226. opentrons/protocol_api/_parameters.py +31 -0
  227. opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
  228. opentrons/protocol_api/_types.py +43 -0
  229. opentrons/protocol_api/config.py +23 -0
  230. opentrons/protocol_api/core/__init__.py +23 -0
  231. opentrons/protocol_api/core/common.py +33 -0
  232. opentrons/protocol_api/core/core_map.py +74 -0
  233. opentrons/protocol_api/core/engine/__init__.py +22 -0
  234. opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
  235. opentrons/protocol_api/core/engine/deck_conflict.py +400 -0
  236. opentrons/protocol_api/core/engine/exceptions.py +19 -0
  237. opentrons/protocol_api/core/engine/instrument.py +2391 -0
  238. opentrons/protocol_api/core/engine/labware.py +238 -0
  239. opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
  240. opentrons/protocol_api/core/engine/module_core.py +1027 -0
  241. opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
  242. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
  243. opentrons/protocol_api/core/engine/point_calculations.py +64 -0
  244. opentrons/protocol_api/core/engine/protocol.py +1153 -0
  245. opentrons/protocol_api/core/engine/robot.py +139 -0
  246. opentrons/protocol_api/core/engine/stringify.py +74 -0
  247. opentrons/protocol_api/core/engine/transfer_components_executor.py +1006 -0
  248. opentrons/protocol_api/core/engine/well.py +241 -0
  249. opentrons/protocol_api/core/instrument.py +459 -0
  250. opentrons/protocol_api/core/labware.py +151 -0
  251. opentrons/protocol_api/core/legacy/__init__.py +11 -0
  252. opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
  253. opentrons/protocol_api/core/legacy/deck.py +369 -0
  254. opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
  255. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
  256. opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
  257. opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
  258. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
  259. opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
  260. opentrons/protocol_api/core/legacy/load_info.py +67 -0
  261. opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
  262. opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
  263. opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
  264. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
  265. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
  266. opentrons/protocol_api/core/module.py +484 -0
  267. opentrons/protocol_api/core/protocol.py +311 -0
  268. opentrons/protocol_api/core/robot.py +51 -0
  269. opentrons/protocol_api/core/well.py +116 -0
  270. opentrons/protocol_api/core/well_grid.py +45 -0
  271. opentrons/protocol_api/create_protocol_context.py +177 -0
  272. opentrons/protocol_api/deck.py +223 -0
  273. opentrons/protocol_api/disposal_locations.py +244 -0
  274. opentrons/protocol_api/instrument_context.py +3272 -0
  275. opentrons/protocol_api/labware.py +1579 -0
  276. opentrons/protocol_api/module_contexts.py +1447 -0
  277. opentrons/protocol_api/module_validation_and_errors.py +61 -0
  278. opentrons/protocol_api/protocol_context.py +1688 -0
  279. opentrons/protocol_api/robot_context.py +303 -0
  280. opentrons/protocol_api/validation.py +761 -0
  281. opentrons/protocol_engine/__init__.py +155 -0
  282. opentrons/protocol_engine/actions/__init__.py +65 -0
  283. opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
  284. opentrons/protocol_engine/actions/action_handler.py +13 -0
  285. opentrons/protocol_engine/actions/actions.py +302 -0
  286. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  287. opentrons/protocol_engine/clients/__init__.py +5 -0
  288. opentrons/protocol_engine/clients/sync_client.py +174 -0
  289. opentrons/protocol_engine/clients/transports.py +197 -0
  290. opentrons/protocol_engine/commands/__init__.py +757 -0
  291. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
  292. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
  293. opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
  294. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
  295. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
  296. opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
  297. opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
  298. opentrons/protocol_engine/commands/aspirate.py +244 -0
  299. opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
  300. opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
  301. opentrons/protocol_engine/commands/blow_out.py +146 -0
  302. opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
  303. opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
  304. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
  305. opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
  306. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
  307. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
  308. opentrons/protocol_engine/commands/command.py +308 -0
  309. opentrons/protocol_engine/commands/command_unions.py +974 -0
  310. opentrons/protocol_engine/commands/comment.py +57 -0
  311. opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
  312. opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
  313. opentrons/protocol_engine/commands/custom.py +67 -0
  314. opentrons/protocol_engine/commands/dispense.py +194 -0
  315. opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
  316. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  317. opentrons/protocol_engine/commands/drop_tip.py +232 -0
  318. opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
  319. opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
  320. opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
  321. opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
  322. opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
  323. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
  324. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
  325. opentrons/protocol_engine/commands/flex_stacker/store.py +339 -0
  326. opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
  327. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  328. opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
  329. opentrons/protocol_engine/commands/hash_command_params.py +38 -0
  330. opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
  331. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
  332. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
  333. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
  334. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
  335. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
  336. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
  337. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
  338. opentrons/protocol_engine/commands/home.py +100 -0
  339. opentrons/protocol_engine/commands/identify_module.py +86 -0
  340. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  341. opentrons/protocol_engine/commands/liquid_probe.py +464 -0
  342. opentrons/protocol_engine/commands/load_labware.py +210 -0
  343. opentrons/protocol_engine/commands/load_lid.py +154 -0
  344. opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
  345. opentrons/protocol_engine/commands/load_liquid.py +95 -0
  346. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  347. opentrons/protocol_engine/commands/load_module.py +223 -0
  348. opentrons/protocol_engine/commands/load_pipette.py +167 -0
  349. opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
  350. opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
  351. opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
  352. opentrons/protocol_engine/commands/move_labware.py +546 -0
  353. opentrons/protocol_engine/commands/move_relative.py +102 -0
  354. opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
  355. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
  356. opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
  357. opentrons/protocol_engine/commands/move_to_well.py +119 -0
  358. opentrons/protocol_engine/commands/movement_common.py +338 -0
  359. opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
  360. opentrons/protocol_engine/commands/pipetting_common.py +443 -0
  361. opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
  362. opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
  363. opentrons/protocol_engine/commands/reload_labware.py +90 -0
  364. opentrons/protocol_engine/commands/retract_axis.py +75 -0
  365. opentrons/protocol_engine/commands/robot/__init__.py +70 -0
  366. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
  367. opentrons/protocol_engine/commands/robot/common.py +18 -0
  368. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  369. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  370. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  371. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
  372. opentrons/protocol_engine/commands/save_position.py +109 -0
  373. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
  374. opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
  375. opentrons/protocol_engine/commands/set_status_bar.py +89 -0
  376. opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
  377. opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
  378. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
  379. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
  380. opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
  381. opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
  382. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
  383. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
  384. opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
  385. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
  386. opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
  387. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
  388. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
  389. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
  390. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
  391. opentrons/protocol_engine/commands/touch_tip.py +189 -0
  392. opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
  393. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
  394. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
  395. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
  396. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  397. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
  398. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
  399. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
  400. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
  401. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  402. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
  403. opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
  404. opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
  405. opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
  406. opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
  407. opentrons/protocol_engine/create_protocol_engine.py +193 -0
  408. opentrons/protocol_engine/engine_support.py +28 -0
  409. opentrons/protocol_engine/error_recovery_policy.py +81 -0
  410. opentrons/protocol_engine/errors/__init__.py +191 -0
  411. opentrons/protocol_engine/errors/error_occurrence.py +182 -0
  412. opentrons/protocol_engine/errors/exceptions.py +1308 -0
  413. opentrons/protocol_engine/execution/__init__.py +50 -0
  414. opentrons/protocol_engine/execution/command_executor.py +216 -0
  415. opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
  416. opentrons/protocol_engine/execution/door_watcher.py +119 -0
  417. opentrons/protocol_engine/execution/equipment.py +819 -0
  418. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  419. opentrons/protocol_engine/execution/gantry_mover.py +686 -0
  420. opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
  421. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
  422. opentrons/protocol_engine/execution/labware_movement.py +297 -0
  423. opentrons/protocol_engine/execution/movement.py +350 -0
  424. opentrons/protocol_engine/execution/pipetting.py +607 -0
  425. opentrons/protocol_engine/execution/queue_worker.py +86 -0
  426. opentrons/protocol_engine/execution/rail_lights.py +25 -0
  427. opentrons/protocol_engine/execution/run_control.py +33 -0
  428. opentrons/protocol_engine/execution/status_bar.py +34 -0
  429. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
  430. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
  431. opentrons/protocol_engine/execution/tip_handler.py +550 -0
  432. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  433. opentrons/protocol_engine/notes/__init__.py +17 -0
  434. opentrons/protocol_engine/notes/notes.py +59 -0
  435. opentrons/protocol_engine/plugins.py +104 -0
  436. opentrons/protocol_engine/protocol_engine.py +683 -0
  437. opentrons/protocol_engine/resources/__init__.py +26 -0
  438. opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
  439. opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
  440. opentrons/protocol_engine/resources/file_provider.py +161 -0
  441. opentrons/protocol_engine/resources/fixture_validation.py +68 -0
  442. opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
  443. opentrons/protocol_engine/resources/labware_validation.py +73 -0
  444. opentrons/protocol_engine/resources/model_utils.py +32 -0
  445. opentrons/protocol_engine/resources/module_data_provider.py +44 -0
  446. opentrons/protocol_engine/resources/ot3_validation.py +21 -0
  447. opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
  448. opentrons/protocol_engine/slot_standardization.py +128 -0
  449. opentrons/protocol_engine/state/__init__.py +1 -0
  450. opentrons/protocol_engine/state/_abstract_store.py +27 -0
  451. opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
  452. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  453. opentrons/protocol_engine/state/_move_types.py +83 -0
  454. opentrons/protocol_engine/state/_well_math.py +193 -0
  455. opentrons/protocol_engine/state/addressable_areas.py +699 -0
  456. opentrons/protocol_engine/state/command_history.py +309 -0
  457. opentrons/protocol_engine/state/commands.py +1164 -0
  458. opentrons/protocol_engine/state/config.py +39 -0
  459. opentrons/protocol_engine/state/files.py +57 -0
  460. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  461. opentrons/protocol_engine/state/geometry.py +2408 -0
  462. opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
  463. opentrons/protocol_engine/state/labware.py +1432 -0
  464. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  465. opentrons/protocol_engine/state/liquids.py +73 -0
  466. opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
  467. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
  468. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
  469. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
  470. opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
  471. opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
  472. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
  473. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
  474. opentrons/protocol_engine/state/modules.py +1515 -0
  475. opentrons/protocol_engine/state/motion.py +373 -0
  476. opentrons/protocol_engine/state/pipettes.py +905 -0
  477. opentrons/protocol_engine/state/state.py +421 -0
  478. opentrons/protocol_engine/state/state_summary.py +36 -0
  479. opentrons/protocol_engine/state/tips.py +420 -0
  480. opentrons/protocol_engine/state/update_types.py +904 -0
  481. opentrons/protocol_engine/state/wells.py +290 -0
  482. opentrons/protocol_engine/types/__init__.py +310 -0
  483. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  484. opentrons/protocol_engine/types/command_annotations.py +53 -0
  485. opentrons/protocol_engine/types/deck_configuration.py +81 -0
  486. opentrons/protocol_engine/types/execution.py +96 -0
  487. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  488. opentrons/protocol_engine/types/instrument.py +47 -0
  489. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  490. opentrons/protocol_engine/types/labware.py +131 -0
  491. opentrons/protocol_engine/types/labware_movement.py +22 -0
  492. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  493. opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
  494. opentrons/protocol_engine/types/liquid.py +40 -0
  495. opentrons/protocol_engine/types/liquid_class.py +59 -0
  496. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  497. opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
  498. opentrons/protocol_engine/types/location.py +194 -0
  499. opentrons/protocol_engine/types/module.py +310 -0
  500. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  501. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  502. opentrons/protocol_engine/types/tip.py +18 -0
  503. opentrons/protocol_engine/types/util.py +21 -0
  504. opentrons/protocol_engine/types/well_position.py +124 -0
  505. opentrons/protocol_reader/__init__.py +37 -0
  506. opentrons/protocol_reader/extract_labware_definitions.py +66 -0
  507. opentrons/protocol_reader/file_format_validator.py +152 -0
  508. opentrons/protocol_reader/file_hasher.py +27 -0
  509. opentrons/protocol_reader/file_identifier.py +284 -0
  510. opentrons/protocol_reader/file_reader_writer.py +90 -0
  511. opentrons/protocol_reader/input_file.py +16 -0
  512. opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
  513. opentrons/protocol_reader/protocol_reader.py +188 -0
  514. opentrons/protocol_reader/protocol_source.py +124 -0
  515. opentrons/protocol_reader/role_analyzer.py +86 -0
  516. opentrons/protocol_runner/__init__.py +26 -0
  517. opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
  518. opentrons/protocol_runner/json_file_reader.py +55 -0
  519. opentrons/protocol_runner/json_translator.py +314 -0
  520. opentrons/protocol_runner/legacy_command_mapper.py +852 -0
  521. opentrons/protocol_runner/legacy_context_plugin.py +116 -0
  522. opentrons/protocol_runner/protocol_runner.py +530 -0
  523. opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
  524. opentrons/protocol_runner/run_orchestrator.py +496 -0
  525. opentrons/protocol_runner/task_queue.py +95 -0
  526. opentrons/protocols/__init__.py +6 -0
  527. opentrons/protocols/advanced_control/__init__.py +0 -0
  528. opentrons/protocols/advanced_control/common.py +38 -0
  529. opentrons/protocols/advanced_control/mix.py +60 -0
  530. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  531. opentrons/protocols/advanced_control/transfers/common.py +180 -0
  532. opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
  533. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
  534. opentrons/protocols/api_support/__init__.py +0 -0
  535. opentrons/protocols/api_support/constants.py +8 -0
  536. opentrons/protocols/api_support/deck_type.py +110 -0
  537. opentrons/protocols/api_support/definitions.py +18 -0
  538. opentrons/protocols/api_support/instrument.py +151 -0
  539. opentrons/protocols/api_support/labware_like.py +233 -0
  540. opentrons/protocols/api_support/tip_tracker.py +175 -0
  541. opentrons/protocols/api_support/types.py +32 -0
  542. opentrons/protocols/api_support/util.py +403 -0
  543. opentrons/protocols/bundle.py +89 -0
  544. opentrons/protocols/duration/__init__.py +4 -0
  545. opentrons/protocols/duration/errors.py +5 -0
  546. opentrons/protocols/duration/estimator.py +628 -0
  547. opentrons/protocols/execution/__init__.py +0 -0
  548. opentrons/protocols/execution/dev_types.py +181 -0
  549. opentrons/protocols/execution/errors.py +40 -0
  550. opentrons/protocols/execution/execute.py +84 -0
  551. opentrons/protocols/execution/execute_json_v3.py +275 -0
  552. opentrons/protocols/execution/execute_json_v4.py +359 -0
  553. opentrons/protocols/execution/execute_json_v5.py +28 -0
  554. opentrons/protocols/execution/execute_python.py +169 -0
  555. opentrons/protocols/execution/json_dispatchers.py +87 -0
  556. opentrons/protocols/execution/types.py +7 -0
  557. opentrons/protocols/geometry/__init__.py +0 -0
  558. opentrons/protocols/geometry/planning.py +297 -0
  559. opentrons/protocols/labware.py +312 -0
  560. opentrons/protocols/models/__init__.py +0 -0
  561. opentrons/protocols/models/json_protocol.py +679 -0
  562. opentrons/protocols/parameters/__init__.py +0 -0
  563. opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
  564. opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
  565. opentrons/protocols/parameters/exceptions.py +34 -0
  566. opentrons/protocols/parameters/parameter_definition.py +272 -0
  567. opentrons/protocols/parameters/types.py +17 -0
  568. opentrons/protocols/parameters/validation.py +267 -0
  569. opentrons/protocols/parse.py +671 -0
  570. opentrons/protocols/types.py +159 -0
  571. opentrons/py.typed +0 -0
  572. opentrons/resources/scripts/lpc21isp +0 -0
  573. opentrons/resources/smoothie-edge-8414642.hex +23010 -0
  574. opentrons/simulate.py +1065 -0
  575. opentrons/system/__init__.py +6 -0
  576. opentrons/system/camera.py +51 -0
  577. opentrons/system/log_control.py +59 -0
  578. opentrons/system/nmcli.py +856 -0
  579. opentrons/system/resin.py +24 -0
  580. opentrons/system/smoothie_update.py +15 -0
  581. opentrons/system/wifi.py +204 -0
  582. opentrons/tools/__init__.py +0 -0
  583. opentrons/tools/args_handler.py +22 -0
  584. opentrons/tools/write_pipette_memory.py +157 -0
  585. opentrons/types.py +618 -0
  586. opentrons/util/__init__.py +1 -0
  587. opentrons/util/async_helpers.py +166 -0
  588. opentrons/util/broker.py +84 -0
  589. opentrons/util/change_notifier.py +47 -0
  590. opentrons/util/entrypoint_util.py +278 -0
  591. opentrons/util/get_union_elements.py +26 -0
  592. opentrons/util/helpers.py +6 -0
  593. opentrons/util/linal.py +178 -0
  594. opentrons/util/logging_config.py +265 -0
  595. opentrons/util/logging_queue_handler.py +61 -0
  596. opentrons/util/performance_helpers.py +157 -0
  597. opentrons-8.6.0.dist-info/METADATA +37 -0
  598. opentrons-8.6.0.dist-info/RECORD +601 -0
  599. opentrons-8.6.0.dist-info/WHEEL +4 -0
  600. opentrons-8.6.0.dist-info/entry_points.txt +3 -0
  601. opentrons-8.6.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,548 @@
1
+ """Helper functions for liquid-level related calculations inside a given frustum."""
2
+ from typing import List, Tuple
3
+ from numpy import pi, iscomplex, roots, real
4
+ from math import isclose
5
+
6
+ from ..errors.exceptions import InvalidLiquidHeightFound, InvalidUserDefinedVolumesError
7
+
8
+ from opentrons.protocol_engine.types.liquid_level_detection import (
9
+ LiquidTrackingType,
10
+ SimulatedProbeResult,
11
+ )
12
+ from opentrons_shared_data.labware.labware_definition import (
13
+ InnerWellGeometry,
14
+ WellSegment,
15
+ SphericalSegment,
16
+ ConicalFrustum,
17
+ CuboidalFrustum,
18
+ SquaredConeSegment,
19
+ UserDefinedVolumes,
20
+ )
21
+
22
+
23
+ def _reject_unacceptable_heights(
24
+ potential_heights: List[float], max_height: float
25
+ ) -> float:
26
+ """Reject any solutions to a polynomial equation that cannot be the height of a frustum."""
27
+ valid_heights: List[float] = []
28
+ for root in potential_heights:
29
+ # reject any heights that are negative or greater than the max height
30
+ if not iscomplex(root):
31
+ # take only the real component of the root and round to 4 decimal places
32
+ rounded_root = round(real(root), 4)
33
+ if (rounded_root <= max_height) and (rounded_root >= 0):
34
+ if not any([isclose(rounded_root, height) for height in valid_heights]):
35
+ valid_heights.append(rounded_root)
36
+ if len(valid_heights) != 1:
37
+ raise InvalidLiquidHeightFound(
38
+ message="Unable to estimate valid liquid height from volume."
39
+ )
40
+ return valid_heights[0]
41
+
42
+
43
+ def _cross_section_area_circular(diameter: float) -> float:
44
+ """Get the area of a circular cross-section."""
45
+ radius = diameter / 2
46
+ return pi * (radius**2)
47
+
48
+
49
+ def _cross_section_area_rectangular(x_dimension: float, y_dimension: float) -> float:
50
+ """Get the area of a rectangular cross-section."""
51
+ return x_dimension * y_dimension
52
+
53
+
54
+ def _rectangular_frustum_polynomial_roots(
55
+ bottom_length: float,
56
+ bottom_width: float,
57
+ top_length: float,
58
+ top_width: float,
59
+ total_frustum_height: float,
60
+ ) -> Tuple[float, float, float]:
61
+ """Polynomial representation of the volume of a rectangular frustum."""
62
+ # roots of the polynomial with shape ax^3 + bx^2 + cx
63
+ a = (
64
+ (top_length - bottom_length)
65
+ * (top_width - bottom_width)
66
+ / (3 * total_frustum_height**2)
67
+ )
68
+ b = (
69
+ (bottom_length * (top_width - bottom_width))
70
+ + (bottom_width * (top_length - bottom_length))
71
+ ) / (2 * total_frustum_height)
72
+ c = bottom_length * bottom_width
73
+ return a, b, c
74
+
75
+
76
+ def _circular_frustum_polynomial_roots(
77
+ bottom_radius: float,
78
+ top_radius: float,
79
+ total_frustum_height: float,
80
+ ) -> Tuple[float, float, float]:
81
+ """Polynomial representation of the volume of a circular frustum."""
82
+ # roots of the polynomial with shape ax^3 + bx^2 + cx
83
+ a = pi * ((top_radius - bottom_radius) ** 2) / (3 * total_frustum_height**2)
84
+ b = pi * bottom_radius * (top_radius - bottom_radius) / total_frustum_height
85
+ c = pi * bottom_radius**2
86
+ return a, b, c
87
+
88
+
89
+ def _volume_from_height_circular(
90
+ target_height: float, segment: ConicalFrustum
91
+ ) -> float:
92
+ return segment.volume_from_height_circular(
93
+ top_radius=segment.topDiameter / 2,
94
+ bottom_radius=segment.bottomDiameter / 2,
95
+ target_height=target_height,
96
+ total_height=segment.topHeight - segment.bottomHeight,
97
+ )
98
+
99
+
100
+ def _volume_from_height_rectangular(
101
+ target_height: float,
102
+ total_frustum_height: float,
103
+ bottom_length: float,
104
+ bottom_width: float,
105
+ top_length: float,
106
+ top_width: float,
107
+ ) -> float:
108
+ """Find the volume given a height within a rectangular frustum."""
109
+ a, b, c = _rectangular_frustum_polynomial_roots(
110
+ bottom_length=bottom_length,
111
+ bottom_width=bottom_width,
112
+ top_length=top_length,
113
+ top_width=top_width,
114
+ total_frustum_height=total_frustum_height,
115
+ )
116
+ volume = a * (target_height**3) + b * (target_height**2) + c * target_height
117
+ return volume
118
+
119
+
120
+ def _volume_from_height_spherical(
121
+ target_height: float,
122
+ radius_of_curvature: float,
123
+ ) -> float:
124
+ """Find the volume given a height within a spherical frustum."""
125
+ volume = (
126
+ (1 / 3) * pi * (target_height**2) * (3 * radius_of_curvature - target_height)
127
+ )
128
+ return volume
129
+
130
+
131
+ def _volume_from_height_squared_cone(
132
+ target_height: float, segment: SquaredConeSegment
133
+ ) -> float:
134
+ """Find the volume given a height within a squared cone segment."""
135
+ heights = segment.height_to_volume_table.keys()
136
+ best_fit_height = min(heights, key=lambda x: abs(x - target_height))
137
+ return segment.height_to_volume_table[best_fit_height]
138
+
139
+
140
+ def _height_from_volume_circular(
141
+ target_volume: float, segment: ConicalFrustum
142
+ ) -> float:
143
+ """Find the height given a volume within a squared cone segment."""
144
+ return segment.height_from_volume_search(target_volume)
145
+
146
+
147
+ def _height_from_volume_rectangular(
148
+ volume: float,
149
+ total_frustum_height: float,
150
+ bottom_length: float,
151
+ bottom_width: float,
152
+ top_length: float,
153
+ top_width: float,
154
+ ) -> float:
155
+ """Find the height given a volume within a rectangular frustum."""
156
+ a, b, c = _rectangular_frustum_polynomial_roots(
157
+ bottom_length=bottom_length,
158
+ bottom_width=bottom_width,
159
+ top_length=top_length,
160
+ top_width=top_width,
161
+ total_frustum_height=total_frustum_height,
162
+ )
163
+ d = volume * -1
164
+ x_intercept_roots = (a, b, c, d)
165
+
166
+ height_from_volume_roots = roots(x_intercept_roots)
167
+ height = _reject_unacceptable_heights(
168
+ potential_heights=list(height_from_volume_roots),
169
+ max_height=total_frustum_height,
170
+ )
171
+ return height
172
+
173
+
174
+ def _height_from_volume_spherical(
175
+ volume: float,
176
+ radius_of_curvature: float,
177
+ total_frustum_height: float,
178
+ ) -> float:
179
+ """Find the height given a volume within a spherical frustum."""
180
+ a = -1 * pi / 3
181
+ b = pi * radius_of_curvature
182
+ c = 0.0
183
+ d = volume * -1
184
+ x_intercept_roots = (a, b, c, d)
185
+
186
+ height_from_volume_roots = roots(x_intercept_roots)
187
+ height = _reject_unacceptable_heights(
188
+ potential_heights=list(height_from_volume_roots),
189
+ max_height=total_frustum_height,
190
+ )
191
+ return height
192
+
193
+
194
+ def _height_from_volume_squared_cone(
195
+ target_volume: float, segment: SquaredConeSegment
196
+ ) -> float:
197
+ """Find the height given a volume within a squared cone segment."""
198
+ volumes = segment.volume_to_height_table.keys()
199
+ best_fit_volume = min(volumes, key=lambda x: abs(x - target_volume))
200
+ return segment.volume_to_height_table[best_fit_volume]
201
+
202
+
203
+ def _get_segment_capacity(segment: WellSegment) -> float:
204
+ section_height = segment.topHeight - segment.bottomHeight
205
+ match segment:
206
+ case SphericalSegment():
207
+ return (
208
+ _volume_from_height_spherical(
209
+ target_height=segment.topHeight,
210
+ radius_of_curvature=segment.radiusOfCurvature,
211
+ )
212
+ * segment.count
213
+ )
214
+ case CuboidalFrustum():
215
+ return (
216
+ _volume_from_height_rectangular(
217
+ target_height=section_height,
218
+ bottom_length=segment.bottomYDimension,
219
+ bottom_width=segment.bottomXDimension,
220
+ top_length=segment.topYDimension,
221
+ top_width=segment.topXDimension,
222
+ total_frustum_height=section_height,
223
+ )
224
+ * segment.count
225
+ )
226
+ case ConicalFrustum():
227
+ return (
228
+ _volume_from_height_circular(
229
+ target_height=section_height,
230
+ segment=segment,
231
+ )
232
+ * segment.count
233
+ )
234
+ case SquaredConeSegment():
235
+ return (
236
+ _volume_from_height_squared_cone(section_height, segment)
237
+ * segment.count
238
+ )
239
+ case _:
240
+ # TODO: implement volume calculations for truncated circular and rounded rectangular segments
241
+ raise NotImplementedError(
242
+ f"volume calculation for shape: {segment.shape} not yet implemented."
243
+ )
244
+
245
+
246
+ def get_well_volumetric_capacity(
247
+ well_geometry: InnerWellGeometry,
248
+ ) -> List[Tuple[float, float]]:
249
+ """Return the volumetric capacity of a well as a list of pairs relating segment heights to volumes."""
250
+ # [(top_height_0, section_0_volume), (top_height_1, section_1_volume), ...]
251
+ well_volume = []
252
+
253
+ # get the well segments sorted in ascending order
254
+ sorted_well = sorted(well_geometry.sections, key=lambda section: section.topHeight)
255
+
256
+ for segment in sorted_well:
257
+ section_volume = _get_segment_capacity(segment)
258
+ well_volume.append((segment.topHeight, section_volume))
259
+ return well_volume
260
+
261
+
262
+ def height_at_volume_within_section(
263
+ section: WellSegment,
264
+ target_volume_relative: float,
265
+ section_height: float,
266
+ ) -> float:
267
+ """Calculate a height within a bounded section according to geometry."""
268
+ target_volume_relative = target_volume_relative / section.count
269
+ match section:
270
+ case SphericalSegment():
271
+ return _height_from_volume_spherical(
272
+ volume=target_volume_relative,
273
+ total_frustum_height=section_height,
274
+ radius_of_curvature=section.radiusOfCurvature,
275
+ )
276
+ case ConicalFrustum():
277
+ return _height_from_volume_circular(target_volume_relative, section)
278
+ case CuboidalFrustum():
279
+ return _height_from_volume_rectangular(
280
+ volume=target_volume_relative,
281
+ total_frustum_height=section_height,
282
+ bottom_width=section.bottomXDimension,
283
+ bottom_length=section.bottomYDimension,
284
+ top_width=section.topXDimension,
285
+ top_length=section.topYDimension,
286
+ )
287
+ case SquaredConeSegment():
288
+ return _height_from_volume_squared_cone(target_volume_relative, section)
289
+ case _:
290
+ raise NotImplementedError(
291
+ "Height from volume calculation not yet implemented for this well shape."
292
+ )
293
+
294
+
295
+ def volume_at_height_within_section(
296
+ section: WellSegment,
297
+ target_height_relative: float,
298
+ section_height: float,
299
+ ) -> float:
300
+ """Calculate a volume within a bounded section according to geometry."""
301
+ match section:
302
+ case SphericalSegment():
303
+ return (
304
+ _volume_from_height_spherical(
305
+ target_height=target_height_relative,
306
+ radius_of_curvature=section.radiusOfCurvature,
307
+ )
308
+ * section.count
309
+ )
310
+ case ConicalFrustum():
311
+ return (
312
+ _volume_from_height_circular(
313
+ target_height=target_height_relative, segment=section
314
+ )
315
+ * section.count
316
+ )
317
+ case CuboidalFrustum():
318
+ return (
319
+ _volume_from_height_rectangular(
320
+ target_height=target_height_relative,
321
+ total_frustum_height=section_height,
322
+ bottom_width=section.bottomXDimension,
323
+ bottom_length=section.bottomYDimension,
324
+ top_width=section.topXDimension,
325
+ top_length=section.topYDimension,
326
+ )
327
+ * section.count
328
+ )
329
+ case SquaredConeSegment():
330
+ return (
331
+ _volume_from_height_squared_cone(target_height_relative, section)
332
+ * section.count
333
+ )
334
+ case _:
335
+ # TODO(cm): this would be the NEST-96 2uL wells referenced in EXEC-712
336
+ # we need to input the math attached to that issue
337
+ raise NotImplementedError(
338
+ "Height from volume calculation not yet implemented for this well shape."
339
+ )
340
+
341
+
342
+ def _find_volume_in_partial_frustum(
343
+ sorted_well: List[WellSegment],
344
+ target_height: float,
345
+ ) -> float:
346
+ """Look through a sorted list of frusta for a target height, and find the volume at that height."""
347
+ for segment in sorted_well:
348
+ if segment.bottomHeight <= target_height <= segment.topHeight:
349
+ relative_target_height = target_height - segment.bottomHeight
350
+ section_height = segment.topHeight - segment.bottomHeight
351
+ return volume_at_height_within_section(
352
+ section=segment,
353
+ target_height_relative=relative_target_height,
354
+ section_height=section_height,
355
+ )
356
+ # if we've looked through all sections and can't find the target volume, raise an error
357
+ # this code should never be reached- an error should be raised by find_volume_at_well_height
358
+ raise InvalidLiquidHeightFound(
359
+ f"Target height {target_height} mm exceeds the well height."
360
+ )
361
+
362
+
363
+ def _linear_interpolation(
364
+ interpolating_from: List[float], to_interpolate: List[float], target_val: float
365
+ ) -> float:
366
+ if len(interpolating_from) != len(to_interpolate):
367
+ raise InvalidUserDefinedVolumesError(
368
+ "Height and volume data have unequal sizes"
369
+ )
370
+ if not (interpolating_from[0] == to_interpolate[0] == 0.0):
371
+ raise ValueError("linear interpolation datasets must start with 0.0")
372
+
373
+ if target_val == 0.0:
374
+ return 0.0
375
+ for i in range(1, len(interpolating_from)):
376
+ if target_val == interpolating_from[i]:
377
+ return to_interpolate[i]
378
+ if interpolating_from[i - 1] < target_val < interpolating_from[i]:
379
+ proportional_diff = (target_val - interpolating_from[i - 1]) / (
380
+ interpolating_from[i] - interpolating_from[i - 1]
381
+ )
382
+ addend = proportional_diff * (to_interpolate[i] - to_interpolate[i - 1])
383
+ result = to_interpolate[i - 1] + addend
384
+ return result
385
+ raise ValueError("linear interpolation failed")
386
+
387
+
388
+ def find_volume_user_defined_volumes(
389
+ target_height: LiquidTrackingType, well_geometry: UserDefinedVolumes
390
+ ) -> LiquidTrackingType:
391
+ """Return a linear interpolation of volume based on target height."""
392
+ if isinstance(target_height, SimulatedProbeResult):
393
+ return target_height
394
+ sorted_volume_map = sorted(
395
+ well_geometry.heightToVolumeMap, key=lambda section: section.height
396
+ )
397
+ max_height = sorted_volume_map[-1].height
398
+ if target_height < 0 or target_height > max_height:
399
+ raise InvalidLiquidHeightFound(
400
+ f"Invalid target height {target_height} mm; greatest well height in InnerLabwareGeometry is {max_height} mm."
401
+ )
402
+ volumes = [0.0]
403
+ heights = [0.0]
404
+ for pair in sorted_volume_map:
405
+ volumes.append(pair.volume)
406
+ heights.append(pair.height)
407
+
408
+ try:
409
+ return _linear_interpolation(
410
+ interpolating_from=heights, to_interpolate=volumes, target_val=target_height
411
+ )
412
+ except ValueError:
413
+ raise InvalidLiquidHeightFound(
414
+ f"Unable to find volume at target volume {target_height}."
415
+ )
416
+
417
+
418
+ def find_height_user_defined_volumes(
419
+ target_volume: LiquidTrackingType,
420
+ well_geometry: UserDefinedVolumes,
421
+ ) -> LiquidTrackingType:
422
+ """Return a linear interpolation of height based on target volume."""
423
+ if isinstance(target_volume, SimulatedProbeResult):
424
+ return target_volume
425
+ sorted_volume_map = sorted(
426
+ well_geometry.heightToVolumeMap, key=lambda section: section.height
427
+ )
428
+ max_volume = sorted_volume_map[-1].volume
429
+ if target_volume < 0 or target_volume > max_volume:
430
+ raise InvalidLiquidHeightFound(
431
+ f"Invalid target volume {target_volume}µL; greatest well volume in InnerLabwareGeometry is {max_volume}µL."
432
+ )
433
+
434
+ volumes = [0.0]
435
+ heights = [0.0]
436
+ for pair in sorted_volume_map:
437
+ volumes.append(pair.volume)
438
+ heights.append(pair.height)
439
+
440
+ try:
441
+ return _linear_interpolation(
442
+ interpolating_from=volumes, to_interpolate=heights, target_val=target_volume
443
+ )
444
+ except ValueError:
445
+ raise InvalidLiquidHeightFound(
446
+ f"Unable to find volume at target volume {target_volume}."
447
+ )
448
+
449
+
450
+ def find_volume_inner_well_geometry(
451
+ target_height: LiquidTrackingType,
452
+ well_geometry: InnerWellGeometry,
453
+ ) -> LiquidTrackingType:
454
+ """Find the volume within a well, at a known height."""
455
+ # comparisons with SimulatedProbeResult objects aren't meaningful, just return
456
+ if isinstance(target_height, SimulatedProbeResult):
457
+ return target_height
458
+ volumetric_capacity = get_well_volumetric_capacity(well_geometry)
459
+ max_height = volumetric_capacity[-1][0]
460
+ if target_height < 0 or target_height > max_height:
461
+ raise InvalidLiquidHeightFound(
462
+ f"Invalid target height {target_height} mm; max well height is {max_height} mm."
463
+ )
464
+ # volumes in volumetric_capacity are relative to each frustum,
465
+ # so we have to find the volume of all the full sections enclosed
466
+ # beneath the target height
467
+ closed_section_volume = 0.0
468
+ for boundary_height, section_volume in volumetric_capacity:
469
+ if boundary_height > target_height:
470
+ break
471
+ closed_section_volume += section_volume
472
+ # if target height is a boundary cross-section, we already know the volume
473
+ if target_height == boundary_height:
474
+ return closed_section_volume
475
+ # find the section the target height is in and compute the volume
476
+
477
+ sorted_well = sorted(well_geometry.sections, key=lambda section: section.topHeight)
478
+ partial_volume = _find_volume_in_partial_frustum(
479
+ sorted_well=sorted_well,
480
+ target_height=target_height,
481
+ )
482
+ return partial_volume + closed_section_volume
483
+
484
+
485
+ def _find_height_in_partial_frustum(
486
+ sorted_well: List[WellSegment],
487
+ volumetric_capacity: List[Tuple[float, float]],
488
+ target_volume: float,
489
+ ) -> float:
490
+ """Look through a sorted list of frusta for a target volume, and find the height at that volume."""
491
+ bottom_section_volume = 0.0
492
+ if target_volume == 0.0:
493
+ return 0.0
494
+ for section, capacity in zip(sorted_well, volumetric_capacity):
495
+ section_top_height, section_volume = capacity
496
+ if target_volume == section_volume + bottom_section_volume:
497
+ return section_top_height
498
+ if (
499
+ bottom_section_volume
500
+ <= target_volume
501
+ <= (bottom_section_volume + section_volume)
502
+ ):
503
+ relative_target_volume = target_volume - bottom_section_volume
504
+ section_height = section.topHeight - section.bottomHeight
505
+ partial_height = height_at_volume_within_section(
506
+ section=section,
507
+ target_volume_relative=relative_target_volume,
508
+ section_height=section_height,
509
+ )
510
+ return partial_height + section.bottomHeight
511
+ # bottom section volume should always be the volume enclosed in the previously
512
+ # viewed section
513
+ bottom_section_volume += section_volume
514
+
515
+ # if we finish looping through the whole well, bottom_section will be the well's volume
516
+ total_well_volume = bottom_section_volume
517
+ # if we've looked through all sections and can't find the target volume, raise an error
518
+ # also this code should never be reached bc an invalid target volume should be changed
519
+ # by find_height_at_well_volume
520
+ raise InvalidLiquidHeightFound(
521
+ f"Target volume {target_volume}µL exceeds the well volume {total_well_volume}µL."
522
+ )
523
+
524
+
525
+ def find_height_inner_well_geometry(
526
+ target_volume: LiquidTrackingType,
527
+ well_geometry: InnerWellGeometry,
528
+ ) -> LiquidTrackingType:
529
+ """Find the height within a well, at a known volume."""
530
+ # comparisons with SimulatedProbeResult objects aren't meaningful, just
531
+ # return if we have one of those
532
+ if isinstance(target_volume, SimulatedProbeResult):
533
+ return target_volume
534
+
535
+ volumetric_capacity = get_well_volumetric_capacity(well_geometry)
536
+ max_volume = sum(row[1] for row in volumetric_capacity)
537
+
538
+ if target_volume < 0:
539
+ target_volume = 0
540
+ elif target_volume > max_volume:
541
+ target_volume = max_volume
542
+ sorted_well = sorted(well_geometry.sections, key=lambda section: section.topHeight)
543
+ # find the section the target volume is in and compute the height
544
+ return _find_height_in_partial_frustum(
545
+ sorted_well=sorted_well,
546
+ volumetric_capacity=volumetric_capacity,
547
+ target_volume=target_volume,
548
+ )