def __init__(self, value, num_samples = 5, axis = None, ptype = None, name = "ScanVaryingParameterSet"): assert num_samples >= 2 #otherwise use scan-independent parameterisation value = [value] * num_samples self._name_stem = name name = [e + "_sample%d" % i for i, e in enumerate( [self._name_stem] * num_samples)] Parameter.__init__(self, value, axis, ptype, name) self._esd = [None] * num_samples self._num_samples = num_samples return
def __init__(self, detector, experiment_ids=None, level=0): """Initialise the DetectorParameterisationHierarchical object Args: detector: A dxtbx Detector object to be parameterised. experiment_ids (list): The experiment IDs affected by this parameterisation. Defaults to None, which is replaced by [0]. level (int): Select level of the detector hierarchy to determine panel groupings that are treated as separate rigid blocks. """ if experiment_ids is None: experiment_ids = [0] try: h = detector.hierarchy() except AttributeError: print("This detector does not have a hierarchy") raise # list the panel groups at the chosen level try: self._groups = get_panel_groups_at_depth(h, level) except AttributeError: print("Cannot access the hierarchy at the depth level={}".format( level)) raise # collect the panel ids for each Panel within the groups panels = list(detector) self._panel_ids_by_group = [ get_panel_ids_at_root(panels, g) for g in self._groups ] p_list = [] self._group_ids_by_parameter = [] istate = [] self._offsets = [] self._dir1s = [] self._dir2s = [] # loop over the groups, collecting initial parameters and states for igp, pnl_ids in enumerate(self._panel_ids_by_group): panel_centres_in_lab_frame = [] for i in pnl_ids: pnl = detector[i] im_size = pnl.get_image_size_mm() cntr = (matrix.col(pnl.get_origin()) + 0.5 * matrix.col(pnl.get_fast_axis()) * im_size[0] + 0.5 * matrix.col(pnl.get_slow_axis()) * im_size[1]) panel_centres_in_lab_frame.append(cntr) # get some vectors we need from the group go = matrix.col(self._groups[igp].get_origin()) d1 = matrix.col(self._groups[igp].get_fast_axis()) d2 = matrix.col(self._groups[igp].get_slow_axis()) dn = matrix.col(self._groups[igp].get_normal()) # we choose the dorg vector for this group to terminate on the group's # frame, at a point that we consider close to the centre of the group of # panels. This point is defined by taking the 3D centroid of the panel # centres then projecting that point onto the group frame. centroid = reduce( lambda a, b: a + b, panel_centres_in_lab_frame) / len(panel_centres_in_lab_frame) try: gp_centroid = matrix.col( self._groups[igp].get_bidirectional_ray_intersection( centroid)) dorg = go + gp_centroid[0] * d1 + gp_centroid[1] * d2 except RuntimeError: # workaround for a group frame that passes through # the origin dorg = matrix.col((0.0, 0.0, 0.0)) # The offset between the end of the dorg vector and # each Panel origin is a coordinate matrix with elements in the basis d1, # d2, dn. We need also each Panel's plane directions dir1 and dir2 in # terms of d1, d2 and dn. offsets, dir1s, dir2s = [], [], [] # FIXME these dot products would be more efficiently done using a change of # basis matrix instead for p in [detector[i] for i in pnl_ids]: offset = matrix.col(p.get_origin()) - dorg offsets.append( matrix.col( (offset.dot(d1), offset.dot(d2), offset.dot(dn)))) dir1 = matrix.col(p.get_fast_axis()) dir1_new_basis = matrix.col( (dir1.dot(d1), dir1.dot(d2), dir1.dot(dn))) dir1s.append(dir1_new_basis) dir2 = matrix.col(p.get_slow_axis()) dir2_new_basis = matrix.col( (dir2.dot(d1), dir2.dot(d2), dir2.dot(dn))) dir2s.append(dir2_new_basis) # The offsets and directions in the d1, d2, dn basis are fixed # quantities, not dependent on parameter values. Keep these as separate # sub-lists for each group self._offsets.append(offsets) self._dir1s.append(dir1s) self._dir2s.append(dir2s) # Set up the initial state for this group. This is the basis d1, d2, dn, # plus the offset locating the origin of the initial group frame gp_offset = go - dorg # lab frame basis # FIXME another set of dot products better done by a matrix multiplication gp_offset = matrix.col((gp_offset.dot(d1), gp_offset.dot(d2), gp_offset.dot(dn))) # d1,d2,dn basis istate.append({ "d1": d1, "d2": d2, "dn": dn, "gp_offset": gp_offset }) # set up the parameters. # distance from lab origin to ref_panel plane along its normal, # in initial orientation distance = self._groups[igp].get_directed_distance() dist = Parameter(distance, dn, "length (mm)", "Group{}Dist".format(igp + 1)) # shift in the detector model plane to locate dorg, in initial # orientation shift = dorg - dn * distance shift1 = Parameter(shift.dot(d1), d1, "length (mm)", "Group{}Shift1".format(igp + 1)) shift2 = Parameter(shift.dot(d2), d2, "length (mm)", "Group{}Shift2".format(igp + 1)) # rotations of the plane through its origin about: # 1) axis normal to initial orientation # 2) d1 axis of initial orientation # 3) d2 axis of initial orientation tau1 = Parameter(0, dn, "angle (mrad)", "Group{}Tau1".format(igp + 1)) tau2 = Parameter(0, d1, "angle (mrad)", "Group{}Tau2".format(igp + 1)) tau3 = Parameter(0, d2, "angle (mrad)", "Group{}Tau3".format(igp + 1)) # extend the parameter list with those pertaining to this group p_list.extend([dist, shift1, shift2, tau1, tau2, tau3]) self._group_ids_by_parameter.extend([igp] * 6) # set up the base class ModelParameterisation.__init__( self, detector, istate, p_list, experiment_ids=experiment_ids, is_multi_state=True, ) # call compose to calculate all the derivatives self.compose()
def __init__(self, detector, beam, experiment_ids=None): """Initialise the DetectorParameterisationMultiPanel object Args: detector: A dxtbx Detector object to be parameterised. beam: An dxtbx beam object used to calculate the closest panel. experiment_ids (list): The experiment IDs affected by this parameterisation. Defaults to None, which is replaced by [0]. """ # The state of each Panel in the detector model is its matrix # d = (d1|d2|d0). We need to define a new coordinate system rigidly # attached to the detector model in which to express the # parameterisation and compose each of the Panel states. # # We define: # # * a vector 'dorg' locating a point in laboratory space that moves with # the rigid body of the detector and thus is fixed wrt each of the # Panels. # * A pair of orthogonal unit directions 'd1' and 'd2' forming a plane # with its origin at the end of the vector dorg. # * a third unit direction 'dn', orthogonal to both 'd1' & 'd2'. # * offsets to locate the origin of each panel frame from the # tip of the dorg vector, in terms of the coordinate system # formed by d1, d2 and dn. # # Held separately in attribute 'models' are: # * references to detector objects contained in this model # set up the initial state of the detector model from the # orientation of whichever Panel has its centre most closely # located to the direct beam intersection. Call this 'mid_panel' if experiment_ids is None: experiment_ids = [0] beam_centres = [ matrix.col(p.get_beam_centre(beam.get_unit_s0())) for p in detector ] panel_centres = [ 0.5 * matrix.col(p.get_image_size_mm()) for p in detector ] beam_to_centres = [(a - b).length() for a, b in zip(beam_centres, panel_centres)] mid_panel_id = beam_to_centres.index(min(beam_to_centres)) mid_panel = detector[mid_panel_id] # get some vectors we need from the mid_panel so = matrix.col(mid_panel.get_origin()) d1 = matrix.col(mid_panel.get_fast_axis()) d2 = matrix.col(mid_panel.get_slow_axis()) dn = matrix.col(mid_panel.get_normal()) # we choose the dorg vector to terminate in the centre of the mid_panel, # and the offset between the end of the dorg vector and each Panel # origin is a coordinate matrix with elements in the basis d1, d2, dn. # We need also each Panel's plane directions dir1 and dir2 in terms of # d1, d2 and dn. mid_panel_centre = panel_centres[mid_panel_id] dorg = so + mid_panel_centre[0] * d1 + mid_panel_centre[1] * d2 offsets, dir1s, dir2s = [], [], [] for p in detector: offset = matrix.col(p.get_origin()) - dorg offsets.append( matrix.col((offset.dot(d1), offset.dot(d2), offset.dot(dn)))) dir1 = matrix.col(p.get_fast_axis()) dir1_new_basis = matrix.col( (dir1.dot(d1), dir1.dot(d2), dir1.dot(dn))) dir1s.append(dir1_new_basis) dir2 = matrix.col(p.get_slow_axis()) dir2_new_basis = matrix.col( (dir2.dot(d1), dir2.dot(d2), dir2.dot(dn))) dir2s.append(dir2_new_basis) # The offsets and directions in the d1, d2, dn basis are fixed # quantities, not dependent on parameter values. self._offsets = offsets self._dir1s = dir1s self._dir2s = dir2s # Set up the initial state. This is the basis d1, d2, dn. istate = {"d1": d1, "d2": d2, "dn": dn} # set up the parameters. # distance from lab origin to mid_panel plane along its normal, # in initial orientation distance = mid_panel.get_directed_distance() dist = Parameter(distance, dn, "length (mm)", "Dist") # shift in the detector model plane to locate dorg, in initial # orientation shift = dorg - dn * distance shift1 = Parameter(shift.dot(d1), d1, "length (mm)", "Shift1") shift2 = Parameter(shift.dot(d2), d2, "length (mm)", "Shift2") # rotations of the plane through its origin about: # 1) axis normal to initial orientation # 2) d1 axis of initial orientation # 3) d2 axis of initial orientation tau1 = Parameter(0, dn, "angle (mrad)", "Tau1") tau2 = Parameter(0, d1, "angle (mrad)", "Tau2") tau3 = Parameter(0, d2, "angle (mrad)", "Tau3") # build the parameter list in a specific, maintained order p_list = [dist, shift1, shift2, tau1, tau2, tau3] # set up the base class ModelParameterisation.__init__( self, detector, istate, p_list, experiment_ids=experiment_ids, is_multi_state=True, ) # call compose to calculate all the derivatives self.compose()