From 8db231d77dcf6fbbee9aa5aca8f858feb3c34882 Mon Sep 17 00:00:00 2001
From: Valerio Mariani <valerio.mariani@desy.de>
Date: Wed, 14 Mar 2018 18:20:26 +0100
Subject: [PATCH] Fixed a few syntax problems and did some code cleanup

---
 crystfel_utils.py  | 292 +++++++++++++++++++--------------------------
 geometry_utils.py  | 210 ++++++++++++++++----------------
 parameter_utils.py |  70 ++++-------
 3 files changed, 255 insertions(+), 317 deletions(-)

diff --git a/crystfel_utils.py b/crystfel_utils.py
index 18de742..5825fc1 100644
--- a/crystfel_utils.py
+++ b/crystfel_utils.py
@@ -32,42 +32,39 @@ import re
 def _assplode_algebraic(value):
     # Reimplementation of assplode_algegraic from
     # libcrystfel/src/detector.c.
-
     items = [
-        item for item in re.split(
-            pattern='([+-])', string=value.strip()
-        ) if item != ''
+        item
+        for item in re.split(pattern='([+-])', string=value.strip())
+        if item != ''
     ]
 
     if items and items[0] not in ('+', '-'):
         items.insert(index=0, object='+')
 
     return [
-        ''.join(
-            iterable=(items[x], items[x + 1])
-        ) for x in range(start=0, stop=len(items), step=2)
+        ''.join((items[x], items[x + 1]))
+        for x in range(0, len(items), 2)
     ]
 
 
 def _dir_conv(direction_x, direction_y, direction_z, value):
     # Reimplementation of dir_conv from
     # libcrystfel/src/detector.c.
-
     direction = [
-        direction_x, direction_y, direction_z
+        direction_x,
+        direction_y,
+        direction_z
     ]
 
     items = _assplode_algebraic(value)
-
-    if items:
-        raise RuntimeError('Invalid direction: {}.'.format(value))
+    if not items:
+        raise RuntimeError("Invalid direction: {}.".format(value))
 
     for item in items:
         axis = item[-1]
-
         if axis != 'x' and axis != 'y' and axis != 'z':
             raise RuntimeError(
-                'Invalid Symbol: {} (must be x, y or z).'.format(axis)
+                "Invalid Symbol: {} (must be x, y or z).".format(axis)
             )
 
         if item[:-1] == '+':
@@ -90,16 +87,14 @@ def _dir_conv(direction_x, direction_y, direction_z, value):
 def _set_dim_structure_entry(key, value, panel):
     # Reimplementation of set_dim_structure_entry from
     # libcrystfel/src/events.c.
-
     if panel['dim_structure'] is not None:
         dim = panel['dim_structure']
     else:
         dim = []
 
     dim_index = int(key[3])
-
     if dim_index > len(dim) - 1:
-        for _ in range(start=len(dim), stop=dim_index + 1):
+        for _ in range(len(dim), dim_index + 1):
             dim.append(None)
 
     if value == 'ss' or value == 'fs' or value == '%':
@@ -107,35 +102,28 @@ def _set_dim_structure_entry(key, value, panel):
     elif value.isdigit():
         dim[dim_index] = int(value)
     else:
-        raise RuntimeError('Invalid dim entry: {}.'.format(value))
+        raise RuntimeError("Invalid dim entry: {}.".format(value))
 
 
 def _parse_field_for_panel(key, value, panel):
     # Reimplementation of parse_field_for_panel from
     # libcrystfel/src/detector.c.
-
     if key == 'min_fs':
         panel['origin_min_fs'] = int(value)
         panel['min_fs'] = int(value)
-
     elif key == 'max_fs':
         panel['origin_max_fs'] = int(value)
         panel['max_fs'] = int(value)
-
     elif key == 'min_ss':
         panel['origin_min_ss'] = int(value)
         panel['min_ss'] = int(value)
-
     elif key == 'max_ss':
         panel['origin_max_ss'] = int(value)
         panel['max_ss'] = int(value)
-
     elif key == 'corner_x':
         panel['cnx'] = float(value)
-
     elif key == 'corner_y':
         panel['cny'] = float(value)
-
     elif key == 'rail_direction':
         try:
             panel['rail_x'], panel['rail_y'], panel['rail_z'] = _dir_conv(
@@ -145,20 +133,16 @@ def _parse_field_for_panel(key, value, panel):
                 value=value
             )
         except RuntimeError as exc:
-            raise RuntimeError('Invalid rail direction. ', exc)
+            raise RuntimeError("Invalid rail direction. ", exc)
 
     elif key == 'clen_for_centering':
         panel['clen_for_centering'] = float(value)
-
     elif key == 'adu_per_eV':
         panel['adu_per_eV'] = float(value)
-
     elif key == 'adu_per_photon':
         panel['adu_per_photon'] = float(value)
