From a8e672162a88313a5477254a4f891d31a1606d1b Mon Sep 17 00:00:00 2001
From: Valerio Mariani <valerio.mariani@desy.de>
Date: Tue, 16 May 2017 17:16:18 +0200
Subject: [PATCH] Fixed bugs int the cfel_crystfel module

---
 cfel_crystfel.py | 536 ++++++++++++++++++++++++-----------------------
 1 file changed, 273 insertions(+), 263 deletions(-)

diff --git a/cfel_crystfel.py b/cfel_crystfel.py
index d258807..911b63c 100644
--- a/cfel_crystfel.py
+++ b/cfel_crystfel.py
@@ -28,305 +28,307 @@ from math import inf, sqrt
 import re
 
 
-def load_crystfel_geometry(filename):
-
-    def assplode_algebraic(v):
-        items = [item for item in re.split('([+-])', v.strip()) if item != '']
-
-        if len(items) != 0 and items[0] not in ('+', '-'):
-            items.insert(0, '+')
-
-        return [''.join((items[x], items[x + 1])) for x in range(0, len(items), 2)]
-
-    def dir_conv(direction_x, direction_y, direction_z):
-
-        direction = [direction_x, direction_y, direction_z]
+def _assplode_algebraic(value):
+    items = [item for item in re.split('([+-])', value.strip()) if item != '']
 
-        items = assplode_algebraic(value)
+    if len(items) != 0 and items[0] not in ('+', '-'):
+        items.insert(0, '+')
 
-        if len(items) == 0:
-            raise RuntimeError('Invalid direction: {}.'.format(value))
+    return [''.join((items[x], items[x + 1])) for x in range(0, len(items), 2)]
 
-        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))
+def _dir_conv(direction_x, direction_y, direction_z, value):
+    direction = [direction_x, direction_y, direction_z]
 
-            if item[:-1] == '+':
-                v = '1.0'
-            elif item[:-1] == '-':
-                v = '-1.0'
-            else:
-                v = item[-1]
-
-            if axis == 'x':
-                direction[0] = float(v)
-            elif axis == 'y':
-                direction[1] = float(v)
-            elif axis == 'z':
-                direction[2] = float(v)
-
-        return direction
+    items = _assplode_algebraic(value)
 
-    def set_dim_structure_entry(k, v, pan):
+    if len(items) == 0:
+        raise RuntimeError('Invalid direction: {}.'.format(value))
 
-        if pan['dim_structure'] is not None:
-            dim = pan['dim_structure']
-        else:
-            dim = []
-
-        dim_index = int(k[3])
+    for item in items:
+        axis = item[-1]
 
-        if dim_index > len(dim)-1:
-            for index in range(len(dim), dim_index + 1):
-                dim.append(None)
+        if axis != 'x' and axis != 'y' and axis != 'z':
+            raise RuntimeError('Invalid Symbol: {} (must be x, y or z).'.format(axis))
 
-        if v == 'ss' or v == 'fs' or v == '%':
-            dim[dim_index] = v
-        elif v.isdigit():
-            dim[dim_index] = int(v)
+        if item[:-1] == '+':
+            v = '1.0'
+        elif item[:-1] == '-':
+            v = '-1.0'
         else:
-            raise RuntimeError('Invalid dim entry: {}.'.format(v))
-
-    def parse_field_for_panel(k, v, pan):
-
-        if k == 'min_fs':
-            pan['origin_min_fs'] = int(v)
-            pan['min_fs'] = int(v)
-
-        elif k == 'max_fs':
-            pan['origin_max_fs'] = int(v)
-            pan['max_fs'] = int(v)
-
-        elif k == 'min_ss':
-            pan['origin_min_ss'] = int(v)
-            pan['min_ss'] = int(v)
-
-        elif k == 'max_ss':
-            pan['origin_max_ss'] = int(v)
-            pan['max_ss'] = int(v)
-
-        elif k == 'corner_x':
-            pan['cnx'] = float(v)
-
-        elif k == 'corner_y':
-            pan['cny'] = float(v)
-
-        elif k == 'rail_direction':
-            try:
-                pan['rail_x'], pan['rail_y'], pan['rail_z'] = dir_conv(pan['rail_x'],
-                                                                       pan['rail_y'],
-                                                                       pan['rail_z'])
-            except RuntimeError as e:
-                raise RuntimeError('Invalid rail direction. ', e)
-
-        elif k == 'clen_for_centering':
-            pan['clen_for_centering'] = float(v)
-
-        elif k == 'adu_per_eV':
-            pan['adu_per_eV'] = float(v)
-
-        elif k == 'adu_per_photon':
-            pan['adu_per_photon'] = float(v)
-
-        elif k == 'rigid_group':
-            pan['rigid_group'] = v
-
-        elif k == 'clen':
-            try:
-                pan['clen'] = float(v)
-                pan['clen_from'] = None
-            except ValueError:
-                pan['clen'] = -1
-                pan['clen_from'] = v
-
-        elif k == 'data':
-            if not v.startswith('/'):
-                raise RuntimeError('Invalid data location: {}'.format(v))
-            pan['data'] = v
-
-        elif k == 'mask':
-            if not v.startswith('/'):
-                raise RuntimeError('Invalid data location: {}'.format(v))
-            pan['mask'] = v
-
-        elif k == 'mask_file':
-            pan['mask_file'] = v
-
-        elif k == 'saturation_map':
-            pan['saturation_map'] = v
-
-        elif k == 'saturation_map_file':
-            pan['saturation_map_file'] = v
-
-        elif k == 'coffset':
-            pan['coffset'] = float(v)
-
-        elif k == 'res':
-            pan['res'] = float(v)
-
-        elif k == 'max_adu':
-            pan['max_adu'] = v
-
-        elif k == 'badrow_direction':
-            if v == 'x':
-                pan['badrow'] = 'f'
-            elif v == 'y':
-                pan['badrow'] = 's'
-            elif v == 'f':
-                pan['badrow'] = 'f'
-            elif v == 's':
-                pan['badrow'] = 's'
-            elif v == '-':
-                pan['badrow'] = '-'
-            else:
-                print('badrow_direction must be x, t, f, s, or \'-\'')
-                print('Assuming \'-\'.')
-                pan['badrow'] = '-'
+            v = item[-1]
 
