diff --git a/.github/img/logo.png b/.github/img/logo.png
index e99200c92d..f2decdf850 100644
Binary files a/.github/img/logo.png and b/.github/img/logo.png differ
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1576701947..c54a8320b3 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,6 +1,10 @@
name: Linting
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - main
+ pull_request:
jobs:
lint:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 31b13de04f..588679c00c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,6 +1,10 @@
name: Unit Tests
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - main
+ pull_request:
jobs:
test:
diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml
index e1763b4c7e..56179bca2c 100644
--- a/.github/workflows/typecheck.yml
+++ b/.github/workflows/typecheck.yml
@@ -1,6 +1,10 @@
name: Type checking
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - main
+ pull_request:
jobs:
typecheck:
diff --git a/.gitignore b/.gitignore
index 9c8333dbe5..3297e1de07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,12 @@
*__pycache__
*.DS_store
*.swp
+*.swo
build/pdf-intermediate.md
pyhamilton/LAY-BACKUP
.ipynb_checkpoints
*.egg-info
*.log
build/lib
-venv/
-venv/*
-venv\
+
+myenv
\ No newline at end of file
diff --git a/.pylintrc b/.pylintrc
index 545f187eb4..523deffdf4 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -148,7 +148,8 @@ disable=abstract-method,
zip-builtin-not-iterating,
missing-module-docstring, # added
missing-function-docstring, # added
- C0103 # added, invalid module name
+ C0103, # added, invalid module name
+ too-many-positional-arguments, # backends take many arguments
[REPORTS]
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e2c8781d5e..b5e3e68556 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,8 +5,11 @@
"agrow",
"agrowpumps",
"coro",
+ "decalibrate",
"Defaultable",
+ "Deprecated",
"frontmost",
+ "hepa",
"Inheco",
"iswap",
"jsonify",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 914a3e7cf2..35a6640da3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Merge `height_functions.py` and `volume_functions.py` into `height_volume_functions.py` (https://github.com/PyLabRobot/pylabrobot/pull/200)
- Type checking for `lh.pick_up_tips`, `lh.drop_tips`, `lh.aspirate`, and `lh.dispense` and 96-channel versions.
- `ChatterBoxBackend` outputs are now pretty (https://github.com/PyLabRobot/pylabrobot/pull/208)
+- `liquid_height` now defaults to 0 instead of 1 (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `material_z_thickness` of a `Container` is used in computing its bottom (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- Default `pickup_distance_from_top` in `LiquidHandler.{move_plate,move_lid}` were lowered by 3.33 (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `PlateCarrierSite` can now take `ResourceStack` as a child, as long as the children are `Plate`s (https://github.com/PyLabRobot/pylabrobot/pull/226)
+- `Resource.get_size_{x,y,z}` now return the size of the resource in local space, not absolute space (https://github.com/PyLabRobot/pylabrobot/pull/235)
+- `Resource.center` now returns the center of the resource in local space, not absolute space (https://github.com/PyLabRobot/pylabrobot/pull/235)
+- Rename `ChatterBoxBackend` to `LiquidHandlerChatterboxBackend` (https://github.com/PyLabRobot/pylabrobot/pull/242)
+- Move `LiquidHandlerChatterboxBackend` from `liquid_handling.backends.chatterbox_backend` to `liquid_handling.backends.chatterbox` (https://github.com/PyLabRobot/pylabrobot/pull/242)
+- Changed `pedestal_size_z=-5` to `pedestal_size_z=-4.74` for `PLT_CAR_L5AC_A00` (https://github.com/PyLabRobot/pylabrobot/pull/255)
+- rename `homogenization_` parameters in `STAR` to `mix_` (https://github.com/PyLabRobot/pylabrobot/pull/261)
+- Lids no longer get special treatment when assigned to a ResourceStack. Assign them to a plate directly (https://github.com/PyLabRobot/pylabrobot/pull/267)
### Added
@@ -47,6 +58,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `adapter_hole_size_z` and `plate_z_offset` parameters to `PlateAdapter` (https://github.com/PyLabRobot/pylabrobot/pull/215)
- `wide_high_volume_tip_with_filter` and `HTF_L_WIDE` (https://github.com/PyLabRobot/pylabrobot/pull/222)
- Serialize code cells and closures (https://github.com/PyLabRobot/pylabrobot/pull/220)
+- `Container.get_anchor()` now supports `"cavity_bottom"` as an argument for `z` (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `pylabrobot.resources.utils.query` for basic querying (https://github.com/PyLabRobot/pylabrobot/commit/4a07f6a32a9a33d0370eb9c29015567c98aea002)
+- `HamiltonLiquidHandler.allow_firmware_planning` to allow STAR/Vantage to plan complex liquid handling operations automatically (may break hardware agnosticity unexpectedly) (https://github.com/PyLabRobot/pylabrobot/pull/224)
+- `size_z` and `nesting_z_height` for `Cor_96_wellplate_360ul_Fb_Lid` (https://github.com/PyLabRobot/pylabrobot/pull/226)
+- `NestedTipRack` (https://github.com/PyLabRobot/pylabrobot/pull/228)
+- `HTF_L_ULTRAWIDE`, `ultrawide_high_volume_tip_with_filter` (https://github.com/PyLabRobot/pylabrobot/pull/229/)
+- `get_absolute_size_x`, `get_absolute_size_y`, `get_absolute_size_z` for `Resource` (https://github.com/PyLabRobot/pylabrobot/pull/235)
+- `Cytation5Backend` for plate reading on BioTek Cytation 5 (https://github.com/PyLabRobot/pylabrobot/pull/238)
+- More chatterboxes (https://github.com/PyLabRobot/pylabrobot/pull/242)
+ - `FanChatterboxBackend`
+ - `PlateReaderChatterboxBackend`
+ - `PowderDispenserChatterboxBackend`
+ - `PumpChatterboxBackend`
+ - `PumpArrayChatterboxBackend`
+ - `ScaleChatterboxBackend`
+ - `ShakerChatterboxBackend`
+ - `TemperatureControllerChatterboxBackend`
+- Add fluorescence reading to Cytation 5 (https://github.com/PyLabRobot/pylabrobot/pull/244)
+- Add `F.linear_tip_spot_generator` and `F.randomized_tip_spot_generator` for looping over tip spots, with caching (https://github.com/PyLabRobot/pylabrobot/pull/256)
+- Add `skip_autoload`, `skip_iswap`, and `skip_core96_head` flags to `STAR.setup` (https://github.com/PyLabRobot/pylabrobot/pull/263)
+- Add `skip_autoload`, `skip_iswap`, and `skip_core96_head` flags to `Vantage.setup` (https://github.com/PyLabRobot/pylabrobot/pull/263)
### Deprecated
@@ -54,6 +86,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Passing single values to LiquidHandler `pick_up_tips`, `drop_tips`, `aspirate`, and `dispense` methods. These methods now require a list of values.
- `utils.positions`: `string_to_position`, `string_to_index`, `string_to_indices`, `string_to_pattern`.
- `ThermoScientific_96_DWP_1200ul_Rd` in favor of `Thermo_TS_96_wellplate_1200ul_Rb` (https://github.com/PyLabRobot/pylabrobot/pull/215)
+- `Azenta4titudeFrameStar_96_wellplate_skirted` in favor of `Azenta4titudeFrameStar_96_wellplate_200ul_Vb` (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `Cos_96_DWP_2mL_Vb` in favor of `Cos_96_wellplate_2mL_Vb (https://github.com/PyLabRobot/pylabrobot/pull/205/)`
### Fixed
@@ -62,9 +96,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Fix Opentrons backend resource definitions: Opentrons takes well locations as ccc instead of lfb
- Fix ThermoScientific_96_DWP_1200ul_Rd to ThermoScientific_96_wellplate_1200ul_Rd (https://github.com/PyLabRobot/pylabrobot/pull/183).
- `libusb_package` is now an optional dependency.
+- Plates with a skirt are now correctly lowered when placed on plate carriers with a pedestal (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `minimum_height` in `STAR` and `Vantage` now correctly refer to a `Container`s bottom instead of being a function of liquid height (https://github.com/PyLabRobot/pylabrobot/pull/205/)
+- `aspirate96` and `dispense96` type check
+- fix angles computed by grip directions (https://github.com/PyLabRobot/pylabrobot/pull/234)
+- picking up rotated resources in `STAR` (https://github.com/PyLabRobot/pylabrobot/pull/233)
+- picking up rotated resources in `Vantage` (https://github.com/PyLabRobot/pylabrobot/pull/268)
+- assigning rotated resources to `PlateReader` now have the correct location (https://github.com/PyLabRobot/pylabrobot/pull/233)
+- use local sizes in computing anchor (https://github.com/PyLabRobot/pylabrobot/pull/233)
+- don't raise a blow out air volume error when requesting 0, or when volume tracking is disabled (https://github.com/PyLabRobot/pylabrobot/pull/262)
+- fix get_child_location for resources rotated by 180 degrees (https://github.com/PyLabRobot/pylabrobot/pull/269)
+- volume tracking on channel 1-n (https://github.com/PyLabRobot/pylabrobot/pull/273)
### Removed
- HamiltonDeck.load_from_lay_file
- `hamilton_parse` module and the VENUS labware database parser.
- `PLT_CAR_L4_SHAKER` was removed in favor of `MFX_CAR_L5_base` (https://github.com/PyLabRobot/pylabrobot/pull/188/).
+- `items`, `num_items_x` and `num_items_y` attributes of `ItemizedResource` (https://github.com/PyLabRobot/pylabrobot/pull/231)
+- `report` is no longer a parameter of `PlateReader.read_absorbance` (default is now OD) (https://github.com/PyLabRobot/pylabrobot/pull/238)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c8f263d42f..3d87b081a6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ See the installation instructions [here](docs/installation.md). For contributing
If this is your first time contributing to open source, check out [How to Open Source](./docs/how-to-open-source.md) for an easy introduction.
-It's highly appreciated by the PyLabRobot developers if you communicate what you want to work on, to minimize any duplicate work. You can do this on the [forum](https://forums.pylabrobot.org/c/pylabrobot-development/23).
+It's highly appreciated by the PyLabRobot developers if you communicate what you want to work on, to minimize any duplicate work. You can do this on the [forum](https://discuss.pylabrobot.org).
## Development Tips
@@ -71,4 +71,4 @@ Backends are the primary objects used to communicate with hardware. If you want
## Support
-If you have any questions, feel free to reach out using the [PyLabRobot forum](https://forums.pylabrobot.org).
+If you have any questions, feel free to reach out using the [PyLabRobot forum](https://discuss.pylabrobot.org).
diff --git a/Makefile b/Makefile
index 1fb5123e17..a1381abedc 100644
--- a/Makefile
+++ b/Makefile
@@ -6,11 +6,17 @@ endif
.PHONY: docs lint test
docs:
- sphinx-build -b html docs docs/build/ -j 1 -W
+ sphinx-build -b html docs docs/build/ -j 16 -W
+
+docs-fast:
+ echo "building docs without api for speed"
+ sphinx-build -t no-api -b html docs docs/build/ -j 16 -W
clean-docs:
rm -rf docs/build
rm -rf docs/_autosummary
+ rm -rf docs/jupyter_execute
+ rm -rf docs/user_guide/jupyter_execute
lint:
$(BIN)python -m pylint pylabrobot
diff --git a/README.md b/README.md
index 5de8b69d9c..589c1c1737 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,31 @@
## What is PyLabRobot?
-PyLabRobot is a hardware agnostic, pure Python library for liquid handling robots and other lab automation equipment. Read [the paper]() in Device.
+PyLabRobot is a hardware agnostic, pure Python library for liquid handling robots, plate readers, pumps, scales, heater shakers, and other lab automation equipment. Read [the paper]() in Device.
+
+Advantages over proprietary software:
-### Liquid handling robots
+- **Cross-platform**: PyLabRobot works on Windows, macOS, and Linux. Many other interfaces are Windows-only.
+- **Universal**: PyLabRobot works with any supported liquid handling robot, plate reader, pump, scale, heater shaker, etc. through a single interface.
+- **Fast iteration**: PyLabRobot enables rapid development of protocols using atomic commands run interactively in Jupyter notebooks or the Python REPL. This decreases iteration time from minutes to seconds.
+- **Open-source**: PyLabRobot is open-source and free to use.
+- **Control**: With Python, you have ultimate flexibility to control your lab automation equipment. You can write Turing-complete protocols that include feedback loops.
+- **Modern**: PyLabRobot is built on modern Python 3.8+ features and async/await syntax.
+- **Fast support**: PyLabRobot has [an active community forum](https://discuss.pylabrobot.org) for support and discussion, and most pull requests are merged within a day.
-PyLabRobot provides a layer of general-purpose abstractions over robot functions, with various device drivers for communicating with different kinds of robots. Right now we only have drivers for Hamilton, Tecan and Opentrons liquid handling robots, but we will soon have drivers for many more. The Hamiton and Tecan backends provide an interactive firmware interface that works on Windows, macOS and Linux. The Opentrons driver is based on the [Opentrons HTTP API](https://github.com/rickwierenga/opentrons-python-api). We also provide a browser-based Visualizer which can visualize the state of the deck during a run, and testing backends which do not require access to a robot.
+### Liquid handling robots ([docs](https://docs.pylabrobot.org/basic.html))
+
+PyLabRobot enables the use of any liquid handling robot through a single universal interface, that works on any modern operating system (Windows, macOS, Linux). We currently support Hamilton STAR, Hamilton Vantage, Tecan Freedom EVO, and Opentrons OT-2 robots, but we will soon support many more.
Here's a quick example showing how to move 100uL of liquid from well A1 to A2 using firmware on **Hamilton STAR** (this will work on any operating system!):
@@ -30,40 +44,34 @@ await lh.dispense(lh.deck.get_resource("plate")["A2"], vols=100)
await lh.return_tips()
```
-To run the same procedure on an **Opentrons**, change the following lines:
-
-```diff
-- from pylabrobot.liquid_handling.backends import STAR
-+ from pylabrobot.liquid_handling.backends import OpentronsBackend
-
-- deck = Deck.load_from_json_file("hamilton-layout.json")
-+ deck = Deck.load_from_json_file("opentrons-layout.json")
+To run the same protocol on an **Opentrons**, use the following:
-- lh = LiquidHandler(backend=STAR(), deck=deck)
-+ lh = LiquidHandler(backend=OpentronsBackend(host="x.x.x.x"), deck=deck)
+```python
+from pylabrobot.liquid_handling.backends import OpentronsBackend
+deck = Deck.load_from_json_file("opentrons-layout.json")
+lh = LiquidHandler(backend=OpentronsBackend(host="x.x.x.x"), deck=deck)
```
Or **Tecan** (also works on any operating system!):
-```diff
-- from pylabrobot.liquid_handling.backends import STAR
-+ from pylabrobot.liquid_handling.backends import EVO
+```python
+from pylabrobot.liquid_handling.backends import EVO
+deck = Deck.load_from_json_file("tecan-layout.json")
+lh = LiquidHandler(backend=EVO(), deck=deck)
+```
-- deck = Deck.load_from_json_file("hamilton-layout.json")
-+ deck = Deck.load_from_json_file("tecan-layout.json")
+We also provide a browser-based Visualizer which can visualize the state of the deck during a run, and can be used to develop and test protocols without a physical robot.
-- lh = LiquidHandler(backend=STAR(), deck=deck)
-+ lh = LiquidHandler(backend=EVO(), deck=deck)
-```
+![Visualizer](.github/img/visualizer.png)
-### Plate readers
+### Plate readers ([docs](https://docs.pylabrobot.org/plate_reading.html))
-PyLabRobot also provides a layer of general-purpose abstractions for plate readers, currently with just a driver for the ClarioStar. This driver works on Windows, macOS and Linux. Here's a quick example showing how to read a plate using the ClarioStar:
+Moving a plate to a ClarioStar using a liquid handler, and reading luminescence:
```python
from pylabrobot.plate_reading import PlateReader, ClarioStar
-pr = PlateReader(name="plate reader", backend=ClarioStar())
+pr = PlateReader(name="plate reader", backend=ClarioStar(), size_x=1, size_y=1, size_z=1)
await pr.setup()
# Use in combination with a liquid handler
@@ -73,6 +81,75 @@ lh.move_plate(lh.deck.get_resource("plate"), pr)
data = await pr.read_luminescence()
```
+For Cytation5, use the `Cytation5` backend.
+
+### Centrifuges
+
+Centrifugation at 800g for 60 seconds:
+
+```python
+from pylabrobot.centrifuge import Centrifuge, VSpin
+cf = Centrifuge(name = 'centrifuge', backend = VSpin(bucket_1_position=0), size_x= 1, size_y=1, size_z=1)
+await cf.setup()
+
+await cf.start_spin_cycle(g = 800, duration = 60)
+```
+
+### Pumps ([docs](https://docs.pylabrobot.org/pumps.html))
+
+Pumping at 100 rpm for 30 seconds using a Masterflex pump:
+
+```python
+from pylabrobot.pumps import Pump
+from pylabrobot.pumps.cole_parmer.masterflex import Masterflex
+
+p = Pump(backend=Masterflex())
+await p.setup()
+await p.run_for_duration(speed=100, duration=30)
+```
+
+### Scales ([docs](https://docs.pylabrobot.org/scales.html))
+
+Taking a measurement from a Mettler Toledo scale:
+
+```python
+from pylabrobot.scales import Scale
+from pylabrobot.scales.mettler_toledo import MettlerToledoWXS205SDU
+
+backend = MettlerToledoWXS205SDU(port="/dev/cu.usbserial-110")
+scale = Scale(backend=backend, size_x=0, size_y=0, size_z=0)
+await scale.setup()
+
+weight = await scale.get_weight()
+```
+
+### Heater shakers ([docs](https://docs.pylabrobot.org/heater_shakers.html))
+
+Setting the temperature of a heater shaker to 37°C:
+
+```python
+from pylabrobot.heating_shaking import HeaterShaker
+from pylabrobot.heating_shaking import InhecoThermoShake
+
+backend = InhecoThermoShake()
+hs = HeaterShaker(backend=backend, name="HeaterShaker", size_x=0, size_y=0, size_z=0)
+await hs.setup()
+await hs.set_temperature(37)
+```
+
+### Fans ([docs](https://docs.pylabrobot.org/fans.html))
+
+Running a fan at 100% intensity for one minute:
+
+```python
+from pylabrobot.only_fans import Fan
+from pylabrobot.only_fans import HamiltonHepaFan
+
+fan = Fan(backend=HamiltonHepaFan(), name="my fan")
+await fan.setup()
+await fan.turn_on(intensity=100, duration=60)
+```
+
## Resources
### Documentation
@@ -86,7 +163,7 @@ data = await pr.read_luminescence()
### Support
-- [forums.pylabrobot.org](https://forums.pylabrobot.org) for questions and discussions.
+- [discuss.pylabrobot.org](https://discuss.pylabrobot.org) for questions and discussions.
- [GitHub Issues](https://github.com/pylabrobot/pylabrobot/issues) for bug reports and feature requests.
## Citing
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
index 0b4d039360..50794315ba 100644
Binary files a/docs/_static/logo.png and b/docs/_static/logo.png differ
diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst
index 71903d8364..38b3ba0e5b 100644
--- a/docs/_templates/autosummary/class.rst
+++ b/docs/_templates/autosummary/class.rst
@@ -11,7 +11,7 @@
.. autosummary::
:toctree: .
{% for item in attributes %}
- ~{{ fullname }}.{{ item }}
+ ~{{ objname }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
@@ -23,7 +23,7 @@
.. autosummary::
:toctree: .
{% for item in methods %}
- ~{{ fullname }}.{{ item }}
+ ~{{ objname }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
diff --git a/docs/api/pylabrobot.config.rst b/docs/api/pylabrobot.config.rst
new file mode 100644
index 0000000000..0f6eb23ec4
--- /dev/null
+++ b/docs/api/pylabrobot.config.rst
@@ -0,0 +1,21 @@
+.. currentmodule:: pylabrobot.config
+
+pylabrobot.config package
+=========================
+
+This package contains APIs for configuring PLR. More information in :doc:`configuration tutorial `.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ config.Config
+ io.ConfigReader
+ io.ConfigWriter
+ io.file.FileReader
+ io.file.FileWriter
+ formats.json_config.JsonLoader
+ formats.json_config.JsonSaver
+ formats.ini_config.IniLoader
+ formats.ini_config.IniSaver
diff --git a/docs/pylabrobot.heating_shaking.rst b/docs/api/pylabrobot.heating_shaking.rst
similarity index 75%
rename from docs/pylabrobot.heating_shaking.rst
rename to docs/api/pylabrobot.heating_shaking.rst
index 0a4cd9e14d..1319f3efb6 100644
--- a/docs/pylabrobot.heating_shaking.rst
+++ b/docs/api/pylabrobot.heating_shaking.rst
@@ -10,7 +10,7 @@ This package contains APIs for working with heater shakers.
:nosignatures:
:recursive:
- pylabrobot.heating_shaking.heater_shaker.HeaterShaker
+ heater_shaker.HeaterShaker
Backends
@@ -21,4 +21,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.heating_shaking.inheco.InhecoThermoShake
+ chatterbox.HeaterShakerChatterboxBackend
+ inheco.InhecoThermoShake
diff --git a/docs/pylabrobot.liquid_handling.backends.rst b/docs/api/pylabrobot.liquid_handling.backends.rst
similarity index 50%
rename from docs/pylabrobot.liquid_handling.backends.rst
rename to docs/api/pylabrobot.liquid_handling.backends.rst
index 4f88c8914f..49b896c3fd 100644
--- a/docs/pylabrobot.liquid_handling.backends.rst
+++ b/docs/api/pylabrobot.liquid_handling.backends.rst
@@ -1,4 +1,4 @@
-.. currentmodule:: pylabrobot.liquid_handling
+.. currentmodule:: pylabrobot.liquid_handling
pylabrobot.liquid_handling.backends package
===========================================
@@ -13,9 +13,8 @@ Abstract
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.backends.backend.LiquidHandlerBackend
- pylabrobot.liquid_handling.backends.serializing_backend.SerializingBackend
- pylabrobot.liquid_handling.backends.USBBackend.USBBackend
+ backends.backend.LiquidHandlerBackend
+ backends.serializing_backend.SerializingBackend
Hardware
--------
@@ -25,11 +24,11 @@ Hardware
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.backends.hamilton.base.HamiltonLiquidHandler
- pylabrobot.liquid_handling.backends.hamilton.STAR.STAR
- pylabrobot.liquid_handling.backends.hamilton.vantage.Vantage
- pylabrobot.liquid_handling.backends.opentrons_backend.OpentronsBackend
- pylabrobot.liquid_handling.backends.tecan.EVO.EVO
+ backends.hamilton.base.HamiltonLiquidHandler
+ backends.hamilton.STAR.STAR
+ backends.hamilton.vantage.Vantage
+ backends.opentrons_backend.OpentronsBackend
+ backends.tecan.EVO.EVO
Net
---
@@ -41,8 +40,8 @@ Net backends can be used to communicate with servers that manage liquid handling
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.backends.http.HTTPBackend
- pylabrobot.liquid_handling.backends.websocket.WebSocketBackend
+ backends.http.HTTPBackend
+ backends.websocket.WebSocketBackend
Testing
@@ -53,4 +52,4 @@ Testing
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.backends.chatterbox_backend.ChatterBoxBackend
+ backends.chatterbox.LiquidHandlerChatterboxBackend
diff --git a/docs/pylabrobot.liquid_handling.rst b/docs/api/pylabrobot.liquid_handling.rst
similarity index 81%
rename from docs/pylabrobot.liquid_handling.rst
rename to docs/api/pylabrobot.liquid_handling.rst
index 63fcd1854b..f0e89e83ef 100644
--- a/docs/pylabrobot.liquid_handling.rst
+++ b/docs/api/pylabrobot.liquid_handling.rst
@@ -1,10 +1,10 @@
-.. currentmodule:: pylabrobot.liquid_handling
+.. currentmodule:: pylabrobot.liquid_handling
pylabrobot.liquid_handling package
==================================
This package contains all APIs relevant to liquid handling.
-See :ref:`Basic liquid handling ` for a simple example.
+.. See :doc:`Basic liquid handling ` for a simple example.
Machine control is split into two parts: backends and front ends. Backends are used to control the
machine, and front ends are used to interact with the backend. Front ends are designed to be
@@ -16,7 +16,7 @@ be run on practically all supported hardware.
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.liquid_handler.LiquidHandler
+ liquid_handler.LiquidHandler
Backends
@@ -39,7 +39,7 @@ Operations are the main data holders used to transmit information from the liqui
:nosignatures:
:recursive:
- pylabrobot.liquid_handling.standard
+ standard
Strictness
diff --git a/docs/pylabrobot.liquid_handling.strictness.rst b/docs/api/pylabrobot.liquid_handling.strictness.rst
similarity index 100%
rename from docs/pylabrobot.liquid_handling.strictness.rst
rename to docs/api/pylabrobot.liquid_handling.strictness.rst
diff --git a/docs/pylabrobot.machine.rst b/docs/api/pylabrobot.machine.rst
similarity index 57%
rename from docs/pylabrobot.machine.rst
rename to docs/api/pylabrobot.machine.rst
index c68fa1dc05..e02dbdd37b 100644
--- a/docs/pylabrobot.machine.rst
+++ b/docs/api/pylabrobot.machine.rst
@@ -1,4 +1,4 @@
-.. currentmodule:: pylabrobot.machine
+.. currentmodule:: pylabrobot.machines
Machine is a backend'd Resource. Check out the contributing section for more information.
@@ -7,5 +7,6 @@ Machine is a backend'd Resource. Check out the contributing section for more inf
:nosignatures:
:recursive:
- pylabrobot.machine.Machine
- pylabrobot.machine.MachineBackend
+ machine.Machine
+ backends.machine.MachineBackend
+ backends.usb.USBBackend
diff --git a/docs/api/pylabrobot.only_fans.rst b/docs/api/pylabrobot.only_fans.rst
new file mode 100644
index 0000000000..8b184a5f72
--- /dev/null
+++ b/docs/api/pylabrobot.only_fans.rst
@@ -0,0 +1,26 @@
+.. currentmodule:: pylabrobot.only_fans
+
+pylabrobot.only_fans package
+================================
+
+This package contains APIs just for fans.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ fan.Fan
+
+
+Backends
+--------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ backend.FanBackend
+ chatterbox.FanChatterboxBackend
+ hamilton_hepa_fan.HamiltonHepaFan
diff --git a/docs/pylabrobot.plate_reading.rst b/docs/api/pylabrobot.plate_reading.rst
similarity index 76%
rename from docs/pylabrobot.plate_reading.rst
rename to docs/api/pylabrobot.plate_reading.rst
index 171927cb27..953a7d8d99 100644
--- a/docs/pylabrobot.plate_reading.rst
+++ b/docs/api/pylabrobot.plate_reading.rst
@@ -10,7 +10,7 @@ This package contains APIs for working with plate readers.
:nosignatures:
:recursive:
- pylabrobot.plate_reading.plate_reader.PlateReader
+ plate_reader.PlateReader
Backends
@@ -21,4 +21,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.plate_reading.clario_star.CLARIOStar
+ chatterbox.PlateReaderChatterboxBackend
+ clario_star.CLARIOStar
diff --git a/docs/pylabrobot.pumps.rst b/docs/api/pylabrobot.pumps.rst
similarity index 77%
rename from docs/pylabrobot.pumps.rst
rename to docs/api/pylabrobot.pumps.rst
index 09a57f6009..9dd7a28fe9 100644
--- a/docs/pylabrobot.pumps.rst
+++ b/docs/api/pylabrobot.pumps.rst
@@ -10,7 +10,7 @@ This package contains APIs for working with pumps.
:nosignatures:
:recursive:
- pylabrobot.pumps.pump.Pump
+ pump.Pump
Backends
@@ -21,4 +21,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.pumps.cole_parmer.masterflex.Masterflex
+ chatterbox.PumpChatterboxBackend
+ cole_parmer.masterflex.Masterflex
diff --git a/docs/api/pylabrobot.resources.rst b/docs/api/pylabrobot.resources.rst
new file mode 100644
index 0000000000..632d3fd9cd
--- /dev/null
+++ b/docs/api/pylabrobot.resources.rst
@@ -0,0 +1,251 @@
+.. currentmodule:: pylabrobot.resources
+
+pylabrobot.resources package
+============================
+
+Resources represent on-deck liquid handling equipment, including tip racks, plates and carriers. Many resources defined in the VENUS and Opentrons labware libraries are also defined in this package. In addition, by (optionally subclassing and) instantiating the appropriate classes, you can define your own resources.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ Carrier
+ Container
+ Coordinate
+ Deck
+ ItemizedResource
+ utils.create_equally_spaced_2d
+ Lid
+ Liquid
+ PetriDish
+ Plate
+ PlateCarrier
+ Resource
+ ResourceStack
+ tip.Tip
+ TipCarrier
+ TipRack
+ Trough
+ Tube
+ TubeCarrier
+ TubeRack
+ Well
+
+
+Azenta
+------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ corning_axygen.plates
+
+
+Boekel
+------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ boekel.tube_carriers
+
+
+Corning Axygen
+--------------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ corning_axygen.plates
+
+
+Corning Costar
+--------------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ corning_costar.plates
+
+
+Falcon
+------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ falcon.tubes
+
+
+Greiner
+-------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ greiner
+ greiner.plates
+
+
+Hamilton
+--------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ hamilton
+ hamilton.hamilton_decks.HamiltonDeck
+ hamilton.STARDeck
+ hamilton.STARLetDeck
+
+
+Limbro
+------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ limbro
+ limbro.plates
+
+
+ML Star resources
+-----------------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ ml_star
+ ml_star.tip_creators
+ ml_star.tip_racks
+ ml_star.tip_carriers
+ ml_star.plate_carriers
+
+
+Opentrons
+---------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ opentrons
+ opentrons.deck
+ opentrons.load
+ opentrons.plates
+ opentrons.tip_racks
+ opentrons.tube_racks
+
+
+Porvair
+-------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ porvair.plates
+
+
+Revvity
+-------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ revvity.plates
+
+
+
+Tecan
+-----
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ tecan
+ tecan.plates
+ tecan.plate_carriers
+ tecan.tecan_decks
+ tecan.tecan_resource
+ tecan.tip_carriers
+ tecan.tip_creators
+ tecan.tip_racks
+ tecan.wash
+
+
+Thermo Fisher
+-------------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ thermo_fisher.troughs
+
+
+VWR
+---
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ vwr.troughs
+
+
+Tip trackers
+------------
+
+See :doc:`Using trackers ` for a tutorial.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ no_tip_tracking
+ set_tip_tracking
+ tip_tracker.TipTracker
+
+
+Volume trackers
+---------------
+
+See :doc:`Using trackers ` for a tutorial.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ no_volume_tracking
+ set_volume_tracking
+ volume_tracker.VolumeTracker
diff --git a/docs/pylabrobot.rst b/docs/api/pylabrobot.rst
similarity index 81%
rename from docs/pylabrobot.rst
rename to docs/api/pylabrobot.rst
index da3a6c9f43..731a74b5fd 100644
--- a/docs/pylabrobot.rst
+++ b/docs/api/pylabrobot.rst
@@ -1,7 +1,7 @@
.. currentmodule:: pylabrobot
-Public API: pylabrobot package
-==============================
+API
+===
Subpackages
-----------
@@ -9,13 +9,16 @@ Subpackages
.. toctree::
:maxdepth: 1
+ pylabrobot.config
pylabrobot.machine
pylabrobot.heating_shaking
pylabrobot.liquid_handling
pylabrobot.plate_reading
pylabrobot.pumps
+ pylabrobot.only_fans
+ pylabrobot.resources
pylabrobot.scales
pylabrobot.shaking
- pylabrobot.resources
pylabrobot.temperature_controlling
+ pylabrobot.tilting
pylabrobot.utils
diff --git a/docs/pylabrobot.scales.rst b/docs/api/pylabrobot.scales.rst
similarity index 76%
rename from docs/pylabrobot.scales.rst
rename to docs/api/pylabrobot.scales.rst
index 3bb174c6d5..63bcb13325 100644
--- a/docs/pylabrobot.scales.rst
+++ b/docs/api/pylabrobot.scales.rst
@@ -10,7 +10,7 @@ This package contains APIs for working with scales.
:nosignatures:
:recursive:
- pylabrobot.scales.scale.Scale
+ scale.Scale
Backends
@@ -21,4 +21,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.scales.mettler_toledo.MettlerToledoWXS205SDU
+ chatterbox.ScaleChatterboxBackend
+ mettler_toledo.MettlerToledoWXS205SDU
diff --git a/docs/pylabrobot.shaking.rst b/docs/api/pylabrobot.shaking.rst
similarity index 79%
rename from docs/pylabrobot.shaking.rst
rename to docs/api/pylabrobot.shaking.rst
index 7ebde74904..66f4570ad1 100644
--- a/docs/pylabrobot.shaking.rst
+++ b/docs/api/pylabrobot.shaking.rst
@@ -10,7 +10,7 @@ This package contains APIs for working with shakers.
:nosignatures:
:recursive:
- pylabrobot.shaking.shaker.Shaker
+ shaker.Shaker
Backends
@@ -21,4 +21,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.shaking.backend.ShakerBackend
+ backend.ShakerBackend
+ chatterbox.ShakerChatterboxBackend
diff --git a/docs/pylabrobot.temperature_controlling.rst b/docs/api/pylabrobot.temperature_controlling.rst
similarity index 61%
rename from docs/pylabrobot.temperature_controlling.rst
rename to docs/api/pylabrobot.temperature_controlling.rst
index 024d3777e2..eaa9e1bdf4 100644
--- a/docs/pylabrobot.temperature_controlling.rst
+++ b/docs/api/pylabrobot.temperature_controlling.rst
@@ -10,8 +10,8 @@ This package contains APIs for working with temperature controllers (heaters and
:nosignatures:
:recursive:
- pylabrobot.temperature_controlling.temperature_controller.TemperatureController
- pylabrobot.temperature_controlling.opentrons.OpentronsTemperatureModuleV2
+ temperature_controller.TemperatureController
+ opentrons.OpentronsTemperatureModuleV2
Backends
@@ -22,4 +22,5 @@ Backends
:nosignatures:
:recursive:
- pylabrobot.temperature_controlling.opentrons_backend.OpentronsTemperatureModuleBackend
+ chatterbox.TemperatureControllerChatterboxBackend
+ opentrons_backend.OpentronsTemperatureModuleBackend
diff --git a/docs/api/pylabrobot.tilting.rst b/docs/api/pylabrobot.tilting.rst
new file mode 100644
index 0000000000..564601d276
--- /dev/null
+++ b/docs/api/pylabrobot.tilting.rst
@@ -0,0 +1,26 @@
+.. currentmodule:: pylabrobot.tilting
+
+pylabrobot.tilting package
+==========================
+
+This package contains APIs for working with tilt modules.
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ tilter.Tilter
+
+
+Backends
+--------
+
+.. autosummary::
+ :toctree: _autosummary
+ :nosignatures:
+ :recursive:
+
+ chatterbox.TilterChatterboxBackend
+ tilter_backend.TilterBackend
+ hamilton_backend.HamiltonTiltModuleBackend
diff --git a/docs/pylabrobot.utils.rst b/docs/api/pylabrobot.utils.rst
similarity index 100%
rename from docs/pylabrobot.utils.rst
rename to docs/api/pylabrobot.utils.rst
diff --git a/docs/conf.py b/docs/conf.py
index dd6c6d0473..0623dbfc91 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -36,11 +36,12 @@
'sphinx.ext.mathjax',
'myst_nb',
'sphinx_copybutton',
- 'IPython.sphinxext.ipython_console_highlighting'
+ 'IPython.sphinxext.ipython_console_highlighting',
+ 'sphinx_reredirects',
]
intersphinx_mapping = {
- 'python': ('https://docs.python.org/3/', None),
+ 'python': ('https://docs.python.org/3/', None),
}
# Add any paths that contain templates here, relative to this directory.
@@ -72,20 +73,50 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = 'sphinx_book_theme'
+html_theme = 'pydata_sphinx_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ['_static', 'resources/library/img']
+html_extra_path = ['resources/library/img']
html_theme_options = {
- 'repository_url': 'https://github.com/pylabrobot/pylabrobot',
- 'use_repository_button': False,
'use_edit_page_button': True,
- 'repository_branch': 'main',
- 'path_to_docs': 'docs',
- 'use_issues_button': False,
+
+ "navbar_start": ["navbar-logo"],
+ "navbar_center": ["navbar-nav"],
+ "navbar_end": ["theme-switcher", "navbar-icon-links"],
+ "navbar_persistent": ["search-button"],
+
+ "icon_links": [
+ {
+ "name": "X",
+ "url": "https://x.com/pylabrobot",
+ "icon": "fa-brands fa-x-twitter",
+ },
+ {
+ "name": "GitHub",
+ "url": "https://github.com/pylabrobot/pylabrobot",
+ "icon": "fa-brands fa-github",
+ },
+ {
+ "name": "YouTube",
+ "url": "https://youtube.com/@pylabrobot",
+ "icon": "fa-brands fa-youtube",
+ }
+ ],
+
+ "logo": {
+ "text": "PyLabRobot",
+ }
+}
+
+html_context = {
+ "github_user": "pylabrobot",
+ "github_repo": "pylabrobot",
+ "github_version": "main",
+ "doc_path": "docs",
}
html_logo = '_static/logo.png'
@@ -107,4 +138,29 @@
nb_execution_mode = 'off'
myst_enable_extensions = ['dollarmath']
-source_suffix = ['.rst', '.md']
+redirects = {
+ "installation.html": "user_guide/installation.html",
+ "contributing.html": "contributor_guide/index.html",
+ "configuration.html": "user_guide/configuration.html",
+ "new-machine-type.html": "contributor_guide/new_machine_type.html",
+ "new-concrete-backend.html": "contributor_guide/new_concrete_backend.html",
+ "how-to-open-source.html": "contributor_guide/how_to_open_source.html",
+ "basic.html": "user_guide/basic.html",
+ "using-the-visualizer.html": "user_guide/using_the_visualizer.html",
+ "using-trackers.html": "user_guide/using_trackers.html",
+ "writing-robot-agnostic-methods.html": "user_guide/writing_robot_agnostic_methods.html",
+ "hamilton-star/hamilton-star.html": "user_guide/hamilton_star/hamilton_star.html",
+ "hamilton-star/iswap-module.html": "user_guide/hamilton_star/iswap_module.html",
+ "plate_reading.html": "user_guide/plate_reading.html",
+ "cytation5.html": "user_guide/cytation5.html",
+ "pumps.html": "user_guide/pumps.html",
+ "scales.html": "user_guide/scales.html",
+ "temperature.html": "user_guide/temperature.html",
+ "tilting.html": "user_guide/tilting.html",
+ "heating-shaking.html": "user_guide/heating_shaking.html",
+ "fans.html": "user_guide/fans.html",
+}
+
+if tags.has('no-api'):
+ exclude_patterns.append('api/**')
+ suppress_warnings = ['toc.excluded']
diff --git a/docs/contributing.md b/docs/contributing.md
deleted file mode 100644
index be01fee69e..0000000000
--- a/docs/contributing.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Contributing to PyLabRobot
-
-Thank you for your interest in contributing to PyLabRobot! Please see [CONTRIBUTING.md](https://github.com/PyLabRobot/pylabrobot/blob/main/CONTRIBUTING.md) on GitHub to learn how you can get started.
diff --git a/docs/contributor_guide/contributing.md b/docs/contributor_guide/contributing.md
new file mode 100644
index 0000000000..a214444b01
--- /dev/null
+++ b/docs/contributor_guide/contributing.md
@@ -0,0 +1,74 @@
+# Contributing to PyLabRobot
+
+Thank you for your interest in contributing to PyLabRobot! This document will help you get started.
+
+## Getting Started
+
+See the installation instructions [here](/user_guide/installation.md). For contributing, you should install PyLabRobot from source.
+
+If this is your first time contributing to open source, check out [How to Open Source](/contributor_guide/how-to-open-source.md) for an easy introduction.
+
+It's highly appreciated by the PyLabRobot developers if you communicate what you want to work on, to minimize any duplicate work. You can do this on [discuss.pylabrobot.org](https://discuss.pylabrobot.org).
+
+## Development Tips
+
+It is recommend that you use VSCode, as we provide a workspace config in `/.vscode/settings.json`, but you can use any editor you like, of course.
+
+Some VSCode Extensions I'd recommend:
+
+- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
+- [Pylint](https://github.com/microsoft/vscode-pylint)
+- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
+- [mypy](https://marketplace.visualstudio.com/items?itemName=matangover.mypy)
+
+## Testing
+
+PyLabRobot uses `pytest` to run unit tests. Please make sure tests pass when you submit a PR. You can run tests as follows.
+
+```bash
+make test # run test on the latest version
+```
+
+`pylint` is used to enforce code style. The rc file is `/.pylintrc`. As mentioned above, it is very helpful to have an editor do style checking as you're writing code.
+
+```bash
+make lint
+```
+
+`mypy` is used to enforce type checking.
+
+```bash
+make typecheck
+```
+
+## Writing documentation
+
+It is important that you write documentation for your code. As a rule of thumb, all functions and classes, whether public or private, are required to have a docstring. PyLabRobot uses [Google Style Python Docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). In addition, PyLabRobot uses [type hints](https://docs.python.org/3/library/typing.html) to document the types of variables.
+
+To build the documentation, run `make docs` in the root directory. The documentation will be built in `docs/_build/html`. Run `open docs/_build/html/index.html` to open the documentation in your browser.
+
+## Common Tasks
+
+### Fixing a bug
+
+Bug fixes are an easy way to get started contributing.
+
+Make sure you write a test that fails before your fix and passes after your fix. This ensures that this bug will never occur again. Tests are written in `_tests.py` files. See [Python's unittest module](https://docs.python.org/3/library/unittest.html) and existing tests for more information. In most cases, adding a few additional lines to an existing test should be sufficient.
+
+### Adding resources
+
+If you have defined a new resource, it is highly appreciated by the community if you add them to the repo. In most cases, a [partial function](https://docs.python.org/3/library/functools.html#functools.partial) is enough. There are many examples, like [tipracks.py](https://github.com/PyLabRobot/pylabrobot/blob/main/pylabrobot/liquid_handling/resources/ml_star/tipracks.py). If you are writing a new kind of resource, you should probably subclass resource in a new file.
+
+Make sure to add your file to the imports in `__init__.py` of your resources package.
+
+### Writing a new backend
+
+Backends are the primary objects used to communicate with hardware. If you want to integrate a new piece of hardware into PyLabRobot, writing a new backend is the way to go. Here's, very generally, how you'd do it:
+
+1. Copy the `pylabrobot/liquid_handling/backends/backend.py` file to a new file, and rename the class to `Backend`.
+2. Remove all `abc` (abstract base class) imports and decorators from the class.
+3. Implement the methods in the class.
+
+## Support
+
+If you have any questions, feel free to reach out using the [PyLabRobot forum](https://discuss.pylabrobot.org).
diff --git a/docs/how-to-open-source.md b/docs/contributor_guide/how-to-open-source.md
similarity index 99%
rename from docs/how-to-open-source.md
rename to docs/contributor_guide/how-to-open-source.md
index a58a620116..17da5f2c16 100644
--- a/docs/how-to-open-source.md
+++ b/docs/contributor_guide/how-to-open-source.md
@@ -138,4 +138,4 @@ Optionally, you can add a description of your changes. Then click "Create pull r
## Support
-If you have any questions, feel free to reach out using the [PyLabRobot forum](https://forums.pylabrobot.org).
+If you have any questions, feel free to reach out using the [PyLabRobot forum](https://discuss.pylabrobot.org).
diff --git a/docs/img/how-to-os/branch-0.png b/docs/contributor_guide/img/how-to-os/branch-0.png
similarity index 100%
rename from docs/img/how-to-os/branch-0.png
rename to docs/contributor_guide/img/how-to-os/branch-0.png
diff --git a/docs/img/how-to-os/branch-1.png b/docs/contributor_guide/img/how-to-os/branch-1.png
similarity index 100%
rename from docs/img/how-to-os/branch-1.png
rename to docs/contributor_guide/img/how-to-os/branch-1.png
diff --git a/docs/img/how-to-os/branch-2.png b/docs/contributor_guide/img/how-to-os/branch-2.png
similarity index 100%
rename from docs/img/how-to-os/branch-2.png
rename to docs/contributor_guide/img/how-to-os/branch-2.png
diff --git a/docs/img/how-to-os/clone-0.png b/docs/contributor_guide/img/how-to-os/clone-0.png
similarity index 100%
rename from docs/img/how-to-os/clone-0.png
rename to docs/contributor_guide/img/how-to-os/clone-0.png
diff --git a/docs/img/how-to-os/clone-1.png b/docs/contributor_guide/img/how-to-os/clone-1.png
similarity index 100%
rename from docs/img/how-to-os/clone-1.png
rename to docs/contributor_guide/img/how-to-os/clone-1.png
diff --git a/docs/img/how-to-os/commit-0.png b/docs/contributor_guide/img/how-to-os/commit-0.png
similarity index 100%
rename from docs/img/how-to-os/commit-0.png
rename to docs/contributor_guide/img/how-to-os/commit-0.png
diff --git a/docs/img/how-to-os/fork-0.png b/docs/contributor_guide/img/how-to-os/fork-0.png
similarity index 100%
rename from docs/img/how-to-os/fork-0.png
rename to docs/contributor_guide/img/how-to-os/fork-0.png
diff --git a/docs/img/how-to-os/fork-1.png b/docs/contributor_guide/img/how-to-os/fork-1.png
similarity index 100%
rename from docs/img/how-to-os/fork-1.png
rename to docs/contributor_guide/img/how-to-os/fork-1.png
diff --git a/docs/img/how-to-os/pull-0.png b/docs/contributor_guide/img/how-to-os/pull-0.png
similarity index 100%
rename from docs/img/how-to-os/pull-0.png
rename to docs/contributor_guide/img/how-to-os/pull-0.png
diff --git a/docs/img/how-to-os/pull-1.png b/docs/contributor_guide/img/how-to-os/pull-1.png
similarity index 100%
rename from docs/img/how-to-os/pull-1.png
rename to docs/contributor_guide/img/how-to-os/pull-1.png
diff --git a/docs/img/how-to-os/push-0.png b/docs/contributor_guide/img/how-to-os/push-0.png
similarity index 100%
rename from docs/img/how-to-os/push-0.png
rename to docs/contributor_guide/img/how-to-os/push-0.png
diff --git a/docs/img/how-to-os/quick-0.png b/docs/contributor_guide/img/how-to-os/quick-0.png
similarity index 100%
rename from docs/img/how-to-os/quick-0.png
rename to docs/contributor_guide/img/how-to-os/quick-0.png
diff --git a/docs/img/how-to-os/quick-1.png b/docs/contributor_guide/img/how-to-os/quick-1.png
similarity index 100%
rename from docs/img/how-to-os/quick-1.png
rename to docs/contributor_guide/img/how-to-os/quick-1.png
diff --git a/docs/img/how-to-os/quick-2.png b/docs/contributor_guide/img/how-to-os/quick-2.png
similarity index 100%
rename from docs/img/how-to-os/quick-2.png
rename to docs/contributor_guide/img/how-to-os/quick-2.png
diff --git a/docs/img/how-to-os/quick-3.png b/docs/contributor_guide/img/how-to-os/quick-3.png
similarity index 100%
rename from docs/img/how-to-os/quick-3.png
rename to docs/contributor_guide/img/how-to-os/quick-3.png
diff --git a/docs/img/how-to-os/quick-4.png b/docs/contributor_guide/img/how-to-os/quick-4.png
similarity index 100%
rename from docs/img/how-to-os/quick-4.png
rename to docs/contributor_guide/img/how-to-os/quick-4.png
diff --git a/docs/img/how-to-os/quick-6.png b/docs/contributor_guide/img/how-to-os/quick-6.png
similarity index 100%
rename from docs/img/how-to-os/quick-6.png
rename to docs/contributor_guide/img/how-to-os/quick-6.png
diff --git a/docs/contributor_guide/index.md b/docs/contributor_guide/index.md
new file mode 100644
index 0000000000..83fb2fdab0
--- /dev/null
+++ b/docs/contributor_guide/index.md
@@ -0,0 +1,15 @@
+# Contributor guide
+
+```{toctree}
+:maxdepth: 2
+
+contributing
+how-to-open-source
+```
+
+```{toctree}
+:maxdepth: 2
+
+new-machine-type
+new-concrete-backend
+```
diff --git a/docs/new-concrete-backend.md b/docs/contributor_guide/new-concrete-backend.md
similarity index 91%
rename from docs/new-concrete-backend.md
rename to docs/contributor_guide/new-concrete-backend.md
index 87f1889e88..b9dca7d47d 100644
--- a/docs/new-concrete-backend.md
+++ b/docs/contributor_guide/new-concrete-backend.md
@@ -4,11 +4,11 @@ This guide explains how to add support for a new machine of an existing type. Fo
The machine types that are currently supported are:
-- [Liquid handlers](basic)
-- [Plate readers](plate_reading)
-- [Pumps](pumps)
-- [Temperature controllers](temperature)
-- [Heater shakers](/heating-shaking)
+- [Liquid handlers](/user_guide/basic)
+- [Plate readers](/user_guide/plate_reading)
+- [Pumps](/user_guide/pumps)
+- [Temperature controllers](/user_guide/temperature)
+- [Heater shakers](/user_guide/heating-shaking)
Two documents that you can read before you start are:
@@ -25,7 +25,7 @@ Backends should contain minimal state. We prefer to manage the state in the fron
## 0. Get in touch
-Please make a post on [the PyLabRobot Development forum](https://forums.pylabrobot.org/c/pylabrobot/23) to let us know what you are working on. This will help you avoid duplicating work, and it is also a good place to get support.
+Please make a post on [the PyLabRobot Development forum](https://discuss.pylabrobot.org) to let us know what you are working on. This will help you avoid duplicating work, and it is also a good place to get support.
## 1. Creating a new concrete backend class
diff --git a/docs/new-machine-type.md b/docs/contributor_guide/new-machine-type.md
similarity index 92%
rename from docs/new-machine-type.md
rename to docs/contributor_guide/new-machine-type.md
index 8a0388e85e..d0b70da4e2 100644
--- a/docs/new-machine-type.md
+++ b/docs/contributor_guide/new-machine-type.md
@@ -2,11 +2,11 @@
PyLabRobot supports a number of different types of machines. Currently, these are:
-- [Liquid handlers](basic)
-- [Plate readers](plate_reading)
-- [Pumps](pumps)
-- [Temperature controllers](temperature)
-- [Heater shakers](/heating-shaking)
+- [Liquid handlers](/user_guide/basic)
+- [Plate readers](/user_guide/plate_reading)
+- [Pumps](/user_guide/pumps)
+- [Temperature controllers](/user_guide/temperature)
+- [Heater shakers](/user_guide/heating-shaking)
If you want to add support for a new type of machine, this guide will explain the process. If you want to add a new machine for a type that already exists, you should read {doc}`this guide ` instead.
@@ -21,7 +21,7 @@ Thank you for contributing to PyLabRobot!
## 0. Get in touch
-Please make a post on [the PyLabRobot Development forum](https://forums.pylabrobot.org/c/pylabrobot/23) to let us know what you are working on. This will help you avoid duplicating work, and it is also a good place to get support.
+Please make a post on [the PyLabRobot Development forum](https://discuss.pylabrobot.org) to let us know what you are working on. This will help you avoid duplicating work, and it is also a good place to get support.
## 1. Creating a new module
@@ -49,7 +49,7 @@ The abstract class {class}`~pylabrobot.machine.MachineFrontend` must be used as
You should put the front end in a file called `.py` in the module you created in step 1. For example, the liquid handling front end is located at `pylabrobot.liquid_handling.liquid_handler.py`.
-If your devices updates the resource tree or its state, the front end should handle this. See [the resources guide](resources/introduction.md) for more information.
+If your devices updates the resource tree or its state, the front end should handle this. See [the resources guide](/resources/introduction.md) for more information.
## 4. Creating a new concrete backend for a specific machine
diff --git a/docs/img/used_by/mit.jpg b/docs/img/used_by/mit.jpg
new file mode 100644
index 0000000000..72c742cb70
Binary files /dev/null and b/docs/img/used_by/mit.jpg differ
diff --git a/docs/img/used_by/retrobio.webp b/docs/img/used_by/retrobio.webp
new file mode 100644
index 0000000000..e982979539
Binary files /dev/null and b/docs/img/used_by/retrobio.webp differ
diff --git a/docs/img/used_by/tt.png b/docs/img/used_by/tt.png
new file mode 100644
index 0000000000..043db25e91
Binary files /dev/null and b/docs/img/used_by/tt.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000..560dc1201a
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,120 @@
+# Welcome to PyLabRobot's documentation!
+
+PyLabRobot is a hardware agnostic, pure Python SDK for liquid handling robots and accessories.
+
+- GitHub repository: [https://github.com/PyLabRobot/pylabrobot](https://github.com/PyLabRobot/pylabrobot)
+- Community: [https://discuss.pylabrobot.org](https://discuss.pylabrobot.org)
+- Paper: [https://www.cell.com/device/fulltext/S2666-9986(23)00170-9](https://www.cell.com/device/fulltext/S2666-9986(23)00170-9)
+
+![Graphical abstract of PyLabRobot](/img/plr.jpg)
+
+```{note}
+PyLabRobot is different from [PyHamilton](https://github.com/dgretton/pyhamilton). While both packages are created by the same lab and both provide a Python interfaces to Hamilton robots, PyLabRobot aims to provide a universal interface to many different robots runnable on many different computers, where PyHamilton is a Windows only interface to Hamilton's VENUS.
+```
+
+## Used by
+
+```{image} /img/used_by/mit.jpg
+:alt: MIT
+:class: company
+```
+
+```{image} /img/used_by/retrobio.webp
+:alt: Retro
+:class: company
+```
+
+```{image} /img/used_by/tt.png
+:alt: T-Therapeutics
+:class: company tt
+```
+
+```{raw} html
+
+```
+
+## Documentation
+
+```{toctree}
+:maxdepth: 2
+:caption: User Guide
+
+user_guide/index
+```
+
+```{toctree}
+:maxdepth: 2
+:caption: Development
+
+contributor_guide/index
+```
+
+```{toctree}
+:maxdepth: 2
+:caption: Resource Library
+
+resources/index
+```
+
+```{toctree}
+:maxdepth: 2
+:caption: API documentation
+
+api/pylabrobot
+```
+
+```{toctree}
+:hidden:
+
+Community
+```
+
+## Citing
+
+If you use PyLabRobot in your research, please cite the following paper:
+
+```bibtex
+@article{WIERENGA2023100111,
+ title = {PyLabRobot: An open-source, hardware-agnostic interface for liquid-handling robots and accessories},
+ journal = {Device},
+ volume = {1},
+ number = {4},
+ pages = {100111},
+ year = {2023},
+ issn = {2666-9986},
+ doi = {https://doi.org/10.1016/j.device.2023.100111},
+ url = {https://www.sciencedirect.com/science/article/pii/S2666998623001709},
+ author = {Rick P. Wierenga and Stefan M. Golas and Wilson Ho and Connor W. Coley and Kevin M. Esvelt},
+ keywords = {laboratory automation, open source, standardization, liquid-handling robots},
+}
+```
+
+```
+Wierenga, R., Golas, S., Ho, W., Coley, C., & Esvelt, K. (2023). PyLabRobot: An Open-Source, Hardware Agnostic Interface for Liquid-Handling Robots and Accessories. Device. https://doi.org/10.1016/j.device.2023.100111
+```
+
+[Cited by](https://scholar.google.com/scholar?cites=4498189371108132583):
+
+- Tom, Gary, et al. "Self-driving laboratories for chemistry and materials science." Chemical Reviews (2024).
+- Anhel, Ana-Mariya, Lorea Alejaldre, and Ángel Goñi-Moreno. "The Laboratory Automation Protocol (LAP) Format and Repository: a platform for enhancing workflow efficiency in synthetic biology." ACS synthetic biology 12.12 (2023): 3514-3520.
+- Bultelle, Matthieu, Alexis Casas, and Richard Kitney. "Engineering biology and automation–Replicability as a design principle." Engineering Biology (2024).
+- Pleiss, Jürgen. "FAIR Data and Software: Improving Efficiency and Quality of Biocatalytic Science." ACS Catalysis 14.4 (2024): 2709-2718.
+- Gopal, Anjali, et al. "Will releasing the weights of large language models grant widespread access to pandemic agents?." arXiv preprint arXiv:2310.18233 (2023).
+- Padhy, Shakti P., and Sergei V. Kalinin. "Domain hyper-languages bring robots together and enable the machine learning community." Device 1.4 (2023).
+- Beaucage, Peter A., Duncan R. Sutherland, and Tyler B. Martin. "Automation and Machine Learning for Accelerated Polymer Characterization and Development: Past, Potential, and a Path Forward." Macromolecules (2024).
+- Bultelle, Matthieu, Alexis Casas, and Richard Kitney. "Construction of a Calibration Curve for Lycopene on a Liquid-Handling Platform─ Wider Lessons for the Development of Automated Dilution Protocols." ACS Synthetic Biology (2024).
+- Hysmith, Holland, et al. "The future of self-driving laboratories: from human in the loop interactive AI to gamification." Digital Discovery 3.4 (2024): 621-636.
+- Casas, Alexis, Matthieu Bultelle, and Richard Kitney. "An engineering biology approach to automated workflow and biodesign." (2024).
+- Jiang, Shuo, et al. "ProtoCode: Leveraging Large Language Models for Automated Generation of Machine-Readable Protocols from Scientific Publications." arXiv preprint arXiv:2312.06241 (2023).
+- Jiang, Shuo, et al. "ProtoCode: Leveraging large language models (LLMs) for automated generation of machine-readable PCR protocols from scientific publications." SLAS technology 29.3 (2024): 100134.
+- Thieme, Anton, et al. "Deep integration of low-cost liquid handling robots in an industrial pharmaceutical development environment." SLAS technology (2024): 100180.
+- Daniel, Čech. Adaptace algoritmů pro navigaci robota na základě apriorních informací. BS thesis. České vysoké učení technické v Praze. Vypočetní a informační centrum., 2024.
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index c5efc4a693..0000000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,102 +0,0 @@
-Welcome to PyLabRobot's documentation!
-======================================
-
-PyLabRobot is a hardware agnostic, pure Python library for liquid handling robots and accessories.
-
-PyLabRobot provides a layer of general-purpose abstractions over robot functions, with various device drivers for communicating with different kinds of robots. Right now we only support Hamilton STAR and STARLet, Tecan EVO, and Opentrons robots, but we will soon support many more. All of these robots can be controlled using any computer running any operating system. We also provide a browser-based Visualizer which can visualize the state of the deck during a run, and testing backends which do not require access to a robot.
-
-- GitHub repository: https://github.com/PyLabRobot/pylabrobot
-- Forum: https://forums.pylabrobot.org
-- Paper: https://www.cell.com/device/fulltext/S2666-9986(23)00170-9
-
-.. image:: img/plr.jpg
- :width: 600
- :alt: Graphical abstract of PyLabRobot
-
-.. note::
- PyLabRobot is different from `PyHamilton `_. While both packages are created by the same lab and both provide a Python interfaces to Hamilton robots, PyLabRobot aims to provide a universal interface to many different robots runnable on many different computers, where PyHamilton is a Windows only interface to Hamilton's VENUS.
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Getting Started
-
- installation.md
- contributing.md
-
-.. toctree::
- :maxdepth: 1
- :caption: Contributing
-
- new-machine-type.md
- new-concrete-backend.md
- how-to-open-source.md
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Liquid handling
-
- basic
- using-the-visualizer
- using-trackers
- writing-robot-agnostic-methods
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Resources
-
- resources/introduction
- resources/custom-resources
- resources/hamilton_parse
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Plate reading
-
- plate_reading
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Pumps
-
- pumps
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Scales
-
- scales
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Temperature controlling
-
- temperature
-
-
-.. toctree::
- :maxdepth: 1
- :caption: Heater shakers
-
- heating-shaking
-
-
-.. toctree::
- :maxdepth: 4
- :caption: API documentation
-
- pylabrobot
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/pylabrobot.resources.rst b/docs/pylabrobot.resources.rst
deleted file mode 100644
index efa2b35ce2..0000000000
--- a/docs/pylabrobot.resources.rst
+++ /dev/null
@@ -1,252 +0,0 @@
-.. currentmodule:: pylabrobot
-
-pylabrobot.resources package
-============================
-
-Resources represent on-deck liquid handling equipment, including tip racks, plates and carriers. Many resources defined in the VENUS and Opentrons labware libraries are also defined in this package. In addition, by (optionally subclassing and) instantiating the appropriate classes, you can define your own resources.
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources
- pylabrobot.resources.Carrier
- pylabrobot.resources.Container
- pylabrobot.resources.Coordinate
- pylabrobot.resources.Deck
- pylabrobot.resources.ItemizedResource
- pylabrobot.resources.create_equally_spaced
- pylabrobot.resources.Lid
- pylabrobot.resources.Liquid
- pylabrobot.resources.PetriDish
- pylabrobot.resources.Plate
- pylabrobot.resources.PlateCarrier
- pylabrobot.resources.Resource
- pylabrobot.resources.ResourceStack
- pylabrobot.resources.tip.Tip
- pylabrobot.resources.TipCarrier
- pylabrobot.resources.TipRack
- pylabrobot.resources.Trough
- pylabrobot.resources.Tube
- pylabrobot.resources.TubeCarrier
- pylabrobot.resources.TubeRack
- pylabrobot.resources.Well
-
-
-Azenta
-------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.corning_axygen.plates
-
-
-Boekel
-------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.boekel.tube_carriers
-
-
-Corning Axygen
---------------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.corning_axygen.plates
-
-
-Corning Costar
---------------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.corning_costar.plates
-
-
-Falcon
-------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.falcon.tubes
-
-
-Greiner
--------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.greiner
- pylabrobot.resources.greiner.plates
-
-
-Hamilton
---------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.hamilton
- pylabrobot.resources.hamilton.hamilton_decks.HamiltonDeck
- pylabrobot.resources.hamilton.STARDeck
- pylabrobot.resources.hamilton.STARLetDeck
-
-
-Limbro
-------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.limbro
- pylabrobot.resources.limbro.plates
-
-
-ML Star resources
------------------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.ml_star
- pylabrobot.resources.ml_star.tip_creators
- pylabrobot.resources.ml_star.tip_racks
- pylabrobot.resources.ml_star.tip_carriers
- pylabrobot.resources.ml_star.plate_carriers
-
-
-Opentrons
----------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.opentrons
- pylabrobot.resources.opentrons.deck
- pylabrobot.resources.opentrons.load
- pylabrobot.resources.opentrons.plates
- pylabrobot.resources.opentrons.tip_racks
- pylabrobot.resources.opentrons.tube_racks
-
-
-Porvair
--------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.porvair.plates
-
-
-Revvity
--------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.revvity.plates
-
-
-
-Tecan
------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.tecan
- pylabrobot.resources.tecan.plates
- pylabrobot.resources.tecan.plate_carriers
- pylabrobot.resources.tecan.tecan_decks
- pylabrobot.resources.tecan.tecan_resource
- pylabrobot.resources.tecan.tip_carriers
- pylabrobot.resources.tecan.tip_creators
- pylabrobot.resources.tecan.tip_racks
- pylabrobot.resources.tecan.wash
-
-
-Thermo Fisher
--------------
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.thermo_fisher.troughs
-
-
-VWR
----
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.vwr.troughs
-
-
-Tip trackers
-------------
-
-See :doc:`Using trackers ` for a tutorial.
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.no_tip_tracking
- pylabrobot.resources.set_tip_tracking
- pylabrobot.resources.tip_tracker.TipTracker
-
-
-Volume trackers
----------------
-
-See :doc:`Using trackers ` for a tutorial.
-
-.. autosummary::
- :toctree: _autosummary
- :nosignatures:
- :recursive:
-
- pylabrobot.resources.no_volume_tracking
- pylabrobot.resources.set_volume_tracking
- pylabrobot.resources.volume_tracker.VolumeTracker
diff --git a/docs/resources/containers.md b/docs/resources/containers.md
new file mode 100644
index 0000000000..0570348d4f
--- /dev/null
+++ b/docs/resources/containers.md
@@ -0,0 +1,12 @@
+# Containers
+
+Resources that contain liquid are subclasses of {class}`pylabrobot.resources.container.Container`. This class provides a {class}`pylabrobot.resources.volume_tracker.VolumeTracker` that helps {class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler` keep track of the liquid in the resource. (For more information on trackers, check out {doc}`/user_guide/using-trackers`). Examples of subclasses of `Container` are {class}`pylabrobot.resources.Well` and {class}`pylabrobot.resources.trough.Trough`.
+
+It is possible to instantiate a `Container` directly:
+
+```python
+from pylabrobot.resources import Container
+container = Container(name="container", size_x=10, size_y=10, size_z=10)
+# volume is computed by assuming the container is a cuboid, and can be adjusted with the max_volume
+# parameter
+```
diff --git a/docs/resources/custom-resources.md b/docs/resources/custom-resources.md
index 6bd07409c5..32a9efdddf 100644
--- a/docs/resources/custom-resources.md
+++ b/docs/resources/custom-resources.md
@@ -4,7 +4,7 @@ This document describes how to define custom resources in PyLabRobot. We will bu
## Defining a custom liquid container
-![Blue Bucket](../img/custom-resources/blue-bucket.jpg)
+![Blue Bucket](/resources/img/custom-resources/blue-bucket.jpg)
Defining create a custom liquid container, like the blue bucket above, is as easy as instantiating a {class}`pylabrobot.resources.Resource` object:
@@ -62,7 +62,7 @@ The default behavior when aspirating from a resource is to aspirate from the bot
lh.aspirate(blue_bucket, vols=10)
```
-![Aspirating from the blue bucket](../img/custom-resources/aspirate-blue-bucket.jpg)
+![Aspirating from the blue bucket](/resources/img/custom-resources/aspirate-blue-bucket.jpg)
With multiple channels, the channels will be spread evenly across the bottom of the resource:
@@ -70,7 +70,7 @@ With multiple channels, the channels will be spread evenly across the bottom of
await lh.aspirate(blue_bucket, vols=[10, 10, 10], use_channels=[0, 1, 2])
```
-![Aspirating from the blue bucket with multiple channels](../img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg)
+![Aspirating from the blue bucket with multiple channels](/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg)
What happens when aspirating resources is that PLR creates a list of offsets that equally space the channels across the y-axis in the middle of the resource. These offsets are computed using {meth}`pylabrobot.resources.Resource.get_2d_center_offsets`. We can use this list and modify it to aspirate from a different location. In the example below, we will aspirate 10 mm from the left edge of the resource:
@@ -80,7 +80,7 @@ offsets = [Coordinate(x=10, y=c.y, z=c.z) for c in offsets] # set x coordinate o
await lh.aspirate(blue_bucket, vols=[10, 10], offsets=offsets) # pass the offsets to the aspirate
```
-![Aspirating from the blue bucket with multiple channels and custom offsets](../img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg)
+![Aspirating from the blue bucket with multiple channels and custom offsets](/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg)
### Serializing data
@@ -103,7 +103,7 @@ class BlueBucket(Resource):
## Defining a custom plate
-![Custom Plate](../img/custom-resources/tube-plate.jpg)
+![Custom Plate](/resources/img/custom-resources/tube-plate.jpg)
The resource pictured above is a custom plate, consisting of tubes in a rack.
@@ -125,7 +125,7 @@ class Tube(Container):
Next, let's define the custom plate. The `Tube` class is passed as a type argument to the `ItemizedResource` class with `[Tube]`:
```python
-from pylabrobot.resources import ItemizedResource, create_equally_spaced
+from pylabrobot.resources import ItemizedResource, create_equally_spaced_2d
class TubePlate(ItemizedResource[Tube]):
def __init__(self, name: str):
@@ -134,7 +134,7 @@ class TubePlate(ItemizedResource[Tube]):
size_x=127.0,
size_y=86.0,
size_z=45.0,
- items=create_equally_spaced(Tube,
+ items=create_equally_spaced_2d(Tube,
num_items_x=12,
num_items_y=8,
dx=9.5,
@@ -146,7 +146,7 @@ class TubePlate(ItemizedResource[Tube]):
)
```
-The {meth}`pylabrobot.resources.create_equally_spaced` function creates a list of items, equally spaced in a grid.
+The {meth}`pylabrobot.resources.create_equally_spaced_2d` function creates a list of items, equally spaced in a grid.
This resource is automatically compatible with the rest of PyLabRobot. For example, we can aspirate from the plate:
@@ -158,4 +158,4 @@ lh.aspirate(tube_plate["A1":"C1"], vols=10)
lh.dispense(tube_plate["A2":"C2"], vols=10)
```
-![Aspirating from the tube plate](../img/custom-resources/aspirate-tube-plate.jpg)
+![Aspirating from the tube plate](/resources/img/custom-resources/aspirate-tube-plate.jpg)
diff --git a/docs/resources/hamilton_parse.md b/docs/resources/hamilton_parse.md
deleted file mode 100644
index 51b0f355b2..0000000000
--- a/docs/resources/hamilton_parse.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Parsing Hamilton VENUS Resources
-
-PyLabRobot allows you to easily import resources from the VENUS labware library.
-
-There are two ways to do this:
-
-1. Creating a Python resource definition file
-2. Importing a resource directly into Python
-
-(creating-a-python-resource-definition-file)=
-
-## Creating a Python resource definition file
-
-To create a Python resource definition file, you will need to use the `make_ham_resources.py` script. This script will generate a Python resource definition file for you.
-
-```bash
-# from the root of the repository
-python tools/make_resources/make_ham_resources.py -o --filepath /path/to/file.rck
-```
-
-Where `` is the name of the file you want to create `path/to/file.rck` is the path to the `.rck` or `.tml` file you want to parse.
-
-The `-o` flag is optional. If you do not provide an output file, the script will print the resource definition to the console.
-
-You can also parse an entire directory of `.rck` or `.tml` files by providing the `--base-dir` flag.
-
-```bash
-# from the root of the repository
-python tools/make_resources/make_ham_resources.py -o --base-dir /path/to/directory
-```
-
-The Hamilton labware library is usually located at `C:\Program Files (x86)\HAMILTON\LabWare` on the Windows computer where VENUS is installed.
-
-## Importing a resource directly into Python
-
-You can also import a resource directly into Python using the `hamilton_parse` module.
-
-```python
-from pylabrobot.resources.hamilton_parse import create_plate
-create_plate('/path/to/file.rck', name='my_plate')
-```
-
-## Contributing resources
-
-Please contribute resources to PLR if you have any that are not already in the library! (This makes for a great first contribution to the project!)
-
-To contribute resources, create resource definitions as described [above](#creating-a-python-resource-definition-file) and add it to the `pylabrobot/resources` module. Then create a pull request. You can find a guide on creating pull requests {doc}`here `.
diff --git a/docs/img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg b/docs/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg
similarity index 100%
rename from docs/img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg
rename to docs/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels-custom-offsets.jpg
diff --git a/docs/img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg b/docs/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg
similarity index 100%
rename from docs/img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg
rename to docs/resources/img/custom-resources/aspirate-blue-bucket-multiple-channels.jpg
diff --git a/docs/img/custom-resources/aspirate-blue-bucket.jpg b/docs/resources/img/custom-resources/aspirate-blue-bucket.jpg
similarity index 100%
rename from docs/img/custom-resources/aspirate-blue-bucket.jpg
rename to docs/resources/img/custom-resources/aspirate-blue-bucket.jpg
diff --git a/docs/img/custom-resources/aspirate-tube-plate.jpg b/docs/resources/img/custom-resources/aspirate-tube-plate.jpg
similarity index 100%
rename from docs/img/custom-resources/aspirate-tube-plate.jpg
rename to docs/resources/img/custom-resources/aspirate-tube-plate.jpg
diff --git a/docs/img/custom-resources/blue-bucket.jpg b/docs/resources/img/custom-resources/blue-bucket.jpg
similarity index 100%
rename from docs/img/custom-resources/blue-bucket.jpg
rename to docs/resources/img/custom-resources/blue-bucket.jpg
diff --git a/docs/img/custom-resources/tube-plate.jpg b/docs/resources/img/custom-resources/tube-plate.jpg
similarity index 100%
rename from docs/img/custom-resources/tube-plate.jpg
rename to docs/resources/img/custom-resources/tube-plate.jpg
diff --git a/docs/resources/img/pedestal/measure.jpeg b/docs/resources/img/pedestal/measure.jpeg
new file mode 100644
index 0000000000..0f867cbecc
Binary files /dev/null and b/docs/resources/img/pedestal/measure.jpeg differ
diff --git a/docs/resources/img/plate/lid_nesting_z_height.jpeg b/docs/resources/img/plate/lid_nesting_z_height.jpeg
new file mode 100644
index 0000000000..084b399638
Binary files /dev/null and b/docs/resources/img/plate/lid_nesting_z_height.jpeg differ
diff --git a/docs/resources/index.md b/docs/resources/index.md
new file mode 100644
index 0000000000..69452e681a
--- /dev/null
+++ b/docs/resources/index.md
@@ -0,0 +1,79 @@
+# Resource Library
+
+The PLR Resource Library (PLR-RL) is the world's biggest and most accurate centralized collection of labware. If you cannot find something, please contribute what you are looking for!
+
+
+```{toctree}
+:maxdepth: 1
+
+introduction
+custom-resources
+```
+
+## `Resource` subclasses
+
+In PLR every physical object is a subclass of the `Resource` superclass (except for `Tip`).
+Each subclass adds unique methods or attributes to represent its unique physical specifications and behavior.
+
+Some standard `Resource` subclasses in the inheritance tree are:
+
+```
+Resource
+├── Carrier
+│ ├── TipCarrier
+│ ├── PlateCarrier
+│ ├── MFXCarrier
+│ ├── ShakerCarrier
+│ └── TubeCarrier
+├── Container
+│ ├── Well
+│ ├── PetriDish
+│ ├── Tube
+│ └── Trough
+├── ItemizedResource
+│ ├── Plate
+│ ├── TipRack
+│ └── TubeRack
+├── Lid
+├── PlateAdapter
+└── MFXModule
+```
+
+See more detailed documentatino below (WIP).
+
+```{toctree}
+:caption: Resource subclasses
+
+containers
+itemized_resource
+plates
+plate_carriers
+```
+
+## Library
+
+### Plate Naming Standard
+
+PLR is not actively enforcing a specific plate naming standard but recommends the following:
+
+
+
+This standard is similar to the [Opentrons API labware naming standard](https://ecatalog.corning.com/life-sciences/b2b/UK/en/Microplates/Assay-Microplates/96-Well-Microplates/Costar%C2%AE-Multiple-Well-Cell-Culture-Plates/p/3516) but 1) further sub-categorizes "wellplates" to facilitate communication with day-to-day users, and 2) adds information about the well-bottom geometry.
+
+```{toctree}
+:caption: Library
+
+library/alpaqua
+library/azenta
+library/boekel
+library/celltreat
+library/corning_axygen
+library/corning_costar
+library/eppendorf
+library/falcon
+library/ml_star
+library/opentrons
+library/porvair
+library/revvity
+library/thermo_fisher
+```
diff --git a/docs/resources/introduction.md b/docs/resources/introduction.md
index 690a12fb26..fe90ca13ca 100644
--- a/docs/resources/introduction.md
+++ b/docs/resources/introduction.md
@@ -2,17 +2,17 @@
This document introduces PyLabRobot Resources (labware and deck definitions) and general subclasses. You can find more information on creating custom resources in the {doc}`custom-resources` section.
-In PyLabRobot, a {class}`pylabrobot.resources.Resource` is a piece of labware or equipment used in a protocol or program, a part of a labware item (such as a Well) or a container of labware items (such as a Deck). All resources inherit from a single base class {class}`pylabrobot.resources.Resource` that provides most of the functionality, such as the name, sizing, type, model, as well as methods for dealing with state. The name and sizing are required for all resources, with the name being a unique identifier for the resource and the sizing being the x, y and z-dimensions of the resource in millimeters when conceptualized as a cuboid.
+In PyLabRobot, a {class}`pylabrobot.resources.resource.Resource` is a piece of labware or equipment used in a protocol or program, a part of a labware item (such as a Well) or a container of labware items (such as a Deck). All resources inherit from a single base class {class}`pylabrobot.resources.resource.Resource` that provides most of the functionality, such as the name, sizing, type, model, as well as methods for dealing with state. The name and sizing are required for all resources, with the name being a unique identifier for the resource and the sizing being the x, y and z-dimensions of the resource in millimeters when conceptualized as a cuboid.
-While you can instantiate a `Resource` directly, several subclasses of methods exist to provide additional functionality and model specific resource attributes. For example, a {class}`pylabrobot.resources.plate.Plate` has methods for easily accessing {class}`pylabrobot.resources.Well`s.
+While you can instantiate a `Resource` directly, several subclasses of methods exist to provide additional functionality and model specific resource attributes. For example, a {class}`pylabrobot.resources.plate.Plate` has methods for easily accessing {class}`pylabrobot.resources.well.Well`s.
-The relation between resources is modelled by a tree, specifically an [_arborescence_]() (a directed, rooted tree). The location of a resource in the tree is a Cartesian coordinate and always relative to the bottom front left corner of its immediate parent. The absolute location can be computed using {func}`pylabrobot.resources.Resource.get_absolute_location`. The x-axis is left (smaller) and right (larger); the y-axis is front (small) and back (larger); the z-axis is down (smaller) and up (higher). Each resource has `children` and `parent` attributes that allow you to navigate the tree.
+The relation between resources is modelled by a tree, specifically an [_arborescence_]() (a directed, rooted tree). The location of a resource in the tree is a Cartesian coordinate and always relative to the bottom front left corner of its immediate parent. The absolute location can be computed using {meth}`~pylabrobot.resources.resource.Resource.get_absolute_location`. The x-axis is left (smaller) and right (larger); the y-axis is front (small) and back (larger); the z-axis is down (smaller) and up (higher). Each resource has `children` and `parent` attributes that allow you to navigate the tree.
-{class}`pylabrobot.machine.Machine` is a special type of resource that represents a physical machine, such as a liquid handling robot ({class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler`) or a plate reader ({class}`pylabrobot.plate_reading.plate_reader.PlateReader`). Machines have a `backend` attribute linking to the backend that is responsible for converting PyLabRobot commands into commands that a specific machine can understand. Other than that, Machines, including {class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler`, are just like any other Resource.
+{class}`pylabrobot.machines.machine.Machine` is a special type of resource that represents a physical machine, such as a liquid handling robot ({class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler`) or a plate reader ({class}`pylabrobot.plate_reading.plate_reader.PlateReader`). Machines have a `backend` attribute linking to the backend that is responsible for converting PyLabRobot commands into commands that a specific machine can understand. Other than that, Machines, including {class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler`, are just like any other Resource.
## Defining a simple resource
-The simplest way to define a resource is to subclass {class}`pylabrobot.resources.Resource` and define the `name` and `size_x`, `size_y` and `size_z` attributes. Here's an example of a simple resource:
+The simplest way to define a resource is to subclass {class}`pylabrobot.resources.resource.Resource` and define the `name` and `size_x`, `size_y` and `size_z` attributes. Here's an example of a simple resource:
```python
from pylabrobot.resources import Resource
@@ -28,52 +28,6 @@ child = Resource(name="child", size_x=5, size_y=5, size_z=5)
resource.assign_child_resource(child, Coordinate(x=0, y=0, z=0))
```
-## Some common subclasses of `Resource`
-
-### `Container`: Resources that contain liquid
-
-Resources that contain liquid are subclasses of {class}`pylabrobot.resources.container.Container`. This class provides a {class}`pylabrobot.resources.volume_tracker.VolumeTracker` that helps {class}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler` keep track of the liquid in the resource. (For more information on trackers, check out {doc}`/using-trackers`). Examples of subclasses of `Container` are {class}`pylabrobot.resources.Well` and {class}`pylabrobot.resources.trough.Trough`.
-
-It is possible to instantiate a `Container` directly:
-
-```python
-from pylabrobot.resources import Container
-container = Container(name="container", size_x=10, size_y=10, size_z=10)
-# volume is computed by assuming the container is a cuboid, and can be adjusted with the max_volume
-# parameter
-```
-
-### `ItemizedResource`: Resources that contain items in a grid
-
-Resources that contain items in a grid are subclasses of {class}`pylabrobot.resources.itemized_resource.ItemizedResource`. This class provides convenient methods for accessing the child-resources, such as by integer or SBS "A1" style-notation, as well as for traversing items in an `ItemizedResource`. Examples of subclasses of `ItemizedResource`s are {class}`pylabrobot.resources.plate.Plate` and {class}`pylabrobot.resources.tip_rack.TipRack`.
-
-To instantiate an `ItemizedResource`, it is convenient to use the `pylabrobot.resources.itemized_resource.create_equally_spaced` method to quickly initialize a grid of child-resources in a grid. Here's an example of a simple `ItemizedResource`:
-
-```python
-from pylabrobot.resources import ItemizedResource
-from pylabrobot.resources.itemized_resource import create_equally_spaced
-from pylabrobot.resources.well import Well, WellBottomType
-
-plate = ItemizedResource(
- name="plate",
- size_x=127,
- size_y=86,
- size_z=10,
- items=create_equally_spaced(
- Well, # the class of the items
- num_items_x=12,
- num_items_y=8,
- dx=12, # distance between the first well and the border in the x-axis
- dy=12, # distance between the first well and the border in the y-axis
- dz=0, # distance between the first well and the border in the z-axis
- item_dx=9, # distance between the wells in the x-axis
- item_dy=9, # distance between the wells in the y-axis
-
- bottom_type=WellBottomType.FLAT, # a custom keyword argument passed to the Well initializer
- )
-)
-```
-
## Saving and loading resources
PyLabRobot provide utilities to save and load resources and their states to and from files, as well as to serialize and deserialize resources and their states to and from Python dictionaries.
@@ -82,7 +36,7 @@ PyLabRobot provide utilities to save and load resources and their states to and
#### Saving to and loading from a file
-Resource definitions, that includes deck definitions, can be saved to and loaded from a file using the `pylabrobot.resources.Resource.save` and `pylabrobot.resources.Resource.load` methods. The file format is JSON.
+Resource definitions, that includes deck definitions, can be saved to and loaded from a file using the `pylabrobot.resources.resource.Resource.save` and `pylabrobot.resources.resource.Resource.load` methods. The file format is JSON.
To save a resource to a file:
@@ -133,7 +87,7 @@ Each Resource is responsible for managing its own state, as deep down in the arb
#### Serializing and deserializing state
-The state of a single resource, that includes the volume of a container, can be serialized to and deserialized from a Python dictionary using the `pylabrobot.resources.Resource.serialize_state` and `pylabrobot.resources.Resource.deserialize_state` methods.
+The state of a single resource, that includes the volume of a container, can be serialized to and deserialized from a Python dictionary using the `pylabrobot.resources.resource.Resource.serialize_state` and `pylabrobot.resources.resource.Resource.deserialize_state` methods.
To serialize the state of a resource:
@@ -158,11 +112,11 @@ c.load_state({ "liquids": [], "pending_liquids": [] })
This is convenient if you want to use PLR state in your own state management system, or save to a database.
-Note that above, only the state of a single resource is serialized. If you want to serialize the state of a resource and all its children, you can use the {func}`pylabrobot.resources.Resource.serialize_all_state` and {func}`pylabrobot.resources.Resource.load_all_state` methods. These methods are used internally by the `save_state_to_file` and `load_state_from_file` methods.
+Note that above, only the state of a single resource is serialized. If you want to serialize the state of a resource and all its children, you can use the {meth}`pylabrobot.resources.resource.Resource.serialize_all_state` and {meth}`pylabrobot.resources.resource.Resource.load_all_state` methods. These methods are used internally by the `save_state_to_file` and `load_state_from_file` methods.
#### Saving and loading state to and from a file
-The state of a resource, that includes the volume of a container, can be saved to and loaded from a file using the `pylabrobot.resources.Resource.save_state_to_file` and `pylabrobot.resources.Resource.load_state_from_file` methods. The file format is JSON.
+The state of a resource, that includes the volume of a container, can be saved to and loaded from a file using the `pylabrobot.resources.resource.Resource.save_state_to_file` and `pylabrobot.resources.resource.Resource.load_state_from_file` methods. The file format is JSON.
To save the state of a resource to a file:
diff --git a/docs/resources/itemized_resource.md b/docs/resources/itemized_resource.md
new file mode 100644
index 0000000000..ae70cb3e10
--- /dev/null
+++ b/docs/resources/itemized_resource.md
@@ -0,0 +1,30 @@
+# ItemizedResource
+
+Resources that contain items in a grid are subclasses of {class}`pylabrobot.resources.itemized_resource.ItemizedResource`. This class provides convenient methods for accessing the child-resources, such as by integer or SBS "A1" style-notation, as well as for traversing items in an `ItemizedResource`. Examples of subclasses of `ItemizedResource`s are {class}`pylabrobot.resources.plate.Plate` and {class}`pylabrobot.resources.tip_rack.TipRack`.
+
+To instantiate an `ItemizedResource`, it is convenient to use the `pylabrobot.resources.utils.create_equally_spaced_2d` method to quickly initialize a grid of child-resources in a grid. Here's an example of a simple `ItemizedResource`:
+
+```python
+from pylabrobot.resources import ItemizedResource
+from pylabrobot.resources.utils import create_equally_spaced_2d
+from pylabrobot.resources.well import Well, WellBottomType
+
+plate = ItemizedResource(
+ name="plate",
+ size_x=127,
+ size_y=86,
+ size_z=10,
+ items=create_equally_spaced_2d(
+ Well, # the class of the items
+ num_items_x=12,
+ num_items_y=8,
+ dx=12, # distance between the first well and the border in the x-axis
+ dy=12, # distance between the first well and the border in the y-axis
+ dz=0, # distance between the first well and the border in the z-axis
+ item_dx=9, # distance between the wells in the x-axis
+ item_dy=9, # distance between the wells in the y-axis
+
+ bottom_type=WellBottomType.FLAT, # a custom keyword argument passed to the Well initializer
+ )
+)
+```
diff --git a/docs/resources/library/alpaqua.md b/docs/resources/library/alpaqua.md
new file mode 100644
index 0000000000..7ca7f097f9
--- /dev/null
+++ b/docs/resources/library/alpaqua.md
@@ -0,0 +1,12 @@
+# Alpaqua Engineering, LLC
+
+Company page: [Alpaqua Engineering, LLC](https://www.alpaqua.com/about-us/)
+
+> Alpaqua Engineering, LLC, founded in 2006, is a global provider of tools for accelerating genomic applications such as NGS, nucleic acid extraction and clean up, target capture, and molecular diagnostics.
+Our products include a line of innovative, high performance magnet plates built with proprietary magnet architecture and spring cushion technology. Also available are aluminum tube blocks to help maintain temperature control, SBS /ANSI standard tube racks and the Alpillo® Plate Cushion, which enables pipetting from the bottom of a well without tip occlusion.
+
+## Labware
+
+| Description | Image | PLR definition |
+|-|-|-|
+| 'Alpaqua_96_magnum_flx' Part no.: A000400 [manufacturer website](https://www.alpaqua.com/product/magnum-flx/) | ![](img/alpaqua/Alpaqua_96_magnum_flx.jpg) | `Alpaqua_96_magnum_flx` |
diff --git a/pylabrobot/resources/azenta/README.md b/docs/resources/library/azenta.md
similarity index 80%
rename from pylabrobot/resources/azenta/README.md
rename to docs/resources/library/azenta.md
index cf0cf30377..34ed3ea1a2 100644
--- a/pylabrobot/resources/azenta/README.md
+++ b/docs/resources/library/azenta.md
@@ -1,5 +1,4 @@
-
-## Resource defintions: Azenta
+# Azenta
Company wikipedia: [Azenta](https://en.wikipedia.org/wiki/Azenta)
@@ -7,8 +6,8 @@ Company wikipedia: [Azenta](https://en.wikipedia.org/wiki/Azenta)
> In 2017, Brooks acquired 4titude, a maker of scientific tools and consumables, while in 2018, Brooks acquired GENEWIZ, a genomics services provider as part of their life sciences division's expansion.
> In November 2021, Brooks Automation Inc. split into two entities, Brooks Automation and Azenta Life Sciences. The latter will focus exclusively on their life science division.
-### Currently defined labware:
+## Plates
| Description | Image | PLR definition |
|--------------------|--------------------|--------------------|
-| 'Azenta4titudeFrameStar_96_wellplate_skirted'
- Man. part no.: 4ti-0960 - Supplier part no.: PCR1232 - [manufacturer website](https://www.azenta.com/products/framestar-96-well-skirted-pcr-plate) - [supplier website](https://www.scientificlabs.co.uk/product/pcr-plates/PCR1232) - working volume: <100µl - total well capacity: 200µl| | `Azenta4titudeFrameStar_96_wellplate_skirted` |
+| 'Azenta4titudeFrameStar_96_wellplate_skirted'
- Man. part no.: 4ti-0960 - Supplier part no.: PCR1232 - [manufacturer website](https://www.azenta.com/products/framestar-96-well-skirted-pcr-plate) - [supplier website](https://www.scientificlabs.co.uk/product/pcr-plates/PCR1232) - working volume: <100µl - total well capacity: 200µl| ![](img/azenta/azenta_4titude_96PCR_4ti-0960.jpg) | `Azenta4titudeFrameStar_96_wellplate_skirted` |
diff --git a/pylabrobot/resources/boekel/README.md b/docs/resources/library/boekel.md
similarity index 63%
rename from pylabrobot/resources/boekel/README.md
rename to docs/resources/library/boekel.md
index cacf2b7abc..5e23166b93 100644
--- a/pylabrobot/resources/boekel/README.md
+++ b/docs/resources/library/boekel.md
@@ -1,4 +1,4 @@
-# Resource definitions: Boekel
+# Boekel
## Tube carrier
@@ -11,7 +11,7 @@ The following rack exists in 4 orientations:
| Description | Image | PLR definition |
|--------------------|--------------------|--------------------|
-| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | | `boekel_50mL_falcon_carrier` |
-| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | | `boekel_15mL_falcon_carrier` |
-| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | | `boekel_1_5mL_microcentrifuge_carrier` |
-| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | | `boekel_mini_microcentrifuge_carrier` |
+| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | ![](img/boekel/boekel_carrier50mL.jpg) | `boekel_50mL_falcon_carrier` |
+| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | ![](img/boekel/boekel_carrier15mL.jpg) | `boekel_15mL_falcon_carrier` |
+| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | ![](img/boekel/boekel_carrier1_5mL.jpg) | `boekel_1_5mL_microcentrifuge_carrier` |
+| Multi Tube Rack For 50ml Conical, 15ml Conical, And Microcentrifuge Tubes, PN:120008 [manufacturer website](https://www.boekelsci.com/multi-tube-rack-for-50ml-conical-15ml-conical-and-microcentrifuge-tubes-pn-120008.html) | ![](img/boekel/boekel_carrier_mini.jpg) | `boekel_mini_microcentrifuge_carrier` |
diff --git a/docs/resources/library/celltreat.md b/docs/resources/library/celltreat.md
new file mode 100644
index 0000000000..23a437fc93
--- /dev/null
+++ b/docs/resources/library/celltreat.md
@@ -0,0 +1,8 @@
+# CellTreat
+
+## Plats
+
+| Description | Image | PLR definition |
+|-|-|-|
+| 'CellTreat_6_DWP_16300ul_Fb' Part no.: 229105 [manufacturer website](https://www.celltreat.com/product/229105/) | ![](img/celltreat/CellTreat_6_DWP_16300ul_Fb.jpg) | `CellTreat_6_DWP_16300ul_Fb` |
+| 'CellTreat_96_DWP_350ul_Ub' Part no.: 229591 [manufacturer website](https://www.celltreat.com/product/229591/) | ![](img/celltreat/CellTreat_96_DWP_350ul_Ub.jpg) | `CellTreat_96_DWP_350ul_Ub` |
diff --git a/pylabrobot/resources/corning_axygen/README.md b/docs/resources/library/corning_axygen.md
similarity index 72%
rename from pylabrobot/resources/corning_axygen/README.md
rename to docs/resources/library/corning_axygen.md
index 0dad75974a..8e5438ee2e 100644
--- a/pylabrobot/resources/corning_axygen/README.md
+++ b/docs/resources/library/corning_axygen.md
@@ -1,12 +1,11 @@
-
-## Resource defintions: Corning - Axygen
+# Corning - Axygen
Company page: [Corning - Axygen® Brand Products](https://www.corning.com/emea/en/products/life-sciences/resources/brands/axygen-brand-products.html)
> Corning acquired Axygen BioScience, Inc. and its subsidiaries in 2009. This acquisition included Axygen's broad portfolio of high-quality plastic consumables, liquid handling products, and bench-top laboratory equipment, which complemented and expanded Corning's offerings in the life sciences segment.
-### Currently defined labware:
+## Plates
-| Description | Image |
-|--------------------|--------------------|
-| 'Axy_24_DW_10ML' Part no.: P-DW-10ML-24-C-S [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/UK/en/Genomics-&-Molecular-Biology/Automation-Consumables/Deep-Well-Plate/Axygen%C2%AE-Deep-Well-and-Assay-Plates/p/P-DW-10ML-24-C-S) | | `Axy_24_DW_10ML` |
+| Description | Image | PLR definition |
+|-|-|-|
+| 'Axy_24_DW_10ML' Part no.: P-DW-10ML-24-C-S [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/UK/en/Genomics-&-Molecular-Biology/Automation-Consumables/Deep-Well-Plate/Axygen%C2%AE-Deep-Well-and-Assay-Plates/p/P-DW-10ML-24-C-S) | ![](img/corning_axygen/axygen_Axy_24_DW_10ML.jpg) | `Axy_24_DW_10ML` |
diff --git a/docs/resources/library/corning_costar.md b/docs/resources/library/corning_costar.md
new file mode 100644
index 0000000000..4a55ab1bb2
--- /dev/null
+++ b/docs/resources/library/corning_costar.md
@@ -0,0 +1,15 @@
+# Corning - Costar
+
+Wikipedia page: [Corning](https://en.wikipedia.org/wiki/Corning_Inc.)
+
+> CCorning Incorporated is an American multinational technology company that specializes in specialty glass, ceramics, and related materials and technologies including advanced optics, primarily for industrial and scientific applications. The company was named Corning Glass Works until 1989. Corning divested its consumer product lines (including CorningWare and Visions Pyroceram-based cookware, Corelle Vitrelle tableware, and Pyrex glass bakeware) in 1998 by selling the Corning Consumer Products Company subsidiary (later Corelle Brands, now known as Instant Brands) to Borden.
+
+As of 2014, Corning had five major business sectors: display technologies, environmental technologies, life sciences, optical communications, and specialty materials.
+
+## Plates
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Cos_6_MWP_16800ul_Fb' Part no.: 3516 [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/UK/en/Microplates/Assay-Microplates/96-Well-Microplates/Costar%C2%AE-Multiple-Well-Cell-Culture-Plates/p/3516)
- Material: ? - Cleanliness: 3516: sterilized by gamma irradiation - Nonreversible lids with condensation rings to reduce contamination - Treated for optimal cell attachment - Cell growth area: 9.5 cm² (approx.) - Total volume: 16.8 mL| ![](img/corning_costar/Cos_6_MWP_16800ul_Fb.jpg) | `Cos_6_MWP_16800ul_Fb` |
+| 'Cos_96_DWP_2mL_Vb' Part no.: 3516 [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/UK/en/Microplates/Assay-Microplates/96-Well-Microplates/Costar%C2%AE-Multiple-Well-Cell-Culture-Plates/p/3516)
- Material: Polypropylene - Resistant to many common organic solvents (e.g., DMSO, ethanol, methanol) - 3960: Sterile and DNase- and RNase-free - Total volume: 2 mL - Features uniform skirt heights for greater robotic gripping surface| ![](img/corning_costar/Cos_96_DWP_2mL_Vb.jpg) | `Cos_96_DWP_2mL_Vb` |
+[ 'Cor_96_wellplate_360ul_Fb' Part no.: 353376 [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/NL/en/Microplates/Assay-Microplates/96-Well-Microplates/Falcon®-96-well-Polystyrene-Microplates/p/353376)
- Material: TC-treated polystyrene - Cleanliness: sterile - Total volume: 392 uL - Working volume: 25-340 uL | ![](img/corning_costar/Cos_96_wellplate_360ul_Fb.jpg) | `Cor_96_wellplate_360ul_Fb` |
diff --git a/docs/resources/library/eppendorf.md b/docs/resources/library/eppendorf.md
new file mode 100644
index 0000000000..0c70ad2f81
--- /dev/null
+++ b/docs/resources/library/eppendorf.md
@@ -0,0 +1,15 @@
+# Eppendorf
+
+Company page: [Eppendorf Wikipedia](https://en.wikipedia.org/wiki/Eppendorf_(company))
+
+> Eppendorf, a company with its registered office in Germany, develops, produces and sells products and services for laboratories around the world.
+
+> Founding year: 1945
+> Company type: private
+
+
+## Plates
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Eppendorf_96_wellplate_250ul_Vb' Part no.: 0030133374 [manufacturer website](https://www.eppendorf.com/gb-en/Products/Laboratory-Consumables/Plates/Eppendorf-twintec-PCR-Plates-p-0030133374)
- Material: polycarbonate (frame), polypropylene (wells) - part of the twin.tec(R) product line - WARNING: not ANSI/SLAS 1-2004 footprint dimenions (123x81 mm^2!) ==> requires `PlateAdapter` - 'Can be divided into 4 segments of 24 wells each to prevent waste and save money'. | ![](img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_COMPLETE.png) ![](img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_DIVIDED.png) | `Eppendorf_96_wellplate_250ul_Vb` |
diff --git a/docs/resources/library/falcon.md b/docs/resources/library/falcon.md
new file mode 100644
index 0000000000..f6432be107
--- /dev/null
+++ b/docs/resources/library/falcon.md
@@ -0,0 +1,9 @@
+# Falcon
+
+## Tubes
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 50mL Falcon Tube [manufacturer website](https://www.fishersci.com/shop/products/falcon-50ml-conical-centrifuge-tubes-2/1495949A) | ![](img/falcon/falcon-tube-50mL.webp) | `falcon_tube_50mL`
+| 15mL Falcon Tube [manufacturer website](https://www.fishersci.com/shop/products/falcon-15ml-conical-centrifuge-tubes-5/p-193301) | ![](img/falcon/falcon-tube-15mL.webp) | `falcon_tube_15mL`
+| Falcon_tube_14mL_Rb Corning cat. no.: 352059 [manufacturer website](https://ecatalog.corning.com/life-sciences/b2b/UK/en/General-Labware/Tubes/Tubes,-Round-Bottom/Falcon%C2%AE-Round-Bottom-High-clarity-Polypropylene-Tube/p/352059) | ![](img/falcon/Falcon_tube_14mL_Rb.jpg) | `Falcon_tube_14mL_Rb`
diff --git a/docs/resources/library/img/alpaqua/Alpaqua_96_magnum_flx.jpg b/docs/resources/library/img/alpaqua/Alpaqua_96_magnum_flx.jpg
new file mode 100644
index 0000000000..d774f36e2a
Binary files /dev/null and b/docs/resources/library/img/alpaqua/Alpaqua_96_magnum_flx.jpg differ
diff --git a/pylabrobot/resources/azenta/ims/azenta_4titude_96PCR_4ti-0960.jpg b/docs/resources/library/img/azenta/azenta_4titude_96PCR_4ti-0960.jpg
similarity index 100%
rename from pylabrobot/resources/azenta/ims/azenta_4titude_96PCR_4ti-0960.jpg
rename to docs/resources/library/img/azenta/azenta_4titude_96PCR_4ti-0960.jpg
diff --git a/pylabrobot/resources/boekel/imgs/boekel_carrier15mL.jpg b/docs/resources/library/img/boekel/boekel_carrier15mL.jpg
similarity index 100%
rename from pylabrobot/resources/boekel/imgs/boekel_carrier15mL.jpg
rename to docs/resources/library/img/boekel/boekel_carrier15mL.jpg
diff --git a/pylabrobot/resources/boekel/imgs/boekel_carrier1_5mL.jpg b/docs/resources/library/img/boekel/boekel_carrier1_5mL.jpg
similarity index 100%
rename from pylabrobot/resources/boekel/imgs/boekel_carrier1_5mL.jpg
rename to docs/resources/library/img/boekel/boekel_carrier1_5mL.jpg
diff --git a/pylabrobot/resources/boekel/imgs/boekel_carrier50mL.jpg b/docs/resources/library/img/boekel/boekel_carrier50mL.jpg
similarity index 100%
rename from pylabrobot/resources/boekel/imgs/boekel_carrier50mL.jpg
rename to docs/resources/library/img/boekel/boekel_carrier50mL.jpg
diff --git a/pylabrobot/resources/boekel/imgs/boekel_carrier_mini.jpg b/docs/resources/library/img/boekel/boekel_carrier_mini.jpg
similarity index 100%
rename from pylabrobot/resources/boekel/imgs/boekel_carrier_mini.jpg
rename to docs/resources/library/img/boekel/boekel_carrier_mini.jpg
diff --git a/docs/resources/library/img/celltreat/CellTreat_6_DWP_16300ul_Fb.jpg b/docs/resources/library/img/celltreat/CellTreat_6_DWP_16300ul_Fb.jpg
new file mode 100644
index 0000000000..8db01e4f44
Binary files /dev/null and b/docs/resources/library/img/celltreat/CellTreat_6_DWP_16300ul_Fb.jpg differ
diff --git a/docs/resources/library/img/celltreat/CellTreat_96_DWP_350ul_Ub.jpg b/docs/resources/library/img/celltreat/CellTreat_96_DWP_350ul_Ub.jpg
new file mode 100644
index 0000000000..3c1193b04a
Binary files /dev/null and b/docs/resources/library/img/celltreat/CellTreat_96_DWP_350ul_Ub.jpg differ
diff --git a/pylabrobot/resources/corning_axygen/ims/axygen_Axy_24_DW_10ML.jpg b/docs/resources/library/img/corning_axygen/axygen_Axy_24_DW_10ML.jpg
similarity index 100%
rename from pylabrobot/resources/corning_axygen/ims/axygen_Axy_24_DW_10ML.jpg
rename to docs/resources/library/img/corning_axygen/axygen_Axy_24_DW_10ML.jpg
diff --git a/docs/resources/library/img/corning_costar/Cos_6_MWP_16800ul_Fb.jpg b/docs/resources/library/img/corning_costar/Cos_6_MWP_16800ul_Fb.jpg
new file mode 100644
index 0000000000..5ff1c3909c
Binary files /dev/null and b/docs/resources/library/img/corning_costar/Cos_6_MWP_16800ul_Fb.jpg differ
diff --git a/docs/resources/library/img/corning_costar/Cos_96_DWP_2mL_Vb.jpg b/docs/resources/library/img/corning_costar/Cos_96_DWP_2mL_Vb.jpg
new file mode 100644
index 0000000000..d357b3148d
Binary files /dev/null and b/docs/resources/library/img/corning_costar/Cos_96_DWP_2mL_Vb.jpg differ
diff --git a/docs/resources/library/img/corning_costar/Cos_96_wellplate_360ul_Fb.jpg b/docs/resources/library/img/corning_costar/Cos_96_wellplate_360ul_Fb.jpg
new file mode 100644
index 0000000000..47445653fb
Binary files /dev/null and b/docs/resources/library/img/corning_costar/Cos_96_wellplate_360ul_Fb.jpg differ
diff --git a/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_COMPLETE.png b/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_COMPLETE.png
new file mode 100644
index 0000000000..ee9a0b4b7b
Binary files /dev/null and b/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_COMPLETE.png differ
diff --git a/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_DIVIDED.png b/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_DIVIDED.png
new file mode 100644
index 0000000000..b2ad38823c
Binary files /dev/null and b/docs/resources/library/img/eppendorf/Eppendorf_96_wellplate_250ul_Vb_DIVIDED.png differ
diff --git a/pylabrobot/resources/falcon/imgs/Falcon_tube_14mL_Rb.jpg b/docs/resources/library/img/falcon/Falcon_tube_14mL_Rb.jpg
similarity index 100%
rename from pylabrobot/resources/falcon/imgs/Falcon_tube_14mL_Rb.jpg
rename to docs/resources/library/img/falcon/Falcon_tube_14mL_Rb.jpg
diff --git a/pylabrobot/resources/falcon/imgs/falcon-tube-15mL.webp b/docs/resources/library/img/falcon/falcon-tube-15mL.webp
similarity index 100%
rename from pylabrobot/resources/falcon/imgs/falcon-tube-15mL.webp
rename to docs/resources/library/img/falcon/falcon-tube-15mL.webp
diff --git a/pylabrobot/resources/falcon/imgs/falcon-tube-50mL.webp b/docs/resources/library/img/falcon/falcon-tube-50mL.webp
similarity index 100%
rename from pylabrobot/resources/falcon/imgs/falcon-tube-50mL.webp
rename to docs/resources/library/img/falcon/falcon-tube-50mL.webp
diff --git a/pylabrobot/resources/ml_star/ims/Hamilton_1_trough_200ml_Vb.jpg b/docs/resources/library/img/ml_star/Hamilton_1_trough_200ml_Vb.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/Hamilton_1_trough_200ml_Vb.jpg
rename to docs/resources/library/img/ml_star/Hamilton_1_trough_200ml_Vb.jpg
diff --git a/pylabrobot/resources/ml_star/ims/Hamilton_96_adapter_188182.png b/docs/resources/library/img/ml_star/Hamilton_96_adapter_188182.png
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/Hamilton_96_adapter_188182.png
rename to docs/resources/library/img/ml_star/Hamilton_96_adapter_188182.png
diff --git a/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR.png b/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR.png
new file mode 100644
index 0000000000..85869b4ef0
Binary files /dev/null and b/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR.png differ
diff --git a/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR_CLEAR.png b/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR_CLEAR.png
new file mode 100644
index 0000000000..17420acfda
Binary files /dev/null and b/docs/resources/library/img/ml_star/Hamilton_96_tiprack_50ul_NTR_CLEAR.png differ
diff --git a/pylabrobot/resources/ml_star/ims/Hamilton_carrier_naming_guide.png b/docs/resources/library/img/ml_star/Hamilton_carrier_naming_guide.png
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/Hamilton_carrier_naming_guide.png
rename to docs/resources/library/img/ml_star/Hamilton_carrier_naming_guide.png
diff --git a/pylabrobot/resources/ml_star/ims/MFX_CAR_L4_SHAKER_187001.png b/docs/resources/library/img/ml_star/MFX_CAR_L4_SHAKER_187001.png
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/MFX_CAR_L4_SHAKER_187001.png
rename to docs/resources/library/img/ml_star/MFX_CAR_L4_SHAKER_187001.png
diff --git a/pylabrobot/resources/ml_star/ims/MFX_CAR_L5_base_188039.jpg b/docs/resources/library/img/ml_star/MFX_CAR_L5_base_188039.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/MFX_CAR_L5_base_188039.jpg
rename to docs/resources/library/img/ml_star/MFX_CAR_L5_base_188039.jpg
diff --git a/pylabrobot/resources/ml_star/ims/MFX_DWP_RB_module_188229_.jpg b/docs/resources/library/img/ml_star/MFX_DWP_RB_module_188229_.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/MFX_DWP_RB_module_188229_.jpg
rename to docs/resources/library/img/ml_star/MFX_DWP_RB_module_188229_.jpg
diff --git a/pylabrobot/resources/ml_star/ims/MFX_TIP_module_188040.jpg b/docs/resources/library/img/ml_star/MFX_TIP_module_188040.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/MFX_TIP_module_188040.jpg
rename to docs/resources/library/img/ml_star/MFX_TIP_module_188040.jpg
diff --git a/pylabrobot/resources/ml_star/ims/PLT_CAR_L5AC_A00_182090.jpg b/docs/resources/library/img/ml_star/PLT_CAR_L5AC_A00_182090.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/PLT_CAR_L5AC_A00_182090.jpg
rename to docs/resources/library/img/ml_star/PLT_CAR_L5AC_A00_182090.jpg
diff --git a/docs/resources/library/img/ml_star/TIP_50ul_L.png b/docs/resources/library/img/ml_star/TIP_50ul_L.png
new file mode 100644
index 0000000000..d10e4b91aa
Binary files /dev/null and b/docs/resources/library/img/ml_star/TIP_50ul_L.png differ
diff --git a/pylabrobot/resources/ml_star/ims/TIP_CAR_480_A00_182085.jpg b/docs/resources/library/img/ml_star/TIP_CAR_480_A00_182085.jpg
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/TIP_CAR_480_A00_182085.jpg
rename to docs/resources/library/img/ml_star/TIP_CAR_480_A00_182085.jpg
diff --git a/pylabrobot/resources/ml_star/ims/Trough_CAR_4R200_A00.png b/docs/resources/library/img/ml_star/Trough_CAR_4R200_A00.png
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/Trough_CAR_4R200_A00.png
rename to docs/resources/library/img/ml_star/Trough_CAR_4R200_A00.png
diff --git a/pylabrobot/resources/ml_star/ims/Tube_CAR_24_A00.png b/docs/resources/library/img/ml_star/Tube_CAR_24_A00.png
similarity index 100%
rename from pylabrobot/resources/ml_star/ims/Tube_CAR_24_A00.png
rename to docs/resources/library/img/ml_star/Tube_CAR_24_A00.png
diff --git a/docs/resources/library/img/opentrons/Opentrons_96_adapter_Vb.jpg b/docs/resources/library/img/opentrons/Opentrons_96_adapter_Vb.jpg
new file mode 100644
index 0000000000..d68210e97b
Binary files /dev/null and b/docs/resources/library/img/opentrons/Opentrons_96_adapter_Vb.jpg differ
diff --git a/pylabrobot/resources/porvair/ims/porvair_6x47_reservoir_390015.jpg b/docs/resources/library/img/porvair/porvair_6x47_reservoir_390015.jpg
similarity index 100%
rename from pylabrobot/resources/porvair/ims/porvair_6x47_reservoir_390015.jpg
rename to docs/resources/library/img/porvair/porvair_6x47_reservoir_390015.jpg
diff --git a/pylabrobot/resources/revvity/ims/revvity_ProxiPlate-384-Plus-White-384-shallow-well-Microplate.jpg b/docs/resources/library/img/revvity/revvity_ProxiPlate-384-Plus-White-384-shallow-well-Microplate.jpg
similarity index 100%
rename from pylabrobot/resources/revvity/ims/revvity_ProxiPlate-384-Plus-White-384-shallow-well-Microplate.jpg
rename to docs/resources/library/img/revvity/revvity_ProxiPlate-384-Plus-White-384-shallow-well-Microplate.jpg
diff --git a/pylabrobot/resources/thermo_fisher/imgs/ThermoFisherMatrixTrough8094.jpg.avif b/docs/resources/library/img/thermo_fisher/ThermoFisherMatrixTrough8094.jpg.avif
similarity index 100%
rename from pylabrobot/resources/thermo_fisher/imgs/ThermoFisherMatrixTrough8094.jpg.avif
rename to docs/resources/library/img/thermo_fisher/ThermoFisherMatrixTrough8094.jpg.avif
diff --git a/pylabrobot/resources/thermo_fisher/imgs/Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.png b/docs/resources/library/img/thermo_fisher/Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.png
similarity index 100%
rename from pylabrobot/resources/thermo_fisher/imgs/Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.png
rename to docs/resources/library/img/thermo_fisher/Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.png
diff --git a/pylabrobot/resources/thermo_fisher/imgs/Thermo_TS_96_wellplate_1200ul_Rb.webp b/docs/resources/library/img/thermo_fisher/Thermo_TS_96_wellplate_1200ul_Rb.webp
similarity index 100%
rename from pylabrobot/resources/thermo_fisher/imgs/Thermo_TS_96_wellplate_1200ul_Rb.webp
rename to docs/resources/library/img/thermo_fisher/Thermo_TS_96_wellplate_1200ul_Rb.webp
diff --git a/docs/resources/library/ml_star.md b/docs/resources/library/ml_star.md
new file mode 100644
index 0000000000..46af84620d
--- /dev/null
+++ b/docs/resources/library/ml_star.md
@@ -0,0 +1,70 @@
+# Hamilton STAR "ML_STAR"
+
+Company history: [Hamilton Robotics history](https://www.hamiltoncompany.com/history)
+
+> Hamilton Robotics provides automated liquid handling workstations for the scientific community. Our portfolio includes three liquid handling platforms, small devices, consumables, and OEM solutions.
+
+## Carriers
+
+### Tip carriers
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'TIP_CAR_480_A00' Part no.: 182085 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/182085) Carrier for 5x 96 tip (10μl, 50μl, 300μl, 1000μl) racks or 5x 24 tip (5ml) racks (6T) | ![](img/ml_star/TIP_CAR_480_A00_182085.jpg) | `TIP_CAR_480_A00` |
+
+### Plate carriers
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'PLT_CAR_L5AC_A00' Part no.: 182090 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/182090) Carrier for 5x 96 Deep Well Plates or for 5x 384 tip racks (e.g.384HEAD_384TIPS_50μl) (6T) | ![](img/ml_star/PLT_CAR_L5AC_A00_182090.jpg) | `PLT_CAR_L5AC_A00` |
+
+### MFX carriers
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'MFX_CAR_L5_base' Part no.: 188039 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/188039) Labware carrier base for up to 5 Multiflex Modules Occupies 6 tracks (6T). | ![](img/ml_star/MFX_CAR_L5_base_188039.jpg) | `MFX_CAR_L5_base` |
+| 'MFX_CAR_L4_SHAKER' Part no.: 187001 [secondary supplier website](https://www.testmart.com/estore/unit.cfm/PIPPET/HAMROB/187001/automated_pippetting_devices_and_systems/8.html) (cannot find information on Hamilton website) Sometimes referred to as "PLT_CAR_L4_SHAKER" by Hamilton. Template carrier with 4 positions for Hamilton Heater Shaker. Occupies 7 tracks (7T). Can be screwed onto the deck. | ![](img/ml_star/MFX_CAR_L4_SHAKER_187001.png) | `MFX_CAR_L4_SHAKER_187001` |
+
+### MFX modules
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'MFX_TIP_module' Part no.: 188160 or 188040 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/188040) Module to position a high-, standard-, low volume or 5ml tip rack (but not a 384 tip rack) | ![](img/ml_star/MFX_TIP_module_188040.jpg) | `MFX_TIP_module` |
+| 'MFX_DWP_rackbased_module' Part no.: 188229? [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/188229) (<-non-functional link?) MFX DWP module rack-based | ![](img/ml_star/MFX_DWP_RB_module_188229_.jpg) | `MFX_DWP_rackbased_module` |
+
+### Tube carriers
+
+Sometimes called "sample carriers" in Hamilton jargon.
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Tube_CAR_24_A00' Part no.: 173400 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/173400) Carries 24 "sample" tubes with 14.5–18 mm outer diameter, 60–120 mm high. Occupies one track. | ![](img/ml_star/Tube_CAR_24_A00.png) | `Tube_CAR_24_A00` |
+
+### Trough carriers
+
+Sometimes called "reagent carriers" in Hamilton jargon.
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Trough_CAR_4R200_A00' Part no.: 185436 (same as 96890-01?) [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/96890-01) Trough carrier for 4x 200ml troughs. 2 tracks(T) wide. | ![](img/ml_star/Trough_CAR_4R200_A00.png) | `Trough_CAR_4R200_A00` |
+
+## Labware
+
+### TipRacks
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'TIP_50ul_L' Formats: - "50μL CO-RE Tips, sterile with filter": Part no.: [235979](https://www.hamiltoncompany.com/automated-liquid-handling/disposable-tips/50-%CE%BCl-conductive-sterile-filter-tips) • Filter=Filter • Sterile=Sterile • Tip Color (Conductivity)=Black (Conductive) | ![](img/ml_star/TIP_50ul_L.png) | `TIP_50ul_L` |
+| 'Hamilton_96_tiprack_50ul_NTR' Formats: - "50μL CO-REII Tips, stacked NTRs, sterile": Part no.: [235987](https://www.hamiltoncompany.com/automated-liquid-handling/disposable-tips/50-%C2%B5l-nested-clear-sterile-tips) • Filter=Non-Filter • Sterile=Sterile • Tip Color (Conductivity)=Black (Conductive) - "50uL CO-REII Nested Clear Tips": Part no.: [235964](https://www.hamiltoncompany.com/automated-liquid-handling/disposable-tips/50-%C2%B5l-nested-clear-tips) • Filter=Non-Filter • Sterile=Non-Sterile • Tip Color (Conductivity)=Clear (Non-Conductive)
Note: a **single** `NTR` is only **one rack**. Multiple NTRs stacked on top of each other (as shown in the images on the right) are called a `TipStack`. | ![](img/ml_star/Hamilton_96_tiprack_50ul_NTR.png) ![](img/ml_star/Hamilton_96_tiprack_50ul_NTR_CLEAR.png) | `Hamilton_96_tiprack_50ul_NTR` |
+
+### Troughs
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Hamilton_1_trough_200ml_Vb' Part no.: 56695-02 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/56695-02) Trough 200ml, w lid, self standing, Black. Compatible with Trough_CAR_4R200_A00 (185436). | ![](img/ml_star/Hamilton_1_trough_200ml_Vb.jpg) | `Hamilton_1_trough_200ml_Vb` |
+
+## Adapters
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Hamilton_96_adapter_188182' Part no.: 188182 [manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/188182) (<-non-functional link?) Adapter for 96 well PCR plate, plunged. Does not have an ANSI/SLAS footprint -> requires assignment with specified location. | ![](img/ml_star/Hamilton_96_adapter_188182.png) | `Hamilton_96_adapter_188182` |
diff --git a/docs/resources/library/opentrons.md b/docs/resources/library/opentrons.md
new file mode 100644
index 0000000000..949f76c176
--- /dev/null
+++ b/docs/resources/library/opentrons.md
@@ -0,0 +1,88 @@
+# Opentrons
+
+Company page: [Opentrons Wikipedia](https://en.wikipedia.org/wiki/Opentrons)
+
+> Opentrons Labworks, Inc. (or Opentrons) is a biotechnology company that manufactures liquid handling robots that use open-source software, which at one point used open-source hardware but no longer does.
+
+NB: The [Opentrons Labware Library](https://labware.opentrons.com/) is a wonderful resource to see what Opentrons offers in terms of resources.
+
+We can automatically convert Opentrons resources to PLR resources using two methods in `pylabrobot.resources.opentrons`:
+
+- {func}`pylabrobot.resources.opentrons.load.load_opentrons_resource`: loading from a file
+- {func}`pylabrobot.resources.opentrons.load.load_shared_opentrons_resource`: load from https://pypi.org/project/opentrons-shared-data/ (https://github.com/Opentrons/opentrons/tree/edge/shared-data)
+
+In addition, we provide convenience methods for loading many resources (see below).
+
+## Plates
+
+Note that Opentrons definitions typically lack information that is required to make them work on other robots.
+
+- `corning_384_wellplate_112ul_flat`
+- `corning_96_wellplate_360ul_flat`
+- `nest_96_wellplate_2ml_deep`
+- `nest_96_wellplate_100ul_pcr_full_skirt`
+- `appliedbiosystemsmicroamp_384_wellplate_40ul`
+- `thermoscientificnunc_96_wellplate_2000ul`
+- `usascientific_96_wellplate_2point4ml_deep`
+- `thermoscientificnunc_96_wellplate_1300ul`
+- `nest_96_wellplate_200ul_flat`
+- `corning_6_wellplate_16point8ml_flat`
+- `corning_24_wellplate_3point4ml_flat`
+- `corning_12_wellplate_6point9ml_flat`
+- `biorad_96_wellplate_200ul_pcr`
+- `corning_48_wellplate_1point6ml_flat`
+- `biorad_384_wellplate_50ul`
+
+## Tip racks
+
+- `eppendorf_96_tiprack_1000ul_eptips`
+- `tipone_96_tiprack_200ul`
+- `opentrons_96_tiprack_300ul`
+- `opentrons_96_tiprack_10ul`
+- `opentrons_96_filtertiprack_10ul`
+- `geb_96_tiprack_10ul`
+- `opentrons_96_filtertiprack_200ul`
+- `eppendorf_96_tiprack_10ul_eptips`
+- `opentrons_96_tiprack_1000ul`
+- `opentrons_96_tiprack_20ul`
+- `opentrons_96_filtertiprack_1000ul`
+- `opentrons_96_filtertiprack_20ul`
+- `geb_96_tiprack_1000ul`
+
+## Reservoirs
+
+- `agilent_1_reservoir_290ml`
+- `axygen_1_reservoir_90ml`
+- `nest_12_reservoir_15ml`
+- `nest_1_reservoir_195ml`
+- `nest_1_reservoir_290ml`
+- `usascientific_12_reservoir_22ml`
+
+## Tube racks
+
+- `opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap`
+- `opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap_acrylic`
+- `opentrons_6_tuberack_falcon_50ml_conical`
+- `opentrons_15_tuberack_nest_15ml_conical`
+- `opentrons_24_tuberack_nest_2ml_screwcap`
+- `opentrons_24_tuberack_generic_0point75ml_snapcap_acrylic`
+- `opentrons_10_tuberack_nest_4x50ml_6x15ml_conical`
+- `opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical_acrylic`
+- `opentrons_24_tuberack_nest_1point5ml_screwcap`
+- `opentrons_24_tuberack_nest_1point5ml_snapcap`
+- `opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical`
+- `opentrons_24_tuberack_nest_2ml_snapcap`
+- `opentrons_24_tuberack_nest_0point5ml_screwcap`
+- `opentrons_24_tuberack_eppendorf_1point5ml_safelock_snapcap`
+- `opentrons_6_tuberack_nest_50ml_conical`
+- `opentrons_15_tuberack_falcon_15ml_conical`
+- `opentrons_24_tuberack_generic_2ml_screwcap`
+- `opentrons_96_well_aluminum_block`
+- `opentrons_24_aluminumblock_generic_2ml_screwcap`
+- `opentrons_24_aluminumblock_nest_1point5ml_snapcap`
+
+## Plate Adapters
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Opentrons_96_adapter_Vb' Part no.: 999-00028 (one of the three adapters purchased in the "Aluminum Block Set") [manufacturer website](https://opentrons.com/products/aluminum-block-set) | ![](imgs/Opentrons_96_adapter_Vb.jpg" alt="Opentrons_96_adapter_Vb" width="250"/> | `Opentrons_96_adapter_Vb` |
diff --git a/pylabrobot/resources/porvair/README.md b/docs/resources/library/porvair.md
similarity index 51%
rename from pylabrobot/resources/porvair/README.md
rename to docs/resources/library/porvair.md
index a4d528c494..8d20653956 100644
--- a/pylabrobot/resources/porvair/README.md
+++ b/docs/resources/library/porvair.md
@@ -1,12 +1,11 @@
-
-## Resource defintions: Porvair
+# Porvair
Company history: [Porvair Filtration Group](https://www.porvairfiltration.com/about/our-history/)
> Porvair Filtration Group, a wholly owned subsidiary of Porvair plc, is a specialist filtration and environmental technology group involved in developing, designing and manufacturing filtration and separation solutions to industry sectors such as the aviation, molten metal, energy, water treatment and life sciences markets. Porvair plc is a publically owned company with four principal subsidiaries: Porvair Filtration Group Ltd., Porvair Sciences Ltd., Selee Corporation and Seal Analytical Ltd.
-### Currently defined labware:
+## Reservoirs
| Description | Image | PLR definition |
|--------------------|--------------------|--------------------|
-| 'Porvair_6x47_Reservoir' Part no.: 6008280 [manufacturer website](https://www.microplates.com/product/282-ml-reservoir-plate-6-columns-v-bottom/) | | `Porvair_6x47_Reservoir` |
+| 'Porvair_6_reservoir_47ml_Vb' Part no.: 6008280 [manufacturer website](https://www.microplates.com/product/282-ml-reservoir-plate-6-columns-v-bottom/) - Material: Polypropylene - Sterilization compatibility: Autoclaving (15 minutes at 121°C) or Gamma Irradiation - Chemical resistance: "High chemical resistance" - Temperature resistance: high: -196°C to + 121°C - Cleanliness: 390015: Free of detectable DNase, RNase - ANSI/SLAS-format for compatibility with automated systems - Tolerances: "Uniform external dimensions and tolerances"| ![](img/porvair/porvair_6x47_reservoir_390015.jpg) | `Porvair_6_reservoir_47ml_Vb` |
diff --git a/pylabrobot/resources/revvity/README.md b/docs/resources/library/revvity.md
similarity index 82%
rename from pylabrobot/resources/revvity/README.md
rename to docs/resources/library/revvity.md
index c11226ccd7..6ca88c0134 100644
--- a/pylabrobot/resources/revvity/README.md
+++ b/docs/resources/library/revvity.md
@@ -1,12 +1,11 @@
-
-## Resource defintions: Revvity
+# Revvity
Company wikipedia: [Revvity, Inc. (formerly PerkinElmer, Inc.)](https://en.wikipedia.org/wiki/Revvity)
> In 2022, a split of PerkinElmer resulted in one part, comprising its applied, food and enterprise services businesses, being sold to the private equity firm New Mountain Capital for $2.45 billion and thus no longer being public but keeping the PerkinElmer name. The other part, comprised of the life sciences and diagnostics businesses, remained public but required a new name, which in 2023 was announced as Revvity, Inc. From the perspective of Revvity, the goal of creating a separate company was that its businesses might show greater profit margins and more in the way of growth potential. An associated goal was to have more financial flexibility moving forward. On May 16, 2023, the PerkinElmer stock symbol PKI was replaced by the new symbol RVTY.
-### Currently defined labware:
+## Plates
| Description | Image | PLR definition |
|--------------------|--------------------|--------------------|
-| 'Revvity_ProxiPlate_384Plus' Part no.: 6008280 [manufacturer website](https://www.perkinelmer.com/uk/Product/proxiplate-384-plus-50w-6008280) | | `Revvity_ProxiPlate_384Plus`
+| 'Revvity_ProxiPlate_384Plus' Part no.: 6008280 [manufacturer website](https://www.perkinelmer.com/uk/Product/proxiplate-384-plus-50w-6008280) | ![](img/revvity/revvity_ProxiPlate-384-Plus-White-384-shallow-well-Microplate.jpg) | `Revvity_ProxiPlate_384Plus`
diff --git a/docs/resources/library/thermo_fisher.md b/docs/resources/library/thermo_fisher.md
new file mode 100644
index 0000000000..043c937408
--- /dev/null
+++ b/docs/resources/library/thermo_fisher.md
@@ -0,0 +1,38 @@
+# Thermo Fisher Scientific Inc.
+
+Company page: [Thermo Fisher Scientific Inc. Wikipedia](https://en.wikipedia.org/wiki/Thermo_Fisher_Scientific)
+
+> Thermo Fisher Scientific Inc. is an American supplier of analytical instruments, life sciences solutions, specialty diagnostics, laboratory, pharmaceutical and biotechnology services. Based in Waltham, Massachusetts, Thermo Fisher was formed through the **merger of Thermo Electron and Fisher Scientific in 2006**. Thermo Fisher Scientific has acquired other reagent, consumable, instrumentation, and service providers, including Life Technologies Corporation (2013), Alfa Aesar (2015), Affymetrix (2016), FEI Company (2016), BD Advanced Bioprocessing (2018),and PPD (2021).
+
+A basic structure of the companiy, [its brands](https://www.thermofisher.com/uk/en/home/brands.html) and product lines looks like this:
+
+```
+Thermo Fisher Scientific Inc. (TFS, aka "Thermo")
+├── Applied Biosystems (AB; brand)
+│ └── MicroAmp
+│ └── EnduraPlate
+├── Fisher Scientific (FS; brand)
+├── Invitrogen (INV; brand)
+├── Ion Torrent (IT; brand)
+├── Gibco (GIB; brand)
+├── Thermo Scientific (TS; brand)
+│ ├── Nalgene
+│ ├── Nunc
+│ └── Pierce
+├── Unity Lab Services (brand, services)
+├── Patheon (brand, services)
+└── PPD (brand, services)
+```
+
+## Plates
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'Thermo_TS_96_wellplate_1200ul_Rb' Part no.: AB-1127 or 10243223 [manufacturer website](https://www.fishersci.co.uk/shop/products/product/10243223)
- Material: Polypropylene (AB-1068, polystyrene) | ![](img/thermo_fisher/Thermo_TS_96_wellplate_1200ul_Rb.webp) | `Thermo_TS_96_wellplate_1200ul_Rb` |
+| 'Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate' Part no.: 4483354 (TFS) or 15273005 (FS) (= with barcode) Part no.: 16698853 (FS) (= **without** barcode) [manufacturer website](https://www.thermofisher.com/order/catalog/product/4483354)
- Material: Polycarbonate, Polypropylene - plate_type: semi-skirted - product line: "MicroAmp" - (sub)product line: "EnduraPlate" | ![](img/thermo_fisher/Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.png) | `Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate` |
+
+## Troughs
+
+| Description | Image | PLR definition |
+|--------------------|--------------------|--------------------|
+| 'ThermoFisherMatrixTrough8094' Part no.: 8094 [manufacturer website](https://www.thermofisher.com/order/catalog/product/8094) | ![](img/thermo_fisher/ThermoFisherMatrixTrough8094.jpg.avif) | `ThermoFisherMatrixTrough8094` |
diff --git a/docs/resources/plate_carriers.md b/docs/resources/plate_carriers.md
index 8f6673bcff..4457247895 100644
--- a/docs/resources/plate_carriers.md
+++ b/docs/resources/plate_carriers.md
@@ -12,8 +12,8 @@ The pedestal information is not typically available in labware databases (like t
Here's how you measure the pedestal height:
-![Pedestal height measurement](/img/pedestal/measure.jpeg)
+![Pedestal height measurement](/resources/img/pedestal/measure.jpeg)
-Once you have measured the pedestal height, you can contribute this information to the PyLabRobot Labware database. Here's a guide on contributing to the open-source project: ["How to Open Source"](/how-to-open-source.md).
+Once you have measured the pedestal height, you can contribute this information to the PyLabRobot Labware database. Here's a guide on contributing to the open-source project: ["How to Open Source"](/contributor_guide/how-to-open-source.md).
For background, see PR 143: [https://github.com/PyLabRobot/pylabrobot/pull/143](https://github.com/PyLabRobot/pylabrobot/pull/143).
diff --git a/docs/resources/plates.md b/docs/resources/plates.md
index 93aed790d6..13ac4aebde 100644
--- a/docs/resources/plates.md
+++ b/docs/resources/plates.md
@@ -1,6 +1,6 @@
# Plates
-Microplates are modelled by the {class}`~pylabrobot.resources.plate.Plate` class consist of equally spaced wells. Wells are children of the `Plate` and are modelled by the {class}`~pylabrobot.resources.well.Well` class. The relative positioning of `Well`s is what determines their location. `Plate` is a subclass of {class}`~pylabrobot.resources.itemized_resource.ItemizedResource`, allowing convenient integer and string indexing.
+Microplates are modelled by the {class}`~pylabrobot.resources.plate.Plate` class consisting of equally spaced wells. Wells are children of the `Plate` and are modelled by the {class}`~pylabrobot.resources.well.Well` class. The relative positioning of `Well`s is what determines their location. `Plate` is a subclass of {class}`~pylabrobot.resources.itemized_resource.ItemizedResource`, allowing convenient integer and string indexing.
There is some standardization on plate dimensions by SLAS, which you can read more about in the [ANSI SLAS 1-2004 (R2012): Footprint Dimensions doc](https://www.slas.org/SLAS/assets/File/public/standards/ANSI_SLAS_1-2004_FootprintDimensions.pdf). Note that PLR fully supports all plate dimensions, sizes, relative well spacings, etc.
@@ -12,4 +12,4 @@ Plates can optionally have a lid, which will also be a child of the `Plate` clas
The `nesting_z_height` is the overlap between the lid and the plate when the lid is placed on the plate. This property can be measured using a caliper.
-![nesting_z_height measurement](/img/plate/lid_nesting_z_height.jpeg)
+![nesting_z_height measurement](/resources/img/plate/lid_nesting_z_height.jpeg)
diff --git a/docs/basic.ipynb b/docs/user_guide/basic.ipynb
similarity index 97%
rename from docs/basic.ipynb
rename to docs/user_guide/basic.ipynb
index ed3cff2d9b..2df3a22c9e 100644
--- a/docs/basic.ipynb
+++ b/docs/user_guide/basic.ipynb
@@ -4,13 +4,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Basic liquid handling\n",
+ "# Getting started with liquid handling on a Hamilton STAR(let)\n",
"\n",
"In this notebook, you will learn how to use PyLabRobot to move water from one range of wells to another.\n",
"\n",
"**Note: before running this notebook, you should have**:\n",
"\n",
- "- Installed PyLabRobot and the USB driver as described in [the installation guide](installation).\n",
+ "- Installed PyLabRobot and the USB driver as described in [the installation guide](/user_guide/installation).\n",
"- Connected the Hamilton to your computer using the USB cable.\n",
"\n",
"Video of what this code does:\n",
@@ -115,7 +115,7 @@
"\n",
"- {class}`~pylabrobot.resources.ml_star.tip_carriers.TIP_CAR_480_A00` tip carrier\n",
"- {class}`~pylabrobot.resources.ml_star.plate_carriers.PLT_CAR_L5AC_A00` plate carrier\n",
- "- {class}`~pylabrobot.resources.corning_costar.plates.Cos_96_DW_1mL` wells\n",
+ "- {class}`~pylabrobot.resources.corning_costar.plates.Cor_96_wellplate_360ul_Fb` wells\n",
"- {class}`~pylabrobot.resources.ml_star.tip_racks.HTF_L` tips"
]
},
@@ -128,7 +128,7 @@
"from pylabrobot.resources import (\n",
" TIP_CAR_480_A00,\n",
" PLT_CAR_L5AC_A00,\n",
- " Cos_96_DW_1mL,\n",
+ " Cor_96_wellplate_360ul_Fb,\n",
" HTF_L\n",
")"
]
@@ -217,7 +217,7 @@
"source": [
"## CHANGE TIP CARRIERS TO CORRECT SIZE\n",
"plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n",
- "plt_car[0] = Cos_96_DW_1mL(name='plate_01')"
+ "plt_car[0] = Cor_96_wellplate_360ul_Fb(name='plate_01')"
]
},
{
diff --git a/docs/user_guide/configuration.md b/docs/user_guide/configuration.md
new file mode 100644
index 0000000000..8fb9a63662
--- /dev/null
+++ b/docs/user_guide/configuration.md
@@ -0,0 +1,81 @@
+# Configuring PLR
+
+The `pylabrobot.config` module provides the `Config` class for configuring PLR. The configuration can be set programmatically or loaded from a file.
+
+The configuration currently only supports logging configuration.
+
+## The `Config` class
+
+You can create a `Config` object as follows:
+
+```python
+import logging
+from pathlib import Path
+from pylabrobot.config import Config
+
+config = Config(
+ logging=Config.Logging(
+ level=logging.DEBUG,
+ log_dir=Path("my_logs")
+ )
+)
+```
+
+Then, call `pylabrobot.configure` to apply the configuration:
+
+```python
+import pylabrobot
+pylabrobot.configure(config)
+```
+
+## Loading from a file
+
+PLR supports loading configuration from a number of file formats. The supported formats are:
+
+- INI files
+- JSON files
+
+Files are loaded using the `pylabrobot.config.load_config` function:
+
+```python
+from pylabrobot.config import load_config
+config = load_config("config.json")
+
+import pylabrobot
+pylabrobot.configure(config)
+```
+
+If no file is found, a default configuration is used.
+
+`load_config` has the following parameters:
+
+```python
+def load_config(
+ base_file_name: str,
+ create_default: bool = False,
+ create_module_level: bool = True
+) -> Config:
+```
+
+A `pylabrobot.ini` file is used if found in the current directory. If not found, it is searched for in all parent directories. If it still is not found, it gets created at either the project level that contains the `.git` directory, or the current directory.
+
+### INI files
+
+Example of an INI file:
+
+```ini
+[logging]
+level = DEBUG
+log_dir = .
+```
+
+### JSON files
+
+```json
+{
+ "logging": {
+ "level": "DEBUG",
+ "log_dir": "."
+ }
+}
+```
diff --git a/docs/user_guide/cytation5.ipynb b/docs/user_guide/cytation5.ipynb
new file mode 100644
index 0000000000..6a3157a821
--- /dev/null
+++ b/docs/user_guide/cytation5.ipynb
@@ -0,0 +1,245 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Cytation 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%load_ext autoreload\n",
+ "%autoreload 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "from pylabrobot.plate_reading import PlateReader, Cytation5Backend"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pr = PlateReader(name=\"PR\", size_x=0,size_y=0,size_z=0, backend=Cytation5Backend())\n",
+ "await pr.setup()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'1320200 Version 2.07'"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "await pr.backend.get_firmware_version()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "22.9"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "await pr.backend.get_current_temperature()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "await pr.open()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "await pr.close()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Plate reading"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "