scanspec.specs#

Spec and its subclasses.

Inheritance diagram of scanspec.specs

Members

VARIABLE_DURATION

A string returned from Spec.duration to signify it produces a different duration for each point

Concat

Concatenate two Specs together, running one after the other.

ConstantDuration

Apply a constant duration to every point in a Spec.

Ellipse

Grid of points masked to an elliptical footprint.

Fly

Move through lower to upper bounds of the Spec rather than stopping.

Line

Linspace

Linearly spaced frames with start and stop as first and last midpoints.

Polygon

Grid of points masked to a polygonal footprint.

Product

Outer product of two Specs, nesting inner within outer.

Range

Linearly spaced frames with start and stop as the bounding midpoints.

Snake

Run the Spec in reverse on every other iteration when nested.

Spec

A serializable representation of the type and parameters of a scan.

Spiral

Archimedean spiral of "x_axis" and "y_axis".

Squash

Squash a stack of Dimension together into a single expanded Dimension object.

Static

A static frame, repeated num times, with axis at value.

Zip

Run two Specs in parallel, merging their midpoints together.

fly

Flyscan, zipping with fixed duration for every frame.

step

Step scan, with num frames of given duration at each frame in the spec.

scanspec.specs.VARIABLE_DURATION = 'VARIABLE_DURATION'#

A string returned from Spec.duration to signify it produces a different duration for each point

class scanspec.specs.Concat(*args: Any, left: Spec[Axis], right: Spec[Axis], gap: bool = False, check_path_changes: bool = True, type: Literal['Concat'] = 'Concat')[source]#

Concatenate two Specs together, running one after the other.

Each Dimension of left and right must contain the same axes. Typically formed using Spec.concat.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("x", 1, 3, 3).concat(Linspace("x", 4, 5, 5)))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-1.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.ConstantDuration(*args: Any, constant_duration: float, spec: Spec[Axis] | None = None, type: Literal['ConstantDuration'] = 'ConstantDuration')[source]#

Apply a constant duration to every point in a Spec.

Typically applied with the @ modifier.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Linspace

spec = 0.1 @ Linspace("x", 1, 2, 3)
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-2.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Ellipse(*args: ~typing.Any, x_axis: ~scanspec.core.Axis, x_centre: float, x_diameter: float, x_step: ~typing.Annotated[float, ~annotated_types.Gt(gt=0)], y_axis: ~scanspec.core.Axis, y_centre: float, y_diameter: float = <factory>, y_step: ~typing.Annotated[float, ~annotated_types.Gt(gt=0)] = <factory>, snake: bool = False, vertical: bool = False, type: ~typing.Literal['Ellipse'] = 'Ellipse')[source]#

Grid of points masked to an elliptical footprint.

Constructs a 2-D scan over an axis-aligned ellipse defined by (x_axis, y_axis), centred at (x_centre, y_centre), with diameters x_diameter and y_diameter. Grid spacing along each axis is controlled by x_step and y_step. If snake is True, the fast axis will zigzag like a snake. If vertical is True, the y axis will be the fast axis.

Starts from one of the four extremes of the ellipse identified by the signs of x_step and y_step.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Ellipse, Fly

# An elliptical region centred at (0, 0) on axes "x" and "y",
# with 10x6 diameters and steps of 0.5 in both directions.
spec = Fly(
    Ellipse(
        "x", 0, 10, 0.5,
        "y", 0, 6,
        snake=True,
        vertical=False,
    )
)
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-3.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Fly(*args: Any, spec: Spec[Axis], type: Literal['Fly'] = 'Fly')[source]#

Move through lower to upper bounds of the Spec rather than stopping.

This is commonly termed a “fly scan” rather than a “step scan”

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("x", 1, 2, 3))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-4.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

scanspec.specs.Line#

alias of Linspace

class scanspec.specs.Linspace(*args: Any, axis: Axis, start: float, stop: float, num: Annotated[int, Ge(ge=1)] = 1, type: Literal['Linspace'] = 'Linspace')[source]#

Linearly spaced frames with start and stop as first and last midpoints.

This class is intended to handle linearly spaced frames defined with a specific number of frames.

See also

Range: For linearly spaced frames defined with a step size.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("x", 1, 2, 5))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-5.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

