Creating a new plate

Hello everyone,

I am creating a new plate using create_ordered_items_2d.
From the doc string I can see these instructions:
What is the proper way of measuring dx, dy, dz?

  """Make ordered resources in a 2D grid, with the keys being the identifiers in transposed
  MS-Excel style. This is useful for initializing `ItemizedResource`.

  Args:
    klass: The class of the resource to create
    num_items_x: The number of items in the x direction
    num_items_y: The number of items in the y direction
    dx: The bottom left corner for items in the left column
    dy: The bottom left corner for items in the bottom row
    dz: The z coordinate for all items
    item_dx: The size of the items in the x direction
    item_dy: The size of the items in the y direction
    **kwargs: Additional keyword arguments to pass to the resource constructor

  Returns:
    A dict of resources. The keys are the identifiers in transposed MS-Excel format, so the top
    left item is "A1", the item to the bottom is "B1", the item to the right is "A2", and so on.
  """```

2 Likes

for background: dx, dy, and dz are parameters to the utility function create_ordered_items_2d. create_ordered_items_2d creates items on a 2d grid, like the wells on a plate. It returns a dict mapping a grid identifier like A1 to an item like a tip spot or well. It would be possible to creaet this dictionary yourself and simply pass it to the plate. For the plate, the wells are simply children and they do not have to exist in a grid per se.

create_ordered_items_2d returns items with a location pre-populated so that they can be assigned to a plate directly. dx is the location of the first column (X1) to the left hand side of the plate. dy is the location of the last row (HX) to the bottom of the plate. dz is the location of the very bottom of the well, the bottom of the well material, to the bottom of the plate.

manufacturer’s often publish the location of the center of well a1 wrt the top left corner. Using symmetry, you can get the location of H1. Then, you need to subtract half the width and height of the well to derive the plr units dx and dy because plr is based on the front left bottom instead of centers to define locations.

1 Like

apologies for lack of docs on creating plates, let this thread be the original material that we use on the site!

2 Likes

Then let’s throw in some more infographics :sweat_smile:

Because material_z_thickness and dz will require more detailed explanations next :slight_smile:

I will also work on the docs more during the Christmas break

1 Like

do you want to take on plate.md?

1 Like

Yes, very happy to

1 Like

great! thanks!

And I like your idea of using @dkone’s post here as an opportunity to aggregate all the information into one place while helping them get their plate up and ready as quickly as possible

2 Likes

This is a great idea! Let’s add all those beautiful images and explanations on the website.

2 Likes

How are you defining a new plate? I’m trying to follow information in the User Guide and API and am getting errors depending on which class or function I am using.

For example, when I follow this:

class TubePlate(ItemizedResource[Tube]):
  def __init__(self, name: str):
    super().__init__(
      name=name,
      size_x=127.0,
      size_y=86.0,
      size_z=45.0,
      ordered_items=create_equally_spaced_2d(Tube,
        num_items_x=12,
        num_items_y=8,
        dx=9.5,
        dy=7.0,
        dz=1.0,
        item_dx=9.0,
        item_dy=9.0,
      )
    )

tube_plate = TubePlate(name="tube_plate")

I get

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[16], line 1
----> 1 tube_plate = TubePlate(name="tube_plate")
      2 lh.deck.assign_child_resource(tube_plate, location=plt_car[0])

Cell In[15], line 5
      4 def __init__(self, name: str):
----> 5   super().__init__(
      6     name=name,
      7     size_x=127.0,
      8     size_y=86.0,
      9     size_z=45.0,
     10     ordered_items=create_equally_spaced_2d(Tube,
     11       num_items_x=12,
     12       num_items_y=8,
     13       dx=9.5,
     14       dy=7.0,
     15       dz=1.0,
     16       item_dx=9.0,
     17       item_dy=9.0,
     18     )
     19   )

File ~/Documents/Coding/plr/pylabrobot/pylabrobot/resources/itemized_resource.py:93, in ItemizedResource.__init__(self, name, size_x, size_y, size_z, ordered_items, ordering, category, model)
...
---> 93 for item in ordered_items.values():
     94   if item.location is None:
     95     raise ValueError("Item location must be specified if supplied at initialization.")

AttributeError: 'list' object has no attribute 'values'

Or when I do

plate = Plate("plate", size_x=1, size_y=1, size_z=1,
    ordered_items=create_ordered_items_2d(Well( size_x=1, size_y=1, size_z= 1, name="Well"),
    dx=0, dy=0, dz=0, item_dx=1, item_dy=1,
    num_items_x=1, num_items_y=1))

I get

AttributeError: 'Well' object has no attribute '__name__'

I’m trying to add a new plate definition for a 2-well reservoir plate.

camillo is still working on a user guide tutorial for new plate definitions

in the first example, you have to use create_ordered_items_2d. create_equally_spaced_2d was used in the past, and is still a useful function I think, but since it returns a list it is not compatible with the ordered_items parameter which expects a dict. Note that you are also missing the size_{x,y,z} and max_volume arguments. I updated your code to this:

class TubePlate(ItemizedResource[Tube]):
  def __init__(self, name: str):
    tube_kwargs = {
      "size_x": 9.0,
      "size_y": 9.0,
      "size_z": 45.0,
      "max_volume": 200.0,
    }
    super().__init__(
      name=name,
      size_x=127.0,
      size_y=86.0,
      size_z=45.0,
      ordered_items=create_ordered_items_2d(Tube,
        num_items_x=12,
        num_items_y=8,
        dx=9.5,
        dy=7.0,
        dz=1.0,
        item_dx=9.0,
        item_dy=9.0,
        **tube_kwargs,
      )
    )

I see that the “custom resources” tutorial is still using create_equally_spaced_2d. I will fix it.

In your second example, the first parameter create_ordered_items_2d expects a class, like Well or Tube. By adding the parameters to Well directly, it actually creates an instance of that class, which is different from the class itself. See code above.

Got it, thanks Rick!

When I tried just passing Well in for the class, I get

TypeError: Well.__init__() missing 3 required positional arguments: 'size_x', 'size_y', and 'size_z'

So if I am not making any custom labware and instead want an ordered plate with different number of wells, which method makes the most sense?

Just Well, without any parentheses. There are many examples in plates.py for each manufacturer. Also see example above with Tube.

I am not sure which methods you are considering. The way I shared in the code in my response above is equivalent to the existing definitions, which is how i would do it. This uses the create_ordered_items_2d function to create a 2d grid of items with canonical names (A1, B1, …, A2, …, etc.). If your plates does not follow this format, you can pass in a dictionary directly, but that’s less convenient. could you share a picture of your plate?

the method in the user guide for custom resources right now is wrong because it still uses create_equally_spaced_2d for ordered_items.

I may be missing something about the Class or the ordered items call?
When I run

from pylabrobot.resources import Plate, create_ordered_items_2d, Well

plate = Plate("plate", size_x=1, size_y=1, size_z=1,
    ordered_items=create_ordered_items_2d(Well,
    dx=1, dy=2, dz=1, item_dx=1, item_dy=1,
    num_items_x=1, num_items_y=1))

I get

TypeError: Well.__init__() missing 3 required positional arguments: 'size_x', 'size_y', and 'size_z'

And I will also try copying examples directly from plates.py - thanks for that suggestion!

pass these kwargs to create_ordered_items_2d.

create_ordered_items_2d will pass them to Well each time it creates a new instance

2 Likes