-        elif k == 'no_index':
-            pan['no_index'] = bool(v)
+        if axis == 'x':
+            direction[0] = float(v)
+        elif axis == 'y':
+            direction[1] = float(v)
+        elif axis == 'z':
+            direction[2] = float(v)
 
-        elif k == 'fs':
-            try:
-                pan['fsx'], pan['fsy'], pan['fsz'] = dir_conv(pan['fsx'], pan['fsy'],
-                                                              pan['fsz'])
+    return direction
 
-            except RuntimeError as e:
-                raise RuntimeError('Invalid fast scan direction. ', e)
 
-        elif k == 'ss':
-            try:
-                pan['ssx'], pan['ssy'], pan['ssz'] = dir_conv(pan['ssx'], pan['ssy'],
-                                                              pan['ssz'])
+def _set_dim_structure_entry(key, value, panel):
+    if panel['dim_structure'] is not None:
+        dim = panel['dim_structure']
+    else:
+        dim = []
 
-            except RuntimeError as e:
-                raise RuntimeError('Invalid slow scan direction. ', e)
-
-        elif k.startswith('dim'):
-            set_dim_structure_entry(k, v, pan)
+    dim_index = int(key[3])
+
+    if dim_index > len(dim) - 1:
+        for index in range(len(dim), dim_index + 1):
+            dim.append(None)
+
+    if value == 'ss' or value == 'fs' or value == '%':
+        dim[dim_index] = value
+    elif value.isdigit():
+        dim[dim_index] = int(value)
+    else:
+        raise RuntimeError('Invalid dim entry: {}.'.format(value))
+
+
+def _parse_field_for_panel(key, value, panel):
+    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(panel['rail_x'],
+                                                                          panel['rail_y'],
+                                                                          panel['rail_z'],
+                                                                          value)
+        except RuntimeError as e:
+            raise RuntimeError('Invalid rail direction. ', e)
+
+    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)
+            panel['clen_from'] = None
+        except ValueError:
+            panel['clen'] = -1
+            panel['clen_from'] = value
+
+    elif key == 'data':
+        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))
+        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'
+        elif value == 'y':
+            panel['badrow'] = 's'
+        elif value == 'f':
+            panel['badrow'] = 'f'
+        elif value == 's':
+            panel['badrow'] = 's'
+        elif value == '-':
+            panel['badrow'] = '-'
         else:
-            raise RuntimeError('Unrecognised field: {}'.format(k))
-
-    def parse_top_level(k, v, det, b, pan):
-
-        if k == 'mask_bad':
-            try:
-                det['mask_bad'] = int(v)
-            except ValueError:
-                det['mask_bad'] = int(v, 16)
+            print('badrow_direction must be x, t, f, s, or \'-\'')
+            print('Assuming \'-\'.')
+            panel['badrow'] = '-'
 
-        elif k == 'mask_good':
-            try:
-                det['mask_good'] = int(v)
-            except ValueError:
-                det['mask_good'] = int(v, 16)
+    elif key == 'no_index':
+        panel['no_index'] = bool(value)
 
-        elif k == 'coffset':
-            pan['coffset'] = float(v)
+    elif key == 'fs':
+        try:
+            panel['fsx'], panel['fsy'], panel['fsz'] = _dir_conv(panel['fsx'], panel['fsy'],
+                                                                 panel['fsz'], value)
 