bounded(lower: float = FieldInfo(annotation=NoneType, required=True, description='Lower bound of the first point of the line'), upper: float = FieldInfo(annotation=NoneType, required=True, description='Upper bound of the last point of the line'), num: int = FieldInfo(annotation=NoneType, required=False, default=1, description='Number of frames to produce (defaults to 1)', metadata=[Ge(ge=1)])) Linspace[OtherAxis][source]#

Specify a Linspace by extreme bounds instead of midpoints.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace.bounded("x", 1, 2, 5))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-6.png
class scanspec.specs.Polygon(x_axis: Axis, y_axis: Axis, vertices: list[tuple[float, float]], x_step: float, y_step: float = <factory>, snake: bool = False, vertical: bool = False, type: ~typing.Literal['Polygon'] = 'Polygon')[source]#

Grid of points masked to a polygonal footprint.

Constructs a 2-D scan over an axis-aligned polygon defined by an ordered list of vertices “(x, y)” given in vertices. The polygon may be convex or concave, and the interior is determined using an even-odd ray-casting rule. Grid spacing along each axis is controlled by x_step and y_step, If snake is True, the fast axis will zigzag like a snake. If vertical is True, the y axis will be the fast axis.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Polygon, Fly

# A triangular region on axes "x" and "y", stepped by 0.2 units
# in both directions.
spec = Fly(
    Polygon(
        x_axis="x",
        y_axis="y",
        vertices=[(0, 0), (5, 0), (2.5, 4)],
        x_step=0.2,
        y_step=0.2,
        snake=True,
        vertical=False,
    )
)
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-7.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Product(*args: Any, outer: Spec[Axis] | int, inner: Spec[Axis] | int, gap: bool = True, type: Literal['Product'] = 'Product')[source]#

Outer product of two Specs, nesting inner within outer.

This means that inner will run in its entirety at each point in outer.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("y", 1, 2, 3) * Linspace("x", 3, 4, 12))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-8.png

An inner integer can be used to repeat the same point many times.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("y", 1, 2, 3) * 2)
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-9.png

An outer integer can be used to repeat the same scan many times.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(2 * ~Linspace.bounded("x", 3, 4, 1))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-10.png

If you want snaked axes to have no gap between iterations you can do:

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace, Product

spec = Fly(Product(2, ~Linspace.bounded("x", 3, 4, 1), gap=False))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-11.png

Note

There is no turnaround arrow at x=4

axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Range(*args: Any, axis: Axis, start: float, stop: float, step: Annotated[float, ~annotated_types.Gt(gt=0)] = <factory>, type: Literal['Range'] = 'Range')[source]#

Linearly spaced frames with start and stop as the bounding midpoints.

step defines the distance between midpoints.

See also

Linspace: For linearly spaced frames defined with a number of frames.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Range

spec = Fly(Range("x", 1, 2, 0.25))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-12.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

bounded(lower: float = FieldInfo(annotation=NoneType, required=True, description='Lower bound of the first point of the line'), upper: float = FieldInfo(annotation=NoneType, required=True, description='Upper bound of the last point of the line'), step: float = FieldInfo(annotation=NoneType, required=True, description='Step size')) Range[OtherAxis][source]#

Specify a Range by extreme bounds instead of midpoints.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Range

spec = Fly(Range.bounded("x", 1, 5, 2))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-13.png
class scanspec.specs.Snake(*args: Any, spec: Spec[Axis], type: Literal['Snake'] = 'Snake')[source]#

Run the Spec in reverse on every other iteration when nested.

Typically created with the ~ operator.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(Linspace("y", 1, 3, 3) * ~Linspace("x", 3, 5, 5))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-14.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Spec[source]#

A serializable representation of the type and parameters of a scan.

Abstract baseclass for the specification of a scan. Supports operators:

  • *: Outer Product of two Specs or ints, nesting the second within the first.

  • @: ConstantDuration of the Spec, setting a constant duration for each point.

  • ~: Snake the Spec, reversing every other iteration of it

axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

frames(bounds: bool = False) Dimension[Axis][source]#

Expand all the scan Dimension and return them.

midpoints() Midpoints[Axis][source]#

Return Midpoints that can be iterated point by point.

shape() tuple[int, ...][source]#

