Make number of steps in loop command predictable#3305
Make number of steps in loop command predictable#3305kasemir merged 5 commits intoControlSystemStudio:masterfrom
Conversation
The idea was that you can open a running scan in the Scan editor and then change the properties of the loop while it's running. You're not supposed to try that at home, but if you have a loop that takes hours to complete and you need to cut it short for some pressing reason, you could change the "end" that way. |
|
Wouldn't you just be able to abort the scan? I believe this still saves any data that was obtained before aborting |
|
Maybe you don't want to abort. You ramped some temperature over a wide range, which takes hours. You notice that the range covered so far includes the signal of interest, so you want to end the loop and otherwise move on, not abort. |
|
.. but modifying a running scan is not the best idea. You certainly can't modify commands that already executed, nor add/remove commands. So maybe we simply kill this hardly-ever-used feature and from now on always do the loop your way. |
|
Another option might be to do some sort of "dual" approach, where we calculate the number of steps predictably, but deviate if the step size / loop end changed during iteration. |
|
More important than the somewhat hidden feature of being able to modify the loop in the editor is the "alternating scan". See https://controlssoftware.sns.ornl.gov/css_pyscanclient/html/commands.html#loop and note the |
|
Right, so something like int num_steps = getNumSteps();
if (step < 0) // step is < 0, so stepping down
start = end;
for (int i = 0; i < num_steps; i++)
executeStep(context, device, condition, readback, start + i * step); |
|
That should work. Please test it with the example from the doc, |
|
Not sure how to launch the Jython interpreter, but creating and submitting this scan: <commands>
<loop>
<device>loc://xpos</device>
<start>0.0</start>
<end>5.0</end>
<step>1.0</step>
<tolerance>0.1</tolerance>
<body>
<loop>
<device>loc://ypos</device>
<start>0.0</start>
<end>5.0</end>
<step>-1.0</step>
<tolerance>0.1</tolerance>
<body>
<log>
<devices>
<device>loc://xpos</device>
<device>loc://ypos</device>
</devices>
</log>
</body>
</loop>
</body>
</loop>
</commands>yielded this data with the "old" implementation: # Data for scan ID 1635
Time loc://xpos loc://ypos
2025-03-03 16:50:06.089 0.0 5.0
2025-03-03 16:50:06.109 0.0 4.0
2025-03-03 16:50:06.112 0.0 3.0
2025-03-03 16:50:06.114 0.0 2.0
2025-03-03 16:50:06.116 0.0 1.0
2025-03-03 16:50:06.120 0.0 0.0
2025-03-03 16:50:06.123 1.0 0.0
2025-03-03 16:50:06.126 1.0 1.0
2025-03-03 16:50:06.130 1.0 2.0
2025-03-03 16:50:06.132 1.0 3.0
2025-03-03 16:50:06.135 1.0 4.0
2025-03-03 16:50:06.136 1.0 5.0
2025-03-03 16:50:06.139 2.0 5.0
2025-03-03 16:50:06.142 2.0 4.0
2025-03-03 16:50:06.144 2.0 3.0
2025-03-03 16:50:06.145 2.0 2.0
2025-03-03 16:50:06.147 2.0 1.0
2025-03-03 16:50:06.149 2.0 0.0
2025-03-03 16:50:06.151 3.0 0.0
2025-03-03 16:50:06.154 3.0 1.0
2025-03-03 16:50:06.156 3.0 2.0
2025-03-03 16:50:06.158 3.0 3.0
2025-03-03 16:50:06.159 3.0 4.0
2025-03-03 16:50:06.161 3.0 5.0
2025-03-03 16:50:06.163 4.0 5.0
2025-03-03 16:50:06.165 4.0 4.0
2025-03-03 16:50:06.167 4.0 3.0
2025-03-03 16:50:06.168 4.0 2.0
2025-03-03 16:50:06.170 4.0 1.0
2025-03-03 16:50:06.172 4.0 0.0
2025-03-03 16:50:06.174 5.0 0.0
2025-03-03 16:50:06.176 5.0 1.0
2025-03-03 16:50:06.178 5.0 2.0
2025-03-03 16:50:06.179 5.0 3.0
2025-03-03 16:50:06.181 5.0 4.0
2025-03-03 16:50:06.182 5.0 5.0and this with the "new" implementation: (I broke something in my build so I had to come back to it this morning). I guess that we will always need to do an extra step. |
|
After the latest commit: which seems to be the same as the old value. Just wanted to note that the plot you sent is in reverse of what is actually happens. |
|
Thanks for testing that loop example! The plot from the documentation is a power point drawing, might well have been wrong about the initial direction of the inner loop. Still, the end result is now the same as before, so that's good. |

In the previous situation, the loop command was executed as
which made the number of steps in the loop highly unpredictable due to finite precision errors. It seems that there was left some possibility to change the loop step and end "on the fly", though I don't see how the values could ever change given a
LoopCommandinstance.Making the number of steps in the loop command easily computable would make it a lot easier to make 2D plots from scans executed with the scan server, as the image dimensions can be computed ahead of time (without using an O(n) algorithm, i.e. just doing the for loop).
Perhaps I missed something in why/how the loop bounds can be changed on the fly, unless one somehow inherits and re-implements
LoopCommandwith some overrides, but this seems like a strange edge case to take into account.Perhaps you prefer a floor or round on the
getNumSteps, I just figured the closest behavior to the "old" implementation would be this, because of the <= comparison.