-        elif k == 'photon_energy':
-            if v.startswith('/'):
-                b['photon_energy'] = 0.0
-                b['photon_energy_from'] = v
-            else:
-                b['photon_energy'] = float(v)
-                b['photon_energy_from'] = None
-
-        elif k == 'photon_energy_scale':
-            b['photon_energy_scale'] = float(v)
+        except RuntimeError as e:
+            raise RuntimeError('Invalid fast scan direction. ', e)
 
-        elif k == 'peak_info_location':
-            det['peak_info_location'] = v
+    elif key == 'ss':
+        try:
+            panel['ssx'], panel['ssy'], panel['ssz'] = _dir_conv(panel['ssx'], panel['ssy'],
+                                                                 panel['ssz'], value)
 
-        elif k.startswith('rigid_group') and not k.startswith('rigid_group_collection'):
-            det['rigid_groups'][k[12:]] = v.split(',')
+        except RuntimeError as e:
+            raise RuntimeError('Invalid slow scan direction. ', e)
 
-        elif k.startswith('rigid_group_collection'):
-            det['rigid_group_collections'][k[23:]] = v.split(',')
+    elif key.startswith('dim'):
+        _set_dim_structure_entry(key, value, panel)
 
-        else:
-            parse_field_for_panel(k, v, pan)
+    else:
+        raise RuntimeError('Unrecognised field: {}'.format(key))
 
-    def check_bad_fsss(bad, is_fsss):
 
-        if bad['is_fsss'] == 99:
-            bad['is_fsss'] = is_fsss
-            return
+def _parse_top_level(key, value, detector, beam, panel):
+    if key == 'mask_bad':
+        try:
+            detector['mask_bad'] = int(value)
+        except ValueError:
+            detector['mask_bad'] = int(value, 16)
 
-        if is_fsss != bad['is_fsss']:
-            raise RuntimeError("You can't mix x/y and fs/ss in a bad region")
+    elif key == 'mask_good':
+        try:
+            detector['mask_good'] = int(value)
+        except ValueError:
+            detector['mask_good'] = int(value, 16)
 
-        return
+    elif key == 'coffset':
+        panel['coffset'] = float(value)
 
-    def parse_field_bad(k, v, bad):
-
-        if k == 'min_x':
-            bad['min_x'] = float(v)
-            check_bad_fsss(bad, False)
-        elif k == 'max_x':
-            bad['max_x'] = float(v)
-            check_bad_fsss(bad, False)
-        elif k == 'min_y':
-            bad['min_y'] = float(v)
-            check_bad_fsss(bad, False)
-        elif k == 'max_y':
-            bad['max_y'] = float(v)
-            check_bad_fsss(bad, False)
-        elif k == 'min_fs':
-            bad['min_fs'] = int(v)
-            check_bad_fsss(bad, True)
-        elif k == 'max_fs':
-            bad['max_fs'] = int(v)
-            check_bad_fsss(bad, True)
-        elif k == 'min_ss':
-            bad['min_ss'] = int(v)
-            check_bad_fsss(bad, True)
-        elif k == 'max_ss':
-            bad['max_ss'] = int(v)
-            check_bad_fsss(bad, True)
-        elif k == 'panel':
-            bad['panel'] = v
+    elif key == 'photon_energy':
+        if value.startswith('/'):
+            beam['photon_energy'] = 0.0
+            beam['photon_energy_from'] = value
         else:
-            raise RuntimeError('Unrecognised field: {}'.format(k))
+            beam['photon_energy'] = float(value)
+            beam['photon_energy_from'] = None
 
-        return
+    elif key == 'photon_energy_scale':
+        beam['photon_energy_scale'] = float(value)
 
-    def check_point(n, pan, fs, ss, min_d, max_d, det):
+    elif key == 'peak_info_location':
+        detector['peak_info_location'] = value
 
-        xs = fs * pan['fsx'] + ss * pan['ssx']
-        ys = fs * pan['fsy'] + ss * pan['ssy']
+    elif key.startswith('rigid_group') and not key.startswith('rigid_group_collection'):
+        detector['rigid_groups'][key[12:]] = value.split(',')
 
-        rx = (xs + pan['cnx']) / pan['res']
-        ry = (ys + pan['cny']) / pan['res']
+    elif key.startswith('rigid_group_collection'):
+        detector['rigid_group_collections'][key[23:]] = value.split(',')
 
-        dist = sqrt(rx * rx + ry * ry)
+    else:
+        _parse_field_for_panel(key, value, panel)
 
