Sweeps
When thinking of measurement, one generally imagines measuring some dependent variable with respect to some independent variable that is swept across a range of values. While the devil is in the details of the measurement, with Julia's multiple dispatch functionality, it should be possible to abstract executing a "sweep" to just one function, thus affording a simple but general interface for the user to conduct his measurements.
Moreover, the need to automate measurements and facilitate use of the same instruments by multiple users warrants some sort of queueing structure for "sweeps", which is maintained automatically in the background of the Julia interface that the user is using.
We have achieved precisely these goals in a object-oriented way with our "Sweep" portion of IntrumentControl.jl" package. Below we describe the functionality meant for the user: how to use Response
and Stimulus
objects as well as source
and measure
functions to submit a sweep, how to interact with the sweep job to see it's result, estimated time of completion, etc. In Implementation we describe how exactly sweep and queueing functionality is implemented
Prerequisites for a Sweep
A sweep is submitted by the user via the sweep
function:
sweep{N}(dep::Response, indep::Vararg{Tuple{Stimulus, AbstractVector}, N}; priority = NORMAL)
sweep
measures a response as a function of an arbitrary number of stimuli, sourced over the values given in the AbstractVector
input, and returns a handle to the sweep job in the form of a SweepJob
object, which will be discussed more in detail in Implementation. This can be used to access the results while the sweep is being measured. priority
is an Int
from 0 to typemax(Int)
, inclusive, and is used to prioritize the next sweep in a queue (with multiple sweeps) to be run. It is typically one of the following:
HIGH == 10
NORMAL == 5
LOW == 0
Thus, the priority
keyword argument may be LOW
, NORMAL
, or HIGH
, or any integer greater than or equal to zero.
The sweep
functions assumes that appropriate measure
methods have been written for the dep
object passed, and that appropriate source
methods have been written for all Stimulus
objects in the passed indep
argument. Thus, to use the function the user must have defined Response
and Stimulus
subtypes, as well as source
and measure
functions for those types.
For example, ... put example here with definition of Stimulus
, source
, response
, measure
, and sweep
Queueing
InstrumentControl employs a queueing structure for job handling through manipulation of SweepJob
objects, which are instantiated when the sweep
function is used. The actual "queue" is a SweepJobQueue
object, described more in detail in Implementation. When the InstrumentControl package is imported by the user, a 'SweepJobQueue' object, as well as communication with the database set up by ICDataServer.jl, is automatically initialized; in the documentation this object is referred to as the "default sweep job queue object".
When sweep
is used, a job ID is automatically obtained for the new sweep job (which exists in Julia as a SweepJob
object) via communication with the database; the job is then put into the queue with the job ID as it's identifier. In essence, a SweepJobQueue
object can be thought of as a collection (in the Julia sense) of SweepJob
objects, indexed by their job ID, along with other fields and helper functions for automatic scheduling of jobs in the background. The queue prioritizes jobs based on their priorities; for equal priority values, the job submitted earlier takes precedent.
Interacting with a sweep job
When sweep
is called, the returned SweepJob
object displays it's job ID as well as it's status (waiting, running, done, etc), priority, progress (percentage of job completed), and time of submission. Both the job ID and the returned SweepJob
object itself can be used to interact with the job in the following ways:
#
Base.eta
— Function.
eta(x::SweepJob)
Return the estimated time of completion for sweep job x
. It estimates this time by first calculating the amount of time passed from when the job was submitted to the time the function is called, and then calculating the ETA based on the current progress of the job and this passed time
#
InstrumentControl.progress
— Function.
progress(x::SweepJob)
Returns the sweep job progress, a Float64
between 0 and 1 (inclusive).
#
InstrumentControl.jobs
— Method.
jobs(job_id)
Return the SweepJob
object associated with job_id
scheduled in the default sweep job queue object.
#
InstrumentControl.result
— Function.
result(sj::SweepJob)
Returns the result array of a sweep job sj
. Throws an error if the result array has not yet been initialized.
result(i::Integer)
Returns a result array by job id in job scheduled in the default sweep job queue object.
result()
Returns the result array from the last finished or aborted job scheduled in the default sweep job queue object.
#
InstrumentControl.abort!
— Function.
abort!(x::SweepJob)
Abort a sweep job. Practically, the status of the job is changed to "Aborted", and the job is put into the update_channel of the default SweepJobQueue
object. This automatically leads to: update of job metadata in the SweepJob
object as well as in the ICDataServer, update of queue metadata, archiving of the job's result array, and start of the highest priority job in the default sweep job queue. Thus, aborting a job is guaranteed to bail out of the sweep in such a way that the data that has been measured for most recent sourcing of a stimulus, i.e. at the very start of a "for loop" in InstrumentControl._sweep!
. You can also abort a sweep before it even begins.
Presently this function does not interrupt measure
, so if a single measurement takes a long time then the sweep is only aborted after that finishes.
abort!()
Abort the currently running job (if any). If no job is running, the method does not throw an error.
#
InstrumentControl.prune!
— Function.
prune!(q::SweepJobQueue)
Prunes a SweepJobQueue
of all InstrumentControl.SweepJob
s with a status of Done
or Aborted
. This will have the side effect of releasing old sweep results if there remain no references to them in julia, however they should still be saved to disk and can be recovered later.