Skip to content

Make number of steps in loop command predictable#3305

Merged
kasemir merged 5 commits intoControlSystemStudio:masterfrom
High-Voltage-Engineering:master
Mar 4, 2025
Merged

Make number of steps in loop command predictable#3305
kasemir merged 5 commits intoControlSystemStudio:masterfrom
High-Voltage-Engineering:master

Conversation

@DenSinH
Copy link
Copy Markdown
Contributor

@DenSinH DenSinH commented Mar 3, 2025

In the previous situation, the loop command was executed as

for (double value = start; value <= end; value += step)

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 LoopCommand instance.

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 LoopCommand with 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.

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 3, 2025

I don't see how the values could ever change given a LoopCommand instance.

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.

@DenSinH
Copy link
Copy Markdown
Contributor Author

DenSinH commented Mar 3, 2025

Wouldn't you just be able to abort the scan? I believe this still saves any data that was obtained before aborting

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 3, 2025

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.

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 3, 2025

.. 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.

@DenSinH
Copy link
Copy Markdown
Contributor Author

DenSinH commented Mar 3, 2025

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.

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 3, 2025

More important than the somewhat hidden feature of being able to modify the loop in the editor is the "alternating scan".
You always loop start to end, but for step<0 we want to go end to start.

See https://controlssoftware.sns.ornl.gov/css_pyscanclient/html/commands.html#loop and note the reverse flag in the loop code.

image

@DenSinH
Copy link
Copy Markdown
Contributor Author

DenSinH commented Mar 3, 2025

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);

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 3, 2025

That should work. Please test it with the example from the doc,

Loop('xpos', 0, 5, 1, [ Loop('ypos', 0, 5, -1 ])

@DenSinH
Copy link
Copy Markdown
Contributor Author

DenSinH commented Mar 4, 2025

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.0

and this with the "new" implementation:

Time	loc://xpos	loc://ypos
2025-03-04 08:47:59.836	0.0	5.0
2025-03-04 08:47:59.845	0.0	4.0
2025-03-04 08:47:59.847	0.0	3.0
2025-03-04 08:47:59.850	0.0	2.0
2025-03-04 08:47:59.852	0.0	1.0
2025-03-04 08:47:59.858	1.0	5.0
2025-03-04 08:47:59.861	1.0	4.0
2025-03-04 08:47:59.863	1.0	3.0
2025-03-04 08:47:59.864	1.0	2.0
2025-03-04 08:47:59.866	1.0	1.0
2025-03-04 08:47:59.869	2.0	5.0
2025-03-04 08:47:59.871	2.0	4.0
2025-03-04 08:47:59.873	2.0	3.0
2025-03-04 08:47:59.875	2.0	2.0
2025-03-04 08:47:59.877	2.0	1.0
2025-03-04 08:47:59.880	3.0	5.0
2025-03-04 08:47:59.882	3.0	4.0
2025-03-04 08:47:59.885	3.0	3.0
2025-03-04 08:47:59.886	3.0	2.0
2025-03-04 08:47:59.888	3.0	1.0
2025-03-04 08:47:59.890	4.0	5.0
2025-03-04 08:47:59.894	4.0	4.0
2025-03-04 08:47:59.896	4.0	3.0
2025-03-04 08:47:59.897	4.0	2.0
2025-03-04 08:47:59.899	4.0	1.0

(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.

@DenSinH
Copy link
Copy Markdown
Contributor Author

DenSinH commented Mar 4, 2025

After the latest commit:

# Data for scan ID 2934
Time	loc://xpos	loc://ypos
2025-03-04 09:12:23.076	0.0	5.0
2025-03-04 09:12:23.097	0.0	4.0
2025-03-04 09:12:23.098	0.0	3.0
2025-03-04 09:12:23.100	0.0	2.0
2025-03-04 09:12:23.102	0.0	1.0
2025-03-04 09:12:23.105	0.0	0.0
2025-03-04 09:12:23.108	1.0	0.0
2025-03-04 09:12:23.113	1.0	1.0
2025-03-04 09:12:23.114	1.0	2.0
2025-03-04 09:12:23.117	1.0	3.0
2025-03-04 09:12:23.117	1.0	4.0
2025-03-04 09:12:23.119	1.0	5.0
2025-03-04 09:12:23.121	2.0	5.0
2025-03-04 09:12:23.123	2.0	4.0
2025-03-04 09:12:23.125	2.0	3.0
2025-03-04 09:12:23.127	2.0	2.0
2025-03-04 09:12:23.128	2.0	1.0
2025-03-04 09:12:23.130	2.0	0.0
2025-03-04 09:12:23.133	3.0	0.0
2025-03-04 09:12:23.135	3.0	1.0
2025-03-04 09:12:23.137	3.0	2.0
2025-03-04 09:12:23.139	3.0	3.0
2025-03-04 09:12:23.142	3.0	4.0
2025-03-04 09:12:23.144	3.0	5.0
2025-03-04 09:12:23.147	4.0	5.0
2025-03-04 09:12:23.150	4.0	4.0
2025-03-04 09:12:23.153	4.0	3.0
2025-03-04 09:12:23.154	4.0	2.0
2025-03-04 09:12:23.156	4.0	1.0
2025-03-04 09:12:23.158	4.0	0.0
2025-03-04 09:12:23.160	5.0	0.0
2025-03-04 09:12:23.162	5.0	1.0
2025-03-04 09:12:23.163	5.0	2.0
2025-03-04 09:12:23.164	5.0	3.0
2025-03-04 09:12:23.166	5.0	4.0
2025-03-04 09:12:23.169	5.0	5.0

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.

@kasemir
Copy link
Copy Markdown
Collaborator

kasemir commented Mar 4, 2025

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.

@kasemir kasemir merged commit b86f899 into ControlSystemStudio:master Mar 4, 2025
@kasemir kasemir mentioned this pull request Apr 29, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants