Why create a stack of Frames?#

If a Spec tells you the parameters of a scan, Frames gives you the Points that will let you actually execute the scan. A stack of Frames is interpreted as nested from slowest moving to fastest moving, so each faster Frames object will iterate once per position of the slower Frames object. When fly-scanning the axis will traverse lower-midpoint-upper on the fastest Frames object for each point in the scan.

An Example#

>>> from scanspec.specs import Line
>>> spec = Line("y", 4, 5, 6) * Line("x", 1, 2, 3)
>>> stack = spec.calculate()
>>> len(stack)
2
>>> len(stack[0])
6
>>> len(stack[1])
3
>>> stack[0].midpoints
{'y': array([4. , 4.2, 4.4, 4.6, 4.8, 5. ])}
>>> stack[1].midpoints
{'x': array([1. , 1.5, 2. ])}

So the Product of two Lines creates a stack of 2 Frames objects, the first having the same size as the outer line, and the second having the same size as the inner. Executing the scan will iterate the inner Frames object 6 times, once for each point in the outer Frames object, 18 points in all.

Why not squash them into a flat sequence?#

A stack of Frames objects are created to give the most compact representation of the scan. Imagine a 100x2000x2000 point scan, which creates a list of 3 Frames objects. Considering just the midpoint arrays, they would take 100 + 2000 + 2000 float64 = 32.8kB RAM in our list form. If we squashed the list into a single Frames object it would take 100 * 2000 * 2000 float64 = 3.2GB of RAM. The scan itself is likely to be executed over a long period of time, so it makes sense to save on the memory and calculate the squashed points as they are needed.

What about Regions?#

Regions will stop the regularity of the nested Frames objects, so will cause them to be squashed into a single Frames object. Taking our example above, if we Mask the grid with a Circle, then the Line in x won’t have 3 points in each iteration, the number of points is dependent on y. This means that a Mask will squash any Specs together referred to by its Regions.

How does this stack relate to HDF5 Dimensions?#

A Spec by itself does not specify how the scan points should be written to disk. The simplest strategy is to simply write a stack of images, along with the setpoint positions specified in the Frames objects, and let the reader of the data pick out the required frames from the positions. However, the stack of Frames objects contains all the information required to reshape a sequence of HDF5 frames into the dimensionality of the scan using a VDS. This holds until we turn snake on, at which point it destroys the performance of the VDS. For this reason, it is advisable to Squash any snaking Specs with the first non-snaking axis above it so that the HDF Dimension will not be snaking. See Why Squash (and Mask) can change the Path for some details on this.