def __init__(self, crystal, experiment_ids=[0]): # 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 istate = None ### Set up symmetrizing object self._S = symmetrize_reduce_enlarge(crystal.get_space_group()) self._S.set_orientation(orientation=crystal.get_B()) X = self._S.forward_independent_parameters() dB_dp = self._S.forward_gradients() B = self._S.backward_orientation(independent=X).reciprocal_matrix() ### Set up the independent parameters, with a change of scale p_list = [Parameter(e * 1.e5, name = "g_param_%d" % i) \ for i, e in enumerate(X)] # 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): # 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, 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, crystal, experiment_ids=[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 istate = crystal.get_U() ### Set up the parameters phi1 = Parameter(.0, matrix.col((1, 0, 0)), 'angle (mrad)', 'Phi1') phi2 = Parameter(.0, matrix.col((0, 1, 0)), 'angle (mrad)', 'Phi2') phi3 = Parameter(.0, matrix.col((0, 0, 1)), 'angle (mrad)', 'Phi3') # build the parameter list in a specific, maintained order p_list = [phi1, phi2, phi3] # 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, 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 = 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, detector, experiment_ids=None): # 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() return
def __init__(self, beam, goniometer=None, experiment_ids=[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). # # The 'models' attribute refers to the beam vector contained by this # model. ### Set up the initial state s0 = matrix.col(beam.get_s0()) s0dir = matrix.col(beam.get_unit_s0()) istate = s0dir ### Set up the parameters if goniometer: spindle = matrix.col(goniometer.get_rotation_axis()) s0_plane_dir2 = s0.cross(spindle).normalize() s0_plane_dir1 = s0_plane_dir2.cross(s0).normalize() else: s0_plane_dir1 = s0.ortho().normalize() s0_plane_dir2 = s0.cross(s0_plane_dir1).normalize() # rotation around s0_plane_dir1 mu1 = Parameter(.0, s0_plane_dir1, 'angle (mrad)', 'Mu1') # rotation around s0_plane_dir2 mu2 = Parameter(.0, s0_plane_dir2, 'angle (mrad)', 'Mu2') # length of s0 nu = Parameter(s0.length(), ptype='wavenumber (Angstroem^-1)', name='nu') # build the parameter list in a specific, maintained order p_list = [mu1, mu2, nu] # 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): # the state of the detector model is comprised of: # # * a vector 'dorg' locating the origin of the detector model # * two 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 sensor 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 sensor objects contained in this model # # For this simplified class there is only a single sensor frame # and the vector dn is not 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 model from the # orientation of the single sensor it contains # get some vectors we need from the Panel panel = detector[0] so = matrix.col(panel.get_origin()) d1 = matrix.col(panel.get_fast_axis()) d2 = matrix.col(panel.get_slow_axis()) dn = matrix.col(panel.get_normal()) # we choose the dorg vector to terminate in the centre of the # sensor, and the offset between the end of the dorg vector and # the sensor origin is a coordinate matrix with elements in the # basis d1, d2, dn panel_lim = panel.get_image_size_mm() offset = matrix.col( (-1.0 * panel_lim[0] / 2.0, -1.0 * panel_lim[1] / 2.0, 0.0)) dorg = so - offset[0] * d1 - offset[1] * d2 # Set up the initial state. There are multiple items of interest, so # use a dictionary here (note for a single sensor model we can do # without the first 3 of these, but will need them for multiple sensors) istate = {"d1": d1, "d2": d2, "dn": dn, "offset": offset} # set up the parameters. # distance from lab origin to detector model plane along its # normal, in initial orientation distance = panel.get_directed_distance() dist = Parameter(distance, dn, "length") # shift in the detector model plane to locate dorg, in initial # orientation shift = dorg - dn * distance shift1 = Parameter(shift.dot(d1), d1, "length") shift2 = Parameter(shift.dot(d2), d2, "length") # 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") tau2 = Parameter(0, d1, "angle") tau3 = Parameter(0, d2, "angle") # 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) # call compose to calculate all the derivatives self.compose()
def __init__(self, detector): # the state of the detector model is comprised of: # # * a vector 'dorg' locating the origin of the detector model # * two 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 sensor 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 sensor objects contained in this model # # For this simplified class there is only a single sensor frame # and the vector dn is not 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 model from the # orientation of the single sensor it contains # get some vectors we need from the Panel panel = detector[0] so = matrix.col(panel.get_origin()) d1 = matrix.col(panel.get_fast_axis()) d2 = matrix.col(panel.get_slow_axis()) dn = matrix.col(panel.get_normal()) # we choose the dorg vector to terminate in the centre of the # sensor, and the offset between the end of the dorg vector and # the sensor origin is a coordinate matrix with elements in the # basis d1, d2, dn panel_lim = panel.get_image_size_mm() offset = matrix.col((-1. * panel_lim[0] / 2., -1. * panel_lim[1] / 2., 0.)) dorg = so - offset[0] * d1 - offset[1] * d2 # Set up the initial state. There are multiple items of interest, so # use a dictionary here (note for a single sensor model we can do # without the first 3 of these, but will need them for multiple sensors) istate = {'d1':d1, 'd2':d2, 'dn':dn, 'offset':offset} # set up the parameters. # distance from lab origin to detector model plane along its # normal, in initial orientation distance = panel.get_directed_distance() dist = Parameter(distance, dn, 'length') # shift in the detector model plane to locate dorg, in initial # orientation shift = dorg - dn * distance shift1 = Parameter(shift.dot(d1), d1, 'length') shift2 = Parameter(shift.dot(d2), d2, 'length') # 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') tau2 = Parameter(0, d1, 'angle') tau3 = Parameter(0, d2, 'angle') # 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) # call compose to calculate all the derivatives self.compose()
def __init__(self, detector, experiment_ids=None, level=0): """The additional 'level' argument selects which level of the detector hierarchy is chosen 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={0}".format(level) raise # collect the panel ids for each Panel within the groups panels = [p for p in 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_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.)) # 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{0}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{0}Shift1'.format(igp + 1)) shift2 = Parameter(shift.dot(d2), d2, 'length (mm)', 'Group{0}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{0}Tau1'.format(igp + 1)) tau2 = Parameter(0, d1, 'angle (mrad)', 'Group{0}Tau2'.format(igp + 1)) tau3 = Parameter(0, d2, 'angle (mrad)', 'Group{0}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() return
def __init__(self, detector, beam, experiment_ids=None): # 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() return