Skip to content

Commit

Permalink
Merge pull request #270 from zhujun98/implement_pulse_resolved_correl…
Browse files Browse the repository at this point in the history
…ation

Implement pulse resolved correlation
  • Loading branch information
zhujun98 authored Jul 30, 2020
2 parents 2b0850f + 6b88a33 commit 460a4e7
Show file tree
Hide file tree
Showing 8 changed files with 480 additions and 3 deletions.
Binary file added docs/images/special_suite_vector_view.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 32 additions & 1 deletion docs/special_suite.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ More info on command line arguments can be obtained as
General purposed apps
---------------------

Camera view, multi-camera view
:ref:`Camera view`, :ref:`Vector view`, multi-camera view


.. _Camera view:
Expand Down Expand Up @@ -122,6 +122,37 @@ More small features will be added in the future.
+----------------------------+--------------------------------------------------------------------+


.. _Vector view:

Vector view
"""""""""""

.. image:: images/special_suite_vector_view.jpg
:width: 800

Vector view helps you monitor 1D data like XGM intensity, digitizer pulse integral, pulse-resolved
ROI FOM and the correlation between them. **Vector view receives processed data from a main EXtra-foam
instance.**

+----------------------------+--------------------------------------------------------------------+
| Input | Description |
+============================+====================================================================+
| ``Vector1`` | Vector 1. |
+----------------------------+--------------------------------------------------------------------+
| ``Vector2`` | Vector 2 (optional). |
+----------------------------+--------------------------------------------------------------------+

.. note::
In order to correlate two vectors, you will need to use the `Pulse slicer` in the data source
tree located in the main GUI to select the data.

.. warning::
To have the pulse-resolved ROI FOM data, currently you must open the :ref:`Histogram` window
in the main GUI and select `ROI FOM` as the analysis type. This will activate the pulse-resolved
ROI FOM calculation, which is a little bit expensive. Also, make sure the `Pulse resolved`
checkbox is checked.


Special purposed apps
---------------------

Expand Down
File renamed without changes
25 changes: 23 additions & 2 deletions extra_foam/special_suite/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .module_scan_w import ModuleScanWindow
from .multicam_view_w import MultiCamViewWindow
from .trxas_w import TrXasWindow
from .vector_view import VectorViewWindow
from .xas_tim_w import XasTimWindow
from .xas_tim_xmcd_w import XasTimXmcdWindow
from .xes_timing_w import XesTimingWindow
Expand Down Expand Up @@ -82,8 +83,10 @@ def initUI(self):
layout_row.setRowStretch(2, 2)
self._cw.setLayout(layout)

self.setFixedSize(
self._WIDTH, (len(self._buttons) // 4 + 1) * self._ROW_HEIGHT)
h = len(self._buttons) // 4
if len(self._buttons) % 4 != 0:
h += 1
self.setFixedSize(self._WIDTH, h * self._ROW_HEIGHT)

def addSpecial(self, instance_type):
"""Add a button for the given analysis."""
Expand All @@ -98,6 +101,7 @@ def addSpecial(self, instance_type):

def addCommonSpecials(self):
self.addSpecial(CamViewWindow)
self.addSpecial(VectorViewWindow)
self.addSpecial(MultiCamViewWindow)


Expand All @@ -118,6 +122,8 @@ def __init__(self):
super().__init__("FXE")

self.addSpecial(XesTimingWindow)
self.addSpecial(GotthardWindow)
self.addSpecial(GotthardPumpProbeWindow)
self.addCommonSpecials()

self.initUI()
Expand Down Expand Up @@ -151,6 +157,18 @@ def __init__(self):
self.show()


class HedSpecialSuiteFacade(_SpecialSuiteFacadeBase):
def __init__(self):
super().__init__("HED")

self.addSpecial(GotthardWindow)
self.addSpecial(GotthardPumpProbeWindow)
self.addCommonSpecials()

self.initUI()
self.show()


class DetSpecialSuiteFacade(_SpecialSuiteFacadeBase):
def __init__(self):
super().__init__("DET")
Expand All @@ -175,6 +193,9 @@ def create_special_suite(topic):
if topic == "MID":
return MidSpecialSuiteFacade()

if topic == "HED":
return HedSpecialSuiteFacade()

if topic == "DET":
return DetSpecialSuiteFacade()

Expand Down
31 changes: 31 additions & 0 deletions extra_foam/special_suite/special_analysis_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,37 @@ def squeezeToImage(self, tid, arr):

return img

def squeezeToVector(self, tid, arr):
"""Try to squeeze an array to get a 1D vector data.
It attempts to squeeze the input array if its dimension is 2D.
:param int tid: train ID.
:param numpy.ndarray arr: image data.
"""
if arr is None:
return

if arr.ndim not in (1, 2):
self.log.error(f"[{tid}] Array dimension must be either 1 or 2! "
f"actual {arr.ndim}!")
return

if arr.ndim == 2:
try:
img = np.squeeze(arr, axis=0)
except ValueError:
try:
img = np.squeeze(arr, axis=-1)
except ValueError:
self.log.error(
"f[{tid}] Failed to squeeze a 2D array to 1D!")
return
else:
img = arr

return img

def getRoiData(self, img, copy=False):
"""Get the ROI(s) of an image or arrays of images.
Expand Down
16 changes: 16 additions & 0 deletions extra_foam/special_suite/tests/test_special_analysis_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,22 @@ def testRoiCtrl(self):
pass