-        if dist > max_d:
-            det['furthest_out_panel'] = n
-            det['furthest_out_fs'] = fs
-            det['furthest_out_ss'] = ss
-            max_d = dist
-        elif dist < min_d:
-            det['furthest_in_panel'] = n
-            det['furthest_in_fs'] = fs
-            det['furthest_in_ss'] = ss
-            min_d = dist
 
-        return min_d, max_d
+def _check_bad_fsss(bad_region, is_fsss):
+    if bad_region['is_fsss'] == 99:
+        bad_region['is_fsss'] = is_fsss
+        return
 
-    def find_min_max_d(det):
+    if is_fsss != bad_region['is_fsss']:
+        raise RuntimeError("You can't mix x/y and fs/ss in a bad region")
+
+    return
+
+
+def _parse_field_bad(key, value, bad):
+    if key == 'min_x':
+        bad['min_x'] = float(value)
+        _check_bad_fsss(bad, False)
+    elif key == 'max_x':
+        bad['max_x'] = float(value)
+        _check_bad_fsss(bad, False)
+    elif key == 'min_y':
+        bad['min_y'] = float(value)
+        _check_bad_fsss(bad, False)
+    elif key == 'max_y':
+        bad['max_y'] = float(value)
+        _check_bad_fsss(bad, False)
+    elif key == 'min_fs':
+        bad['min_fs'] = int(value)
+        _check_bad_fsss(bad, True)
+    elif key == 'max_fs':
+        bad['max_fs'] = int(value)
+        _check_bad_fsss(bad, True)
+    elif key == 'min_ss':
+        bad['min_ss'] = int(value)
+        _check_bad_fsss(bad, True)
+    elif key == 'max_ss':
+        bad['max_ss'] = int(value)
+        _check_bad_fsss(bad, True)
+    elif key == 'panel':
+        bad['panel'] = value
+    else:
+        raise RuntimeError('Unrecognised field: {}'.format(key))
+
+    return
+
+
+def _check_point(name, panel, fs, ss, min_d, max_d, detector):
+    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 = sqrt(rx * rx + ry * ry)
+
+    if dist > max_d:
+        detector['furthest_out_panel'] = name
+        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
+        min_d = dist
+
+    return min_d, max_d
+
+
+def _find_min_max_d(detector):
+    min_d = inf
+    max_d = 0.0
+
+    for name, panel in detector['panels'].items():
+        min_d, max_d = _check_point(name, panel, 0, 0, min_d, max_d, detector)
+        min_d, max_d = _check_point(name, panel, panel['w'], 0, min_d, max_d, detector)
+        min_d, max_d = _check_point(name, panel, 0, panel['h'], min_d, max_d, detector)
+        min_d, max_d = _check_point(name, panel, panel['w'], panel['h'], min_d, max_d, detector)
 
-        min_d = inf
-        max_d = 0.0
 
-        for n, pan in det['panels'].items():
-            min_d, max_d = check_point(n, pan, 0, 0, min_d, max_d, det)
-            min_d, max_d = check_point(n, pan, pan['w'], 0, min_d, max_d, det)
-            min_d, max_d = check_point(n, pan, 0, pan['h'], min_d, max_d, det)
-            min_d, max_d = check_point(n, pan, pan['w'], pan['h'], min_d, max_d, det)
+def load_crystfel_geometry(filename):
 
     fh = open(filename, 'r')
 
@@ -413,7 +415,7 @@ def load_crystfel_geometry(filename):
         path = [item for item in path if item not in '/']
 
         if len(path) < 2:
-            parse_top_level(line_items[0], value, detector, beam, default_panel)
+            _parse_top_level(line_items[0], value, detector, beam, default_panel)
             continue
 
         curr_bad = None
@@ -436,9 +438,9 @@ def load_crystfel_geometry(filename):
                 detector['panels'][path[0]] = curr_panel
 
         if curr_panel is not None:
-            parse_field_for_panel(path[1], value, curr_panel)
+            _parse_field_for_panel(path[1], value, curr_panel)
         else:
-            parse_field_bad(path[1], value, curr_bad)
+            _parse_field_bad(path[1], value, curr_bad)
 
     if len(detector['panels']) == 0:
         raise RuntimeError("No panel descriptions in geometry file.")
@@ -506,6 +508,14 @@ def load_crystfel_geometry(filename):
                 else:
                     found_placeholder = True
 
+        if dim_length is None:
+            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.')
+
+        if len(dim_length) == 1:
+            raise RuntimeError('Number of dim coordinates must be at least two.')
+
     for panel in detector['panels'].values():
 
         if panel['origin_min_fs'] < 0:
@@ -574,7 +584,7 @@ def load_crystfel_geometry(filename):
         panel['xss'] = panel['fsy'] / d
         panel['yss'] = panel['fsx'] / d
 
-    find_min_max_d(detector)
+    _find_min_max_d(detector)
     fh.close()
 
     return detector
-- 
GitLab