From 1949465fe22cdbacfe939d49694cec52b5363ec9 Mon Sep 17 00:00:00 2001 From: Patrick Avery Date: Tue, 30 Apr 2024 09:58:57 -0500 Subject: [PATCH] Allow Euler convention setting for calibration Some users strongly prefer to use a different Euler convention for calibration, such as extrinsic XYZ. It is easy to convert angles from one Euler convention to another. However, it is difficult to convert boundary conditions (min/max ranges for each angle) from one convention to another. So we add the ability to choose the Euler convention to use in calibration. This allows the min/max ranges to be specified in lmfit using that convention. This Euler angle convention setting applies to detector tilt angles and grain parameter orientations. Signed-off-by: Patrick Avery --- hexrdgui/calibration/auto/powder_runner.py | 4 +++- hexrdgui/calibration/calibration_dialog.py | 24 ++++++++++++++++++- .../calibration_dialog_callbacks.py | 14 ++++++++++- hexrdgui/calibration/calibration_runner.py | 1 + .../material_calibration_dialog_callbacks.py | 10 +++++++- .../calibration/pick_based_calibration.py | 9 +++++-- hexrdgui/calibration/structureless/runner.py | 1 + .../calibration_params_tree_view.yml | 3 --- 8 files changed, 57 insertions(+), 9 deletions(-) diff --git a/hexrdgui/calibration/auto/powder_runner.py b/hexrdgui/calibration/auto/powder_runner.py index a462105e0..9eea762cb 100644 --- a/hexrdgui/calibration/auto/powder_runner.py +++ b/hexrdgui/calibration/auto/powder_runner.py @@ -86,7 +86,9 @@ def _run(self): self.pc = PowderCalibrator(**kwargs) self.ic = InstrumentCalibrator( - self.pc, engineering_constraints=engineering_constraints + self.pc, + engineering_constraints=engineering_constraints, + euler_convention=HexrdConfig().euler_angle_convention, ) self.extract_powder_lines() diff --git a/hexrdgui/calibration/calibration_dialog.py b/hexrdgui/calibration/calibration_dialog.py index 19bd2ce60..99ff19c0d 100644 --- a/hexrdgui/calibration/calibration_dialog.py +++ b/hexrdgui/calibration/calibration_dialog.py @@ -6,6 +6,11 @@ from PySide6.QtGui import QColor from PySide6.QtWidgets import QComboBox, QDoubleSpinBox, QMessageBox, QSpinBox +from hexrd.fitting.calibration.lmfit_param_handling import ( + normalize_euler_convention, + param_names_euler_convention, +) + from hexrdgui import resource_loader from hexrdgui.constants import ViewType from hexrdgui.hexrd_config import HexrdConfig @@ -341,6 +346,16 @@ def recursively_format_det(det, this_config, this_template): this_dict[i + 1] = create_param_item(param) i += 1 current = template.format(det=det, i=i) + elif k == 'tilt': + # Special case. Take into account euler angles. + convention = HexrdConfig().euler_angle_convention + normalized = normalize_euler_convention(convention) + param_names = param_names_euler_convention(det, convention) + labels = TILT_LABELS_EULER[normalized] + this_dict = this_config.setdefault(k, {}) + for label, param_name in zip(labels, param_names): + param = params_dict[param_name] + this_dict[label] = create_param_item(param) else: # Should be a string. Replace {det} with det if needed if '{det}' in v: @@ -367,7 +382,7 @@ def recursively_format_det(det, this_config, this_template): used = ', '.join(sorted(used_params)) params = ', '.join(sorted(params_dict)) msg = ( - f'Internal error: used_params ({used}) did not match ' + f'Internal error: used_params ({used})\n\ndid not match ' f'params_dict! ({params})' ) raise Exception(msg) @@ -460,3 +475,10 @@ def set_config_val(self, path, value): # Now set the attribute on the param attribute = path[-1].removeprefix('_') setattr(param, attribute, value) + + +TILT_LABELS_EULER = { + None: ('X', 'Y', 'Z'), + ('xyz', True): ('X', 'Y', 'Z'), + ('zxz', False): ('Z', "X'", "Z''"), +} diff --git a/hexrdgui/calibration/calibration_dialog_callbacks.py b/hexrdgui/calibration/calibration_dialog_callbacks.py index f72e1fa98..0521cba32 100644 --- a/hexrdgui/calibration/calibration_dialog_callbacks.py +++ b/hexrdgui/calibration/calibration_dialog_callbacks.py @@ -4,6 +4,10 @@ from PySide6.QtCore import Signal from PySide6.QtWidgets import QFileDialog +from hexrd.fitting.calibration.lmfit_param_handling import ( + update_instrument_from_params, +) + from hexrdgui.hexrd_config import HexrdConfig from hexrdgui.utils import instr_to_internal_dict from hexrdgui.utils.abc_qobject import ABCQObject @@ -55,6 +59,10 @@ def setup_connections(self): def canvas(self): return HexrdConfig().active_canvas + @property + def euler_convention(self): + return HexrdConfig().euler_angle_convention + @abstractmethod def draw_picks_on_canvas(self): pass @@ -127,7 +135,11 @@ def pop_undo_stack(self): v = stack_item[k] setattr(self.calibrator, k, v) - self.instr.update_from_lmfit_parameter_list(self.calibrator.params) + update_instrument_from_params( + self.instr, + self.calibrator.params, + self.euler_convention, + ) self.update_config_from_instrument() self.update_dialog_from_calibrator() self.dialog.advanced_options = stack_item['advanced_options'] diff --git a/hexrdgui/calibration/calibration_runner.py b/hexrdgui/calibration/calibration_runner.py index 81e750b02..63ff6ee07 100644 --- a/hexrdgui/calibration/calibration_runner.py +++ b/hexrdgui/calibration/calibration_runner.py @@ -859,6 +859,7 @@ def auto_pick_laue_points(self): 'grain_params': overlay.crystal_params, 'min_energy': overlay.min_energy, 'max_energy': overlay.max_energy, + 'euler_convention': HexrdConfig().euler_angle_convention, } self.laue_auto_picker = LaueCalibrator(**init_kwargs) diff --git a/hexrdgui/calibration/material_calibration_dialog_callbacks.py b/hexrdgui/calibration/material_calibration_dialog_callbacks.py index 424bbb49b..15235f29c 100644 --- a/hexrdgui/calibration/material_calibration_dialog_callbacks.py +++ b/hexrdgui/calibration/material_calibration_dialog_callbacks.py @@ -1,3 +1,8 @@ +from hexrd.fitting.calibration.lmfit_param_handling import ( + normalize_euler_convention +) + +from hexrdgui.calibration.calibration_dialog import TILT_LABELS_EULER from hexrdgui.calibration.calibration_dialog_callbacks import ( CalibrationDialogCallbacks, ) @@ -5,6 +10,7 @@ overlays_to_tree_format, HKLPicksTreeViewDialog, ) from hexrdgui.constants import ViewType +from hexrdgui.hexrd_config import HexrdConfig class MaterialCalibrationDialogCallbacks(CalibrationDialogCallbacks): @@ -118,7 +124,9 @@ def format_material_params_func(params_dict, tree_dict, create_param_item, else: # Assume grain parameters d['Orientation'] = {} - labels = ['Z', "X'", "Z''"] + euler_convention = normalize_euler_convention( + HexrdConfig().euler_angle_convention) + labels = TILT_LABELS_EULER[euler_convention] for i in range(3): param = params_dict[calibrator.param_names[i]] d['Orientation'][labels[i]] = create_param_item(param) diff --git a/hexrdgui/calibration/pick_based_calibration.py b/hexrdgui/calibration/pick_based_calibration.py index 34f012e9a..5b4fcadb6 100644 --- a/hexrdgui/calibration/pick_based_calibration.py +++ b/hexrdgui/calibration/pick_based_calibration.py @@ -3,10 +3,12 @@ LaueCalibrator, PowderCalibrator, ) +from hexrdgui.hexrd_config import HexrdConfig from hexrdgui.utils.guess_instrument_type import guess_instrument_type -def make_calibrators_from_picks(instr, processed_picks, materials, img_dict): +def make_calibrators_from_picks(instr, processed_picks, materials, img_dict, + euler_convention): calibrators = [] for pick_data in processed_picks: if pick_data['type'] == 'powder': @@ -30,17 +32,20 @@ def make_calibrators_from_picks(instr, processed_picks, materials, img_dict): 'min_energy': pick_data['options']['min_energy'], 'max_energy': pick_data['options']['max_energy'], 'calibration_picks': pick_data['picks'], + 'euler_convention': euler_convention, } calibrators.append(LaueCalibrator(**kwargs)) return calibrators def create_instrument_calibrator(picks, instr, img_dict, materials): + euler_convention = HexrdConfig().euler_angle_convention engineering_constraints = guess_instrument_type(instr.detectors) calibrators = make_calibrators_from_picks(instr, picks, materials, - img_dict) + img_dict, euler_convention) return InstrumentCalibrator( *calibrators, engineering_constraints=engineering_constraints, + euler_convention=euler_convention, ) diff --git a/hexrdgui/calibration/structureless/runner.py b/hexrdgui/calibration/structureless/runner.py index 6fbae792d..ddaf8e160 100644 --- a/hexrdgui/calibration/structureless/runner.py +++ b/hexrdgui/calibration/structureless/runner.py @@ -207,6 +207,7 @@ def show_calibration_dialog(self, calibrator_lines): 'instr': self.instr, 'data': calibrator_lines, 'engineering_constraints': engineering_constraints, + 'euler_convention': HexrdConfig().euler_angle_convention, } self.calibrator = StructurelessCalibrator(**kwargs) diff --git a/hexrdgui/resources/calibration/calibration_params_tree_view.yml b/hexrdgui/resources/calibration/calibration_params_tree_view.yml index 188f2d83d..6b9d92e98 100644 --- a/hexrdgui/resources/calibration/calibration_params_tree_view.yml +++ b/hexrdgui/resources/calibration/calibration_params_tree_view.yml @@ -13,9 +13,6 @@ detectors: '{det}': transform: tilt: - Z: '{det}_euler_z' - "X'": '{det}_euler_xp' - "Z''": '{det}_euler_zpp' translation: X: '{det}_tvec_x' Y: '{det}_tvec_y'