Skip to content

Commit

Permalink
Transfer composite calibration class to HEXRD
Browse files Browse the repository at this point in the history
This is coming from HEXRDGUI as well. It belongs here better.

Signed-off-by: Patrick Avery <patrick.avery@kitware.com>
  • Loading branch information
psavery committed Sep 13, 2023
1 parent 4682978 commit ce93842
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 0 deletions.
2 changes: 2 additions & 0 deletions hexrd/fitting/calibration/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .composite import CompositeCalibration
from .instrument import InstrumentCalibrator
from .laue import LaueCalibrator
from .multigrain import calibrate_instrument_from_sx, generate_parameter_names
Expand All @@ -8,6 +9,7 @@
StructureLessCalibrator = StructurelessCalibrator

__all__ = [
'CompositeCalibration',
'calibrate_instrument_from_sx',
'generate_parameter_names',
'InstrumentCalibrator',
Expand Down
151 changes: 151 additions & 0 deletions hexrd/fitting/calibration/composite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import copy

import numpy as np

from .laue import LaueCalibrator
from .powder import PowderCalibrator


class CompositeCalibration:
def __init__(self, instr, processed_picks, img_dict):
self.instr = instr
self.original_instr = copy.deepcopy(instr)
self.npi = len(self.instr.calibration_parameters)
self.data = processed_picks
calibrator_list = []
params = []
param_flags = []
for pick_data in processed_picks:
if pick_data['type'] == 'powder':
# flags for calibrator
lpflags = [i[1] for i in pick_data['refinements']]
flags = np.hstack(
[self.instr.calibration_flags, lpflags]
)
param_flags.append(lpflags)

kwargs = {
'instr': self.instr,
'plane_data': pick_data['plane_data'],
'img_dict': img_dict,
'flags': flags,
'tth_distortion': pick_data['tth_distortion'],
}
calib = PowderCalibrator(**kwargs)

params.append(calib.full_params[-calib.npe:])
calibrator_list.append(calib)

elif pick_data['type'] == 'laue':
# flags for calibrator
gparams = pick_data['options']['crystal_params']
min_energy = pick_data['options']['min_energy']
max_energy = pick_data['options']['max_energy']

gpflags = [i[1] for i in pick_data['refinements']]
flags = np.hstack(
[self.instr.calibration_flags, gpflags]
)
param_flags.append(gpflags)
calib = LaueCalibrator(
self.instr, pick_data['plane_data'],
gparams, flags,
min_energy=min_energy, max_energy=max_energy
)
params.append(calib.full_params[-calib.npe:])
calibrator_list.append(calib)

self.calibrators = calibrator_list
self.params = np.hstack(params)
self.param_flags = np.hstack(param_flags)
self.full_params = np.hstack(
[self.instr.calibration_parameters, self.params]
)
self.flags = np.hstack(
[self.instr.calibration_flags, self.param_flags]
)

def reduced_params(self):
return self.full_params[self.flags]

def residual(self, reduced_params, pick_data_list):
# first update a copy of the full parameter list
full_params = np.array(self.full_params)
full_params[self.flags] = reduced_params
instr_params = full_params[:self.npi]
addtl_params = full_params[self.npi:]

def powder_residual(calib, these_reduced_params, data_dict):
# Convert our data_dict into the input data format that
# the powder calibrator is expecting.
calibration_data = {}
for det_key in data_dict['hkls']:
# MUST use original instrument to convert these coordinates
# so that we are consistent. This is because the instrument
# gets modified with each call to `residual()`.
panel = self.original_instr.detectors[det_key]

picks_list = []
for i, hkl in enumerate(data_dict['hkls'][det_key]):
picks = data_dict['picks'][det_key][i]
if not picks:
continue

# Each row consists of 8 columns:
# First two are measured x, y
# Third is unknown (appears unused in the PowderCalibrator)
# Four - six are the hkl indices
# Seven and Eight are unknown (appears unused here)

# We are going to convert our data into rows of 6 columns.
# We will fill in zero for the third column, and ignore
# the last two columns.

# Convert the angles to Cartesian
cartesian_picks = panel.angles_to_cart(
np.radians(picks),
apply_distortion=True,
)
repeated_zero = np.repeat([[0]], len(cartesian_picks),
axis=0)
repeated_hkl = np.repeat([hkl], len(cartesian_picks),
axis=0)
formatted = np.hstack((cartesian_picks, repeated_zero,
repeated_hkl))
picks_list.append(formatted)

calibration_data[det_key] = picks_list

return calib.residual(these_reduced_params, calibration_data)

def laue_residual(calib, these_reduced_params, data_dict):
return calib.residual(these_reduced_params, data_dict)

residual_funcs = {
PowderCalibrator: powder_residual,
LaueCalibrator: laue_residual,
}

# loop calibrators and collect residuals
ii = 0
residual = []
for ical, calib in enumerate(self.calibrators):
# make copy offull params for this calibrator
these_full_params = np.hstack(
[instr_params, addtl_params[ii:ii + calib.npe]]
)

# pull out reduced list
these_reduced_params = these_full_params[calib.flags]

# call to calibrator residual api with proper index into pick data
f = residual_funcs[type(calib)]
residual.append(
f(calib, these_reduced_params, pick_data_list[ical])
)

# advance alibrator extra parameter offset
ii += calib.npe

# return single hstacked residual
return np.hstack(residual)

0 comments on commit ce93842

Please sign in to comment.