-
     elif key == 'rigid_group':
         panel['rigid_group'] = value
-
     elif key == 'clen':
         try:
             panel['clen'] = float(value)
@@ -168,33 +152,27 @@ def _parse_field_for_panel(key, value, panel):
             panel['clen_from'] = value
 
     elif key == 'data':
-        if not value.startswith('/'):
-            raise RuntimeError('Invalid data location: {}'.format(value))
+        if not value.startswith("/"):
+            raise RuntimeError("Invalid data location: {}".format(value))
         panel['data'] = value
 
     elif key == 'mask':
-        if not value.startswith('/'):
-            raise RuntimeError('Invalid data location: {}'.format(value))
+        if not value.startswith("/"):
+            raise RuntimeError("Invalid data location: {}".format(value))
         panel['mask'] = value
 
     elif key == 'mask_file':
         panel['mask_file'] = value
-
     elif key == 'saturation_map':
         panel['saturation_map'] = value
-
     elif key == 'saturation_map_file':
         panel['saturation_map_file'] = value
-
     elif key == 'coffset':
         panel['coffset'] = float(value)
-
     elif key == 'res':
         panel['res'] = float(value)
-
     elif key == 'max_adu':
         panel['max_adu'] = value
-
     elif key == 'badrow_direction':
         if value == 'x':
             panel['badrow'] = 'f'
@@ -207,13 +185,12 @@ def _parse_field_for_panel(key, value, panel):
         elif value == '-':
             panel['badrow'] = '-'
         else:
-            print('badrow_direction must be x, t, f, s, or \'-\'')
-            print('Assuming \'-\'.')
+            print("badrow_direction must be x, t, f, s, or \'-\'")
+            print("Assuming \'-\'.")
             panel['badrow'] = '-'
 
     elif key == 'no_index':
         panel['no_index'] = bool(value)
-
     elif key == 'fs':
         try:
             panel['fsx'], panel['fsy'], panel['fsz'] = _dir_conv(
@@ -222,9 +199,8 @@ def _parse_field_for_panel(key, value, panel):
                 direction_z=panel['fsz'],
                 value=value
             )
-
         except RuntimeError as exc:
-            raise RuntimeError('Invalid fast scan direction. ', exc)
+            raise RuntimeError("Invalid fast scan direction.", exc)
 
     elif key == 'ss':
         try:
@@ -234,25 +210,22 @@ def _parse_field_for_panel(key, value, panel):
                 direction_z=panel['ssz'],
                 value=value
             )
-
         except RuntimeError as exc:
-            raise RuntimeError('Invalid slow scan direction. ', exc)
+            raise RuntimeError("Invalid slow scan direction.", exc)
 
-    elif key.startswith('dim'):
+    elif key.startswith("dim"):
         _set_dim_structure_entry(
             key=key,
             value=value,
             panel=panel
         )
-
     else:
-        raise RuntimeError('Unrecognised field: {}'.format(key))
+        raise RuntimeError("Unrecognised field: {}".format(key))
 
 
 def _parse_toplevel(key, value, detector, beam, panel):
     # Reimplementation of parse_toplevel from
     # libcrystfel/src/detector.c.
-
     if key == 'mask_bad':
         try:
             detector['mask_bad'] = int(value)
@@ -267,7 +240,6 @@ def _parse_toplevel(key, value, detector, beam, panel):
 
     elif key == 'coffset':
         panel['coffset'] = float(value)
-
     elif key == 'photon_energy':
         if value.startswith('/'):
             beam['photon_energy'] = 0.0
@@ -278,19 +250,15 @@ def _parse_toplevel(key, value, detector, beam, panel):
 
     elif key == 'photon_energy_scale':
         beam['photon_energy_scale'] = float(value)
-
     elif key == 'peak_info_location':
         detector['peak_info_location'] = value
-
     elif (
-            key.startswith('rigid_group') and not
-            key.startswith('rigid_group_collection')
+            key.startswith("rigid_group") and not
+            key.startswith("rigid_group_collection")
     ):
         detector['rigid_groups'][key[12:]] = value.split(',')
-
-    elif key.startswith('rigid_group_collection'):
+    elif key.startswith("rigid_group_collection"):
         detector['rigid_group_collections'][key[23:]] = value.split(',')