def testSqueezeCameraImage(self):
a1d = np.ones((4, ))
a2d = np.ones((2, 1))
a3d = np.ones((3, 3, 1))

func = functools.partial(self._win._worker_st.squeezeToVector, 1234)

assert func(None) is None
assert func(a3d) is None

ret_1d = func(a1d)
np.testing.assert_array_equal(a1d, ret_1d)

ret_2d = func(a2d)
np.testing.assert_array_equal(a2d.squeeze(axis=-1), ret_2d)

def testSqueezeToVector(self):
a1d = np.ones((4, ))
a2d = np.ones((2, 2))
a3d = np.ones((3, 3, 1))
Expand Down
108 changes: 108 additions & 0 deletions extra_foam/special_suite/tests/test_vectorview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from collections import Counter

import pytest
import numpy as np

from extra_foam.pipeline.tests import _TestDataMixin

from extra_foam.special_suite import logger, mkQApp
from extra_foam.pipeline.exceptions import ProcessingError
from extra_foam.special_suite.vector_view import (
VectorViewProcessor, VectorViewWindow, VectorPlot, VectorCorrelationPlot,
InTrainVectorCorrelationPlot
)

from . import _SpecialSuiteWindowTestBase, _SpecialSuiteProcessorTestBase


app = mkQApp()

logger.setLevel('INFO')


class TestCamViewWindow(_SpecialSuiteWindowTestBase):
@classmethod
def setUpClass(cls):
cls._win = VectorViewWindow('SCS')

@classmethod
def tearDownClass(cls):
# explicitly close the MainGUI to avoid error in GuiLogger
cls._win.close()

@staticmethod
def data4visualization():
"""Override."""
return {
"vector1": np.arange(10),
"vector2": np.arange(10) + 5,
"vector1_full": np.arange(100),
"vector2_full": np.arange(100) + 5,
}

def testWindow(self):
win = self._win

self.assertEqual(3, len(win._plot_widgets_st))
counter = Counter()
for key in win._plot_widgets_st:
counter[key.__class__] += 1

self.assertEqual(1, counter[VectorPlot])
self.assertEqual(1, counter[InTrainVectorCorrelationPlot])
self.assertEqual(1, counter[VectorCorrelationPlot])

self._check_update_plots()

def testCtrl(self):
win = self._win
ctrl_widget = win._ctrl_widget_st
proc = win._worker_st

# test default values
self.assertEqual('XGM intensity', proc._vector1)
self.assertEqual('', proc._vector2)

# test set new values
widget = ctrl_widget.vector1_cb
widget.setCurrentText("ROI FOM")
self.assertEqual("ROI FOM", proc._vector1)

widget = ctrl_widget.vector2_cb
widget.setCurrentText("Digitizer pulse integral")
self.assertEqual("Digitizer pulse integral", proc._vector2)


class TestVectorViewProcessor(_TestDataMixin, _SpecialSuiteProcessorTestBase):
@pytest.fixture(autouse=True)
def setUp(self):
self._proc = VectorViewProcessor(object(), object())
self._proc._vector1 = "XGM intensity"

def testProcessing(self):
data, processed = self.simple_data(1001, (4, 2, 2))
proc = self._proc

with pytest.raises(ProcessingError, match="XGM intensity is not available"):
proc.process(data)

processed.pulse.xgm.intensity = np.random.rand(4)
ret = proc.process(data)
self._check_processed_data_structure(ret)

self._proc._vector2 = "ROI FOM"
processed.pulse.roi.fom = np.random.rand(5)
with pytest.raises(ProcessingError, match="Vectors have different lengths"):
proc.process(data)
processed.pulse.roi.fom = np.random.rand(4)
proc.process(data)

self._proc._vector2 = "Digitizer pulse integral"
processed.pulse.digitizer.ch_normalizer = 'B'
processed.pulse.digitizer['B'].pulse_integral = np.random.rand(4)
proc.process(data)

def _check_processed_data_structure(self, ret):
"""Override."""
data_gt = TestCamViewWindow.data4visualization().keys()
assert set(ret.keys()) == set(data_gt)
Loading

0 comments on commit 460a4e7

Please sign in to comment.