Commit 31b68b14 authored by Jose Tiago Macara Coutinho's avatar Jose Tiago Macara Coutinho Committed by Jose Tiago Macara Coutinho
Browse files

scan: add motion time estimation

parent 66d3547f
......@@ -39,6 +39,7 @@ from bliss.common.encoder import Encoder
from bliss.common.hook import MotionHook
import gevent
import re
import math
import types
import functools
import numpy
......@@ -105,6 +106,57 @@ class Motion(object):
return self.__axis
class MotionEstimation(object):
"""
Estimate motion time and displacement based on current axis position
and configuration
"""
def __init__(self, axis, target_pos, initial_pos=None):
self.axis = axis
ipos = axis.position() if initial_pos is None else initial_pos
self.ipos = ipos
fpos = target_pos
delta = fpos - ipos
do_backlash = cmp(delta, 0) != cmp(axis.backlash, 0)
if do_backlash:
delta -= axis.backlash
fpos -= axis.backlash
self.fpos = fpos
self.displacement = displacement = abs(delta)
try:
self.vel = vel = axis.velocity()
self.accel = accel = axis.acceleration()
except NotImplementedError:
self.vel = float('+inf')
self.accel = float('+inf')
self.duration = 0
return
full_accel_time = vel / accel
full_accel_dplmnt = 0.5*accel * full_accel_time**2
full_dplmnt_non_const_vel = 2 * full_accel_dplmnt
reaches_max_velocity = displacement > full_dplmnt_non_const_vel
if reaches_max_velocity:
max_vel = vel
accel_time = full_accel_time
accel_dplmnt = full_accel_dplmnt
dplmnt_non_const_vel = full_dplmnt_non_const_vel
else:
max_vel = math.sqrt(2*displacement)
accel_time = max_vel / accel
accel_dplmnt = displacement / 2.0
dplmnt_non_const_vel = displacement
max_vel_dplmnt = displacement - dplmnt_non_const_vel
max_vel_time = max_vel_dplmnt / vel
self.duration = max_vel_time + 2*accel_time
if do_backlash:
backlash_estimation = MotionEstimation(axis, target_pos, self.fpos)
self.duration += backlash_estimation.duration
@with_custom_members
class Axis(object):
"""
......
......@@ -22,6 +22,7 @@ import numpy
import gevent
from bliss import setup_globals
from bliss.common.axis import MotionEstimation
from bliss.common.temperature import Input, Output, TempControllerCounter
from bliss.common.task_utils import *
from bliss.common.motor_group import Group
......@@ -174,11 +175,22 @@ def ascan(motor, start, stop, npoints, count_time, *counters, **kwargs):
counters = _get_all_counters(counters)
scan_info.update({ 'npoints': npoints, 'total_acq_time': npoints * count_time,
# estimate scan time
step_size = abs(stop - start) / npoints
i_motion_t = MotionEstimation(motor, start).duration
n_motion_t = MotionEstimation(motor, start, start + step_size).duration
total_motion_t = i_motion_t + npoints * n_motion_t
total_count_t = npoints * count_time
estimation = {'total_motion_time': total_motion_t,
'total_count_time': total_count_t,
'total_time': total_motion_t + total_count_t}
scan_info.update({ 'npoints': npoints, 'total_acq_time': total_count_t,
'motors': [TimestampPlaceholder(), motor],
'counters': counters,
'start': [start], 'stop': [stop],
'count_time': count_time })
'count_time': count_time,
'estimation': estimation})
chain = AcquisitionChain(parallel_prepare=True)
timer = default_chain(chain,scan_info)
......@@ -266,13 +278,34 @@ def mesh(motor1, start1, stop1, npoints1, motor2, start2, stop2, npoints2, count
scan_info['title'] = template.format(*args)
counters = _get_all_counters(counters)
# estimate scan time
step_size1 = abs(stop1 - start1) / npoints1
i_motion_t1 = MotionEstimation(motor1, start1).duration
n_motion_t1 = MotionEstimation(motor1, start1, start1 + step_size1).duration
total_motion_t1 = npoints1 *npoints2 * n_motion1_t
step_size2 = abs(stop2 - start2) / npoints2
i_motion_t2 = MotionEstimation(motor2, start2).duration
n_motion_t2 = max(MotionEstimation(motor2, start2, start2 + step_size2).duration,
MotionEstimation(motor1, end1, start1).duration)
total_motion_t2 = npoints2 * n_motion2_t
imotion_t = max(i_motion_t1, i_motion_t2)
total_motion_t = imotion_t + total_motion_t1 + total_motion_t2
total_count_t = npoints1 * npoints2 * count_time
estimation = {'total_motion_time': total_motion_t,
'total_count_time': total_count_t,
'total_time': total_motion_t + total_count_t}
scan_info.update({ 'npoints1': npoints1, 'npoints2': npoints2,
'total_acq_time': npoints1 * npoints2 * count_time,
'total_acq_time': total_count_t,
'motors': [TimestampPlaceholder(), motor1, motor2],
'counters': counters,
'start': [start1, start2], 'stop': [stop1, stop2],
'count_time': count_time })
'count_time': count_time,
'estimation': estimation})
chain = AcquisitionChain(parallel_prepare=True)
timer = default_chain(chain,scan_info)
......@@ -342,11 +375,29 @@ def a2scan(motor1, start1, stop1, motor2, start2, stop2, npoints, count_time,
counters = _get_all_counters(counters)
scan_info.update({ 'npoints': npoints, 'total_acq_time': npoints * count_time,
# estimate scan time
step_size1 = abs(stop1 - start1) / npoints
i_motion1_t = MotionEstimation(motor1, start1).duration
n_motion1_t = MotionEstimation(motor1, start1, start1 + step_size1).duration
step_size2 = abs(stop2 - start2) / npoints
i_motion2_t = MotionEstimation(motor2, start2).duration
n_motion2_t = MotionEstimation(motor2, start2, start2 + step_size2).duration
i_motion_t = max(i_motion1_t, i_motion2_t)
n_motion_t = max(n_motion1_t, n_motion2_t)
total_motion_t = i_motion_t + npoints * nmotion_t
total_count_t = npoints * count_time
estimation = {'total_motion_time': total_motion_t,
'total_count_time': total_count_t,
'total_time': total_motion_t + total_count_t}
scan_info.update({ 'npoints': npoints, 'total_acq_time': total_count_t,
'motors': [TimestampPlaceholder(), motor1, motor2],
'counters': counters,
'start': [start1, start2], 'stop': [stop1, stop2],
'count_time': count_time })
'count_time': count_time,
'estimation': estimation })
chain = AcquisitionChain(parallel_prepare=True)
timer = default_chain(chain,scan_info)
......@@ -460,11 +511,19 @@ def timescan(count_time, *counters, **kwargs):
counters = _get_all_counters(counters)
npoints = kwargs.get("npoints", 0)
scan_info.update({ 'npoints': npoints, 'total_acq_time': npoints * count_time,
total_count_t = npoints * count_time
scan_info.update({ 'npoints': npoints, 'total_acq_time': total_count_t,
'motors': [TimestampPlaceholder()],
'counters': counters,
'start': [], 'stop': [], 'count_time': count_time,
'total_acq_time': npoints * count_time })
'start': [], 'stop': [], 'count_time': count_time })
if npoints > 0:
# estimate scan time
estimation = {'total_motion_time': 0,
'total_count_time': total_count_t,
'total_time': total_count_t}
scan_info['estimation'] = estimation
_log.info("Doing %s", scan_info['type'])
......
......@@ -56,7 +56,7 @@ def initialize(*session_names):
class ScanListener:
'''listen to scan events and compose output'''
HEADER = "Total {npoints} points, {total_acq_time} seconds\n\n" + \
HEADER = "Total {npoints} points{estimation_str}\n\n" + \
"Scan {scan_nb} {start_time_str} {root_path} " + \
"{session_name} user = {user_name}\n" + \
"{title}\n\n" + \
......@@ -112,12 +112,23 @@ class ScanListener:
if scan_info['type'] == 'ct':
return
estimation = scan_info.get('estimation')
if estimation:
total = datetime.timedelta(seconds=estimation['total_time'])
motion = datetime.timedelta(seconds=estimation['total_motion_time'])
count = datetime.timedelta(seconds=estimation['total_count_time'])
estimation_str = ', {0} (motion: {1}, count: {2})'.format(total, motion, count)
else:
estimation_str = ''
col_lens = map(lambda x: max(len(x), self.DEFAULT_WIDTH), col_labels)
h_templ = ["{{0:>{width}}}".format(width=col_len)
for col_len in col_lens]
header = " ".join([templ.format(label)
for templ, label in zip(h_templ, col_labels)])
header = self.HEADER.format(column_header=header, **scan_info)
header = self.HEADER.format(column_header=header,
estimation_str=estimation_str,
**scan_info)
self.col_templ = ["{{0: >{width}}}".format(width=col_len)
for col_len in col_lens]
print_(header)
......@@ -152,6 +163,16 @@ class ScanListener:
dispatcher.disconnect(self.__on_motor_position_changed,
signal='position', sender=motor)
end = datetime.datetime.fromtimestamp(time.time())
start = datetime.datetime.fromtimestamp(scan_info['start_time_stamp'])
dt = end - start
msg = 'Took {0}'.format(dt)
if 'estimation' in scan_info:
time_estimation = scan_info['estimation']['total_time']
msg += ' (estimation was for {0})'.format(datetime.timedelta(seconds=time_estimation))
print_(msg)
def __on_motor_position_changed(self, position, signal=None, sender=None):
labels = []
for motor in self.real_motors:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment