Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Issue 1303: change the timescan first parameter to nb_points #1634

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions src/sardana/macroserver/macros/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,16 @@ def _prepare(self, motorlist, startlist, endlist, scan_length, integ_time,

self.motors = motorlist
self.starts = numpy.array(startlist, dtype='d')
self.finals = numpy.array(endlist, dtype='d')
if scan_length:
self.finals = numpy.array(endlist, dtype='d')
else:
self.finals = numpy.array(startlist, dtype='d')
if scan_length < 0:
self.do_last_point = False
scan_length = -scan_length
else:
self.do_last_point = True

self.mode = mode
self.integ_time = integ_time
self.opts = opts
Expand Down Expand Up @@ -156,7 +165,11 @@ def _prepare(self, motorlist, startlist, endlist, scan_length, integ_time,
if mode == StepMode:
self.nr_interv = scan_length
self.nb_points = self.nr_interv + 1
self.interv_sizes = (self.finals - self.starts) / self.nr_interv
if self.nr_interv:
self.interv_sizes = (self.finals - self.starts) / self.nr_interv
else:
self.interv_sizes = (self.finals - self.starts) * 0

self.name = opts.get('name', 'a%iscan' % self.N)
self._gScan = SScan(self, self._stepGenerator,
moveables, env, constrains, extrainfodesc)
Expand Down Expand Up @@ -310,7 +323,10 @@ def getTimeEstimation(self):
path = MotionPath(v_motor, 0, length)
max_step0_time = max(max_step0_time, path0.duration)
max_step_time = max(max_step_time, path.duration)
motion_time = max_step0_time + self.nr_interv * max_step_time
if self.nr_interv:
motion_time = max_step0_time + self.nr_interv * max_step_time
else:
motion_time = max_step0_time
# calculate acquisition time
acq_time = self.nb_points * self.integ_time
total_time = motion_time + acq_time
Expand All @@ -332,7 +348,10 @@ def _fill_missing_records(self):
nb_of_points = self.nb_points
scan = self._gScan
nb_of_records = len(scan.data.records)
missing_records = nb_of_points - nb_of_records
if not hasattr(self, "do_last_point") or self.do_last_point:
missing_records = nb_of_points - nb_of_records
else:
missing_records = nb_of_points - nb_of_records - 1
scan.data.initRecords(missing_records)

def _get_nr_points(self):
Expand Down Expand Up @@ -1936,6 +1955,7 @@ def prepare(self, m1, m1_start_pos, m1_final_pos, m1_nr_interv,
self.starts = numpy.array([m1_start_pos, m2_start_pos], dtype='d')
self.finals = numpy.array([m1_final_pos, m2_final_pos], dtype='d')
self.nr_intervs = numpy.array([m1_nr_interv, m2_nr_interv], dtype='i')
self.do_last_point = True

# Number of intervals of the first motor which is doing the
# continuous scan.
Expand Down Expand Up @@ -2055,7 +2075,7 @@ def _get_nr_points(self):

class timescan(Macro, Hookable):
"""Do a time scan over the specified time intervals. The scan starts
immediately. The number of data points collected will be nr_interv + 1.
immediately. The number of data points collected will be nr_points.
Count time is given by integ_time. Latency time will be the longer one
of latency_time and measurement group latency time.
"""
Expand All @@ -2064,13 +2084,13 @@ class timescan(Macro, Hookable):
'post-acq', 'post-scan')}

param_def = [
['nr_interv', Type.Integer, None, 'Number of scan intervals'],
['nb_points', Type.Integer, None, 'Number of scan points'],
['integ_time', Type.Float, None, 'Integration time'],
['latency_time', Type.Float, 0, 'Latency time']]

def prepare(self, nr_interv, integ_time, latency_time):
self.nr_interv = nr_interv
self.nb_points = nr_interv + 1
def prepare(self, nb_points, integ_time, latency_time):
self.nr_interv = nb_points - 1
self.nb_points = nb_points
self.integ_time = integ_time
self.latency_time = latency_time
self._gScan = TScan(self)
Expand Down
55 changes: 55 additions & 0 deletions src/sardana/macroserver/macros/test/test_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,61 @@ def macro_runs(self, macro_params=None, wait_timeout=30.0):
self.assertAlmostEqual(data[last_pos][value], expected_final_pos, 7,
"Final possition differs from set value (using getLog)")

@testRun(macro_params=[_m1, '0', '5', '0', '.1'], wait_timeout=30.0)
@testStop(macro_params=[_m1, '0', '5', '0', '.1'])
class AscanOnePointTest(ANscanTest, unittest.TestCase):

"""Test of ascan macro. See :class:`ANscanTest` for requirements.
It verifies that macro ascan can be executed and stoped and tests
the output of the ascan using data from log system and macro data.
"""
macro_name = 'ascan'

def macro_runs(self, macro_params=None, wait_timeout=30.0):
"""Reimplementation of macro_runs method for ascan macro.
It verifies using double checking, with log output and data from
the macro:

- The motor initial and final positions of the scan are the
ones given as input.

- Intervals in terms of motor position between one point and
the next one are equidistant.
"""
# call the parent class implementation
ANscanTest.macro_runs(self, macro_params=macro_params,
wait_timeout=wait_timeout)

mot_name = macro_params[0]
expected_init_pos = float(macro_params[1])
expected_final_pos = float(macro_params[2])
self.steps = int(macro_params[-2])

# Test data from macro (macro_executor.getData())
data = self.macro_executor.getData()
mot_init_pos = data[min(data.keys())].data[mot_name]
mot_final_pos = data[max(data.keys())].data[mot_name]
pre = mot_init_pos


self.assertAlmostEqual(mot_init_pos, expected_init_pos, 7,
"Initial possition differs from set value (using getData)")
self.assertAlmostEqual(mot_final_pos, expected_init_pos, 7,
"Final possition differs from set value (using getData)")

# Test data from log_output (macro_executor.getLog('output'))
log_output = self.macro_executor.getLog('output')
data = parsing_log_output(log_output)
init_pos = 0
last_pos = -1
value = 1
pre = data[init_pos]

self.assertAlmostEqual(data[init_pos][value], expected_init_pos, 7,
"Initial possition differs from set value (using getLog)")
self.assertAlmostEqual(data[last_pos][value], expected_init_pos, 7,
"Final possition differs from set value (using getLog)")


@testRun(macro_params=[_m1, '-1', '1', '2', '.1'], wait_timeout=30)
@testStop(macro_params=[_m1, '1', '-1', '3', '.1'])
Expand Down
15 changes: 10 additions & 5 deletions src/sardana/macroserver/macros/test/test_scanct.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ def check_using_output(self, expected_nb_points):
"Checked using macro output")
self.assertEqual(obtained_nb_points, expected_nb_points, msg)

msg = "Scan points are NOT in good order.\nChecked using macro output"
self.assertTrue(ordered_points, msg)
if expected_nb_points > 1:
msg = "Scan points are NOT in good order.\nChecked using macro output"
self.assertTrue(ordered_points, msg)

def check_using_data(self, expected_nb_points):
# Test data from macro (macro_executor.getData())
Expand All @@ -145,9 +146,10 @@ def check_using_data(self, expected_nb_points):
"\nChecked using macro data.")
self.assertEqual(obtained_nb_points_data, expected_nb_points, msg)

msg = ("Scan points are NOT in good order."
"\nChecked using macro data.")
self.assertTrue(order_points_data, msg)
if expected_nb_points > 1:
msg = ("Scan points are NOT in good order."
"\nChecked using macro data.")
self.assertTrue(order_points_data, msg)

def check_stopped(self):
self.assertStopped('Macro %s did not stop' % self.macro_name)
Expand Down Expand Up @@ -225,6 +227,7 @@ def tearDown(self):
}
}
ascanct_params_1 = ['_test_mt_1_1', '0', '10', '100', '0.1']
ascanct_params_2 = ['_test_mt_1_1', '10', '10', '0', '0.1']


@testRun(meas_config=mg_config1, macro_params=ascanct_params_1,
Expand All @@ -235,6 +238,8 @@ def tearDown(self):
wait_timeout=30)
@testRun(meas_config=mg_config4, macro_params=ascanct_params_1,
wait_timeout=30)
@testRun(meas_config=mg_config4, macro_params=ascanct_params_2,
wait_timeout=30)
@testStop(meas_config=mg_config1, macro_params=ascanct_params_1,
stop_delay=5, wait_timeout=20)
class AscanctTest(ScanctTest, unittest.TestCase):
Expand Down
52 changes: 39 additions & 13 deletions src/sardana/macroserver/scan/gscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,9 @@ def stepUp(self, n, step, lstep):
if 'extrainfo' in step:
data_line.update(step['extrainfo'])

self.data.addRecord(data_line)
if not hasattr(self.macro, "do_last_point") or \
self.macro.do_last_point or n + 1 != self.macro.nb_points:
self.data.addRecord(data_line)

# post-step hooks
for hook in step.get('post-step-hooks', ()):
Expand Down Expand Up @@ -1904,7 +1906,6 @@ def scan_loop(self):
# start move & acquisition as close as possible
# from this point on synchronization becomes critical
manager.add_job(self.go_through_waypoints)

while not self._all_waypoints_finished:

# wait for motor to reach start position
Expand Down Expand Up @@ -2002,7 +2003,9 @@ def scan_loop(self):
if 'extrainfo' in step:
data_line.update(step['extrainfo'])

self.data.addRecord(data_line)
if not hasattr(self.macro, "do_last_point") or \
self.macro.do_last_point or point_nb + 1 != nb_points:
self.data.addRecord(data_line)

