Skip to content

Commit

Permalink
Merge pull request #1764 from HEXRD/calibration-center-of-rotation
Browse files Browse the repository at this point in the history
Add ability to specify center of rotation
  • Loading branch information
saransh13 authored Dec 24, 2024
2 parents ca87363 + 830855b commit 06e84e2
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 89 deletions.
86 changes: 75 additions & 11 deletions hexrdgui/calibration/calibration_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
)
from hexrd.fitting.calibration.relative_constraints import (
RelativeConstraintsType,
RotationCenter,
)

from hexrdgui import resource_loader
Expand Down Expand Up @@ -42,7 +43,9 @@ class CalibrationDialog(QObject):
save_picks_clicked = Signal()
load_picks_clicked = Signal()
relative_constraints_changed = Signal(RelativeConstraintsType)
tilt_center_of_rotation_changed = Signal(RotationCenter)
engineering_constraints_changed = Signal(str)
reset_relative_params_to_zero_clicked = Signal()

pinhole_correction_settings_modified = Signal()

Expand Down Expand Up @@ -75,6 +78,7 @@ def __init__(self, instr, params_dict, format_extra_params_func=None,
self.on_pinhole_correction_settings_modified)

self.populate_relative_constraint_options()
self.populate_tilt_rotation_center_options()