Return the final, simplified shape of the scan.

zip(other: Spec[OtherAxis]) Zip[Axis | OtherAxis][source]#

Zip the Spec with another, iterating in tandem.

concat(other: Spec[Axis]) Concat[Axis][source]#

Concat the Spec with another, iterating one after the other.

serialize() Mapping[str, Any][source]#

Serialize the Spec to a dictionary.

Any value that pydantic cannot natively convert to a JSON-serializable type (e.g. an ophyd_async device used as an axis) is serialized by using its name attribute (if it has one), otherwise its repr() string.

static deserialize(obj: Any) Spec[Any][source]#

Deserialize a Spec from a dictionary.

class scanspec.specs.Spiral(*args: Any, x_axis: Axis, x_centre: float, x_diameter: float, x_step: float, y_axis: Axis, y_centre: float, y_diameter: float = <factory>, type: Literal['Spiral'] = 'Spiral')[source]#

Archimedean spiral of “x_axis” and “y_axis”.

Starts at centre point (“x_start”, “y_start”)”. Produces “num” points in a spiral spanning width of “x_range” and height of “y_range”

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Spiral

spec = Fly(Spiral("x", 1, 10, 2.5, "y", 5, 50))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-15.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Squash(*args: Any, spec: Spec[Axis], check_path_changes: bool = True, type: Literal['Squash'] = 'Squash')[source]#

Squash a stack of Dimension together into a single expanded Dimension object.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace, Squash

spec = Fly(Squash(Linspace("y", 1, 2, 3) * Linspace("x", 0, 1, 4)))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-16.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Static(*args: Any, axis: Axis, value: float, num: Annotated[int, Ge(ge=1)] = 1, type: Literal['Static'] = 'Static')[source]#

A static frame, repeated num times, with axis at value.

Can be used to set axis=value at every point in a scan.

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace, Static

spec = Fly(Linspace("y", 1, 2, 3).zip(Static("x", 3)))
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-17.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

class scanspec.specs.Zip(*args: Any, left: Spec[Axis], right: Spec[Axis], type: Literal['Zip'] = 'Zip')[source]#

Run two Specs in parallel, merging their midpoints together.

Typically formed using Spec.zip.

Stacks of Dimension are merged by:

  • If right creates a stack of a single Dimension object of size 1, expand it to the size of the fastest Dimension object created by left

  • Merge individual Dimension objects together from fastest to slowest

This means that Zipping a Spec producing stack [l2, l1] with a Spec producing stack [r1] will assert len(l1)==len(r1), and produce stack [l2, l1.zip(r1)].

# Example Spec

from scanspec.plot import plot_spec
from scanspec.specs import Fly, Linspace

spec = Fly(
    Linspace("z", 1, 2, 3) * Linspace("y", 3, 4, 5).zip(Linspace("x", 4, 5, 5))
)
plot_spec(spec)

(Source code, png, hires.png, pdf)

../_images/scanspec-specs-18.png
axes() list[Axis][source]#

Return the list of axes that are present in the scan.

Ordered from slowest moving to fastest moving.

duration() float | None | Literal['VARIABLE_DURATION'][source]#

Returns the duration of each scan point.

Return value will be one of: - None: No duration defined - float: A constant duration for each point - VARIABLE_DURATION: A different duration for each point

calculate(bounds: bool = False, nested: bool = False) list[Dimension[Axis]][source]#

Produce a stack of nested Dimension that form the scan.

Ordered from slowest moving to fastest moving.

scanspec.specs.fly(spec: Spec[Axis], duration: float) Spec[Axis | str][source]#

Flyscan, zipping with fixed duration for every frame.

Parameters:
  • spec – The source Spec to continuously move

  • duration – How long to spend at each frame in the spec

Deprecated since version 1.0.0: You should use Fly and ConstantDuration instead

scanspec.specs.step(spec: Spec[Axis], duration: float, num: int = 1) Spec[Axis][source]#

Step scan, with num frames of given duration at each frame in the spec.

Parameters:
  • spec – The source Spec with midpoints to move to and stop

  • duration – The duration of each scan frame

  • num – Number of frames to produce with given duration at each of frame in the spec

Deprecated since version 1.0.0: You should use ConstantDuration instead.