Commit 561477c8 authored by Vacaliuc, Bogdan's avatar Vacaliuc, Bogdan
Browse files

add prompt 3 and 3.1

parent 36663cd0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ There are many files that have been collected during previous investigations. Th

* /SNS/users/6ov/BL4B/2026/02/07/
* /SNS/users/6ov/BL4B/2026/03/26/
* /SNS/users/6ov/BL4B/2026/04/13/
* /home/controls/var/tmp/ScriptScan/2026A/
* /home/controls/var/tmp/ScriptScan/scripts/
* /home/controls/var/tmp/PYTHON/2025-B/
+137 −0
Original line number Diff line number Diff line
@@ -328,3 +328,140 @@ Claude updated the code in [scantools:issue/4850](https://code.ornl.gov/idac/cs/
## Prompt 3 

You are working in the tasking project, bl4b-alignment-integration branch. The code uses the Experimental Physics Industrial Control System (EPICS) as its basis. Inspect bl4b-scripts; it is currently on the ScriptScan/2026A branch. Read the file bl4b-scripts/flying_z.py - it is a recent example of a very efficient way to opitimize the position of BL4B:Mot:hs. Read the file bl4b-scripts/CHI_scan.py. Please make a plan to synthesize a new file bl4b-scripts/flying_chi.py that performs the function of CHI_scan.py using the efficient camonitor callback and timestamp correlation technique in flying_z.py. You may freely inspect the git log of all code repositories as you need to develop your understanding of the scan system, including modifications to the existing functions as that may be informative towards the types of parameterizations that are needed. If you need a tool to analyze the code or artifacts, please ask me to fetch it for you. If you run into an issue where you need sudo, ask me to execute the line in a separate terminal.

```
● I've read flying_z.py, CHI_scan.py, CHI_measurement.py, move_align.py, and the analysis-chis-scan-integration.md notes. Here's my    
  plan.                                                        
                                                                                                                                       
  Plan: bl4b-scripts/flying_chi.py                                                                                                     
                                                                                                                                       
  Core idea                                                                                                                            
                                                                  
  Replace CHI_scan.py's 11-step move → accumulate Cv=1.0 mC → read XY image → compute σ loop with one continuous chis sweep from -1.8° 
  → +1.8°. Run camonitor callbacks on timestamped PV updates, then np.interp each observable onto the chis.RBV timeline just like      
  flying_z.py does. Fit the resulting curve the same way CHI_scan.py already fits step-scan data.                                      
                                                                  
  Key PV difference from flying_z

  flying_z reduces to a single scalar (BL4B:Det:N1:Det1:EventRate_RBV). CHI_scan needs beam shape, not rate. The scantools integration 
  work (analysis-chis-scan-integration.md §4 / §9) confirms ADnED publishes pre-computed 1D projections we can camonitor directly:
  - BL4B:Det:N1:Det1:XY:Stat:ProfileAverageX_RBV (256-element X profile)                                                               
  - BL4B:Det:N1:Det1:XY:Stat:ProfileAverageY_RBV (304-element Y profile)                                                               
                                                                        
  That avoids shipping the full 256×304 XY:Array:ArrayData waveform through every callback. Each profile update is ~1 KB and can be    
  reduced to (MeanX, SigmaX, totalX) inside the callback.                                                                              
                                                                                                                                       
  Proposed structure                                                                                                                   
                                                                  
  1. User parameters at top (match CHI_scan.py naming):                                                                                
  startCHI=-1.8, endCHI=+1.8, SlitHeight=0.25, Xrange=[50,200], Yrange=[120,180], nx=256, ny=304, FitXdist=True, FitYdist=False,
  FitIdist=False, MoveToXFit=True, and a new flight_velocity (default TBD, see Q1 below).                                              
  2. Fit helpers: reuse Gauss_ConstantBkg and GaussDip_ConstantBkg verbatim from CHI_scan.py lines 74–78.
  3. PV bindings:                                                                                                                      
    - Motor: BL4B:Mot:chis / .RBV / _Stat / .VMAX / .VELO / .BDST 
    - Detector control: BL4B:Det:N1:Start, ...:Stop, ...:EventUpdatePeriod, BL4B:Det:PCharge                                           
    - Projections: the two ProfileAverage{X,Y}_RBV PVs above                                                                           
    - Slits: si:Y:Gap, s1:Y:Gap, s2:Y:Gap (+ :Status each)                                                                             
  4. Callbacks (mirroring populate_HS_array / populate_RATE_array / populate_PC_array):                                                
    - cb_chi(ts, v) → append (ts, v) to CHIarray                                                                                       
    - cb_pc(ts, v) → append to PCarray                                                                                                 
    - cb_xprof(ts, profile) → compute (meanX, sigmaX, totalX) in-callback over Xrange ROI, append (ts, mean, sigma, total) to          
  XPROFarray                                                                                                                           
    - cb_yprof(ts, profile) → same for Y over Yrange              
    - Callbacks do the reduction so we store scalars, not waveforms (bounded memory).                                                  
  5. Scan sequence (same skeleton as flying_z):                                                                                        
    a. Save/override EventUpdatePeriod → 10, chis.BDST → 0, chis.VELO → flight_velocity, stash initials.                               
    b. Set slits (si:Y, s1:Y = SlitHeight; s2:Y = 2 × SlitHeight), wait idle.                                                          
    c. Move chis to startCHI, wait idle.                                                                                               
    d. Register the four camonitor callbacks.                                                                                          
    e. StartDiag.put(1), chis.put(endCHI), wait idle, StopDiag.put(1).                                                                 
    f. camonitor_clear all four PVs.                                                                                                   
    g. Restore EventUpdatePeriod, BDST, VELO; reset slits to nominal (0.25/0.25/0.5) the way CHI_scan.py does.                         
  6. Post-process (mirror flying_z lines 112–138, then CHI_scan lines 245–326):                                                        
    - Extract (tCHI, vCHI), (tPC, vPC), and the (t, mean, sigma, total) tuples for each profile.                                       
    - np.interp each derived observable (sigmaX, sigmaY, totalX) onto tCHI.                                                            
    - For intensity fits, normalize by np.gradient(PC_on_chi, tCHI) (proton-charge rate).                                              
    - Fit per enabled mode:                                                                                                            
        - FitXdist → Gauss_ConstantBkg(vCHI, sigmaX_interp) → max σₓ                                                                   
      - FitYdist → GaussDip_ConstantBkg(vCHI, sigmaY_interp) → min σᵧ                                                                  
      - FitIdist → Gauss_ConstantBkg(vCHI, totalX_interp / dPC) → max intensity                                                        
    - Plot the chosen curve(s) with fit centerline + FWHM, same style as CHI_scan.                                                     
    - If MoveToXFit and the fit center lies inside [startCHI, endCHI], CHI.put(fit_center).                                            
                                                                                                                                       
  Open questions for you before I code it                                                                                              
                                                                                                                                       
  1. Flight velocity for chis — flying_z.py pins hs at VMAX=VELO=2 mm/s. For a 3.6° chis sweep we need to pick a VELO that's (a) safe  
  for the stage and (b) slow enough that each ~10 ms profile update covers <<1 chi FWHM. Do you have a target °/s in mind (or should I
  camonitor chis during a current CHI_scan step-scan to measure the stage's typical slew rate, then pick ~25%)?                        
  2. VELO/VMAX restore on exit — flying_z does not restore these. Is that intentional, or should flying_chi restore them? (I'd
  recommend save-and-restore; happy to do the same in a follow-up to flying_z.)                                                        
  3. Drop FitImage? — the N-slice line-fit (CHI_scan.py lines 176–224) needs the full 2D image at each chi. In a flying scan, that'd
  mean camonitoring XY:Array:ArrayData at ~10 Hz — doable but ~3 MB/s and a lot of per-callback work. I'd like to omit FitImage from v1
   and rely on the X/Y profile PVs only. Acceptable?              
  4. EventUpdatePeriod units — I'm assuming the 10 in flying_z is ms (so ~100 Hz). Can you confirm, or point me at where the IOC DB    
  declares it? I need it to size the sweep duration properly.                                                                          
  5. Profile-PV update cadence — do the ProfileAverage{X,Y}_RBV waveforms republish on every EventUpdatePeriod tick, or on a separate
  Stats-plugin clock? If separate, we may need to coerce it (or pick a matching value).                                                
                                                                  
  If you want, I can dig (Q4/Q5) from the running IOC — would need you to run a cainfo + a timed camonitor on the three detector PVs   
  for me to read back. Say the word and I'll hand you the exact commands.
```

### Prompt 3.1

> Good! To answer your questions:
> Re: 1) Code an optional function to analyze the slew rate for the target motor (in this case BL4B:Mot:chis{.RBV}). This would be used once choose the required VELO to optimize statistics for the target motor. If unset, use the values as configured (do not alter, nor restore).
> Re: 2) good catch, thanks! Yes, please code restore code on exit, subject to 1) above.
> Re: 3) Yes, you may defer FitImage to this version and rely on X/Y profile PVs only.
> Re: 4) You can read the units of EventUpdatePeriod from the PV directly: BL4B:Det:N1:EventUpdatePeriod.EGU (a caget for me gives 'ms', so confirmed).
> Re: 5) For this one, lets dig it from the running IOC, go ahead and give me a command to run to inform your plan

