plotting
agipd_single_module_geometry()
¶
Initialize AGIPD single module detector geometry.
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def agipd_single_module_geometry():
"""Initialize AGIPD single module detector geometry."""
# TODO: use extra_geom implementation when ready. This has no module name
# tiles text is different from other AGIPD detectors.
pixel_size = 2e-4
simple_config = {
'pixel_size': pixel_size,
'slow_pixels': 64,
'fast_pixels': 128,
'n_tiles_per_module': 8,
'corner_coordinates': [np.array([-256, -64, 0]) * pixel_size],
}
return GenericGeometry.from_simple_description(**simple_config)
create_constant_overview(constant, name, cells, vmin=None, vmax=None, entries=3, badpixels=None, gmap=None, marker=None)
¶
Create a step plot for constant data across memory cells for requested gain entries
:param constant: dict with constants for each module. :param name: Name to be used for the x-axis :param cells: Number of memory cells :param vmin: plot minumim value boundaries :param vmax: plot maximum value boundaries :param entries: (int)number of gain entries. A tuple specifying the range can also be used. TODO: remove unused inputs from notebooks. :param out_folder: out_folder for showing .md table statistics :param infix: infix for the output png image :param badpixels: A list of 2 elements. badpixels[0] has the dict with badpixels constant for each module and badpixels[1] has the value to apply for bad pixels. :param gmap: A list with len equal to number of gain entires. if not supported, a default would be used for 3 entries. ['High gain', 'Medium gain', 'Low gain'] :param marker: A list of line markers for each gain entry. default: ['']*entries :return:
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def create_constant_overview(constant, name, cells, vmin=None, vmax=None,
entries=3, badpixels=None, gmap=None,
marker=None):
"""
Create a step plot for constant data across memory cells for requested
gain entries
:param constant: dict with constants for each module.
:param name: Name to be used for the x-axis
:param cells: Number of memory cells
:param vmin: plot minumim value boundaries
:param vmax: plot maximum value boundaries
:param entries: (int)number of gain entries.
A tuple specifying the range can also be used.
TODO: remove unused inputs from notebooks.
:param out_folder: out_folder for showing .md table statistics
:param infix: infix for the output png image
:param badpixels: A list of 2 elements.
badpixels[0] has the dict with badpixels constant for each module
and badpixels[1] has the value to apply for bad pixels.
:param gmap: A list with len equal to number of gain entires.
if not supported, a default would be used for 3 entries.
['High gain', 'Medium gain', 'Low gain']
:param marker: A list of line markers for each gain entry.
default: ['']*entries
:return:
"""
if gmap is None:
gmap = ['High gain', 'Medium gain', 'Low gain']
if marker is None:
marker = [''] * entries
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111)
for g in range(entries):
table = []
dbp = None
for qm in constant.keys():
if len(constant[qm].shape) == 4:
d = constant[qm][..., g]
if badpixels is not None and isinstance(badpixels, list):
dbp = np.copy(d)
dbp[badpixels[0][qm][..., g] > 0] = badpixels[1]
else:
# e.g. DSSC
d = constant[qm]
if badpixels is not None and isinstance(badpixels, list):
dbp = np.copy(d)
dbp[badpixels[0][qm] > 0] = badpixels[1]
# TODO: check if not used to remove.
table.append([name, qm, gmap[g], np.nanmean(d), np.nanmedian(d),
np.nanstd(d)])
ax.step(np.arange(cells), np.nanmean(d, axis=(0, 1)),
label=gmap[g], color=f'C{g}', marker=marker[g])
# Plotting good pixels only if bad-pixels were given
if dbp is not None:
ax.step(np.arange(cells), np.nanmean(dbp, axis=(0, 1)),
label=f'Good pixels {gmap[g]}', color=f'C{g}',
linestyle='--')
ax.set_xlabel("Memory cell")
ax.set_ylabel(name)
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
ax.set_title(f"{name} Median per Cell")
if vmin and vmax:
ax.set_ylim(vmin, vmax)
init_jungfrau_geom(karabo_id, karabo_da)
¶
Initiate JUNGFRAUGeometry object based on the selected detector (JF4M, JF1M, or JF500K detectors).
:param karabo_id: the detector identifer of an expected multimodular detector or a single module detector. :param karabo_da: the data aggregator names that defines the module name. This is more relevant for the single module detector to display the correct module name. :return: List of expected module names, JUNGFRAUGeometry object.
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def init_jungfrau_geom(
karabo_id: str,
karabo_da: List[str]
) -> Tuple[List[str], JUNGFRAUGeometry]:
""" Initiate JUNGFRAUGeometry object based on the selected detector
(JF4M, JF1M, or JF500K detectors).
:param karabo_id: the detector identifer of an expected multimodular
detector or a single module detector.
:param karabo_da: the data aggregator names that defines the module name.
This is more relevant for the single module detector to display the
correct module name.
:return: List of expected module names, JUNGFRAUGeometry object.
"""
# Positions are given in pixels
mod_width = (256 * 4) + (2 * 3) # inc. 2px gaps between tiles
mod_height = (256 * 2) + 2
if "JF4M" in karabo_id:
nmods = 8
expected_modules = [f"JNGFR{i:02d}" for i in range(1, nmods+1)]
# The first 4 modules are rotated 180 degrees relative to the others.
# We pass the bottom, beam-right corner of the module regardless
# of its orientation, requiring a subtraction from the
# symmetric positions we'd otherwise calculate.
x_start, y_start = 1125, 1078
module_pos = [
(x_start - mod_width, y_start - mod_height - (i*(mod_height+33)))
for i in range(4)
] + [
(-x_start, -y_start + (i*(mod_height + 33))) for i in range(4)
]
orientations = [
(-1, -1) for _ in range(4)] + [(1, 1) for _ in range(4)]
elif "JF1M" in karabo_id:
nmods = 2
st_modno = 1
# TODO: This is a temporary workaround. A proper solution is needed.
if karabo_id == "SPB_CFEL_JF1M":
st_modno = 9
expected_modules = [f"JNGFR{i:02d}" for i in range(st_modno, st_modno+nmods)]
module_pos = ((-mod_width//2, 33), (-mod_width//2, -mod_height-33))
orientations = [(-1,-1), (1,1)]
else: # e.g. HED_IA1_JF500K1, FXE_XAD_JF500K, FXE_XAD_JFHZ
nmods = 1
expected_modules = karabo_da
module_pos = ((-mod_width//2, -mod_height//2),)
orientations = None
return expected_modules, JUNGFRAUGeometry.from_module_positions(
module_pos, orientations=orientations, asic_gap=6)
rebin(a, *args)
¶
rebin ndarray data into a smaller ndarray of the same rank whose dimensions are factors of the original dimensions. eg. An array with 6 columns and 4 rows can be reduced to have 6,3,2 or 1 columns and 4,2 or 1 rows. example usages: https://scipy-cookbook.readthedocs.io/items/Rebinning.html
a=rand(6,4); b=rebin(a,3,2) a=rand(6); b=rebin(a,2)
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def rebin(a, *args):
'''rebin ndarray data into a smaller ndarray of the same rank whose
dimensions are factors of the original dimensions. eg. An array with 6
columns and 4 rows can be reduced to have 6,3,2 or 1 columns and 4,2 or 1
rows. example usages:
https://scipy-cookbook.readthedocs.io/items/Rebinning.html
>>> a=rand(6,4); b=rebin(a,3,2)
>>> a=rand(6); b=rebin(a,2)
'''
shape = a.shape
lenShape = len(shape)
factor = np.asarray(shape) // np.asarray(args)
evList = ['a.reshape('] + \
['args[%d],factor[%d],' % (i, i) for i in range(lenShape)] + \
[')'] + ['.sum(%d)' % (i + 1) for i in range(lenShape)] + \
['/factor[%d]' % i for i in range(lenShape - 1)]
ta = eval(''.join(evList))
return ta.astype(np.uint32), np.indices([s + 1 for s in ta.shape])
show_overview(d, cell_to_preview, gain_to_preview, out_folder=None, infix=None)
¶
Show an overview :param d: A dict with the number of modules and a dict for the constant names and their data. :param cell_to_preview: Integer number of memory cells to preview. :param gain_to_preview: Integer number for the gain stages to preview. :param out_folder: Output folder for saving the plotted.png image. :param infix: infix to include in the png file name. :return:
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_overview(
d, cell_to_preview, gain_to_preview, out_folder=None, infix=None
):
"""
Show an overview
:param d: A dict with the number of modules and
a dict for the constant names and their data.
:param cell_to_preview: Integer number of memory cells to preview.
:param gain_to_preview: Integer number for the gain stages to preview.
:param out_folder: Output folder for saving the plotted.png image.
:param infix: infix to include in the png file name.
:return:
"""
for module, data in d.items():
# Adapt number of columns to number of constants in d.
ncols = (len(d[module]) + (2 - 1)) // 2
fig = plt.figure(figsize=(20, 20))
grid = AxesGrid(fig, 111,
nrows_ncols=(2, ncols),
axes_pad=(0.9, 0.15),
label_mode="1",
share_all=True,
cbar_location="right",
cbar_mode="each",
cbar_size="7%",
cbar_pad="2%",
)
items = list(data.items())
for ax, cbar_ax, (key, item) in zip(grid, grid.cbar_axes, items):
cf = 0
if "ThresholdsDark" in key:
cf = -1
if len(item.shape) == 4:
med = np.nanmedian(item[..., cell_to_preview,
gain_to_preview + cf])
else:
med = np.nanmedian(item[..., cell_to_preview])
medscale = med
if med == 0:
medscale = 0.1
bound = 0.2
while (np.count_nonzero((item[..., cell_to_preview, gain_to_preview + cf] < med - np.abs(bound * medscale)) | # noqa
(item[..., cell_to_preview, gain_to_preview + cf] > med + np.abs(bound * medscale))) / # noqa
item[..., cell_to_preview, gain_to_preview + cf].size > 0.01): # noqa
bound *= 2
is_badpixels = "BadPixels" in key
if is_badpixels:
im = ax.imshow(
item[..., cell_to_preview, gain_to_preview + cf] != 0,
cmap=plt.cm.colors.ListedColormap(["w", "k"]),
aspect="auto",
)
else:
if len(item.shape) == 4:
im_prev = item[..., cell_to_preview, gain_to_preview + cf]
vmax = np.abs(med + bound * med)
else:
im_prev = item[..., cell_to_preview]
# move the axis of the image to show horizontally
# on the output report.
if im_prev.shape[0] > im_prev.shape[1]:
im_prev = np.moveaxis(item[..., cell_to_preview], 0, 1)
vmax = med + np.abs(bound * medscale)
im = ax.imshow(im_prev, interpolation="nearest",
vmin=med - np.abs(bound * medscale),
vmax=vmax, aspect='auto')
cb = cbar_ax.colorbar(im)
if is_badpixels:
cb.set_ticks([0.25, 0.75])
cb.set_ticklabels(["good", "bad"])
else:
cb.set_label("ADU")
ax.text(
5, 20, key, color="k" if is_badpixels else "w", fontsize=20
)
grid[0].text(5, 50, module, color="k" if "BadPixels" in items[0][0] else "r", fontsize=20) # noqa
if out_folder and infix:
fig.savefig(f"{out_folder}/"
f"dark_analysis_{infix}_module_{module}.png")
show_processed_modules(detector, constants, mnames, mode)
¶
Show the status of the processed modules. Green: Processed. Gray: Not Processed. Red: No data available.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
detector |
str
|
The detector name (karabo_id) (e.g. MID_DET_AGIPD1M-1 or FXE_DET_LPD1M-1) |
required |
constants |
Optional[Dict[str, Any]]
|
A dict of the plotted constants data. {"const_name":constant_data}. Can be None in case of position mode. |
required |
mnames |
str
|
A list of available module names. |
required |
mode |
str
|
String selecting on of the two modes of operation. "position": To just show the position of the processed modules. "processed": To show the modules successfully processed. |
required |
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_processed_modules(
detector: str,
constants: Optional[Dict[str, Any]],
mnames: str,
mode: str
):
"""
Show the status of the processed modules.
Green: Processed. Gray: Not Processed. Red: No data available.
Args:
detector: The detector name (karabo_id) (e.g. MID_DET_AGIPD1M-1 or FXE_DET_LPD1M-1)
constants: A dict of the plotted constants data.
{"const_name":constant_data}. Can be None in case of position mode.
mnames: A list of available module names.
mode: String selecting on of the two modes of operation.
"position": To just show the position of the processed modules.
"processed": To show the modules successfully processed.
"""
# Create the geometry figure for each detector
if "AGIPD1M" in detector:
quadrants = 4
modules = 4
tiles = 8
quad_pos = [(-525, 625), (-550, -10), (520, -160), (542.5, 475)]
geom = AGIPD_1MGeometry.from_quad_positions(quad_pos)
elif "AGIPD500K" in detector:
quadrants = 2
modules = 4
tiles = 8
geom = AGIPD_500K2GGeometry.from_origin()
elif "AGIPD65K" in detector:
quadrants = 1
modules = 1
tiles = 8
geom = agipd_single_module_geometry()
elif 'LPD' in detector:
quadrants = 4
modules = 4
tiles = 16
quad_pos = [(11.4, 299), (-11.5, 8), (254.5, -16), (278.5, 275)]
geom = LPD_1MGeometry.from_quad_positions(quad_pos)
elif 'DSSC' in detector:
quadrants = 4
modules = 4
tiles = 2
quad_pos = [(-130, 5), (-130, -125), (5, -125), (5, 5)]
geom = DSSC_1MGeometry.from_h5_file_and_quad_positions(
Path(eg_tests.__file__).parent / 'dssc_geo_june19.h5',
quad_pos)
else:
raise ValueError(f'{detector} detector is not available for plotting.')
# Create a dict that contains the range of tiles, in the figure,
# that belong to a module.
ranges = {}
tile_count = 0
for quadrant in range(1, quadrants+1):
for module in range(1, modules+1):
ranges[f'Q{quadrant}M{module}'] = [tile_count, tile_count + tiles]
tile_count += tiles
# Create the figure
ax = geom.inspect()
ax.set_title('') # Cannot remove title
ax.set_axis_off()
ax.get_legend().set_visible(False)
# Remove non-tiles markings from figure
tiles, = ax.collections = ax.collections[:1]
# Set each tile colour individually, extra_geom provides a single color
# for all tiles.
facecolors = np.repeat(tiles.get_facecolor(), tile_count, axis=0)
# Set module name fonts
for text in ax.texts:
text.set_fontweight('regular')
texts = [t for t in ax.texts if t.get_text() in mnames]
for text in texts:
text.set_fontweight('extra bold')
text.set_fontsize(14)
if mode == 'position': # Highlight selected modules
for module in mnames:
start, stop = ranges[module]
facecolors[start:stop] = colors.to_rgba('pink')
else: # mode == 'processed': Highlight processed modules
counter = 0 # Used as index within the `Noise` matrix
for module, (start, stop) in ranges.items():
color = 'grey' # Unprocessed modules are grey
if module in mnames:
color = 'green'
if ('Noise' not in constants.keys() or
np.nanmean(constants['Noise'][counter, ..., 0]) == 0): # noqa
color = 'red'
counter += 1
for idx in range(start, stop): # Set the colours
facecolors[idx] = colors.to_rgba(color)
tiles.set_facecolors(facecolors) # Update colours in figure
if mode == "processed":
_ = ax.legend(handles=[Patch(facecolor='red', label='No data'),
Patch(facecolor='gray', label='Not processed'),
Patch(facecolor='green', label='Processed')],
loc='best', ncol=3, bbox_to_anchor=(0.1, 0.25, 0.7, 0.8))
plt.show()
show_processed_modules_jungfrau(jungfrau_geom, constants, processed_modules, expected_modules=None, display_module_names=None)
¶
Show the status of the processed modules.
Green
: Processed. Gray
: Not Processed. Red
: No data available.
:param geom: JUNGFRAUGeometry object to change the colors of
it based on the processed modules.
:param constants: A dict of the plotted constants data.
{"const_name":constant_data}. Can be None in case of position mode.
:param processed_modules: A list of processed module names.
:param expected_modules: A list of full module names expected
for this detector.
:param display_module_names: A list of the module names to display.
:return
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_processed_modules_jungfrau(
jungfrau_geom: JUNGFRAUGeometry,
constants: Optional[Dict[str, Any]],
processed_modules: List[str],
expected_modules: Optional[List[str]]=None,
display_module_names: Optional[List[str]] = None,
):
""" Show the status of the processed modules.
`Green`: Processed. `Gray`: Not Processed. `Red`: No data available.
:param geom: JUNGFRAUGeometry object to change the colors of
it based on the processed modules.
:param constants: A dict of the plotted constants data.
{"const_name":constant_data}. Can be None in case of position mode.
:param processed_modules: A list of processed module names.
:param expected_modules: A list of full module names expected
for this detector.
:param display_module_names: A list of the module names to display.
:return
"""
# Estimate expected modules from number of modules in extra_geom object.
# This will work fine as long as the related modules start from JNGFR01
# incrementaly for the number of modules available.
if expected_modules is None:
expected_modules = [
f"JNGFR{i:02d}" for i in len(jungfrau_geom.modules)]
# Create the figure
ax = jungfrau_geom.inspect(
module_names=[
f'{mod}\n{pdu}' for mod, pdu in zip(
processed_modules, display_module_names)])
ax.set_title('') # Cannot remove title
ax.set_axis_off()
ax.get_legend().set_visible(False)
# Set each module colour individually,
# extra_geom provides a single color for all modules.
modules, = ax.collections = ax.collections[:1]
facecolors = np.repeat(modules.get_facecolor(), len(expected_modules), axis=0)
counter = 0 # Used as index within the `Noise` matrix
for idx, module in enumerate(expected_modules):
color = 'grey' # Unprocessed modules are grey
if module in processed_modules:
color = 'green'
if (
'Noise' not in constants.keys() or
np.nanmean(constants['Noise'][counter, ..., 0]) == 0
):
color = 'red'
counter += 1
# Set the colours
facecolors[idx] = colors.to_rgba(color)
modules.set_facecolors(facecolors) # Update colours in figure
ax.legend(
handles=[
Patch(facecolor='red', label='No data'),
Patch(facecolor='gray', label='Not processed'),
Patch(facecolor='green', label='Processed'),],
loc='best', ncol=3,
bbox_to_anchor=(0.1, 0.25, 0.7, 0.8))
plt.show()