Skip to content

Commit

Permalink
Update the docs
Browse files Browse the repository at this point in the history
  • Loading branch information
tfarago committed Oct 13, 2014
1 parent 870915e commit 7aab612
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 28 deletions.
38 changes: 29 additions & 9 deletions concert/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,39 @@ def _pull_first(tuple_list):


def scan(feedback, regions, callbacks=None):
"""A multidimensional scan. *feedback* is a callable which takes no arguments and it provides
feedback after some parameter is changed. *regions* specifies the scanned parameter, it is either
a :class:`concert.helpers.Region` or a list of those for multidimensional scan. The fastest
changing parameter is the last one specified. One would use it like this::
scan(feedback, [Region(motor['position'], 0 * q.mm, 10 * q.mm, 10),
Region(camera['frame_rate'], 1 / q.s, 100 / q.s, 100)])
"""A multidimensional scan. *feedback* is a callable which takes no arguments and provides
feedback after some parameter is changed. *regions* specifies the scanned parameter, it is
either a :class:`concert.helpers.Region` or a list of those for multidimensional scan. The
fastest changing parameter is the last one specified. *callbacks* is a dictionary in the form
{region: function}, where *function* is a callable with no arguments (just like *feedback*) and
is called every time the parameter in *region* is changed. One would use a scan for example like
this::
import numpy as np
from concert.async import resolve
from concert.helpers import Region
def take_flat_field():
# Do something here
pass
exp_region = Region(camera['exposure_time'], np.linspace(1, 100, 100) * q.ms)
position_region = Region(motor['position'], np.linspace(0, 180, 1000) * q.deg)
callbacks = {exp_region: take_flat_field}
# This is a 2D scan with position_region in the inner loop. It acquires a tomogram, changes
# the exposure time and continues like this until all exposure times are exhausted.
# Take_flat_field is called every time the exposure_time of the camera is changed
# (in this case after every tomogram) and you can use it to correct the acquired images.
for result in resolve(scan(camera.grab, [exp_region, position_region], callbacks=callbacks)):
# Do something real instead of just a print
print result
From the execution order it is equivalent to (in reality there is more for making the code
asynchronous)::
for position in np.linspace(0 * q.mm, 10 * q.mm, 10):
for frame_rate in np.linspace(1 / q.s, 100 / q.s, 100):
for exp_time in np.linspace(1, 100, 100) * q.ms:
for position in np.linspace(0, 180, 1000) * q.deg:
yield feedback()
"""
Expand Down
66 changes: 47 additions & 19 deletions docs/user/topics/processes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,30 @@ For instance, to set 10 motor positions between 5 and 12 millimeter and acquire
the flow rate of a pump could be written like::

from concert.processes import scan
from concert.helpers import Range
from concert.helpers import Region

# Assume motor and pump are already defined

def get_flow_rate():
return pump.flow_rate

# A parameter object encapsulated with its scanning positions
param_range = Range(motor['position'], 5*q.mm, 12*q.mm, 10)

generator = scan(get_flow_rate, param_range)

The parameter is first wrapped into a :class:`concert.helpers.Range` object
which holds the parameter and the scanning range. :func:`.scan` is
multidimensional, i.e. you can scan as many parameters as you need, from 1D
scans to complicated multidimensional scans. :func:`.scan` returns a generator
which yields futures. This way the scan is asynchronous and you can continuously
see its progress by resolving the yielded futures. Each future then returns the
result of one iteration as tuples, which depends on how many parameters scan
gets on input (scan dimensionality). The general signature of future results is
*(x_0, x_1, ..., x_n, y)*, where *x_i* are the scanned parameter values and *y*
is the result of *feedback*. For resolving the futures you would use
region = Region(motor['position'], np.linspace(5, 12, 10) * q.mm)

generator = scan(get_flow_rate, region)

The parameter is first wrapped into a :class:`concert.helpers.Region` object
which holds the parameter and the scanning region for parameters. :func:`.scan`
is multidimensional, i.e. you can scan as many parameters as you need, from 1D
scans to complicated multidimensional scans. If you want to scan just one
parameter, pass the region instance, if you want to scan more, pass a list or
tuple of region instances. :func:`.scan` returns a generator which yields
futures. This way the scan is asynchronous and you can continuously see its
progress by resolving the yielded futures. Each future then returns the result
of one iteration as tuples, which depends on how many parameters scan gets on
input (scan dimensionality). The general signature of future results is *(x_0,
x_1, ..., x_n, y)*, where *x_i* are the scanned parameter values and *y* is the
result of *feedback*. For resolving the futures you would use
:func:`concert.async.resolve` like this::

from concert.async import resolve
Expand All @@ -50,13 +52,39 @@ To continuously plot the values obtained by a 1D scan by a

inject(resolve(generator), viewer())

A two-dimensional scan with *range_2* parameter in the inner (fastest changing)
A two-dimensional scan with *region_2* parameter in the inner (fastest changing)
loop could look as follows::

range_1 = Range(motor_1['position'], 5*q.mm, 12*q.mm, 10)
range_2 = Range(motor_2['position'], 0*q.mm, 10*q.mm, 5)
region_1 = Region(motor_1['position'], np.linspace(5, 12, 10) * q.mm)
region_2 = Region(motor_2['position'], np.linspace(0, 10, 5) * q.mm)

generator = scan(get_flow_rate, range_1, range_2)
generator = scan(get_flow_rate, [region_1, region_2])

You can set callbacks which are called when some parameter is changed during a
scan. This can be useful when you e.g. want to acquire a flat field when the
scan takes a long time. For example, to acquire tomograms with different
exposure times and flat field images you can do::


import numpy as np
from concert.async import resolve
from concert.helpers import Region

def take_flat_field():
# Do something here
pass

exp_region = Region(camera['exposure_time'], np.linspace(1, 100, 100) * q.ms)
position_region = Region(motor['position'], np.linspace(0, 180, 1000) * q.deg)
callbacks = {exp_region: take_flat_field}

# This is a 2D scan with position_region in the inner loop. It acquires a tomogram, changes
# the exposure time and continues like this until all exposure times are exhausted.
# Take_flat_field is called every time the exposure_time of the camera is changed
# (in this case after every tomogram) and you can use it to correct the acquired images.
for result in resolve(scan(camera.grab, [exp_region, position_region], callbacks=callbacks)):
# Do something real instead of just a print
print result


:func:`.ascan` and :func:`.dscan` are used to scan multiple parameters
Expand Down

0 comments on commit 7aab612

Please sign in to comment.