Skip to content
Snippets Groups Projects
Commit 2c0aaf44 authored by Valerio Mariani's avatar Valerio Mariani
Browse files

Major clean-up and refactoring to prepare for OnDA 1.0. Currently only the...

Major clean-up and refactoring to prepare for OnDA 1.0. Currently only the refactored modules are left in the branch. The other missing modules will be reintroduced later
parent 5b8f15c1
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
# This file is part of cfelpyutils.
#
# cfelpyutils is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cfelpyutils is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cfelpyutils. If not, see <http://www.gnu.org/licenses/>.
"""
Utilities based on the fabio python module.
This module contains utilities based on the fabio python module.
files.
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import fabio.cbfimage
import numpy
def read_cbf_from_stream(stream):
"""Reads a cbfimage object out of a data string buffer.
Read a data string buffer received as a payload from the PETRAIII P11 sender, and creates a cbfimage object from
it (See the documentation of the fabio python module).
Args:
stream (str): a data string buffer received from the PETRAIII P11 sender.
Returns:
cbf_obj (fabio.cbfimage): a cbfimage object containing the data extracted
from the string buffer.
"""
cbf_obj = fabio.cbfimage.cbfimage()
cbf_obj.header = {}
cbf_obj.resetvals()
infile = stream
cbf_obj._readheader(infile)
if cbf_obj.CIF_BINARY_BLOCK_KEY not in cbf_obj.cif:
err = "Not key %s in CIF, no CBF image in stream" % fabio.cbfobj.CIF_BINARY_BLOCK_KEY
logger.error(err)
for kv in cbf_obj.cif.items():
print("%s: %s" % kv)
raise RuntimeError(err)
if cbf_obj.cif[cbf_obj.CIF_BINARY_BLOCK_KEY] == "CIF Binary Section":
cbf_obj.cbs += infile.read(len(cbf_obj.STARTER) + int(cbf_obj.header["X-Binary-Size"]) -
len(cbf_obj.cbs) + cbf_obj.start_binary)
else:
if len(cbf_obj.cif[cbf_obj.CIF_BINARY_BLOCK_KEY]) > int(
cbf_obj.header["X-Binary-Size"]) + cbf_obj.start_binary + len(cbf_obj.STARTER):
cbf_obj.cbs = cbf_obj.cif[cbf_obj.CIF_BINARY_BLOCK_KEY][:int(cbf_obj.header["X-Binary-Size"]) +
cbf_obj.start_binary +
len(cbf_obj.STARTER)]
else:
cbf_obj.cbs = cbf_obj.cif[cbf_obj.CIF_BINARY_BLOCK_KEY]
binary_data = cbf_obj.cbs[cbf_obj.start_binary + len(cbf_obj.STARTER):]
if "Content-MD5" in cbf_obj.header:
ref = numpy.string_(cbf_obj.header["Content-MD5"])
obt = fabio.cbfimage.md5sum(binary_data)
if ref != obt:
logger.error("Checksum of binary data mismatch: expected %s, got %s" % (ref, obt))
if cbf_obj.header["conversions"] == "x-CBF_BYTE_OFFSET":
cbf_obj.data = cbf_obj._readbinary_byte_offset(binary_data).astype(cbf_obj.bytecode).reshape(
(cbf_obj.dim2, cbf_obj.dim1))
else:
raise Exception(IOError, "Compression scheme not yet supported, please contact the author")
cbf_obj.resetvals()
# ensure the PIL image is reset
cbf_obj.pilimage = None
return cbf_obj
# This file is part of cfelpyutils.
#
# cfelpyutils is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cfelpyutils is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cfelpyutils. If not, see <http://www.gnu.org/licenses/>.
"""
Utilities for CrystFEL-style geometry files.
This module contains utilities for the processing of CrystFEL-style geometry
files.
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from collections import namedtuple
import numpy
from cfelpyutils.cfel_crystfel import load_crystfel_geometry
PixelMaps = namedtuple('PixelMaps', ['x', 'y', 'r'])
ImageShape = namedtuple('ImageShape', ['ss', 'fs'])
def _find_minimum_image_shape(x, y):
# find the smallest size of cspad_geom that contains all
# xy values but is symmetric about the origin
n = 2 * int(max(abs(y.max()), abs(y.min()))) + 2
m = 2 * int(max(abs(x.max()), abs(x.min()))) + 2
return n, m
def apply_geometry_from_file(data_as_slab, geometry_filename):
"""Parses a geometry file and applies the geometry to data.
Parses a geometry file and applies the geometry to detector data in 'slab' format. Turns a 2d array of pixel
values into an array containing a representation of the physical layout of the detector, keeping the origin of
the reference system at the beam interaction point.
Args:
data_as_slab (numpy.ndarray): the pixel values to which geometry is to be applied.
geometry_filename (str): geometry filename.
Returns:
im_out (numpy.ndarray data_as_slab.dtype): Array containing a representation of the physical layout of the
detector, with the origin of the reference system at the beam interaction point.
"""
x, y, _, img_shape = pixel_maps_for_image_view(geometry_filename)
im_out = numpy.zeros(img_shape, dtype=data_as_slab.dtype)
im_out[y, x] = data_as_slab.ravel()
return im_out
def apply_geometry_from_pixel_maps(data_as_slab, y, x, im_out=None):
"""Applies geometry in pixel map format to data.
Applies geometry, in the form of pixel maps, to detector data in 'slab' format. Turns a 2d array of pixel values
into an array containing a representation of the physical layout of the detector, keeping the origin of the
reference system at the beam interaction point.
Args:
data_as_slab (numpy.ndarray): the pixel values to which geometry is to be applied.
y (numpy.ndarray): the y pixel map describing the geometry of the detector
x (numpy.ndarray): the x pixel map describing the geometry of the detector
im_out (Optional[numpy.ndarray]): array to hold the output; if not provided, one will be generated
automatically.
Returns:
im_out (numpy.ndarray data_as_slab.dtype): Array containing a representation of the physical layout of the
detector, with the origin of the reference system at the beam interaction point.
"""
if im_out is None:
im_out = numpy.zeros(data_as_slab.shape, dtype=data_as_slab.dtype)
im_out[y, x] = data_as_slab.ravel()
return im_out
def pixel_maps_for_image_view(geometry_filename):
"""Parses a geometry file and creates pixel maps for pyqtgraph visualization.
Parses the geometry file and creates pixel maps for an array in 'slab' format containing pixel values. The pixel
maps can be used to create a representation of the physical layout of the detector in a pyqtgraph ImageView
widget (i.e. they apply the detector geometry setting the origin of the reference system is in the top left corner
of the output array). The representation is centered at the point where the beam hits the detector according to
the geometry in the file.
Args:
geometry_filename (str): geometry filename.
Returns:
x (numpy.ndarray int): pixel map for x coordinate
y (numpy.ndarray int): pixel map for x coordinate
"""
pixm = pixel_maps_from_geometry_file(geometry_filename)
x, y = pixm.x, pixm.y
n, m = _find_minimum_image_shape(x, y)
# convert y x values to i j values
i = numpy.array(y, dtype=numpy.int) + n // 2 - 1
j = numpy.array(x, dtype=numpy.int) + m // 2 - 1
y = i.flatten()
x = j.flatten()
return PixelMaps(x, y, None)
def get_image_shape(geometry_filename):
"""Parses a geometry file and returns the minimum size of an image that can represent the detector.
Parses the geometry file and return a numpy shape object representing the minimum size of an image that
can contain the physical representation of the detector. The representation is centered at the point where the beam
hits the detector according to the geometry in the file.
Args:
geometry_filename (str): geometry filename.
Returns:
img_shape tuple (int, int): shape of the array needed to contain the representation of the physical layout
of the detector.
"""
pixm = pixel_maps_from_geometry_file(geometry_filename)
x, y = pixm.x, pixm.y
n, m = _find_minimum_image_shape(x, y)
img_shape = ImageShape(n, m)
return img_shape
def pixel_maps_from_geometry_file(fnam):
"""Parses a geometry file and creates pixel maps.
Extracts pixel maps from a CrystFEL-style geometry file. The pixel maps can be used to create a representation of
the physical layout of the detector, keeping the origin of the reference system at the beam interaction
point.
Args:
fnam (str): geometry filename.
Returns:
x,y,r (numpy.ndarray float, numpy.ndarray float, numpy.ndarray float): slab-like pixel maps with
respectively x, y coordinates of the pixel and distance of the pixel from the center of the reference system.
"""
detector = load_crystfel_geometry(fnam)
max_slab_fs = numpy.array([detector['panels'][k]['max_fs'] for k in detector['panels']]).max()
max_slab_ss = numpy.array([detector['panels'][k]['max_ss'] for k in detector['panels']]).max()
x = numpy.zeros((max_slab_ss + 1, max_slab_fs + 1), dtype=numpy.float32)
y = numpy.zeros((max_slab_ss + 1, max_slab_fs + 1), dtype=numpy.float32)
for p in detector['panels']:
# get the pixel coords for this asic
i, j = numpy.meshgrid(
numpy.arange(detector['panels'][p]['max_ss'] - detector['panels'][p]['min_ss'] + 1),
numpy.arange(detector['panels'][p]['max_fs'] - detector['panels'][p]['min_fs'] + 1),
indexing='ij'
)
# make the y-x ( ss, fs ) vectors, using complex notation
dx = detector['panels'][p]['fsy'] + 1J * detector['panels'][p]['fsx']
dy = detector['panels'][p]['ssy'] + 1J * detector['panels'][p]['ssx']
r_0 = detector['panels'][p]['cny'] + 1J * detector['panels'][p]['cnx']
r = i * dy + j * dx + r_0
y[detector['panels'][p]['min_ss']: detector['panels'][p]['max_ss'] + 1,
detector['panels'][p]['min_fs']: detector['panels'][p]['max_fs'] + 1] = r.real
x[detector['panels'][p]['min_ss']: detector['panels'][p]['max_ss'] + 1,
detector['panels'][p]['min_fs']: detector['panels'][p]['max_fs'] + 1] = r.imag
r = numpy.sqrt(numpy.square(x) + numpy.square(y))
return PixelMaps(x, y, r)
This diff is collapsed.
This diff is collapsed.
# This file is part of cfelpyutils.
#
# cfelpyutils is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cfelpyutils is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cfelpyutils. If not, see <http://www.gnu.org/licenses/>.
"""
Utilities for CrystFEL-style geometry files.
This module contains utilities for the processing of CrystFEL-style geometry
files.
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import collections
import numpy
PixelMaps = collections.namedtuple('PixelMaps', ['x', 'y', 'r'])
ImageShape = collections.namedtuple('ImageShape', ['ss', 'fs'])
def compute_pixel_maps(geometry):
"""Create pixel maps from a CrystFEL geometry object.
Compute pixel maps from a CrystFEL-style geometry object (A dictionary
returned by the load_crystfel_geometry function from the crystfel_utils
module). The pixel maps can be used to create a representation of
the physical layout of the geometry, keeping the origin of the reference
system at the beam interaction point.
Args:
geometry (dict): A CrystFEL geometry object (A dictionary returned by
the load_crystfel_geometry function from the crystfel_utils
module).
Returns:
tuple: a tuple containing three float32 numpy arrays ('slab'-like pixel
maps) with respectively x, y coordinates of the data pixels and
distance of each pixel from the center of the reference system.
"""
# Determine the max fs and ss in the geometry object.
max_slab_fs = numpy.array(
[geometry['panels'][k]['max_fs'] for k in geometry['panels']]
).max()
max_slab_ss = numpy.array(
[geometry['panels'][k]['max_ss'] for k in geometry['panels']]
).max()
# Create the empty arrays that will contain the pixel maps.
x_map = numpy.zeros(
shape=(max_slab_ss + 1, max_slab_fs + 1),
dtype=numpy.float32
)
y_map = numpy.zeros(
shape=(max_slab_ss + 1, max_slab_fs + 1),
dtype=numpy.float32
)
# Iterate over the panels.
for pan in geometry['panels']:
# Determine the pixel coordinates for the current panel.
i, j = numpy.meshgrid(
numpy.arange(
geometry['panels'][pan]['max_ss'] -
geometry['panels'][pan]['min_ss'] +
1
),
numpy.arange(
geometry['panels'][pan]['max_fs'] -
geometry['panels'][pan]['min_fs'] +
1
),
indexing='ij'
)
# Compute the x,y vectors, using complex notation.
d_x = (
geometry['panels'][pan]['fsy'] +
1J * geometry['panels'][pan]['fsx']
)
d_y = (
geometry['panels'][pan]['ssy'] +
1J * geometry['panels'][pan]['ssx']
)
r_0 = (
geometry['panels'][pan]['cny'] +
1J * geometry['panels'][pan]['cnx']
)
cmplx = i * d_y + j * d_x + r_0
# Fill maps that will be returned with the computed
# values (x and y maps).
y_map[
geometry['panels'][pan]['min_ss']:
geometry['panels'][pan]['max_ss'] + 1,
geometry['panels'][pan]['min_fs']:
geometry['panels'][pan]['max_fs'] + 1
] = cmplx.real
x_map[
geometry['panels'][pan]['min_ss']:
geometry['panels'][pan]['max_ss'] + 1,
geometry['panels'][pan]['min_fs']:
geometry['panels'][pan]['max_fs'] + 1
] = cmplx.imag
# Compute the values for the radius pixel maps that will be returned.
r_map = numpy.sqrt(numpy.square(x_map) + numpy.square(y_map))
# Return the pixel maps as a tuple.
return PixelMaps(x_map, y_map, r_map)
def apply_pixel_maps(data_as_slab, pixel_maps, output_array=None):
"""Apply geometry in pixel map format to the input data.
Applies geometry, described by pixel maps, to input data in 'slab' format.
Turns a 2d array of pixel values into an array containing a representation
of the physical layout of the geometry, keeping the origin of the reference
system at the beam interaction point.
Args:
data_as_slab (ndarray): the pixel values on which to apply the
geometry, in 'slab' format.
pixel_maps (tuple): pixel maps, as returned by the
compute_pixel_maps function in this module.
output_array (Optional[numpy.ndarray]): array to hold the output.
If the array is not provided, one will be generated automatically.
Defaults to None (No array provided).
Returns:
ndarray: Array with the same dtype as the input data containing a
representation of the physical layout of the geometry (i.e.: the
geometry information applied to the input data).
"""
# If no array was provided, generate one.
if output_array is None:
output_array = numpy.zeros(
shape=data_as_slab.shape,
dtype=data_as_slab.dtype
)
# Apply the pixel map geometry to the data.
output_array[pixel_maps.y, pixel_maps.x] = data_as_slab.ravel()
# Return the output array.
return output_array
def compute_minimum_image_size(pixel_maps):
"""
Compute the minimum size of an image that can represent the geometry.
Compute the minimum size of an image that can contain a
representation of the geometry described by the pixel maps, assuming
that the image is center at the center of the reference system.
Args:
pixel_maps (tuple): pixel maps, as returned by the
compute_pixel_maps function in this module.
Returns:
tuple: numpy shape object describing the minimum image size.
"""
# Recover the x and y pixel maps.
x_map, y_map = pixel_maps.x, pixel_maps.x.y
# Find the largest absolute values of x and y in the maps.
y_largest = 2 * int(max(abs(y_map.max()), abs(y_map.min()))) + 2
x_largest = 2 * int(max(abs(x_map.max()), abs(x_map.min()))) + 2
# Return a tuple with the computed shape.
return ImageShape(y_largest, x_largest)
def adjust_pixel_maps_for_pyqtgraph(pixel_maps):
"""
Adjust pixel maps for visualization of the data in a pyqtgraph widget.
Adjust the pixel maps for use in a Pyqtgraph's ImageView widget.
Essentially, the origin of the reference system is moved to the
top-left of the image.
Args:
pixel_maps (tuple): pixel maps, as returned by the
compute_pixel_maps function in this module.
Returns:
tuple: a three-element tuple containing two float32 numpy arrays
('slab'-like pixel maps) with respectively x, y coordinates of the
data pixels as the first two elements, and None as the third.
"""
# Compute the minimum image shape needed to represent the coordinates
min_ss, min_fs = compute_minimum_image_size(pixel_maps)
# convert y x values to i j values
i = numpy.array(pixel_maps.y, dtype=numpy.int) + min_ss // 2 - 1
j = numpy.array(pixel_maps.x, dtype=numpy.int) + min_fs // 2 - 1
y_map = i.flatten()
x_map = j.flatten()
return PixelMaps(x_map, y_map, None)
......@@ -27,24 +27,33 @@ import numpy
def load_nparray_from_hdf5_file(data_filename, data_group):
"""Loads a numpy.ndarray from an HDF5 file.
"""Load a numpy.ndarray from an HDF5 file.
Args:
data_filename (str): filename of the file to read.
data_group (str): internal HDF5 path of the data block to read.
data_group (str): internal HDF5 path of the data block containing
the array to load.
Returns:
nparray (numpy.ndarray): numpy array with the data read from the file.
ndarray: numpy array with the data read from the file.
"""
try:
# Open the h5py file, and try to read the data.
with h5py.File(data_filename, 'r') as hdfile:
nparray = numpy.array(hdfile[data_group])
hdfile.close()
except:
raise RuntimeError('Error reading file {0} (data block: {1}).'.format(data_filename, data_group))
# Raise an exception in case of error.
raise RuntimeError(
'Error reading file {0} (data block: {1}).'.format(
data_filename,
data_group
)
)
else:
return nparray
......@@ -25,18 +25,37 @@ from __future__ import (absolute_import, division, print_function,
import ast
def parse_parameters(config):
"""Sets correct types for parameter dictionaries.
def _parsing_error(section, option):
# Raise an exception after a parsing error.
Reads a parameter dictionary returned by the ConfigParser python module, and assigns correct types to parameters,
without changing the structure of the dictionary.
raise RuntimeError(
'Error parsing parameter {0} in section [{1}]. Make sure that the '
'syntax is correct: list elements must be separated by commas and '
'dict entries must contain the colon symbol. Strings must be quoted, '
'even in lists and dicts.'.format(
option,
section
)
)
The parser tries to interpret each entry in the dictionary according to the following rules:
def convert_parameters(config_dict):
"""Convert strings in parameter dictionaries to the corrent data type.
Read a parameter dictionary returned by the ConfigParser python module,
and assign correct types to the parameters, without changing the structure
of the dictionary.
Try to interpret each entry in the dictionary according to the following
rules. The first rule that returns an valid result determines the type in
which the entry will be converted.
- If the entry starts and ends with a single quote or double quote, it is
interpreted as a string.
- If the entry starts and ends with a square bracket, it is interpreted as a list.
- If the entry starts and ends with a brace, it is interpreted as a dictionary.
- If the entry starts and ends with a square bracket, it is interpreted as
a list.
- If the entry starts and ends with a brace, it is interpreted as a
dictionary.
- If the entry is the word None, without quotes, then the entry is
interpreted as NoneType.
- If the entry is the word False, without quotes, then the entry is
......@@ -50,67 +69,111 @@ def parse_parameters(config):
- A float number.
- A string.
The first choice that succeeds determines the entry type.
Args:
config (class RawConfigParser): ConfigParser instance.
config (dict): a dictionary containing the parameters as strings
(the dictionary as returned by COnfig Parser).
Returns:
monitor_params (dict): dictionary with the same structure as the input dictionary, but with correct types
assigned to each entry.
dict: dictionary with the same structure as the input
dictionary, but with correct types assigned to each entry.
"""
# Create the dictionary that will be returned.
monitor_params = {}
for sect in config.sections():
monitor_params[sect] = {}
for op in config.options(sect):
monitor_params[sect][op] = config.get(sect, op)
if monitor_params[sect][op].startswith("'") and monitor_params[sect][op].endswith("'"):
monitor_params[sect][op] = monitor_params[sect][op][1:-1]
# Iterate over the first level of the configuration dictionary.
for section in config_dict.keys():
# Add the section to the dictionary that will be returned.
monitor_params[section] = {}
# Iterate over the content of each section (the second level in the
# configuration dictonary).
for option in config_dict['section'].keys():
# Get the option from the dictionary (None is returned if the
# dictionary does not contain the option).
recovered_option = config_dict['section'].get(option)
# Check if the option is a string delimited by single quotes.
if (
recovered_option.startswith("'") and
recovered_option.endswith("'")
):
monitor_params[section][option] = recovered_option[1:-1]
continue
if monitor_params[sect][op].startswith('"') and monitor_params[sect][op].endswith('"'):
monitor_params[sect][op] = monitor_params[sect][op][1:-1]
# Check if the option is a string delimited by double quotes.
if (
recovered_option.startswith('"') and
recovered_option.endswith('"')
):
monitor_params[section][option] = recovered_option[1:-1]
continue
if monitor_params[sect][op].startswith("[") and monitor_params[sect][op].endswith("]"):
# Check if the option is a list. If it is, interpret it using the
# literal_eval function.
if (
recovered_option.startswith("[") and
recovered_option.endswith("]")
):
try:
monitor_params[sect][op] = ast.literal_eval(config.get(sect, op))
monitor_params[section][option] = ast.literal_eval(
recovered_option
)
continue
except (SyntaxError, ValueError):
raise RuntimeError('Error parsing parameter {0} in section [{1}]. Make sure that the syntax is '
'correct: list elements must be separated by commas and dict entries must '
'contain the colon symbol. Strings must be quoted, even in lists and '
'dicts.'.format(op, sect))
if monitor_params[sect][op].startswith("{") and monitor_params[sect][op].endswith("}"):
_parsing_error(section, option)
# Check if the option is a dictionary or a set. If it is,
# interpret it using the literal_eval function.
if (
recovered_option.startswith("{") and
recovered_option.endswith("}")
):
try:
monitor_params[sect][op] = ast.literal_eval(config.get(sect, op))
monitor_params[section][option] = ast.literal_eval(
recovered_option
)
continue
except (SyntaxError, ValueError):
raise RuntimeError('Error parsing parameter {0} in section [{1}]. Make sure that the syntax is '
'correct: list elements must be separated by commas and dict entries must '
'contain the colon symbol. Strings must be quoted, even in lists and '
'dicts.'.format(op, sect))
if monitor_params[sect][op] == 'None':
monitor_params[sect][op] = None
_parsing_error(section, option)
# Check if the option is the special string 'None' (without
# quotes).
if recovered_option == 'None':
monitor_params[section][option] = None
continue
if monitor_params[sect][op] == 'False':
monitor_params[sect][op] = False
# Check if the option is the special string 'False' (without
# quotes).
if recovered_option == 'False':
monitor_params[section][option] = False
continue
if monitor_params[sect][op] == 'True':
monitor_params[sect][op] = True
# Check if the option is the special string 'True' (without
# quotes).
if recovered_option == 'True':
monitor_params[section][option] = True
continue
# Check if the option is an int by trying to convert it to an int.
try:
monitor_params[sect][op] = int(monitor_params[sect][op])
monitor_params[section][option] = int(recovered_option)
continue
except ValueError:
# If the conversion to int falied, try to conver it to a float.
try:
monitor_params[sect][op] = float(monitor_params[sect][op])
monitor_params[section][option] = float(
recovered_option
)
continue
except ValueError:
raise RuntimeError('Error parsing parameter {0} in section [{1}]. Make sure that the syntax is '
'correct: list elements must be separated by commas and dict entries must '
'contain the colon symbol. Strings must be quoted, even in lists and '
'dicts.'.format(op, sect))
# If the conversion to float also failed, return a parsing
# error.
_parsing_error(section, option)
# Returned the converted dictionary.
return monitor_params
......@@ -13,101 +13,76 @@
# You should have received a copy of the GNU General Public License
# along with cfelpyutils. If not, see <http://www.gnu.org/licenses/>.
"""
Utilities based on the psana python module.
Utilities for the psana python module.
This module provides utilities that build on the functionality provided by the psana python module.
This module provides utilities that build on the functionality provided by the
psana python module (developed at the SLAC National Laboratories).
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
def psana_obj_from_string(name):
"""Converts a string into a psana object type.
def first_event_inspection(source):
"""Inspect the content of the first psana event.
Takes a string and returns the python object type described by the string.
Args:
name (str): a string describing a python type.
Returns:
mod (type): the python type described by the string.
"""
components = name.split('.')
mod = __import__(components[0])
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def psana_event_inspection(source):
"""Prints the structure of psana events.
Takes a psana source string (e.g. exp=CXI/cxix....) and inspects the structure of the first event in the data,
printing information about the the content of the event.
Takes psana source string (e.g. exp=CXI/cxix....) and inspect the
content in the first event in the data described by the string.
Print information about the the content of the event.
Args:
source (str): a psana source string (e.g. exp=CXI/cxix....).
"""
# Import the psana module.
import psana
# Print the name of the source.
print('\n\n')
print('data source :', source)
print('data source :{}'.format(source))
print('\n')
print('Opening dataset...')
ds = psana.DataSource(source)
# Open the psana data source.
dsource = psana.DataSource(source)
# Print the content of the Epics portion of the data.
# First the Epics names.
print('\n')
print('Epics pv Names (the confusing ones):')
print(ds.env().epicsStore().pvNames())
print('Epics pv Names:')
print(dsource.env().epicsStore().pvNames())
# Then the Epics aliases.
print('\n')
print('Epics aliases (the not so confusing ones):')
print(ds.env().epicsStore().aliases())
print(dsource.env().epicsStore().aliases())
# Move to the first event in the data.
print('\n')
print('Event structure:')
itr = ds.events()
itr = dsource.events()
evt = itr.next()
# Print the content of the first event.
for k in evt.keys():
print('Type: {0} Source: {1} Alias: {2} Key: {3}'.format(k.type(), k.src(), k.alias(), k.key()))
print(
'Type: {0} Source: {1} Alias: {2}'
'Key: {3}'.format(
k.type(),
k.src(),
k.alias(),
k.key()
)
)
print('')
for k in evt.keys():
print(k)
# Extract and print the photon energy.
beam = evt.get(psana.Bld.BldDataEBeamV7, psana.Source('BldInfo(EBeam)'))
print('Photon energy: %f' % beam.ebeamPhotonEnergy())
def dirname_from_source_runs(source):
"""Returns a directory name based on a psana source string.
Takes a psana source string (e.g exp=CXI/cxix....) and returns a string that can be used as a subdirectory name or
a prefix for files and directories.
Args:
source (str): a psana source string (e.g. exp=CXI/cxi....).
Returns:
dirname (str): a string that can be used as a filename or a prefix .
"""
start = source.find('run=') + 4
stop = source.find(':idx')
if stop == -1:
stop = len(source)
runs = source[start:stop]
nums = runs.split(',')
if not nums:
nums = runs
dirname = 'run_' + '_'.join(nums)
return dirname
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment