DPC: the field map tracks the beam region, and the pass is cancellable - #146
Open
CSSFrancis wants to merge 2 commits into
Open
DPC: the field map tracks the beam region, and the pass is cancellable#146CSSFrancis wants to merge 2 commits into
CSSFrancis wants to merge 2 commits into
Conversation
…llable
Dragging the beam region did nothing until the pass before it had run all
the way through. Measured in the real app on a 48x48x64x64 lazy scan: ZERO
map repaints across a 6.4 s drag.
Three separate causes, and the first is the one that matters.
THE MAP DID NOT FOLLOW THE REGION. `_on_region_drag` armed a debounce on
`pointer_up` only, so the map was frozen for the whole gesture. The region
is now a real BaseSelector — the same CircleSelector/AnnularSelector a
virtual image puts on the same plot — so it inherits the navigator's serial
latest-wins dispatcher and its trailing settle instead of a hand-rolled
debounce. The re-measure hangs off `index_hooks`, the seam the vector
overlays already use. That is what "it should be identical to the virtual
image" actually means, and it deletes more than it adds: the settle timer,
the live timer, the pending flags and the paint gate are all gone.
THE PASS COULD NOT BE STOPPED. It ran through `signal.map`, which has no
interruption point anywhere inside it, so a superseded pass finished and
only THEN had its result discarded. It is now one dask graph and one
`client.compute` future held on `_measure_future` — the cancellation
`virtual_image.reduce` already uses. Chunks paint into the display array as
they land (`compute_with_live_buffer`, the virtual-image stream's own call),
and the map BLANKS when a new pass starts, so superseding is something you
can see rather than something you have to trust.
THE PASS WAS SLOW ENOUGH THAT ALL OF THIS SHOWED. The centre of mass ran a
Python function per frame with a `scipy.ndimage` call inside it; at tens of
thousands of frames that overhead IS the runtime. It is now one contraction
per block against stacked detector-sized weights:
64x64 scan of 64x64 frames before 1429.4 ms
after 38.3 ms (37x)
the virtual image, for scale 15.5 ms
Stacking the three moments into one `(3, sy, sx)` weight array reads the
block ONCE instead of three times (73 ms -> 38 ms). Bit-identical to the
per-frame reference, which is what keeps pyxem equivalence: a test asserts
`array_equal` frame by frame rather than `allclose`.
Also fixed, found while testing the above:
- Opening the wizard fired TWO passes. Placing the selector writes its
geometry, and the widget reports that write as a move, so an identical
pass immediately superseded the opening one — a whole scan's work and
the progressive fill with it. `measure` now records the region it ran
with and a move to that same geometry does not re-measure.
- The brightness readout did a dask frame read on the event loop at
`pointer_up`. It runs on a worker now and refreshes when a pass lands.
- The beam region has no "off". It IS what the centre of mass is taken
over, so switching it off only meant taking the whole frame with no
handle to grab.
- The default radius went from a quarter of the short detector axis to
HALF — the inscribed circle. With the region always on, the default is
what an unattended scan is measured with, and the old one CLIPPED the
beam: on the synthetic disc it under-read true shifts of 1.5/3.0/4.5 px
by 0.30/1.00/1.76 px. The inscribed circle reproduces the whole-frame
answer exactly.
`dpc_live_region.spec.ts` pins the user-visible claim by sampling the map's
own CANVAS pixels across a real drag: 0 changed frames on the old
behaviour, 12-13 on this one. Deliberately not a window screenshot — the
"Calculating…" chip pulses on a CSS animation, and hashing that scored the
BROKEN build 22 of 24 and passed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dragging the beam region did nothing until the pass before it had run all the way through. Measured in the real app on a 48×48×64×64 lazy scan: zero map repaints across a 6.4 s drag.
Three separate causes. The first is the one that matters.
The map did not follow the region
_on_region_dragarmed a debounce onpointer_uponly, so the map was frozen for the whole gesture.The region is now a real
BaseSelector— the sameCircleSelector/AnnularSelectora virtual image puts on the same plot — so it inherits the navigator's serial latest-wins dispatcher and its trailing settle. The re-measure hangs offindex_hooks, the seam the vector overlays already use. This deletes more than it adds: the settle timer, the live timer, the pending flags and a paint gate all go.The pass could not be stopped
It ran through
signal.map, which has no interruption point anywhere inside it, so a superseded pass finished and only then had its result discarded.It is now one dask graph and one
client.computefuture on_measure_future— the cancellationvirtual_image.reducealready uses. Chunks paint into the display array as they land (compute_with_live_buffer, the virtual-image stream's own call), and the map blanks when a new pass starts, so superseding is visible rather than something to trust.The pass was slow enough that all of this showed
The centre of mass ran a Python function per frame with a
scipy.ndimagecall inside it. At tens of thousands of frames that overhead is the runtime. It is now one contraction per block against stacked detector-sized weights:map+ scipy)Stacking the three moments into one
(3, sy, sx)weight array reads the block once instead of three times (73 → 38 ms). Bit-identical to the per-frame reference, which is what preserves pyxem equivalence — a test assertsarray_equalframe by frame, notallclose.Also fixed, found while testing the above
pointer_up. It runs on a worker now and refreshes when a pass lands.Verification
dpc_live_region.spec.tspins the user-visible claim by sampling the map's own canvas pixels across a real drag: 0 changed frames on the old behaviour, 12–13 on this one.Deliberately not a window screenshot — the "Calculating…" chip pulses on a CSS animation, and hashing that scored the broken build 22 of 24 and passed. If you touch that metric, read the comment on
mapHashfirst.test_dpc.py+test_dpc_action.py: 160 passed. The e2e specs have not been run against this branch.Scope note
The units/
commit_result_treework that also touches these files is deliberately not here — it depends oncommit.pyandviews.pychanges that belong with the report-embed PR.