def __init__(self, crystal, experiment_ids=None): # The state of a crystal orientation parameterisation is an orientation # matrix '[U]'. The initial state is a snapshot of the crystal # orientation at the time of initialisation '[U0]'. Future states are # composed by rotations around axes of the phi-axis frame by Tait-Bryan # angles. # # [U] = [Phi3][Phi2][Phi1][U0] # set up the initial state if experiment_ids is None: experiment_ids = [0] istate = matrix.sqr(crystal.get_U()) # build the parameter list p_list = self._build_p_list() # set up the base class ModelParameterisation.__init__(self, crystal, istate, p_list, experiment_ids=experiment_ids) # call compose to calculate all the derivatives self.compose() return
def __init__( self, model, initial_state, param_sets, smoother, experiment_ids, is_multi_state=False, ): ModelParameterisation.__init__( self, model, initial_state, param_sets, experiment_ids, is_multi_state ) self._num_sets = len(self._param) self._num_samples = len(param_sets[0]) self._total_len = self._num_samples * self._num_sets # ensure all internal parameter sets have the same number of parameters for param in self._param[1:]: assert len(param) == self._num_samples # Link up with an object that will perform the smoothing. self._smoother = smoother assert self._smoother.num_values() == self._num_samples # define an attribute for caching the variance-covariance matrix of # parameters self._var_cov = None return
def __init__(self, crystal, experiment_ids=None): # The state of the unit cell parameterisation is the reciprocal space # orthogonalisation matrix 'B'. The initial state is irrelevant for # this model, as composition of a new B matrix and derivatives can be # done with just the values of 6 unit cell parameters, without # defining axial directions (which are selected by choice of the PDB # convention). For this reason also, the axes of the # parameters are irrelevant and are set here to None. ### Set up the initial state if experiment_ids is None: experiment_ids = [0] istate = None # build the parameter list p_list = self._build_p_list(crystal) # set up the base class ModelParameterisation.__init__(self, crystal, istate, p_list, experiment_ids=experiment_ids) # call compose to calculate all the derivatives self.compose() return
def __init__(self, beam, goniometer=None, experiment_ids=None): """Initialise the BeamParameterisation object Args: beam: A dxtbx Beam object to be parameterised. goniometer: An optional dxtbx Goniometer object. Defaults to None. experiment_ids (list): The experiment IDs affected by this parameterisation. Defaults to None, which is replaced by [0]. """ # The state of the beam model consists of the s0 vector that it is # modelling. The initial state is the direction of this vector at the point # of initialisation. Future states are composed by rotations around axes # perpendicular to that direction and normalisation specified by the # wavenumber (inverse wavelength). # Set up the initial state if experiment_ids is None: experiment_ids = [0] s0 = matrix.col(beam.get_s0()) s0dir = matrix.col(beam.get_unit_s0()) istate = s0dir # build the parameter list p_list = self._build_p_list(s0, goniometer) # set up the base class ModelParameterisation.__init__( self, beam, istate, p_list, experiment_ids=experiment_ids ) # call compose to calculate all the derivatives self.compose() return
def __init__(self, goniometer, beam=None, experiment_ids=None): # The state of the goniometer model consists of the setting matrix [S] that # determines the orientation of the rotation axis in the laboratory frame # e_lab = [S] e_datum. # This parameters are orientation angles around two axes orthogonal to the # initial direction of e_lab. If a beam is supplied, one of these axes will # be within the plane containing s0 and e_lab. # Set up the initial state if experiment_ids is None: experiment_ids = [0] e_lab = matrix.col(goniometer.get_rotation_axis()) istate = matrix.sqr(goniometer.get_setting_rotation()) # build the parameter list p_list = self._build_p_list(e_lab, beam) # set up the base class ModelParameterisation.__init__(self, goniometer, istate, p_list, experiment_ids=experiment_ids) # call compose to calculate all the derivatives self.compose() return
def __init__(self, crystal, experiment_ids=None): """Initialise the CrystalOrientationParameterisation object Args: crystal: A dxtbx Crystal object to be parameterised. experiment_ids (list): The experiment IDs affected by this parameterisation. Defaults to None, which is replaced by [0]. """ # The state of a crystal orientation parameterisation is an orientation # matrix '[U]'. The initial state is a snapshot of the crystal # orientation at the time of initialisation '[U0]'. Future states are # composed by rotations around axes of the phi-axis frame by Tait-Bryan # angles. # # [U] = [Phi3][Phi2][Phi1][U0] # set up the initial state if experiment_ids is None: experiment_ids = [0] istate = matrix.sqr(crystal.get_U()) # build the parameter list p_list = self._build_p_list() # set up the base class ModelParameterisation.__init__( self, crystal, istate, p_list, experiment_ids=experiment_ids ) # call compose to calculate all the derivatives self.compose() return
def __init__(self, goniometer, beam=None, experiment_ids=None): """Initialise the GoniometerParameterisation object Args: goniometer: A dxtbx Beam object to be parameterised. beam: An optional dxtbx Beam object. Defaults to None. experiment_ids (list): The experiment IDs affected by this parameterisation. Defaults to None, which is replaced by [0]. """ # The state of the goniometer model consists of the setting matrix [S] that # determines the orientation of the rotation axis in the laboratory frame # e_lab = [S] e_datum. # This parameters are orientation angles around two axes orthogonal to the # initial direction of e_lab. If a beam is supplied, one of these axes will # be within the plane containing s0 and e_lab. # Set up the initial state if experiment_ids is None: experiment_ids = [0] e_lab = matrix.col(goniometer.get_rotation_axis()) istate = matrix.sqr(goniometer.get_setting_rotation()) # build the parameter list p_list = self._build_p_list(e_lab, beam) # set up the base class ModelParameterisation.__init__( self, goniometer, istate, p_list, experiment_ids=experiment_ids ) # call compose to calculate all the derivatives self.compose() return
def __init__(self, beam, goniometer=None, experiment_ids=None): # The state of the beam model consists of the s0 vector that it is # modelling. The initial state is the direction of this vector at the point # of initialisation. Future states are composed by rotations around axes # perpendicular to that direction and normalisation specified by the # wavenumber (inverse wavelength). # Set up the initial state if experiment_ids is None: experiment_ids = [0] s0 = matrix.col(beam.get_s0()) s0dir = matrix.col(beam.get_unit_s0()) istate = s0dir # build the parameter list p_list = self._build_p_list(s0, goniometer) # set up the base class ModelParameterisation.__init__(self, beam, istate, p_list, experiment_ids=experiment_ids) # call compose to calculate all the derivatives self.compose() return
def __init__(self, detector, experiment_ids=None): """Initialise the DetectorParameterisationSinglePanel 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]. """ # The state of a single Panel is its detector matrix d = (d1|d2|d0). # However, for the purposes of parameterisation we choose a different # vector than d0 to locate the Panel. That's because we want to perform # rotations around a point on the detector surface, and d0 points to the # corner of the Panel. To avoid excess correlations between 'tilt' and # 'twist' angles with the detector distance, we prefer to perform # rotations around a point located at the centre of the Panel. This is # usually close to point of intersection with the plane normal drawn # from the origin of the laboratory frame. # # Therefore we define: # # * a vector 'dorg' locating the centre of the single Panel # * 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 d0 of the 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 the detector objects contained in this model # # For this simplified class there is only a single Panel frame # and the vector dn is not actually required, because the plane formed # by d1 and d2 is coplanar with the sensor plane. Therefore the # offset is fully in terms of d1 and d2 # set up the initial state of the detector parameterisation from the # orientation of the single Panel it contains, in terms of the vectors # dorg, d1 and d2. if experiment_ids is None: experiment_ids = [0] dat = self._init_core(detector) # set up the base class ModelParameterisation.__init__(self, detector, dat["istate"], dat["p_list"], experiment_ids=experiment_ids) # call compose to calculate all the derivatives self.compose()
def __init__(self, model, initial_state, param_sets, smoother, experiment_ids, is_multi_state=False): ModelParameterisation.__init__(self, model, initial_state, param_sets, experiment_ids, is_multi_state) self._num_sets = len(self._param) self._set_len = len(param_sets[0]) self._total_len = self._set_len * self._num_sets # ensure all internal parameter sets have the same number of parameters for param in self._param[1:]: assert len(param) == self._set_len # Link up with an object that will perform the smoothing. self._smoother = smoother assert self._smoother.num_values() == self._set_len # define an attribute for caching the variance-covariance matrix of # parameters self._var_cov = None 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()