```
● Here's the diagnostic command. We want to see how often each of the three detector PVs posts updates, at both the current and the
  flying_z setting of EventUpdatePeriod. Please run this at the BL4B DASsrv where caget/camonitor/caput resolve the BL4B PVs and paste
  back the output:                            
                                                                                                                                       
  # --- A. Baseline at the current EventUpdatePeriod ---
  INIT_PERIOD=$(caget -t BL4B:Det:N1:EventUpdatePeriod)                                                                                
  echo "== current EventUpdatePeriod = ${INIT_PERIOD} $(caget -t BL4B:Det:N1:EventUpdatePeriod.EGU) =="                                
  caput BL4B:Det:N1:Start 1                                                                                                            
  timeout 5 camonitor -# 1 -t u \                                                                                                      
      BL4B:Det:N1:Det1:EventRate_RBV \                                                                                                 
      BL4B:Det:N1:Det1:XY:Stat:ProfileAverageX_RBV \                                                                                   
      BL4B:Det:N1:Det1:XY:Stat:ProfileAverageY_RBV                                                                                     
  caput BL4B:Det:N1:Stop 1                                                                                                             
                                                                                                                                       
  echo                                                                                                                                 
  echo "== EventUpdatePeriod = 10 ms (what flying_z uses) =="     
  caput BL4B:Det:N1:EventUpdatePeriod 10                                                                                               
  caput BL4B:Det:N1:Start 1
  timeout 5 camonitor -# 1 -t u \                                                                                                      
      BL4B:Det:N1:Det1:EventRate_RBV \                                                                                                 
      BL4B:Det:N1:Det1:XY:Stat:ProfileAverageX_RBV \
      BL4B:Det:N1:Det1:XY:Stat:ProfileAverageY_RBV                                                                                     
  caput BL4B:Det:N1:Stop 1                                        
  caput BL4B:Det:N1:EventUpdatePeriod ${INIT_PERIOD}                                                                                   
                                                                  
  -# 1 clips each waveform to its first element so the lines stay short; -t u prints unix-epoch seconds so we can eyeball Δt between   
  consecutive updates per PV and confirm whether the two ProfileAverage*_RBV waveforms update at the same cadence as EventRate_RBV (and
   whether that cadence actually tracks EventUpdatePeriod). If it does, we set the period to 10 ms in flying_chi exactly like flying_z.
   If the Stats plugin is on a separate clock, I'll adjust.       

  Plan updates from your answers

  - Q1 → optional slew-rate probe. Add measure_slew_rate(motor='BL4B:Mot:chis', delta=3.6, velo=None) (not called from the main scan   
  path). It records current .VELO, optionally sets a test velocity, camonitors .RBV over the move, fits slope of vCHI vs tCHI in the
  mid-80% of the interval (avoid accel/decel ends), prints °/s and the observed max |Δ| between samples — enough for the user to pick  
  flight_velocity. Restores .VELO on exit. Lives at the bottom of flying_chi.py under if __name__ == "__main__": pass or as a
  commented-out helper block so it's trivially callable but doesn't fire automatically.
  - Q2 → save/restore VELO (and VMAX if touched). Only write these if flight_velocity is not None. Wrap the whole scan body in
  try/finally so EventUpdatePeriod, BDST, VELO, VMAX, and slit heights all get restored even if the user Ctrl-Cs mid-scan or the fit   
  raises. flying_z leaks these because there's no try/finally; flying_chi will fix it.
  - Q3 → FitImage dropped. v1 handles FitXdist, FitYdist, FitIdist only, using ProfileAverage{X,Y}_RBV. I'll leave a short # FitImage  
  deferred — see CHI_scan.py lines 176–224 pointer in the code so a future version knows where to pick up.                             
  - Q4 → confirmed ms. flight_velocity sized against sweep-time = |endCHI − startCHI| / VELO, target ≥200 profile updates across the
  sweep, so VELO ≤ (endCHI − startCHI) / (200 × EventUpdatePeriod) in °/s.                                                             
                                                                  
  Once you paste Q5's output I'll finalize EventUpdatePeriod handling and produce the file. 
```