self.instr = instr
self._params_dict = params_dict
Expand All @@ -84,12 +88,6 @@ def __init__(self, instr, params_dict, format_extra_params_func=None,

self._ignore_next_tree_view_update = False

instr_type = guess_instrument_type(instr.detectors)
# Use delta boundaries by default for anything other than TARDIS
# and PXRDIP. We might want to change this to a whitelist later.
use_delta_boundaries = instr_type not in ('TARDIS', 'PXRDIP')
self.delta_boundaries = use_delta_boundaries

self.initialize_advanced_options()

self.load_tree_view_mapping()
Expand All @@ -101,6 +99,7 @@ def __init__(self, instr, params_dict, format_extra_params_func=None,
self.update_visibility_states()

self.load_settings()
self.update_relative_constraint_visibilities()
self.setup_connections()

def setup_connections(self):
Expand All @@ -109,8 +108,12 @@ def setup_connections(self):
self.on_active_beam_changed)
self.ui.show_picks_from_all_xray_sources.toggled.connect(
self.show_picks_from_all_xray_sources_toggled)
self.ui.reset_relative_params_to_zero.clicked.connect(
self.on_reset_relative_params_to_zero_clicked)
self.ui.relative_constraints.currentIndexChanged.connect(
self.on_relative_constraints_changed)
self.ui.tilt_center_of_rotation.currentIndexChanged.connect(
self.on_tilt_center_of_rotation_changed)
self.ui.engineering_constraints.currentIndexChanged.connect(
self.on_engineering_constraints_changed)
self.ui.delta_boundaries.toggled.connect(
Expand Down Expand Up @@ -150,7 +153,28 @@ def populate_relative_constraint_options(self):
w = self.ui.relative_constraints
w.clear()
for option in options:
w.addItem(option.value, option)
w.addItem(RELATIVE_CONSTRAINT_LABELS[option], option)

def populate_tilt_rotation_center_options(self):
# We are skipping group constraints until it is actually implemented
w = self.ui.tilt_center_of_rotation
w.clear()
for label in ROTATION_CENTER_LABELS.values():
w.addItem(label)

def set_instrument_defaults(self):
# This function should only be called after the Callbacks have been
# connected, because the changes here may also affect the Calibrator
# classes.
instr_type = guess_instrument_type(self.instr.detectors)
# Use delta boundaries by default for anything other than TARDIS
# and PXRDIP. We might want to change this to a whitelist later.
use_delta_boundaries = instr_type not in ('TARDIS', 'PXRDIP')
self.delta_boundaries = use_delta_boundaries

if instr_type == 'FIDDLE':
self.relative_constraints = RelativeConstraintsType.system
self.tilt_center_of_rotation = RotationCenter.lab_origin

def update_edit_picks_enable_state(self):
is_polar = HexrdConfig().image_mode == ViewType.polar
Expand Down Expand Up @@ -220,6 +244,17 @@ def update_visibility_states(self):
self.ui.active_beam_label.setVisible(has_multi_xrs)
self.ui.show_picks_from_all_xray_sources.setVisible(has_multi_xrs)

def update_relative_constraint_visibilities(self):
visible = self.relative_constraints != RelativeConstraintsType.none

tilt_center_widgets = [
self.ui.reset_relative_params_to_zero,
self.ui.tilt_center_of_rotation,
self.ui.tilt_center_of_rotation_label,
]
for w in tilt_center_widgets:
w.setVisible(visible)

def on_draw_picks_toggled(self, b):
self.draw_picks_toggled.emit(b)

Expand Down Expand Up @@ -341,11 +376,20 @@ def relative_constraints(self) -> RelativeConstraintsType:
def relative_constraints(self, v: RelativeConstraintsType):
v = v if v is not None else RelativeConstraintsType.none
w = self.ui.relative_constraints
options = [w.itemText(i) for i in range(w.count())]
if v.value not in options:
raise Exception(f'Invalid relative constraints: {v.value}')
w.setCurrentText(RELATIVE_CONSTRAINT_LABELS[v])

self.update_relative_constraint_visibilities()

@property
def tilt_center_of_rotation(self) -> RotationCenter:
w = self.ui.tilt_center_of_rotation
return ROTATION_CENTER_LABELS_R[w.currentText()]

w.setCurrentText(v.value)
@tilt_center_of_rotation.setter
def tilt_center_of_rotation(self, v: RotationCenter):
w = self.ui.tilt_center_of_rotation
text = ROTATION_CENTER_LABELS[v]
w.setCurrentText(text)

@property
def engineering_constraints(self):
Expand Down Expand Up @@ -392,6 +436,9 @@ def tth_distortion(self, v):
first = next(iter(v.values()))
self.pinhole_correction_editor.update_from_object(first)

def on_reset_relative_params_to_zero_clicked(self):
self.reset_relative_params_to_zero_clicked.emit()

def on_relative_constraints_changed(self):
# If the relative constraints is not None, then the engineering
# constraints must be set to None
Expand All @@ -405,6 +452,10 @@ def on_relative_constraints_changed(self):

self.relative_constraints_changed.emit(self.relative_constraints)
self.reinitialize_tree_view()
self.update_relative_constraint_visibilities()

def on_tilt_center_of_rotation_changed(self):
self.tilt_center_of_rotation_changed.emit(self.tilt_center_of_rotation)

def on_engineering_constraints_changed(self):
self.engineering_constraints_changed.emit(self.engineering_constraints)
Expand Down Expand Up @@ -458,6 +509,9 @@ def mirror_constraints_from_first_detector(self):

def update_from_calibrator(self, calibrator):
self.relative_constraints = calibrator.relative_constraints_type
self.tilt_center_of_rotation = (
calibrator.relative_constraints.rotation_center
)
self.engineering_constraints = calibrator.engineering_constraints
self.tth_distortion = calibrator.tth_distortion
self.params_dict = calibrator.params
Expand Down Expand Up @@ -721,6 +775,16 @@ def tree_view_model_class(self):
('zxz', False): ('Z', "X'", "Z''"),
}

RELATIVE_CONSTRAINT_LABELS = {
RelativeConstraintsType.none: 'None',
RelativeConstraintsType.system: 'Instrument Rigid Body',
}
ROTATION_CENTER_LABELS = {
RotationCenter.instrument_mean_center: 'Mean Instrument Center',
RotationCenter.lab_origin: 'Origin',
}
ROTATION_CENTER_LABELS_R = {v: k for k, v in ROTATION_CENTER_LABELS.items()}


def guess_engineering_constraints(instr) -> str | None:
# First guess the instrument type.
Expand Down
71 changes: 69 additions & 2 deletions hexrdgui/calibration/calibration_dialog_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import lmfit

from hexrd.fitting.calibration.relative_constraints import (
RelativeConstraintsType,
)
from hexrd.fitting.calibration.lmfit_param_handling import (
create_instr_params,
update_instrument_from_params,
Expand All @@ -17,6 +20,10 @@
from hexrdgui.utils.abc_qobject import ABCQObject


# Number of decimal places to round calibration parameters
ROUND_DECIMALS = 3


class CalibrationDialogCallbacks(ABCQObject):
"""A class with default behavior for calibration dialog callbacks"""

Expand Down Expand Up @@ -44,6 +51,9 @@ def __init__(self, dialog, calibrator, instr, async_runner):
self.draw_picks()
self.setup_connections()

# Trigger the instrument defaults to be set on the dialog
self.dialog.set_instrument_defaults()

def setup_connections(self):
dialog = self.dialog

Expand All @@ -56,6 +66,10 @@ def setup_connections(self):
dialog.load_picks_clicked.connect(self.on_load_picks_clicked)
dialog.relative_constraints_changed.connect(
self.on_relative_constraints_changed)
dialog.reset_relative_params_to_zero_clicked.connect(
self.on_reset_relative_params_to_zero_clicked)
dialog.tilt_center_of_rotation_changed.connect(
self.on_tilt_center_of_rotation_changed)
dialog.engineering_constraints_changed.connect(
self.on_engineering_constraints_changed)
dialog.run.connect(self.on_run_clicked)
Expand Down Expand Up @@ -157,14 +171,62 @@ def pop_undo_stack(self):
self.dialog.advanced_options = stack_item['advanced_options']

self.update_undo_enable_state()
self.save_constraint_params()

def update_undo_enable_state(self):
self.dialog.undo_enabled = bool(self.undo_stack)

def on_relative_constraints_changed(self, new_constraint):
self.calibrator.relative_constraints_type = new_constraint
self.restore_constraint_params()
# Reset the tilt center of rotation in the dialog
self.dialog.tilt_center_of_rotation = (
self.calibrator.relative_constraints.rotation_center
)
self.on_constraints_changed()

def on_reset_relative_params_to_zero_clicked(self):
self.reset_saved_constraint_params()

def save_constraint_params(self):
constraints = self.calibrator.relative_constraints
if constraints.type != RelativeConstraintsType.system:
# Instead of saving, reset them
self.reset_saved_constraint_params()
return

HexrdConfig()._instrument_rigid_body_params = copy.deepcopy(
constraints.params)

def restore_constraint_params(self):
constraints = self.calibrator.relative_constraints
if constraints.type != RelativeConstraintsType.system:
return

params = copy.deepcopy(HexrdConfig()._instrument_rigid_body_params)
if not params:
return

constraints.params.update(**params)
self.calibrator.reset_lmfit_params()
self.round_param_numbers()
self.update_dialog_from_calibrator()

def reset_saved_constraint_params(self):
HexrdConfig()._instrument_rigid_body_params.clear()
constraints = self.calibrator.relative_constraints
constraints.reset_params()
self.calibrator.reset_lmfit_params()
self.update_dialog_from_calibrator()

def on_tilt_center_of_rotation_changed(self, new_center):
relative_constraints = self.calibrator.relative_constraints
if relative_constraints.rotation_center != new_center:
# Some of the relative constraint types have a fixed getter
# for this, and no setter. We should avoid causing an exception
# if we are just setting it unnecessarily.
relative_constraints.rotation_center = new_center

def on_engineering_constraints_changed(self, new_constraint):
self.calibrator.engineering_constraints = new_constraint
self.on_constraints_changed()
Expand Down Expand Up @@ -253,6 +315,7 @@ def run_calibration(self, **extra_kwargs):

def on_calibration_finished(self):
self.update_config_from_instrument()
self.save_constraint_params()
self.dialog.params_dict = self.calibrator.params

def update_config_from_instrument(self):
Expand Down Expand Up @@ -282,7 +345,7 @@ def update_config_from_instrument(self):
def update_tth_distortion_from_dialog(self):
self.calibrator.tth_distortion = self.dialog.tth_distortion

def round_param_numbers(self, decimals=3):
def round_param_numbers(self):
params_dict = self.calibrator.params

attrs = [
Expand All @@ -292,7 +355,11 @@ def round_param_numbers(self, decimals=3):
]
for param in params_dict.values():
for attr in attrs:
setattr(param, attr, round(getattr(param, attr), 3))
setattr(
param,
attr,
round(getattr(param, attr), ROUND_DECIMALS)
)

def on_edit_picks_finished(self):
# Show this again
Expand Down
Loading

0 comments on commit 06e84e2

Please sign in to comment.