Introduction

Welcome to pydfmux! There is a lot of new code here; this document should serve as a short overview of different aspects of the Python stack. You can find more technical descriptions in Object Model and other documents linked from Welcome to pydfmux.

Hello World

First, we’ll create a reference to a single, pydfmux.Dfmux object.

>>> import pydfmux

>>> d = pydfmux.Dfmux(serial='004')
>>> print d.serial
004

This pydfmux.Dfmux resembles an ordinary Python object, but has two tricks up its sleeve:

  1. It’s actually a view into a database (managed by SQLAlchemy), and
  2. It’s capable of communicating with hardware.

In the following sections, we describe the benefits and impacts of each of these points. First, however, we introduce an alternative to describing hardware by writing Python code.

Hardware Maps

Above, we created a Dfmux object by instantiating it directly in Python. This approach is possible throughout the system, including for objects like Bolometer, Wafer, et cetera. However, an experiment is usually catalogued by a “hardware map” – a set of text files (often version-controlled) that describe how an experiment is put together and how these connections evolve. In pydfmux, these files are written in YAML:

>>> import pydfmux
>>> hwm = pydfmux.load_session('''
... !HardwareMap
... - !Dfmux { serial: "004" }
... ''')
>>> d = hwm.query(pydfmux.Dfmux).one()
>>> print d.serial
004

This example has the same effect as the above code, except for a few important points:

  1. We created a HardwareMap object, which is an access point into the SQLAlchemy database allowing us to query any object within it.
  2. The contents of the HardwareMap were described in text, not in Python code.
  3. We retrieved a reference to our Dfmux object by querying the database. (The one() call ensures there was only a single result in the query, and returns it.)

Unlike the Python frameworks in pywtl, pydfmux deeply embeds the hardware map into most of our code. As a result, Python code written at experiment scales (for example, tuning scripts) does not differ structurally from Python code targeting a single board on the benchtop (for example, firmware testing code.)

Object Database

Above, we created a HardwareMap object representing a database with a Dfmux object in it. This database can store many different kinds of objects, and follow links between them:

>>> # Build a Wafer with three Bolometers attached
>>> bs = [pydfmux.Bolometer(name='Frank'),
...       pydfmux.Bolometer(name='Bernice'),
...       pydfmux.Bolometer(name='Andy')]
>>> w = pydfmux.Wafer(name='wafer1', bolometers=bs)

>>> # Add these new objects to the hardware map (you don't need to worry
>>> # about this, since you'd normally create Wafers/Bolometers in YAML.)
>>> hwm.add(w)
>>> hwm.commit()

>>> # Search for all Wafer objects (and ensure there's only one)
>>> wafer = hwm.query(pydfmux.Wafer).one()

>>> # Inspect the Bolometers attached to this Wafer
>>> wafer.bolometers.all()
[Wafer(u'wafer1').Bolometer(u'Frank'), Wafer(u'wafer1').Bolometer(u'Bernice'), Wafer(u'wafer1').Bolometer(u'Andy')]

>>> wafer.bolometer['Andy']
Wafer(u'wafer1').Bolometer(u'Andy')

>>> wafer.bolometers.name
[u'Frank', u'Bernice', u'Andy']

For a comprehensive list of objects (and a description of the linkages betweeen them), see Object Model. Once again, you aren’t intended to add hardware-map objects by writing Python code; for information on how to encode Bolometer and Wafer objects in YAML, see Hardware Map.

Talking to Remote Hardware

So far, we’ve seen how pydfmux objects encode data (e.g. dfmux.serial) and linkages (e.g. wafers.bolometer['Andy']). We can also define and call methods on objects, in one of three ways:

  1. by defining class methods directly, as you would in ordinary Python,
  2. by using the @algorithm() or @macro() decorators (see Algorithms), or
  3. by calling methods defined by remote hardware on an IceBoard.

This section describes how to use the third case.

>>> import pydfmux
>>> hwm = pydfmux.load_session('''
... !HardwareMap
... - !Dfmux { serial: "004" }
... - !Dfmux { serial: "019" }
... ''')
>>> dfmuxes = hwm.query(pydfmux.Dfmux)
>>> print dfmuxes.get_fir_stage()
[6, 6]

This code instantiates 2 Dfmux boards and places them into the hardware map. It then retrieves the Dfmuxes via a query, and issues Dfmux.get_fir_stage() on both boards.

Most importantly, the Dfmux.get_fir_stage() call occurs on both boards in parallel. This is a trivial example, but in an experiment with 100 boards, repeatedly looping over each board in sequence does not scale.

For information on how to simply and efficiently scale your code across large experiments, see parallelizing.