-
     else:
         _parse_field_for_panel(
             key=key,
@@ -316,58 +284,54 @@ def _parse_field_bad(key, value, bad):
     # Reimplementation of parse_field_bad from
     # libcrystfel/src/detector.c.
     if key == 'min_x':
-        bad['min_x'] = float(x=value)
+        bad['min_x'] = float(value)
         _check_bad_fsss(bad_region=bad, is_fsss=False)
     elif key == 'max_x':
-        bad['max_x'] = float(x=value)
+        bad['max_x'] = float(value)
         _check_bad_fsss(bad_region=bad, is_fsss=False)
     elif key == 'min_y':
-        bad['min_y'] = float(x=value)
+        bad['min_y'] = float(value)
         _check_bad_fsss(bad_region=bad, is_fsss=False)
     elif key == 'max_y':
-        bad['max_y'] = float(x=value)
+        bad['max_y'] = float(value)
         _check_bad_fsss(bad_region=bad, is_fsss=False)
     elif key == 'min_fs':
-        bad['min_fs'] = int(x=value)
+        bad['min_fs'] = int(value)
         _check_bad_fsss(bad_region=bad, is_fsss=True)
     elif key == 'max_fs':
-        bad['max_fs'] = int(x=value)
+        bad['max_fs'] = int(value)
         _check_bad_fsss(bad_region=bad, is_fsss=True)
     elif key == 'min_ss':
-        bad['min_ss'] = int(x=value)
+        bad['min_ss'] = int(value)
         _check_bad_fsss(bad_region=bad, is_fsss=True)
     elif key == 'max_ss':
-        bad['max_ss'] = int(x=value)
+        bad['max_ss'] = int(value)
         _check_bad_fsss(bad_region=bad, is_fsss=True)
     elif key == 'panel':
         bad['panel'] = value
     else:
-        raise RuntimeError('Unrecognised field: {}'.format(key))
+        raise RuntimeError("Unrecognised field: {}".format(key))
 
     return
 
 
-def _check_point(name, panel, fs_, ss_, min_d, max_d, detector):
+def _check_point(name, panel, fs, ss, min_d, max_d, detector):
     # Reimplementation of check_point from
     # libcrystfel/src/detector.c.
-
-    xs_ = fs_ * panel['fsx'] + ss_ * panel['ssx']
-    ys_ = fs_ * panel['fsy'] + ss_ * panel['ssy']
-
-    rx_ = (xs_ + panel['cnx']) / panel['res']
-    ry_ = (ys_ + panel['cny']) / panel['res']
-
-    dist = math.sqrt(x=rx_ * rx_ + ry_ * ry_)
-
+    xs = fs * panel['fsx'] + ss * panel['ssx']
+    ys = fs * panel['fsy'] + ss * panel['ssy']
+    rx = (xs + panel['cnx']) / panel['res']
+    ry = (ys + panel['cny']) / panel['res']
+    dist = math.sqrt(rx * rx + ry * ry)
     if dist > max_d:
         detector['furthest_out_panel'] = name
-        detector['furthest_out_fs'] = fs_
-        detector['furthest_out_ss'] = ss_
+        detector['furthest_out_fs'] = fs
+        detector['furthest_out_ss'] = ss
         max_d = dist
     elif dist < min_d:
         detector['furthest_in_panel'] = name
-        detector['furthest_in_fs'] = fs_
-        detector['furthest_in_ss'] = ss_
+        detector['furthest_in_fs'] = fs
+        detector['furthest_in_ss'] = ss
         min_d = dist
 
     return min_d, max_d
@@ -376,17 +340,14 @@ def _check_point(name, panel, fs_, ss_, min_d, max_d, detector):
 def _find_min_max_d(detector):
     # Reimplementation of find_min_max_d from
     # libcrystfel/src/detector.c.
-
     min_d = float('inf')
     max_d = 0.0
-
     for name, panel in detector['panels'].items():
-
         min_d, max_d = _check_point(
             name=name,
             panel=panel,
-            fs_=0,
-            ss_=0,
+            fs=0,
+            ss=0,
             min_d=min_d,
             max_d=max_d,
             detector=detector
@@ -395,18 +356,18 @@ def _find_min_max_d(detector):
         min_d, max_d = _check_point(
             name=name,
             panel=panel,
-            fs_=panel['w'],
-            ss_=0,
+            fs=panel['w'],
+            ss=0,
             min_d=min_d,
             max_d=max_d,
             detector=detector
-
         )
+
         min_d, max_d = _check_point(
             name=name,
             panel=panel,
-            fs_=0,
-            ss_=panel['h'],
+            fs=0,
+            ss=panel['h'],
             min_d=min_d,
             max_d=max_d,
             detector=detector
@@ -415,8 +376,8 @@ def _find_min_max_d(detector):
         min_d, max_d = _check_point(
             name=name,
             panel=panel,
-            fs_=panel['w'],
-            ss_=panel['h'],
+            fs=panel['w'],
+            ss=panel['h'],
             min_d=min_d,
             max_d=max_d,
             detector=detector
@@ -424,13 +385,14 @@ def _find_min_max_d(detector):
 
 
 def load_crystfel_geometry(filename):
-    """Load a CrystFEL geometry file into a dictionary.
+    """
+    Load a CrystFEL geometry file into a dictionary.
 
-    Reimplementation of the get_detector_geometry_2 function from CrystFEL
-    in python. Return a dictionary with the geometry information read from
-    the file. Convert entries in the geometry file to keys in the
-    returned dictionary. For a full documentation on the CrystFEL geometry
-    format, see:
+    Reimplementation of the get_detector_geometry_2 function from
+    CrystFEL in python. Return a dictionary with the geometry
+    information read from the file. Convert entries in the geometry
+    file to keys in the returned dictionary. For a full documentation
+    on the CrystFEL geometry format, see:
 
     http://www.desy.de/~twhite/crystfel/manual-crystfel_geometry.html
 
@@ -444,11 +406,9 @@ def load_crystfel_geometry(filename):
 
     Returns:
 
-        dict: dictionary with the geometry information loaded from the file.
+        dict: dictionary with the geometry information loaded from the
+        file.
     """
-
-    fhandle = open(file=filename, mode='r')
-
     beam = {
         'photon_energy': 0.0,
         'photon_energy_from': None,
@@ -507,33 +467,34 @@ def load_crystfel_geometry(filename):
         'name': ''
     }
 
-    default_dim = ['ss', 'fs']
+    default_dim = [
+        'ss', 'fs'
+    ]
 
+    fhandle = open(file=filename, mode='r')
     fhlines = fhandle.readlines()
-
     for line in fhlines:
-
-        if line.startswith(';'):
+        if line.startswith(";"):
             continue
 
         line_without_comments = line.strip().split(sep=';')[0]
         line_items = re.split(pattern='([ \t])', string=line_without_comments)
         line_items = [
-            item for item in line_items if item not in ('', ' ', '\t')
+            item
+            for item in line_items
+            if item not in ('', ' ', '\t')
         ]
 
-        if len(s=line_items) < 3:
+        if len(line_items) < 3:
             continue
 
         value = ''.join(line_items[2:])
-
         if line_items[1] != '=':
             continue
 
         path = re.split('(/)', line_items[0])
         path = [item for item in path if item not in '/']
-
-        if len(s=path) < 2:
+        if len(path) < 2:
             _parse_toplevel(
                 key=line_items[0],
                 value=value,
@@ -545,9 +506,7 @@ def load_crystfel_geometry(filename):
 
         curr_bad = None
         curr_panel = None
-
-        if path[0].startswith('bad'):
-
+        if path[0].startswith("bad"):
             if path[0] in detector['bad']:
                 curr_bad = detector['bad'][path[0]]
             else:
@@ -555,7 +514,6 @@ def load_crystfel_geometry(filename):
                 detector['bad'][path[0]] = curr_bad
 
         else:
-
             if path[0] in detector['panels']:
                 curr_panel = detector['panels'][path[0]]
             else:
@@ -579,9 +537,7 @@ def load_crystfel_geometry(filename):
         raise RuntimeError("No panel descriptions in geometry file.")
 
     num_placeholders_in_panels = None
-
     for panel in detector['panels'].values():
-
         if panel['dim_structure'] is not None:
             curr_num_placeholders = panel['dim_structure'].values().count('%')
         else:
@@ -592,14 +548,12 @@ def load_crystfel_geometry(filename):
         else:
             if curr_num_placeholders != num_placeholders_in_panels:
                 raise RuntimeError(
-                    'All panels\' data and mask entries must have the same '
-                    'number of placeholders.'
+                    "All panels\' data and mask entries must have the same "
+                    "number of placeholders."
                 )
 
     num_placeholders_in_masks = None
-
     for panel in detector['panels'].values():
-
         if panel['mask'] is not None:
             curr_num_placeholders = panel['mask'].count('%')
         else:
@@ -610,50 +564,50 @@ def load_crystfel_geometry(filename):
         else:
             if curr_num_placeholders != num_placeholders_in_masks:
                 raise RuntimeError(
-                    'All panels\' data and mask entries must have the same '
-                    'number of placeholders.'
+                    "All panels\' data and mask entries must have the same "
+                    "number of placeholders."
                 )
 
     if num_placeholders_in_masks > num_placeholders_in_panels:
         raise RuntimeError(
-            'Number of placeholders in mask cannot be larger the number '
-            'than for data.'
+            "Number of placeholders in mask cannot be larger the number "
+            "than for data."
         )
 
     dim_length = None
-
     for panel in detector['panels'].values():
-
         if panel['dim_structure'] is None:
             panel['dim_structure'] = copy.deepcopy(default_dim)
 
         found_ss = False
         found_fs = False
         found_placeholder = False
-
         for entry in panel['dim_structure']:
             if entry is None:
                 raise RuntimeError(
-                    'Not all dim entries are defined for all panels.'
+                    "Not all dim entries are defined for all panels."
                 )
+
             elif entry == 'ss':
                 if found_ss is True:
                     raise RuntimeError(
-                        'Only one slow scan dim coordinate is allowed.'
+                        "Only one slow scan dim coordinate is allowed."
                     )
                 else:
                     found_ss = True
+
             elif entry == 'fs':
                 if found_fs is True:
                     raise RuntimeError(
-                        'Only one fast scan dim coordinate is allowed.'
+                        "Only one fast scan dim coordinate is allowed."
                     )
                 else:
                     found_fs = True
+
             elif entry == '%':
                 if found_placeholder is True:
                     raise RuntimeError(
-                        'Only one placeholder dim coordinate is allowed.'
+                        "Only one placeholder dim coordinate is allowed."
                     )
                 else:
                     found_placeholder = True
@@ -662,68 +616,67 @@ def load_crystfel_geometry(filename):
             dim_length = len(panel['dim_structure'])
         elif dim_length != len(panel['dim_structure']):
             raise RuntimeError(
-                'Number of dim coordinates must be the same for all panels.'
+                "Number of dim coordinates must be the same for all panels."
             )
 
         if dim_length == 1:
             raise RuntimeError(
-                'Number of dim coordinates must be at least two.'
+                "Number of dim coordinates must be at least two."
             )
 
     for panel in detector['panels'].values():
-
         if panel['origin_min_fs'] < 0:
             raise RuntimeError(
-                'Please specify the minimum fs coordinate for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the minimum fs coordinate for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['origin_max_fs'] < 0:
             raise RuntimeError(
-                'Please specify the maximum fs coordinate for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the maximum fs coordinate for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['origin_min_ss'] < 0:
             raise RuntimeError(
-                'Please specify the minimum ss coordinate for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the minimum ss coordinate for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['origin_max_ss'] < 0:
             raise RuntimeError(
-                'Please specify the maximum ss coordinate for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the maximum ss coordinate for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['cnx'] is None:
             raise RuntimeError(
-                'Please specify the corner X coordinate for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the corner X coordinate for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['clen'] is None and panel['clen_from'] is None:
             raise RuntimeError(
-                'Please specify the camera length for '
-                'panel {}.'.format(panel['name'])
+                "Please specify the camera length for "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['res'] < 0:
             raise RuntimeError(
-                'Please specify the resolution or '
-                'panel {}.'.format(panel['name'])
+                "Please specify the resolution or "
+                "panel {}.".format(panel['name'])
             )
 
         if panel['adu_per_eV'] is None and panel['adu_per_photon'] is None:
             raise RuntimeError(
-                'Please specify either adu_per_eV or adu_per_photon '
-                'for panel {}.'.format(panel['name'])
+                "Please specify either adu_per_eV or adu_per_photon "
+                "for panel {}.".format(panel['name'])
             )
 
         if panel['clen_for_centering'] is None and panel['rail_x'] is not None:
             raise RuntimeError(
-                'You must specify clen_for_centering if you specify the'
-                'rail direction (panel {})'.format(panel['name'])
+                "You must specify clen_for_centering if you specify the "
+                "rail direction (panel {})".format(panel['name'])
             )
 
         if panel['rail_x'] is None:
@@ -740,37 +693,34 @@ def load_crystfel_geometry(filename):
     for bad_region in detector['bad'].values():
         if bad_region['is_fsss'] == 99:
             raise RuntimeError(
-                'Please specify the coordinate ranges for bad '
-                'region {}.'.format(bad_region['name'])
+                "Please specify the coordinate ranges for bad "
+                "region {}.".format(bad_region['name'])
             )
 
     for group in detector['rigid_groups']:
         for name in detector['rigid_groups'][group]:
             if name not in detector['panels']:
                 raise RuntimeError(
-                    'Cannot add panel to rigid_group. Panel not'
-                    'found: {}'.format(name)
+                    "Cannot add panel to rigid_group. Panel not "
+                    "found: {}".format(name)
                 )
 
     for group_collection in detector['rigid_group_collections']:
         for name in detector['rigid_group_collections'][group_collection]:
             if name not in detector['rigid_groups']:
                 raise RuntimeError(
-                    'Cannot add rigid_group to collection. Rigid group '
-                    'not found: {}'.format(name)
+                    "Cannot add rigid_group to collection. Rigid group "
+                    "not found: {}".format(name)
                 )
 
     for panel in detector['panels'].values():
-
-        d__ = panel['fsx'] * panel['ssy'] - panel['ssx'] * panel['fsy']
-
-        if d__ == 0.0:
-            raise RuntimeError('Panel {} transformation is singluar.')
-
-        panel['xfs'] = panel['ssy'] / d__
-        panel['yfs'] = panel['ssx'] / d__
-        panel['xss'] = panel['fsy'] / d__
-        panel['yss'] = panel['fsx'] / d__
+        d = panel['fsx'] * panel['ssy'] - panel['ssx'] * panel['fsy']
+        if d == 0.0:
+            raise RuntimeError("Panel {} transformation is singluar.")
+        panel['xfs'] = panel['ssy'] / d
+        panel['yfs'] = panel['ssx'] / d
+        panel['xss'] = panel['fsy'] / d
+        panel['yss'] = panel['fsx'] / d
 
     _find_min_max_d(detector)
     fhandle.close()
diff --git a/geometry_utils.py b/geometry_utils.py
index 58533e4..699d822 100644
--- a/geometry_utils.py
+++ b/geometry_utils.py
@@ -15,8 +15,8 @@
 """
 Geometry utilities.
 
-Functions that load, manipulate and apply geometry information to detector
-pixel data.
+Functions that load, manipulate and apply geometry information to
+detector pixel data.
 """
 
 from __future__ import (absolute_import, division, print_function,
@@ -27,63 +27,58 @@ import collections
 import numpy
 
 
-PixelMaps = collections.namedtuple('PixelMaps', ['x', 'y', 'r'])
-'''A namedtuple used for pixel maps objects.
-
-Pixel maps are arrays of the same shape of the data whose geometry they
-describe. Each cell in the array holds the coordinate, in the reference system
-of the physical detector, of the corresponding pixel in the data array.
-
-The first two fields store the pixel maps for the x coordinate
-and the y coordinate respectively. The third field is instead a pixel map
-storing the distance of each pixel in the data array from the center of the
-reference system.
-'''
-
-
 def compute_pixel_maps(geometry):
-    """Compute pixel maps from a CrystFEL geometry object.
+    """
+    Compute pixel maps from a CrystFEL geometry object.
 
     Take as input a CrystFEL-style geometry object (A dictionary
     returned by the function load_crystfel_geometry function in the
-    crystfel_utils module) and return a PixelMap tuple . The origin
-    the reference system used by the pixel maps is set at the beam interaction
-    point.
+    crystfel_utils module) and return a PixelMap tuple . The origin the
+    reference system used by the pixel maps is set at the beam
+    interaction point.
 
     Args:
 
-        geometry (dict): A CrystFEL geometry object (A dictionary returned by
-            the :obj:`cfelpyutils.crystfel_utils.load_crystfel_geometry`
+        geometry (dict): A CrystFEL geometry object (A dictionary
+            returned by the
+            :obj:`cfelpyutils.crystfel_utils.load_crystfel_geometry`
             function).
 
     Returns:
 
-        PixelMaps: a PixelMaps tuple.
+        Tuple[ndarray, ndarray, ndarray] A tuple containing the pixel
+        maps. The first two fields, named "x" and "y" respectively,
+        store the pixel maps for the x coordinate and the y coordinate.
+        The third field, named "r", is instead a pixel map storing the
+        distance of each pixel in the data array 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 store the pixel maps.
+    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 empty arrays, of the same size of the input data, that
+    # will store the x and y 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.
+    # Iterate over the panels. For each panel, determine the pixel
+    # indeces, then compute the x,y vectors using a comples notation.
     for pan in geometry['panels']:
-
-        # Determine the pixel indexes for the current panel.
         i, j = numpy.meshgrid(
             numpy.arange(
                 geometry['panels'][pan]['max_ss'] -
@@ -98,46 +93,43 @@ def compute_pixel_maps(geometry):
             indexing='ij'
         )
 
-        # Compute the x,y vectors, using the 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']
-        )
+        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
 
-        # Compute values for the x and y maps.
-        y_map[
+        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.real
+        ] = cmplx.imag
 
-        x_map[
+        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.imag
+        ] = cmplx.real
 
-    # Compute the values for the radius pixel map.
+    # Finally, compute the values for the radius pixel map.
     r_map = numpy.sqrt(numpy.square(x_map) + numpy.square(y_map))
 
-    # Return the pixel maps as a tuple.
+    PixelMaps = collections.namedtuple(
+        typename='PixelMaps',
+        field_names=['x', 'y', 'r']
+    )
     return PixelMaps(x_map, y_map, r_map)
 
 
 def apply_pixel_maps(data, pixel_maps, output_array=None):
-    """Apply geometry in pixel map format to the input data.
+    """
+    Apply geometry in pixel map format to the input data.
 
     Turn an array of detector pixel values into an array
     containing a representation of the physical layout of the detector.
@@ -150,20 +142,19 @@ def apply_pixel_maps(data, pixel_maps, output_array=None):
         pixel_maps (PixelMaps): a pixelmap tuple, as returned by the
             :obj:`compute_pixel_maps` function in this module.
 
-        output_array (Optional[ndarray]): a preallocated array (of dtype
-            numpy.float32) to store the function output. If provided, this
-            array will be filled by the function and returned to the user.
-            If not provided, the function will create a new array
-            automatically and return it to the user. Defaults to None
-            (No array provided).
+        output_array (Optional[ndarray]): a preallocated array (of
+            dtype numpy.float32) to store the function output. If
+            provided, this array will be filled by the function and
+            and returned to the user. If not provided, the function
+            will create a new array automatically and return it to the
+            user. Defaults to None (No array provided).
 
     Returns:
 
-        ndarray: a numpy.float32 array containing the geometry information
-        applied to the input data (i.e.: a physical representation of the
-        layout of the detector).
+        ndarray: a numpy.float32 array containing the geometry
+        information applied to the input data (i.e.: a representation
+        of the physical layout of the detector).
     """
-
     # If no output array was provided, create one.
     if output_array is None:
         output_array = numpy.zeros(
@@ -171,70 +162,83 @@ def apply_pixel_maps(data, pixel_maps, output_array=None):
             dtype=numpy.float32
         )
 
-    # Apply the pixel map geometry information the data.
+    # Apply the pixel map geometry information the data, then return
+    # the resulting array.
     output_array[pixel_maps.y, pixel_maps.x] = data.ravel()
-
-    # Return the output array.
     return output_array
 
 
 def compute_minimum_array_size(pixel_maps):
     """
-    Compute the minimum size of an array that can store the applied geometry.
+    Compute the minimum size of an array that can store the applied
+    geometry.
 
-    Return the minimum size of an array that can store data on which the
-    geometry information described by the pixel maps has been applied.
+    Return the minimum size of an array that can store data on which
+    the geometry information described by the pixel maps has been
+    applied.
 
-    The returned array shape is big enough to display all the input pixel
-    values in the reference system of the physical detector. The array is
-    supposed to be centered at the center of the reference system of the
-    detector (i.e: the beam interaction point).
+    The returned array shape is big enough to display all the input
+    pixel values in the reference system of the physical detector. The
+    array is supposed to be centered at the center of the reference
+    system of the detector (i.e: the beam interaction point).
 
     Args:
 
-        pixel_maps (PixelMaps): a PixelMaps tuple, as returned by the
-            :obj:`compute_pixel_maps` function in this module.
+        Tuple[ndarray, ndarray, ndarray]: a named tuple containing the
+            pixel maps. The first two fields, "x" and "y", should store
+            the pixel maps for the x coordinateand the y coordinate.
+            The third, "r", should instead store the distance of each
+            pixel in the data array from the center of the reference
+            system.
 
     Returns:
 
-        tuple: numpy shape-like tuple storing the minimum array size.
+        Tuple[int, int]: a numpy-style shape tuple storing the minimum
+        array size.
     """
-
-    # Recover the x and y pixel maps.
+    # Find the largest absolute values of x and y in the maps. Since
+    # the returned array is centered on the origin, the minimum array
+    # size along a certain axis must be at least twice the maximum
+    # value for that axis. 2 pixels are added for good measure.
     x_map, y_map = pixel_maps.x, pixel_maps.x.y
+    y_minimum = 2 * int(max(abs(y_map.max()), abs(y_map.min()))) + 2
+    x_minimum = 2 * int(max(abs(x_map.max()), abs(x_map.min()))) + 2
 
-    # 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 (y_largest, x_largest)
+    # Return a numpy-style tuple with the computed shape.
+    return (y_minimum, x_minimum)
 
 
 def adjust_pixel_maps_for_pyqtgraph(pixel_maps):
     """
-    Adjust pixel maps for visualization of the data in a pyqtgraph widget.
+    Adjust pixel maps for visualization of the data in a pyqtgraph
+    widget.
 
     The adjusted maps can be used for a Pyqtgraph ImageView widget.
-    Essentially, the origin of the reference system is moved to the
-    top-left of the image.
 
     Args:
 
-        pixel_maps (PixelMaps): pixel maps, as returned by the
-            :obj:`compute_pixel_maps` function in this module.
+        Tuple[ndarray, ndarray, ndarray]: a named tuple containing the
+        pixel maps. The first two fields, "x" and "y", should store the
+        pixel maps for the x coordinateand the y coordinate. The third,
+        "r", should instead store the distance of each pixel in the
+        data array from the center of the reference system.
 
     Returns:
 
-        PixelMaps: a PixelMaps tuple containing the ajusted pixel maps for
-        the x and y coordinates in the first two fields, and the
-        value None in the third.
+        Tuple[ndarray, ndarray] A tuple containing the pixel
+            maps. The first two fields, named "x" and "y" respectively,
+            store the pixel maps for the x coordinate and the y
+            coordinate. The third field, named "r", is instead a pixel
+            map storing the distance of each pixel in the data array
+            from the center of the reference system.
     """
-
-    # Compute the minimum image shape needed to represent the coordinates.
+    # Essentially, the origin of the reference system needs to be
+    # moved from the beam position to the top-left of the image that
+    # will be displayed. First, compute the size of the array used to
+    # display the data, then use this information to estimate the
+    # magnitude of the shift that needs to be applied to the origin of
+    # the system.
     min_shape = compute_minimum_array_size(pixel_maps)
-
-    # Convert the old pixemap values to the new pixelmap values.
     new_x_map = numpy.array(
         object=pixel_maps.x,
         dtype=numpy.int
@@ -245,4 +249,8 @@ def adjust_pixel_maps_for_pyqtgraph(pixel_maps):
         dtype=numpy.int
     ) + min_shape[0] // 2 - 1
 
-    return PixelMaps(new_x_map, new_y_map, None)
+    PixelMapsForIV = collections.namedtuple(
+        typename='PixelMapsForIV',
+        field_names=['x', 'y']
+    )
+    return PixelMapsForIV(new_x_map, new_y_map)
diff --git a/parameter_utils.py b/parameter_utils.py
index 6e3f977..4a3fde6 100644
--- a/parameter_utils.py
+++ b/parameter_utils.py
@@ -24,36 +24,35 @@ import ast
 
 def _parsing_error(section, option):
     # Raise an exception after a parsing error.
-
     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
-        )
+        "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)
     )
 
 
 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 convert each entry in an object of the corresponding type,
-    without changing the structure of the dictionary.
+    Read a parameter dictionary returned by the ConfigParser python
+    module, and convert each entry in an object of the corresponding
+    type, without changing the structure of the dictionary.
 
-    Try to convert each entry in the dictionary according to the following
-    rules. The first rule that applies to the entry determines the type.
+    Try to convert each entry in the dictionary according to the
+    following rules. The first rule that applies to the entry
+    determines the type.
 
     - If the entry starts and ends with a single quote or double quote,
       leave it as a string.
-    - If the entry starts and ends with a square bracket, convert it to a list.
+    - If the entry starts and ends with a square bracket, convert it to
+      a list.
     - If the entry starts and ends with a curly braces, convert it to a
       dictionary or a set.
-    - If the entry is the word None, without quotes, convert it to NoneType.
-    - If the entry is the word False, without quotes, convert it to a boolean
-      False.
+    - If the entry is the word None, without quotes, convert it to
+      NoneType.
+    - If the entry is the word False, without quotes, convert it to a
+      boolean False.
     - If the entry is the word True, without quotes, convert it to a
       boolean True.
     - If none of the previous options match the content of the entry,
@@ -77,26 +76,24 @@ def convert_parameters(config_dict):
 
     Raises:
 
-        RuntimeError: if an entry cannot be converted to any supported type.
+        RuntimeError: if an entry cannot be converted to any supported
+        type.
     """
 
-    # Create the dictionary that will be returned.
     monitor_params = {}
 
-    # Iterate over the sections in the dictionary (first level).
+    # Iterate over the sections in the dictionary (first level in the
+    # configuration file). Add the section to the dictionary that will
+    # be returned.
     for section in config_dict.keys():
-
-        # Add the section to the dictionary that will be returned.
         monitor_params[section] = {}
 
-        # Iterate over the content of the section (second level in the
-        # configuratio).
+        # Iterate then over the content of the section (second level in
+        # the configuration file). Get each option in turn and perform
+        # all the checks. If all checks fail, call the parsing_error
+        # function.
         for option in config_dict['section'].keys():
-
-            # Get the option from the dictionary.
             recovered_option = config_dict['section']
-
-            # Check if the option is a string delimited by single quotes.
             if (
                     recovered_option.startswith("'") and
                     recovered_option.endswith("'")
@@ -104,7 +101,6 @@ def convert_parameters(config_dict):
                 monitor_params[section][option] = recovered_option[1:-1]
                 continue
 
-            # Check if the option is a string delimited by double quotes.
             if (
                     recovered_option.startswith('"') and
                     recovered_option.endswith('"')
@@ -112,8 +108,6 @@ def convert_parameters(config_dict):
                 monitor_params[section][option] = recovered_option[1:-1]
                 continue
 
-            # 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("]")
@@ -126,8 +120,6 @@ def convert_parameters(config_dict):
                 except (SyntaxError, ValueError):
                     _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("}")
@@ -140,40 +132,28 @@ def convert_parameters(config_dict):
                 except (SyntaxError, ValueError):
                     _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
 
-            # Check if the option is the special string 'False' (without
-            # quotes).
             if recovered_option == 'False':
                 monitor_params[section][option] = False
                 continue
 
-            # 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[section][option] = int(recovered_option)
                 continue
             except ValueError:
-                # If the conversion to int failed, try to convert it to a
-                # float.
                 try:
                     monitor_params[section][option] = float(
                         recovered_option
                     )
                     continue
                 except ValueError:
-                    # If the conversion to float also failed, return a parsing
-                    # error.
                     _parsing_error(section, option)
 
-    # Returned the converted dictionary.
     return monitor_params
-- 
GitLab