if scream:
yield ((point_nb + 1) / nb_points) * 100
Expand Down Expand Up @@ -2052,6 +2055,9 @@ def value_buffer_changed(self, channel, value_buffer):
return

full_name = channel.getFullName()
if hasattr(self.macro, "do_last_point") and not self.macro.do_last_point \
and self.macro.nb_points - 1 in value_buffer['index']:
return

info = {'label': full_name}
if self._index_offset != 0:
Expand Down Expand Up @@ -2080,6 +2086,9 @@ def value_ref_buffer_changed(self, channel, value_ref_buffer):
full_name = channel.getFullName()

info = {'label': full_name}
if hasattr(self.macro, "do_last_point") and not self.macro.do_last_point \
and self.macro.nb_points - 1 in value_ref_buffer['index']:
return
if self._index_offset != 0:
idx = np.array(value_ref_buffer['index'])
idx += self._index_offset
Expand Down Expand Up @@ -2251,7 +2260,6 @@ def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
time of all the motors
- active_time: time interval while all the physical motors will
maintain constant velocity"""

positions = waypoint['positions']
active_time = waypoint["active_time"]

Expand Down Expand Up @@ -2279,10 +2287,14 @@ def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
zip(self._physical_moveables, start_positions, positions):
total_displacement = abs(end - start)
direction = 1 if end > start else -1
interval_displacement = total_displacement / self.macro.nr_interv
if self.macro.nr_interv:
interval_displacement = total_displacement / self.macro.nr_interv
else:
interval_displacement = 0.
# move further in order to acquire the last point at constant
# velocity
end = end + direction * interval_displacement
if not hasattr(self.macro, "do_last_point") or self.macro.do_last_point:
end = end + direction * interval_displacement

base_vel = moveable.getBaseRate()
ideal_vmotor = VMotor(accel_time=acc_time,
Expand All @@ -2294,7 +2306,7 @@ def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
backup_vel = moveable.getVelocity(force=True)
ideal_max_vel = try_vel = ideal_path.max_vel
try:
while True:
while self.macro.nr_interv:
moveable.setVelocity(try_vel)
get_vel = moveable.getVelocity(force=True)
if get_vel < ideal_max_vel:
Expand Down Expand Up @@ -2356,6 +2368,9 @@ def _go_through_waypoints(self):
" theoretical values"
)
for i, waypoint in waypoints:
if hasattr(self.macro, "do_last_point") and \
not self.macro.do_last_point and i + 1 == self.macro.nb_points:
break
self.macro.debug("Waypoint iteration...")

start_positions = waypoint.get('start_positions')
Expand Down Expand Up @@ -2400,7 +2415,7 @@ def _go_through_waypoints(self):
return

# at least one motor must have different start and final positions
if all(self.macro.starts == self.macro.finals):
if self.macro.nb_points > 1 and all(self.macro.starts == self.macro.finals):
if len(self.macro.starts) > 1:
msg = "Scan start and end must be different for at " \
"least one motor"
Expand Down Expand Up @@ -2434,7 +2449,10 @@ def _go_through_waypoints(self):
final = path._final_user_pos
total_position = (final - start) / repeats
initial_position = start
total_time = abs(total_position) / path.max_vel
if self.macro.nb_points > 1:
total_time = abs(total_position) / path.max_vel
else:
total_time = 0.
delay_time = path.max_vel_time
delay_position = start - path.initial_user_pos
synch = [
Expand Down Expand Up @@ -2514,8 +2532,11 @@ def _go_through_waypoints(self):
nb_points)
theoretical_timestamps = generate_timestamps(synch, dt_timestamp)
for index, data in list(theoretical_positions.items()):
data.update(theoretical_timestamps[index])
initial_data[index + self._index_offset] = data
if not hasattr(self.macro, "do_last_point") or \
self.macro.do_last_point or index + 1 != len(theoretical_positions):
data.update(theoretical_timestamps[index])
initial_data[index + self._index_offset] = data

# TODO: this changes the initial data on-the-fly - seems like not
# the best practice
self.data.initial_data = initial_data
Expand Down Expand Up @@ -2741,7 +2762,9 @@ def stepUp(self, n, step, lstep):
if 'extrainfo' in step:
data_line.update(step['extrainfo'])

self.data.addRecord(data_line)
if not hasattr(self.macro, "do_last_point") or \
self.macro.do_last_point or n + 1 != self.macro.nb_points:
self.data.addRecord(data_line)

# post-step hooks
for hook in step.get('post-step-hooks', ()):
Expand Down Expand Up @@ -2858,7 +2881,10 @@ def _fill_missing_records(self):
# fill record list with dummy records for the final padding
nb_points = self.macro.nb_points
records = len(self.data.records)
missing_records = nb_points - records
if not hasattr(self.macro, "do_last_point") or self.macro.do_last_point:
missing_records = nb_points - records
else:
missing_records = nb_points - records - 1
self.data.initRecords(missing_records)

def _estimate(self):
Expand Down