Add Jochen colormap, LT and YT map automatic colorscale adjustment

This commit is contained in:
2025-10-09 13:46:52 +02:00
parent 3f93ec2017
commit 4e2269bdae
3 changed files with 140 additions and 30 deletions

View File

@@ -422,6 +422,7 @@ class PlotColormaps(StrEnum):
inferno = "inferno"
gist_rainbow = "gist_rainbow"
nipy_spectral = "nipy_spectral"
jochen_deluxe = "jochen_deluxe"
@dataclass
class ReflectivityOutputConfig(ArgParsable):
@@ -458,7 +459,7 @@ class ReflectivityOutputConfig(ArgParsable):
)
plot_colormap: PlotColormaps = field(
default=PlotColormaps.gist_ncar,
default=PlotColormaps.jochen_deluxe,
metadata={
'short': 'pcmap',
'group': 'output',
@@ -594,6 +595,7 @@ class E2HPlotSelection(StrEnum):
All = 'all'
YZ = 'Iyz'
LT = 'Ilt'
YT = 'Iyt'
TZ = 'Itz'
Q = 'Iq'
L = 'Il'
@@ -673,7 +675,7 @@ class E2HReductionConfig(ArgParsable):
)
plot_colormap: PlotColormaps = field(
default=PlotColormaps.gist_ncar,
default=PlotColormaps.jochen_deluxe,
metadata={
'short': 'pcmap',
'group': 'output',
@@ -689,6 +691,14 @@ class E2HReductionConfig(ArgParsable):
},
)
thetaRangeR: Tuple[float, float] = field(
default_factory=lambda: [-0.75, 0.75],
metadata={
'short': 'T',
'group': 'region of interest',
'help': 'theta region of interest w.r.t. beam center',
},
)
@dataclass
class E2HConfig:

View File

@@ -3,12 +3,15 @@ Classes used to calculate projections/binnings from event data onto given grids.
"""
import logging
import warnings
from abc import ABC, abstractmethod
from typing import List, Tuple, Union
import numpy as np
from dataclasses import dataclass
from matplotlib.colors import LogNorm
from .event_data_types import EventDatasetProtocol
from .instrument import Detector, LZGrid
from .normalization import LZNormalisation
@@ -275,16 +278,25 @@ class LZProjection(ProjectionInterface):
else:
cmap=False
if not 'norm' in kwargs:
kwargs['norm'] = LogNorm()
if self.is_normalized:
self._graph = plt.pcolormesh(self.lamda, self.alphaF, self.data.ref, **kwargs)
if cmap:
plt.colorbar(label='R')
I = self.data.ref
else:
self._graph = plt.pcolormesh(self.lamda, self.alphaF, self.data.I, **kwargs)
if cmap:
I = self.data.I
if not 'norm' in kwargs:
vmin = I[(I>0)].min()
vmax = np.nanmax(I)
kwargs['norm'] = LogNorm(vmin, vmax, clip=True)
# suppress warning for wrongly sorted y-axis pixels (blades overlap)
with warnings.catch_warnings(action='ignore', category=UserWarning):
self._graph = plt.pcolormesh(self.lamda, self.alphaF, I, **kwargs)
if cmap:
if self.is_normalized:
plt.colorbar(label='R')
else:
plt.colorbar(label='I / cpm')
plt.xlabel('$\\lambda$ / $\\AA$')
plt.ylabel('$\\Theta$ / °')
@@ -300,6 +312,20 @@ class LZProjection(ProjectionInterface):
"""
Inline update of previous plot by just updating the data.
"""
if self.is_normalized:
I = self.data.ref
else:
I = self.data.I
if isinstance(self._graph.norm, LogNorm):
vmin = I[(I>0)].min()*0.5
else:
vmin = 0
vmax = np.nanmax(I)
self._graph.set_array(I)
self._graph.norm.vmin = vmin
self._graph.norm.vmax = vmax
if self.is_normalized:
self._graph.set_array(self.data.ref)
else:
@@ -408,23 +434,21 @@ class YZProjection(ProjectionInterface):
del(kwargs['colorbar'])
else:
cmap=False
if not 'aspect' in kwargs:
kwargs['aspect'] = 'auto'
vmax = self.data.I.max()
if not 'norm' in kwargs:
kwargs['norm'] = LogNorm()
vmin = self.data.I[(self.data.I>0)].min()*0.5
kwargs['norm'] = LogNorm(vmin, vmax)
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)
self._graph = plt.pcolormesh(self.y, self.z, self.data.I.T, **kwargs)
if cmap:
plt.colorbar(label='I / cpm')
plt.xlabel('Y')
plt.ylabel('Z')
plt.xlim(self.y[0], self.y[-1])
plt.ylim(self.z[0], self.z[-1])
plt.ylim(self.z[-1], self.z[0])
plt.title('Horizontal Pixel vs. Vertical Pixel')
self._graph_axis = plt.gca()
@@ -434,7 +458,14 @@ class YZProjection(ProjectionInterface):
"""
Inline update of previous plot by just updating the data.
"""
if isinstance(self._graph.norm, LogNorm):
vmin = self.data.I[(self.data.I>0)].min()*0.5
else:
vmin = 0
vmax = self.data.I.max()
self._graph.set_array(self.data.I.T)
self._graph.norm.vmin = vmin
self._graph.norm.vmax = vmax
def draw_yzcross(self, event):
if event.inaxes is not self._graph_axis:
@@ -451,6 +482,57 @@ class YZProjection(ProjectionInterface):
art.remove()
plt.draw()
class YTProjection(YZProjection):
theta: np.ndarray
def __init__(self, tthh: float):
dd = Detector.delta_z[1]-Detector.delta_z[0]
delta = np.hstack([Detector.delta_z, Detector.delta_z[-1]+dd])-dd/2.
self.theta = tthh + delta
super().__init__()
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 'norm' in kwargs:
kwargs['norm'] = LogNorm()
self._graph = plt.pcolormesh(self.y, self.theta, self.data.I.T, **kwargs)
if cmap:
plt.colorbar(label='I / cpm')
plt.xlabel('Y')
plt.ylabel('Theta / °')
plt.xlim(self.y[0], self.y[-1])
plt.ylim(self.theta[-1], self.theta[0])
plt.title('Horizontal Pixel vs. Angle')
self._graph_axis = plt.gca()
plt.connect('button_press_event', self.draw_tzcross)
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=='':
self._graph_axis.plot([event.xdata, event.xdata], [self.theta[0], self.theta[-1]], '-', color='grey')
self._graph_axis.plot([self.y[0], self.y[-1]], [event.ydata, event.ydata], '-', color='grey')
self._graph_axis.text(event.xdata, event.ydata, f'({event.xdata:.1f}, {event.ydata:.1f})', backgroundcolor='white')
plt.draw()
if event.button is plt.MouseButton.RIGHT and tbm=='':
for art in list(self._graph_axis.lines)+list(self._graph_axis.texts):
art.remove()
plt.draw()
class TofZProjection(ProjectionInterface):
tof: np.ndarray
z: np.ndarray
@@ -494,23 +576,18 @@ class TofZProjection(ProjectionInterface):
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)
self._graph = plt.pcolormesh(self.tof*1e3, self.z, self.data.I.T, **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])
plt.ylim(self.z[-1], self.z[0])
plt.title('Time of Flight vs. Vertical Pixel')
self._graph_axis = plt.gca()

View File

@@ -5,8 +5,9 @@ Can be used as a live preview with automatic update when files are modified.
import logging
import os
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from orsopy import fileio
from datetime import datetime
@@ -20,7 +21,7 @@ from . import event_handling as eh
from .path_handling import PathResolver
from .projection import CombinedProjection, LZProjection, ProjectionInterface, ReflectivityProjector, TofProjection, \
TofZProjection, \
YZProjection
YTProjection, YZProjection
NEEDS_LAMDA = (E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q, E2HPlotSelection.L)
@@ -70,10 +71,12 @@ 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
if self.config.reduction.plot in [E2HPlotSelection.YT, E2HPlotSelection.YZ]:
# perform time fold-back and apply yRange filter if not fast mode
self.event_actions |= ea.MergeFrames()
self.event_actions |= ea.AnalyzePixelIDs(self.config.experiment.yRange)
if self.config.reduction.plot==E2HPlotSelection.YT:
# perform corrections for tof if not fast mode
self.event_actions |= eh.TofTimeCorrection(self.config.experiment.incidentAngle==IncidentAngle.alphaF)
# select needed actions in depenence of plots
if self.config.reduction.plot in NEEDS_LAMDA:
@@ -87,12 +90,14 @@ 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.All, E2HPlotSelection.LT, E2HPlotSelection.YZ, E2HPlotSelection.TZ]:
if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.YZ, E2HPlotSelection.YT]:
self.plot_kwds['colorbar'] = True
self.plot_kwds['cmap'] = str(self.config.reduction.plot_colormap)
if self.config.reduction.plotArgs==E2HPlotArguments.Linear:
self.plot_kwds['norm'] = None
self.register_colormap()
def reduce(self):
if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q]:
if self.config.reduction.normalizationModel:
@@ -121,16 +126,29 @@ class E2HReduction:
if self.config.reduction.show_plot:
plt.show()
def register_colormap(self):
cmap = plt.colormaps['gnuplot'](np.arange(256))
cmap[:1, :] = np.array([256/256, 255/256, 236/256, 1])
cmap = ListedColormap(cmap, name='jochen_deluxe', N=cmap.shape[0])
#cmap.set_bad((1.,1.,0.9))
plt.colormaps.register(cmap)
def prepare_graphs(self):
last_file_header = AmorHeader(self.file_list[-1])
tthh = last_file_header.geometry.nu - last_file_header.geometry.mu
if not self.config.reduction.is_default('thetaRangeR'):
# adjust range based on detector center
thetaRange = [ti+tthh for ti in self.config.reduction.thetaRangeR]
else:
thetaRange = [tthh - last_file_header.geometry.div/2, tthh + last_file_header.geometry.div/2]
if self.config.reduction.plot==E2HPlotSelection.LT:
self.projection = LZProjection(tthh, self.grid)
if not self.config.reduction.fast:
self.projection.correct_gravity(last_file_header.geometry.detectorDistance)
self.projection.apply_lamda_mask(self.config.experiment.lambdaRange)
self.projection.apply_theta_mask(thetaRange)
self.projection.apply_norm_mask(self.norm)
if self.config.reduction.plot==E2HPlotSelection.Q:
@@ -139,12 +157,16 @@ class E2HReduction:
plz.correct_gravity(last_file_header.geometry.detectorDistance)
plz.calculate_q()
plz.apply_lamda_mask(self.config.experiment.lambdaRange)
plz.apply_theta_mask(thetaRange)
plz.apply_norm_mask(self.norm)
self.projection = ReflectivityProjector(plz, self.norm)
if self.config.reduction.plot==E2HPlotSelection.YZ:
self.projection = YZProjection()
if self.config.reduction.plot==E2HPlotSelection.YT:
self.projection = YTProjection(tthh)
if self.config.reduction.plot==E2HPlotSelection.TZ:
self.projection = TofZProjection(last_file_header.timing.tau, foldback=not self.config.reduction.fast)
@@ -157,6 +179,7 @@ class E2HReduction:
plz.correct_gravity(last_file_header.geometry.detectorDistance)
plz.calculate_q()
plz.apply_lamda_mask(self.config.experiment.lambdaRange)
plz.apply_theta_mask(thetaRange)
plz.apply_norm_mask(self.norm)
pr = ReflectivityProjector(plz, self.norm)
pyz = YZProjection()