Conversation
|
We seem to have lost the number of frames for the detector from the windowed_stream. We should add a use case that takes a I think all the information is there:
|
|
@coretl I've been spending some time trying to understand the new syntax. Currently, # The outer Acquire has no detectors or fly — it's just scaffolding
spec = Acquire(
Concat(
Acquire(motion_a, fly=True, detectors=[...]),
Acquire(motion_b, fly=False, detectors=[...]),
),
continuous_streams=[ContinuousStream("cameras", [...])],
)The issue is that Wouldn't introduce a spec = ScanConfig(
Concat(
Acquire(motion_a, fly=True, detectors=[...]),
Acquire(motion_b, fly=False, detectors=[...]),
),
continuous_streams=[ContinuousStream("cameras", [...])],
monitors=[MonitorStream("ring_current", ...)],
)
scan = spec.compile()
|
|
I debated this with claude:
Claude settled on 1. I was on the fence. If you prefer 2 as well then see how that comes out. |
|
Three changes discussed in meetings with @coretl and @shihab-dls:
|
|
That is the correct understanding (plus pass However, I have since thought of some issues with this: PCOMP actually requires the first position in a window at 1/2 deadtime, then all subsequent positions at livetime + 1/2 deadtime. I don't think we should push that down to scanspec. We need the following consumer patterns:
I suggest the following API instead:
|
scanspec 2.0 — Gap Analysis, Code Review & Phased Plan
Fixes #196
A. Gap Analysis
Spec subclasses
LinspaceWindowGenerator.Linspace.boundedLinspacefrom extreme bounds. Nice-to-have for 1.x parity.RangeLinspace. Required per thoughts.md ("All others will be implemented").Range.boundedRangefor extreme-bound construction. Comes withRange.LineLinspace(Line = Linspace). Trivial onceLinspaceexists — add to exports.Staticnum.Spiral_num_pointsauto-computesnum. Uses non-linearWindowGenerator.EllipsePolygonProduct*operator. Compiles to prepended generators.ZipSnake~operator sets snake flag on innermost generator.ConcatRepeatnum.FlyAcquire(fly=True). Fly was a boolean wrapper in 1.x.ConstantDurationAcquire.duration&DetectorGrouptiming.SquashAcquireCore data structures
Dimension(1.x)Dimensionstores axes/length/snake + lazysetpoints().SnakedDimensionDimension/WindowGeneratorin 2.0.PathScan.__iter__yieldingWindowobjects.MidpointsDimension.setpoints()+Scan.__iter__.SliceAxesPointsdict[AxisT, float]onWindow.static_axes/Window.moving_axes.gap_between_framespreviouslinkage.squash_framesSquash.stack2dimensionScan.__iter__/_iter_with_outer.discriminated_union_of_subclassesAnySpecruntime class +PosargsMeta+ pydanticDiscriminator.StrictConfigmodel_config = ConfigDict(frozen=True)onSpecbase.WindowGeneratorWindowScan.__iter__; carries static_axes, moving_axes, AxisMotion, trigger_groups.AxisMotionTriggerPatternTriggerGroupDetectorGroupWindowedStreamContinuousStreamMonitorStreamScan__iter__,with_start.Auxiliary modules
plot_spec/plot.pycli.pyplotandservicecommands.service.pysphinxext.pyexample_specdirective.__main__.pypython -m scanspec.__init__.pyexportscore,specs,__version__.Deprecated 1.x features (not migrated)
fly()functionAcquire(fly=True).step()functionAcquire.get_constant_duration()VARIABLE_DURATIONMaskB. Phased Plan
Phase C: Ellipse, Polygon, Range, Line
Goal: Implement remaining Spec subclasses required by thoughts.md.
Files to create/modify:
src/scanspec2/specs.py— addEllipse,Polygon,Range,Linealias,Linspace.bounded,Range.boundedtests/scanspec2/test_specs.py— add construction tests for new classestests/scanspec2/test_compile.py— add compile/setpoint tests for new classesImplementation details:
Range: LikeLinspacebut parameterised by(axis, start, stop, step). Computenumfrom step. Compile to linearWindowGenerator.Range.bounded(axis, lower, upper, step): Classmethod converting bounds to midpoints.Linspace.bounded(axis, lower, upper, num): Classmethod converting bounds to start/stop.Line = Linspace: Simple alias.Ellipse:(x_axis, x_centre, x_diameter, x_step, y_axis, y_centre, y_diameter, y_step, snake, vertical). BuildRangegrid, apply elliptical mask, compile to non-linearWindowGenerator.Polygon:(x_axis, y_axis, vertices, x_step, y_step, snake, vertical). BuildRangegrid, apply polygon ray-cast mask, compile to non-linearWindowGenerator.Tests to add:
Rangeconstruction, compile, setpoints,boundedLinspace.boundedconstructionEllipseconstruction, compile, mask correctness, point countPolygonconstruction, compile, mask correctness, concave shapeLinealias identityRange,Ellipse,PolygonAcceptance criteria:
pytest tests/scanspec2/ -vpassespyright src/scanspec2/ tests/scanspec2/— 0 errorsruff check src/scanspec2/ tests/scanspec2/— 0 errorsPhase D:
Scan.flyproperty,__init__.pyexports, final validationGoal: Align with API_SPEC on remaining points; clean up exports.
Files to create/modify:
src/scanspec2/core.py— addScan.flypropertysrc/scanspec2/__init__.py— exportcore,specstests/scanspec2/test_compile.py— testscan.flypropertytests/scanspec2/test_core.py— testscan.flypropertyImplementation details:
Scan.fly→@propertyreturningself.generators[-1].fly if self.generators else False__init__.py→ exportcoreandspecsmodules; add__version__if_version.pyexistsTests to add:
scan.flyreturns True for fly scans, False for step scansscan.flyreturns False for empty generator listscanspec2.coreandscanspec2.specsAcceptance criteria:
scan.flyaccessible as propertyfrom scanspec2 import core, specsworksPhase E (optional):
plot.py,cli.py,service.py,sphinxext.py,__main__.pyGoal: Restore non-essential 1.x parity for tooling.
These modules are nice-to-have and not required for the core API. They can be ported incrementally after the core is complete.
Priority order:
plot.py— most useful for development/debuggingcli.py+__main__.py— depends onplot.pyandservice.pyservice.py— REST API for external consumerssphinxext.py— docs infrastructureEach of these should be its own sub-phase or conversation.
Pre-merge checklist
API_SPEC.mdandthoughts.md— these are working documents that should not ship in the final package.docs/) to reflect the scanspec 2.0 API and verify it reads well end-to-end.