From 3aa7974a220c202393f6f7eb8b98b09540450756 Mon Sep 17 00:00:00 2001 From: Dylan McReynolds Date: Mon, 16 Dec 2024 14:00:33 -0800 Subject: [PATCH 01/18] add shot_sum and shot_num to messages --- src/tr_ap_xps/pipeline/fft.py | 5 +++-- src/tr_ap_xps/pipeline/xps_processor.py | 17 ++++++++++++++--- src/tr_ap_xps/schemas.py | 3 ++- src/tr_ap_xps/websockets.py | 2 ++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/tr_ap_xps/pipeline/fft.py b/src/tr_ap_xps/pipeline/fft.py index 5a44b42..2408441 100644 --- a/src/tr_ap_xps/pipeline/fft.py +++ b/src/tr_ap_xps/pipeline/fft.py @@ -58,7 +58,8 @@ def calculate_fft_items(array: np.array, repeat_factor: int = 20, width: int = 0 array = np.array(array) vfft = get_vfft(array) - sum = get_sum(vfft) + # sum = get_sum(vfft) ifft = get_ifft(array, repeat_factor, width) - return vfft, sum, ifft + + return vfft, ifft diff --git a/src/tr_ap_xps/pipeline/xps_processor.py b/src/tr_ap_xps/pipeline/xps_processor.py index f269d90..08bdcf7 100644 --- a/src/tr_ap_xps/pipeline/xps_processor.py +++ b/src/tr_ap_xps/pipeline/xps_processor.py @@ -19,6 +19,8 @@ class XPSProcessor: def __init__(self, message: XPSStart): self.frames_per_cycle = message.f_reset self.integrated_frames: np.ndarray = None + self.shot_num = 0 + self.shot_sum: np.ndarray = None @timer def _compute_mean(self, curr_frame: np.array): @@ -39,13 +41,21 @@ def process_frame(self, message: XPSRawEvent) -> None: self.integrated_frames = np.vstack( (new_integrated_frame, self.integrated_frames) ) - # Things to do every so often + + # Things to do with every shot (a "shot" is a complete cycle of frames) if message.image_info.frame_number % self.frames_per_cycle == 0: + self.shot_num += 1 + if self.shot_sum is None: + self.shot_sum = self.integrated_frames + else: + self.shot_sum = self.shot_sum + self.integrated_frames + + logger.info(f"Processing frame {message.image_info.frame_number}") # Peak detection on new_integrated_frame detected_peaks_df = peak_fit(new_integrated_frame) # TODO: allow user to select repeat factor and width on UI - vfft_np, sum_np, ifft_np = calculate_fft_items( + vfft_np, ifft_np = calculate_fft_items( self.integrated_frames, repeat_factor=20, width=0 ) @@ -55,7 +65,8 @@ def process_frame(self, message: XPSRawEvent) -> None: detected_peaks=DataFrameModel(df=detected_peaks_df), vfft=NumpyArrayModel(array=vfft_np), ifft=NumpyArrayModel(array=ifft_np), - sum=NumpyArrayModel(array=sum_np), + shot_num=self.shot_num, + shot_sum=NumpyArrayModel(array=self.shot_sum), ) return result diff --git a/src/tr_ap_xps/schemas.py b/src/tr_ap_xps/schemas.py index 7076979..7fb93f0 100644 --- a/src/tr_ap_xps/schemas.py +++ b/src/tr_ap_xps/schemas.py @@ -140,7 +140,8 @@ class XPSResult(Event, XPSMessage): detected_peaks: DataFrameModel vfft: NumpyArrayModel ifft: NumpyArrayModel - sum: NumpyArrayModel + shot_num: int + shot_sum: NumpyArrayModel class XPSResultStop(Stop, XPSMessage): diff --git a/src/tr_ap_xps/websockets.py b/src/tr_ap_xps/websockets.py index c5a1ba6..bf6efc1 100644 --- a/src/tr_ap_xps/websockets.py +++ b/src/tr_ap_xps/websockets.py @@ -140,5 +140,7 @@ def pack_images(message: XPSResult) -> bytes: "width": message.integrated_frames.array.shape[0], "height": message.integrated_frames.array.shape[1], "fitted": json.dumps(peaks_output(message.detected_peaks.df)), + "shot_num" : message.shot_num, + "shot_sum": convert_to_uint8(message.shot_sum.array) } ) From 02227887e6058de19cdd063d8c109c7f31f84772 Mon Sep 17 00:00:00 2001 From: Dylan McReynolds Date: Tue, 17 Dec 2024 10:10:59 -0800 Subject: [PATCH 02/18] fix simulator frame number --- src/tr_ap_xps/simulator/h5_simulator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tr_ap_xps/simulator/h5_simulator.py b/src/tr_ap_xps/simulator/h5_simulator.py index cdb0a5c..83e0e6a 100644 --- a/src/tr_ap_xps/simulator/h5_simulator.py +++ b/src/tr_ap_xps/simulator/h5_simulator.py @@ -52,6 +52,7 @@ def start(self, scan_pause: int = 5): ) # pause to let clients connect...without this the first message is lost while True: with h5py.File(self.file, "r") as file: + frame_number = 1 group = file[self.scan] metadata = json.loads(group["Metadata"][()]) logger.info(metadata) @@ -70,7 +71,8 @@ def start(self, scan_pause: int = 5): for _ in range(self.repeat_scans): # repeat the scan logging.info(f"repeating scan {self.repeat_scans} times") for i in range(num_frames): - event_msg["Frame Number"] = i + event_msg["Frame Number"] = frame_number + frame_number += 1 self.zmq_socket.send_json(event_msg) self.zmq_socket.send(frames[i]) progress_bar.update(1) From 259c480abe309a86c54440bc099fc62490850d27 Mon Sep 17 00:00:00 2001 From: Dylan McReynolds Date: Tue, 17 Dec 2024 10:12:02 -0800 Subject: [PATCH 03/18] fix shot sum --- src/_tests/test_processor.py | 7 +++++-- src/tr_ap_xps/pipeline/xps_processor.py | 21 +++++++++++++-------- src/tr_ap_xps/tiled.py | 6 +++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/_tests/test_processor.py b/src/_tests/test_processor.py index 98e970a..93c6740 100644 --- a/src/_tests/test_processor.py +++ b/src/_tests/test_processor.py @@ -1,8 +1,8 @@ import numpy as np import pytest -# from tr_ap_xps.pipeline.xps_operator import XPSProcessor -# from tr_ap_xps.schemas import XPSStart +from tr_ap_xps.pipeline.xps_operator import XPSProcessor +from tr_ap_xps.schemas import XPSRawEvent @pytest.fixture @@ -10,6 +10,9 @@ def integrated_frame(): return np.random.randint(0, 100, size=(5, 10), dtype="int32") +def test_xps_processor(): + pass + # def test_XPSDataSet(client, integrated_frame): # if "runs" not in client: # runs_node = client.create_container("runs") diff --git a/src/tr_ap_xps/pipeline/xps_processor.py b/src/tr_ap_xps/pipeline/xps_processor.py index 08bdcf7..b3a317d 100644 --- a/src/tr_ap_xps/pipeline/xps_processor.py +++ b/src/tr_ap_xps/pipeline/xps_processor.py @@ -20,7 +20,8 @@ def __init__(self, message: XPSStart): self.frames_per_cycle = message.f_reset self.integrated_frames: np.ndarray = None self.shot_num = 0 - self.shot_sum: np.ndarray = None + self.shot_cache = None # built up with each integrated frame, reset at the end of each shot + self.shot_sum = None # updated at the completion of each shot @timer def _compute_mean(self, curr_frame: np.array): @@ -31,7 +32,7 @@ def process_frame(self, message: XPSRawEvent) -> None: # Compute horizontally-integrated frame new_integrated_frame = self._compute_mean(message.image.array) - # Update the local cached dataframes + # Update the local cached arrays if self.integrated_frames is None: self.integrated_frames = new_integrated_frame[None, :] else: @@ -42,15 +43,19 @@ def process_frame(self, message: XPSRawEvent) -> None: (new_integrated_frame, self.integrated_frames) ) + if self.shot_cache is None: + self.shot_cache = new_integrated_frame[np.newaxis, :] # add a new axis frame number + else: + self.shot_cache = np.vstack((self.shot_cache, new_integrated_frame)) + # Things to do with every shot (a "shot" is a complete cycle of frames) - if message.image_info.frame_number % self.frames_per_cycle == 0: + if message.image_info.frame_number != 0 and message.image_info.frame_number % self.frames_per_cycle == 0: self.shot_num += 1 if self.shot_sum is None: - self.shot_sum = self.integrated_frames + self.shot_sum = self.shot_cache else: - self.shot_sum = self.shot_sum + self.integrated_frames + self.shot_sum = self.shot_sum + self.shot_cache - logger.info(f"Processing frame {message.image_info.frame_number}") # Peak detection on new_integrated_frame detected_peaks_df = peak_fit(new_integrated_frame) @@ -68,6 +73,6 @@ def process_frame(self, message: XPSRawEvent) -> None: shot_num=self.shot_num, shot_sum=NumpyArrayModel(array=self.shot_sum), ) - return result - + self.shot_cache = None + return result timer.end_frame() diff --git a/src/tr_ap_xps/tiled.py b/src/tr_ap_xps/tiled.py index ed216d3..0ba915b 100644 --- a/src/tr_ap_xps/tiled.py +++ b/src/tr_ap_xps/tiled.py @@ -28,7 +28,7 @@ class TiledScan: detected_peaks: DataFrameClient = None vfft: ArrayClient = None ifft: ArrayClient = None - sum: ArrayClient = None + shot_sum: ArrayClient = None function_timings: DataFrameClient = None @@ -105,7 +105,7 @@ def update_tiled_scan(self, message: XPSResult) -> None: ) patch_tiled_array(self.current_tiled_scan.vfft, message.vfft.array) patch_tiled_array(self.current_tiled_scan.ifft, message.ifft.array) - patch_tiled_array(self.current_tiled_scan.sum, message.sum.array) + patch_tiled_array(self.current_tiled_scan.shot_sum, message.shot_sum.array, axis_to_increment=1) append_table_node( self.current_tiled_scan.detected_peaks, message.detected_peaks.df ) @@ -123,7 +123,7 @@ def create_data_nodes(tiled_scan: TiledScan, message: XPSResult) -> None: ) tiled_scan.vfft = tiled_scan.run_node.write_array(message.vfft.array, key="vfft") tiled_scan.ifft = tiled_scan.run_node.write_array(message.ifft.array, key="ifft") - tiled_scan.sum = tiled_scan.run_node.write_array(message.sum.array, key="sum") + tiled_scan.shot_sum = tiled_scan.run_node.write_array(message.shot_sum.array, key="shot_sum") tiled_scan.detected_peaks = create_tiled_table_node( tiled_scan.run_node, message.detected_peaks.df, "detected_peaks" ) From d57d32ed21b0f6ed4823e7334a1d374488d20006 Mon Sep 17 00:00:00 2001 From: seijdeleon Date: Tue, 17 Dec 2024 11:52:33 -0800 Subject: [PATCH 04/18] add shot sum array display in heatmap, add shot number to widget title --- frontend/src/App.js | 19 ++++--- frontend/src/components/PlotlyHeatMap.jsx | 5 +- frontend/src/components/Widget.jsx | 2 +- frontend/src/hooks/useAPXPS.js | 63 ++++++++++++++++++++--- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index fe08456..7de67dd 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -36,7 +36,9 @@ export default function App() { status, heatmapSettings, handleHeatmapSettingChange, - metadata + metadata, + shotSumArray, + shotNumber } = useAPXPS({}); //Automatically start the websocket connection on page load @@ -72,19 +74,22 @@ export default function App() {
- +
- - - + + +
- + + + + - +
diff --git a/frontend/src/components/PlotlyHeatMap.jsx b/frontend/src/components/PlotlyHeatMap.jsx index adcf615..571f311 100644 --- a/frontend/src/components/PlotlyHeatMap.jsx +++ b/frontend/src/components/PlotlyHeatMap.jsx @@ -12,7 +12,8 @@ export default function PlotlyHeatMap({ verticalScaleFactor = 0.1, // Scale factor for content growth width = 'w-full', showTicks = false, - tickStep = 100 + tickStep = 100, + fixHeightToParent=false }) { const plotContainer = useRef(null); const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); @@ -68,7 +69,7 @@ export default function PlotlyHeatMap({ }, autosize: true, width: dimensions.width, - height: dynamicHeight, // Dynamically set height + height: fixHeightToParent ? dimensions.height : dynamicHeight, // Dynamically set height margin: { l: showTicks ? 50 : 10, r: 10, diff --git a/frontend/src/components/Widget.jsx b/frontend/src/components/Widget.jsx index 3af83de..1cc7542 100644 --- a/frontend/src/components/Widget.jsx +++ b/frontend/src/components/Widget.jsx @@ -35,7 +35,7 @@ export default function Widget({ {/* Main Body */} -
+
{children}
diff --git a/frontend/src/hooks/useAPXPS.js b/frontend/src/hooks/useAPXPS.js index 047bb0e..a823147 100644 --- a/frontend/src/hooks/useAPXPS.js +++ b/frontend/src/hooks/useAPXPS.js @@ -14,8 +14,10 @@ export const useAPXPS = ({}) => { const [ rawArray, setRawArray ] = useState([]); const [ vfftArray, setVfftArray ] = useState([]); const [ ifftArray, setIfftArray ] = useState([]); + const [ shotSumArray, setShotSumArray ] = useState([]); + const [ shotNumber, setShotNumber ] = useState(0); - const [ singlePeakData, setSinglePeakData ] = useState({x:[], y:[]}); + const [ singlePeakData, setSinglePeakData ] = useState([]); const [ allPeakData, setAllPeakData ] = useState([]); const [ status, setStatus ] = useState({scan: 'N/A', websocket: 'N/A'}); @@ -33,7 +35,7 @@ export const useAPXPS = ({}) => { scaleFactor: { label: 'Scale Factor', type: type.float, - value: '1', + value: '2', description: 'Factor to scale the vertical axis of Raw, VFFT, and IFFT images in the heatmap. Larger number will increase the vertical height.' }, showTicks: { @@ -118,17 +120,20 @@ export const useAPXPS = ({}) => { if ('raw' in newMessage) { //console.log({newMessage}) //send in height as width and vice versa until height/width issues fixed - processArrayData(newMessage.raw, newMessage.height, newMessage.width, setRawArray) + //processArrayData(newMessage.raw, newMessage.height, newMessage.width, setRawArray); + processAndDownsampleArrayData(newMessage.raw, newMessage.height, newMessage.width, 2, setRawArray); } if ('vfft' in newMessage) { //console.log({newMessage}) //send in height as width and vice versa until height/width issues fixed - processArrayData(newMessage.vfft, newMessage.height, newMessage.width, setVfftArray) + //processArrayData(newMessage.vfft, newMessage.height, newMessage.width, setVfftArray); + processAndDownsampleArrayData(newMessage.vfft, newMessage.height, newMessage.width, 2, setVfftArray); } if ('ifft' in newMessage) { //console.log({newMessage}) //send in height as width and vice versa until height/width issues fixed - processArrayData(newMessage.ifft, newMessage.height, newMessage.width, setIfftArray) + //processArrayData(newMessage.ifft, newMessage.height, newMessage.width, setIfftArray); + processAndDownsampleArrayData(newMessage.ifft, newMessage.height, newMessage.width, 2, setIfftArray); } if ('msg_type' in newMessage) { @@ -136,6 +141,13 @@ export const useAPXPS = ({}) => { //add to metadata display and clear cumulative plots handleStartDocument(newMessage); } + if ('shot_sum' in newMessage) { + processArrayData(newMessage.shot_sum, newMessage.height, (newMessage.shot_sum.length / newMessage.height), setShotSumArray) + } + if ('shot_num' in newMessage) { + setShotNumber(newMessage.shot_num); + } + console.log({newMessage}) } catch (error) { console.error('Error processing WebSocket message:', error); } @@ -175,6 +187,43 @@ export const useAPXPS = ({}) => { cb(newData); }; + + const processAndDownsampleArrayData = (data = [], width, height, scaleFactor = 1, cb) => { + if (scaleFactor < 1) throw new Error("Scale factor must be 1 or greater."); + + const downsampledHeight = Math.floor(height / scaleFactor); + const downsampledWidth = Math.floor(width / scaleFactor); + const newData = []; + + for (let row = 0; row < downsampledHeight; row++) { + const newRow = []; + for (let col = 0; col < downsampledWidth; col++) { + let sum = 0; + let count = 0; + + // Sum up values within the scaleFactor x scaleFactor block + for (let i = 0; i < scaleFactor; i++) { + for (let j = 0; j < scaleFactor; j++) { + const originalRow = row * scaleFactor + i; + const originalCol = col * scaleFactor + j; + const index = originalRow * width + originalCol; + + if (originalRow < height && originalCol < width) { + sum += data[index]; + count++; + } + } + } + + // Calculate the average value and add to the downsampled row + newRow.push(sum / count); + } + newData.push(newRow); + } + + cb(newData); + }; + const processPeakData = (peakDataArray=[{x:0, h:0, fwhm: 0}], singlePlotCallback=()=>{}, multiPlotCallback=()=>{}) => { var recentPlots = []; @@ -343,6 +392,8 @@ export const useAPXPS = ({}) => { status, heatmapSettings, handleHeatmapSettingChange, - metadata + metadata, + shotSumArray, + shotNumber } } From 84b231a372873df4c94afdf7055a738c2f00a8e5 Mon Sep 17 00:00:00 2001 From: seijdeleon Date: Tue, 17 Dec 2024 12:43:42 -0800 Subject: [PATCH 05/18] adjust method of determning sum shot height --- frontend/src/App.js | 4 ++-- frontend/src/hooks/useAPXPS.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index 7de67dd..97f688d 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -86,10 +86,10 @@ export default function App() { - + - + diff --git a/frontend/src/hooks/useAPXPS.js b/frontend/src/hooks/useAPXPS.js index a823147..12643ff 100644 --- a/frontend/src/hooks/useAPXPS.js +++ b/frontend/src/hooks/useAPXPS.js @@ -142,7 +142,9 @@ export const useAPXPS = ({}) => { handleStartDocument(newMessage); } if ('shot_sum' in newMessage) { - processArrayData(newMessage.shot_sum, newMessage.height, (newMessage.shot_sum.length / newMessage.height), setShotSumArray) + //technically metadata won't have f_reset due to stale state at time of function initilization, need to put it into a ref + var shotHeight = ("f_reset" in metadata) ? metadata.f_reset : (newMessage.shot_sum.length / newMessage.height) + processArrayData(newMessage.shot_sum, newMessage.height, shotHeight, setShotSumArray) } if ('shot_num' in newMessage) { setShotNumber(newMessage.shot_num); From bb59ade0962125ec790a2ab9877f5e88526b1c0a Mon Sep 17 00:00:00 2001 From: seijdeleon Date: Tue, 17 Dec 2024 13:45:10 -0800 Subject: [PATCH 06/18] add sum vfft and sum ifft processing and displays --- frontend/src/App.js | 24 +++++++++------- frontend/src/components/PlotlyHeatMap.jsx | 9 +++--- frontend/src/components/Widget.jsx | 5 ++-- frontend/src/hooks/useAPXPS.js | 34 +++++++++-------------- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index 97f688d..9bf1000 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -38,7 +38,9 @@ export default function App() { handleHeatmapSettingChange, metadata, shotSumArray, - shotNumber + shotNumber, + shotSumIfftArray, + shotSumVfftArray } = useAPXPS({}); //Automatically start the websocket connection on page load @@ -63,7 +65,7 @@ export default function App() { {socketStatus === 'closed' ?