Add TofZProjection

This commit is contained in:
2025-10-08 09:26:14 +02:00
parent b71b72c6a3
commit 709a0c5392
6 changed files with 124 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ from eos.options import ReflectivityConfig, ReaderConfig, ExperimentConfig, Refl
from eos.command_line import commandLineArgs
from eos.logconfig import setup_logging, update_loglevel
def main():
setup_logging()

View File

@@ -1,10 +1,15 @@
"""
events2histogram vizualising data from Amor@SINQ, PSI
Author: Jochen Stahn (algorithms, python draft),
Artur Glavic (structuring and optimisation of code)
"""
import logging
# need to do absolute import here as pyinstaller requires it
from eos.options import E2HConfig, ReaderConfig, ExperimentConfig, E2HReductionConfig
from eos.command_line import commandLineArgs
from eos.logconfig import setup_logging, update_loglevel
from eos.reduction_e2h import E2HReduction
def main():
@@ -22,6 +27,7 @@ def main():
config = E2HConfig(reader_config, experiment_config, reduction_config)
logging.warning('######## events2histogram - data vizualization for Amor ########')
from eos.reduction_e2h import E2HReduction
# only import heavy module if sufficient command line parameters were provided
from eos.reduction_reflectivity import ReflectivityReduction

View File

@@ -609,6 +609,7 @@ class E2HPlotArguments(StrEnum):
@dataclass
class E2HReductionConfig(ArgParsable):
fileIdentifier: str = field(
default='0',
metadata={
'short': 'f',
'priority': 100,

View File

@@ -49,7 +49,8 @@ class PathResolver:
if len(potential_file)>0:
path = os.path.dirname(potential_file[0])
else:
raise FileNotFoundError(f'# ERROR: the file {fileName} can not be found in {self.rawPath}')
raise FileNotFoundError(f'# ERROR: the file {fileName} can not be found '
f'in {self.rawPath+["/home/amor/data"]}')
return os.path.join(path, fileName)
def search_latest(self, number):
@@ -72,4 +73,10 @@ class PathResolver:
possible_files += glob(f'/home/amor/data/{self.year}/*/amor{self.year}n??????.hdf')
possible_indices = list(set([int(os.path.basename(fi)[9:15]) for fi in possible_files]))
possible_indices.sort()
return possible_indices[number-1]
try:
return possible_indices[number-1]
except IndexError:
raise FileNotFoundError(f'# Could not find suitable file for relative index {number} '
f'in {self.rawPath+["/home/amor/data"]}, '
f'possible indices {possible_indices}')

View File

@@ -394,7 +394,10 @@ class YZProjection(ProjectionInterface):
if not 'norm' in kwargs:
kwargs['norm'] = LogNorm()
self._graph = plt.imshow(self.data.I[:, ::-1].T, **kwargs)
self._graph = plt.imshow(self.data.I.T,
extent=(float(self.y[0]), float(self.y[-1]),
float(self.z[0]), float(self.z[-1])),
**kwargs)
if cmap:
plt.colorbar(label='I / cpm')
@@ -410,7 +413,7 @@ class YZProjection(ProjectionInterface):
"""
Inline update of previous plot by just updating the data.
"""
self._graph.set_array(self.data.I[:, ::-1].T)
self._graph.set_array(self.data.I.T)
def draw_yzcross(self, event):
if event.inaxes is not self._graph_axis:
@@ -426,3 +429,88 @@ class YZProjection(ProjectionInterface):
for art in list(plt.gca().lines)+list(plt.gca().texts):
art.remove()
plt.draw()
class TofZProjection(ProjectionInterface):
tof: np.ndarray
z: np.ndarray
data: np.recarray
_dtype = np.dtype([
('cts', np.float64),
('I', np.float64),
('err', np.float64),
])
def __init__(self, tau, foldback=False):
self.z = np.arange(Detector.nBlades*Detector.nWires+1)-0.5
if foldback:
self.tof = np.arange(0, tau, 0.0005)
else:
self.tof = np.arange(0, 2*tau, 0.0005)
self.data = np.zeros((self.tof.shape[0]-1, self.z.shape[0]-1), dtype=self._dtype).view(np.recarray)
self.monitor = 0.
def project(self, dataset: EventDatasetProtocol, monitor: float):
detYi, detZi, detX, delta = Detector.pixelLookUp[dataset.data.events.pixelID-1].T
cts , *_ = np.histogram2d(dataset.data.events.tof, detZi, bins=(self.tof, self.z))
self.data.cts += cts
self.monitor += monitor
self.data.I = self.data.cts / self.monitor
self.data.err = np.sqrt(self.data.cts) / self.monitor
def clear(self):
self.data[:] = 0
self.monitor = 0.
def plot(self, **kwargs):
from matplotlib import pyplot as plt
from matplotlib.colors import LogNorm
if 'colorbar' in kwargs:
cmap=True
del(kwargs['colorbar'])
else:
cmap=False
if not 'aspect' in kwargs:
kwargs['aspect'] = 'auto'
if not 'norm' in kwargs:
kwargs['norm'] = LogNorm()
self._graph = plt.imshow(self.data.I.T,
extent=(float(self.tof[0])*1e3, float(self.tof[-1])*1e3,
float(self.z[0]), float(self.z[-1])),
**kwargs)
if cmap:
plt.colorbar(label='I / cpm')
plt.xlabel('Time of Flight / ms')
plt.ylabel('Z')
plt.xlim(self.tof[0]*1e3, self.tof[-1]*1e3)
plt.ylim(self.z[0], self.z[-1])
self._graph_axis = plt.gca()
plt.connect('button_press_event', self.draw_tzcross)
def update_plot(self):
"""
Inline update of previous plot by just updating the data.
"""
self._graph.set_array(self.data.I.T)
def draw_tzcross(self, event):
if event.inaxes is not self._graph_axis:
return
from matplotlib import pyplot as plt
tbm = self._graph_axis.figure.canvas.manager.toolbar.mode
if event.button is plt.MouseButton.LEFT and tbm=='':
plt.plot([event.xdata, event.xdata], [self.z[0], self.z[-1]], '-', color='grey')
plt.plot([self.tof[0]*1e3, self.tof[-1]*1e3], [event.ydata, event.ydata], '-', color='grey')
plt.text(event.xdata, event.ydata, f'({event.xdata:.2f}, {event.ydata:.1f})', backgroundcolor='white')
plt.draw()
if event.button is plt.MouseButton.RIGHT and tbm=='':
for art in list(plt.gca().lines)+list(plt.gca().texts):
art.remove()
plt.draw()

View File

@@ -16,10 +16,11 @@ from .header import Header
from .instrument import LZGrid
from .normalization import LZNormalisation
from .options import E2HConfig, E2HPlotArguments, IncidentAngle, MonitorType, E2HPlotSelection
from . import event_handling as eh, event_analysis as ea
from . import event_handling as eh
from .path_handling import PathResolver
from .projection import LZProjection, ProjectionInterface, YZProjection
from .projection import LZProjection, ProjectionInterface, TofZProjection, YZProjection
NEEDS_LAMDA = (E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q, E2HPlotSelection.L)
class E2HReduction:
config: E2HConfig
@@ -50,6 +51,9 @@ class E2HReduction:
# live update implies plotting
self.config.reduction.show_plot = True
if not self.config.reduction.fast or self.config.reduction.plot in NEEDS_LAMDA:
from . import event_analysis as ea
# Actions on datasets not used for normalization
self.event_actions = eh.ApplyPhaseOffset(self.config.experiment.chopperPhaseOffset)
if not self.config.reduction.fast:
@@ -64,9 +68,13 @@ class E2HReduction:
self.event_actions |= eh.FilterMonitorThreshold(self.config.experiment.lowCurrentThreshold)
if not self.config.reduction.fast:
self.event_actions |= eh.FilterStrangeTimes()
if self.config.reduction.plot==E2HPlotSelection.TZ:
# perform time fold-back and corrections for tof if not fast mode
self.event_actions |= ea.MergeFrames()
self.event_actions |= ea.AnalyzePixelIDs(self.config.experiment.yRange)
self.event_actions |= eh.TofTimeCorrection(self.config.experiment.incidentAngle==IncidentAngle.alphaF)
# select needed actions in depenence of plots
if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q,
E2HPlotSelection.L]:
if self.config.reduction.plot in NEEDS_LAMDA:
self.event_actions |= ea.MergeFrames()
self.event_actions |= ea.AnalyzePixelIDs(self.config.experiment.yRange)
self.event_actions |= eh.TofTimeCorrection(self.config.experiment.incidentAngle==IncidentAngle.alphaF)
@@ -77,7 +85,7 @@ class E2HReduction:
if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q]:
self.grid = LZGrid(0.01, [0.0, 0.25])
if self.config.reduction.plot in [E2HPlotSelection.LT, E2HPlotSelection.YZ]:
if self.config.reduction.plot in [E2HPlotSelection.LT, E2HPlotSelection.YZ, E2HPlotSelection.TZ]:
self.plot_kwds['colorbar'] = True
self.plot_kwds['cmap'] = str(self.config.reduction.plot_colormap)
@@ -119,6 +127,9 @@ class E2HReduction:
if self.config.reduction.plot==E2HPlotSelection.YZ:
self.projection = YZProjection()
if self.config.reduction.plot==E2HPlotSelection.TZ:
self.projection = TofZProjection(last_file_header.timing.tau, foldback=not self.config.reduction.fast)
def read_data(self):
fileName = self.file_list[self.file_index]
self.file_index += 1