def reeke_model_for_use_case(phi_beg, phi_end, margin): """Construct a reeke_model for the geometry of the Use Case Thaumatin dataset, taken from the XDS XPARM. The values are hard- coded here so that this module does not rely on the location of that file.""" axis = matrix.col([0.0, 1.0, 0.0]) # original (unrotated) setting ub = matrix.sqr([ -0.0133393674072, -0.00541609051856, -0.00367748834997, 0.00989309470346, 0.000574825936669, -0.0054505379664, 0.00475395109417, -0.0163935257377, 0.00102384915696 ]) r_beg = matrix.sqr( scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=self._axis, angle=phi_beg, deg=True)) r_osc = matrix.sqr( scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=self._axis, angle=(phi_end - phi_beg), deg=True)) ub_beg = r_beg * ub ub_end = self._r_osc * ub_mid s0 = matrix.col([0.00237878589035, 1.55544539299e-16, -1.09015329696]) dmin = 1.20117776325 return reeke_model(ub_beg, ub_end, axis, s0, dmin, margin)
def reeke_model_for_use_case(phi_beg, phi_end, margin): """Construct a reeke_model for the geometry of the Use Case Thaumatin dataset, taken from the XDS XPARM. The values are hard- coded here so that this module does not rely on the location of that file.""" axis = matrix.col([0.0, 1.0, 0.0]) # original (unrotated) setting ub = matrix.sqr( [ -0.0133393674072, -0.00541609051856, -0.00367748834997, 0.00989309470346, 0.000574825936669, -0.0054505379664, 0.00475395109417, -0.0163935257377, 0.00102384915696, ] ) r_beg = matrix.sqr(scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=self._axis, angle=phi_beg, deg=True)) r_osc = matrix.sqr( scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=self._axis, angle=(phi_end - phi_beg), deg=True) ) ub_beg = r_beg * ub ub_end = self._r_osc * ub_mid s0 = matrix.col([0.00237878589035, 1.55544539299e-16, -1.09015329696]) dmin = 1.20117776325 return reeke_model(ub_beg, ub_end, axis, s0, dmin, margin)
def generate_python(self, frame): from dials.algorithms.spot_prediction.reeke import reeke_model ub_beg, ub_end = self.get_ub(frame) r = reeke_model(ub_beg, ub_end, self.axis, self.s0, self.dmin, self.margin) hkl = r.generate_indices() hkl = [h for h in hkl if h != (0, 0, 0)] return sorted(hkl)
def regression_test(): """Perform a regression test by comparing to indices generating by the brute force method used in the Use Case.""" from rstbx.diffraction import rotation_angles from rstbx.diffraction import full_sphere_indices from cctbx.sgtbx import space_group, space_group_symbols from cctbx.uctbx import unit_cell # cubic, 50A cell, 1A radiation, 1 deg osciillation, everything ideal a = 50.0 ub_beg = matrix.sqr( (1.0 / a, 0.0, 0.0, 0.0, 1.0 / a, 0.0, 0.0, 0.0, 1.0 / a)) axis = matrix.col((0, 1, 0)) r_osc = matrix.sqr( scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=axis, angle=1.0, deg=True)) ub_end = r_osc * ub_beg uc = unit_cell((a, a, a, 90, 90, 90)) sg = space_group(space_group_symbols('P23').hall()) s0 = matrix.col((0, 0, 1)) wavelength = 1.0 dmin = 1.5 indices = full_sphere_indices(unit_cell=uc, resolution_limit=dmin, space_group=sg) ra = rotation_angles(dmin, ub_beg, wavelength, axis) obs_indices, obs_angles = ra.observed_indices_and_angles_from_angle_range( phi_start_rad=0.0 * pi / 180.0, phi_end_rad=1.0 * pi / 180.0, indices=indices) r = reeke_model(ub_beg, ub_end, axis, s0, dmin, 1.0) reeke_indices = r.generate_indices() #r.visualize_with_rgl() for oi in obs_indices: assert (tuple(map(int, oi)) in reeke_indices) #TODO Tests for an oblique cell print "OK"
def regression_test(): """Perform a regression test by comparing to indices generating by the brute force method used in the Use Case.""" from rstbx.diffraction import rotation_angles from rstbx.diffraction import full_sphere_indices from cctbx.sgtbx import space_group, space_group_symbols from cctbx.uctbx import unit_cell # cubic, 50A cell, 1A radiation, 1 deg osciillation, everything ideal a = 50.0 ub_beg = matrix.sqr((1.0 / a, 0.0, 0.0, 0.0, 1.0 / a, 0.0, 0.0, 0.0, 1.0 / a)) axis = matrix.col((0, 1, 0)) r_osc = matrix.sqr(scitbx.math.r3_rotation_axis_and_angle_as_matrix(axis=axis, angle=1.0, deg=True)) ub_end = r_osc * ub_beg uc = unit_cell((a, a, a, 90, 90, 90)) sg = space_group(space_group_symbols("P23").hall()) s0 = matrix.col((0, 0, 1)) wavelength = 1.0 dmin = 1.5 indices = full_sphere_indices(unit_cell=uc, resolution_limit=dmin, space_group=sg) ra = rotation_angles(dmin, ub_beg, wavelength, axis) obs_indices, obs_angles = ra.observed_indices_and_angles_from_angle_range( phi_start_rad=0.0 * pi / 180.0, phi_end_rad=1.0 * pi / 180.0, indices=indices ) r = reeke_model(ub_beg, ub_end, axis, s0, dmin, 1.0) reeke_indices = r.generate_indices() # r.visualize_with_rgl() for oi in obs_indices: assert tuple(map(int, oi)) in reeke_indices # TODO Tests for an oblique cell print "OK"
sys.exit( "Expecting either 3 or 4 arguments: path/to/xparm.xds start_phi end_phi margin=3" ) else: # take an xparm.xds, phi_beg, phi_end and margin from the command arguments. from rstbx.cftbx.coordinate_frame_converter import coordinate_frame_converter cfc = coordinate_frame_converter(sys.argv[1]) phi_beg, phi_end = float(sys.argv[2]), float(sys.argv[3]) margin = int(sys.argv[4]) if len(sys.argv) == 5 else 3 # test run for development/debugging. u, b = cfc.get_u_b() ub = matrix.sqr(u * b) axis = matrix.col(cfc.get("rotation_axis")) rub_beg = axis.axis_and_angle_as_r3_rotation_matrix(phi_beg) * ub rub_end = axis.axis_and_angle_as_r3_rotation_matrix(phi_end) * ub wavelength = cfc.get("wavelength") sample_to_source_vec = matrix.col( cfc.get_c("sample_to_source").normalize()) s0 = (-1.0 / wavelength) * sample_to_source_vec dmin = 1.20117776325 r = reeke_model(rub_beg, rub_end, axis, s0, dmin, margin) indices = r.generate_indices() for hkl in indices: print("%4d %4d %4d" % hkl)
from libtbx.utils import Sorry raise Sorry("Expecting either 3 or 4 arguments: path/to/xparm.xds start_phi end_phi margin=3") else: # take an xparm.xds, phi_beg, phi_end and margin from the command arguments. from rstbx.cftbx.coordinate_frame_converter import coordinate_frame_converter cfc = coordinate_frame_converter(sys.argv[1]) phi_beg, phi_end = float(sys.argv[2]), float(sys.argv[3]) margin = int(sys.argv[4]) if len(sys.argv) == 5 else 3 # test run for development/debugging. u, b = cfc.get_u_b() ub = matrix.sqr(u * b) axis = matrix.col(cfc.get("rotation_axis")) rub_beg = axis.axis_and_angle_as_r3_rotation_matrix(phi_beg) * ub rub_end = axis.axis_and_angle_as_r3_rotation_matrix(phi_end) * ub wavelength = cfc.get("wavelength") sample_to_source_vec = matrix.col(cfc.get_c("sample_to_source").normalize()) s0 = (-1.0 / wavelength) * sample_to_source_vec dmin = 1.20117776325 r = reeke_model(rub_beg, rub_end, axis, s0, dmin, margin) indices = r.generate_indices() for hkl in indices: print "%4d %4d %4d" % hkl
def tst_use_in_stills_parameterisation_for_crystal(crystal_param=0): # test use of analytical expression in stills prediction parameterisation from scitbx import matrix from math import pi, sqrt, atan2 import random print print "Test use of analytical expressions in stills prediction " + "parameterisation for crystal parameters" # crystal model from dxtbx.model.crystal import crystal_model from dials.algorithms.refinement.parameterisation.crystal_parameters import ( CrystalOrientationParameterisation, CrystalUnitCellParameterisation, ) crystal = crystal_model((20, 0, 0), (0, 30, 0), (0, 0, 40), space_group_symbol="P 1") # random reorientation e = matrix.col((random.random(), random.random(), random.random())).normalize() angle = random.random() * 180 crystal.rotate_around_origin(e, angle) wl = 1.1 s0 = matrix.col((0, 0, 1 / wl)) s0u = s0.normalize() # these are stills, but need a rotation axis for the Reeke algorithm axis = matrix.col((1, 0, 0)) # crystal parameterisations xlop = CrystalOrientationParameterisation(crystal) xlucp = CrystalUnitCellParameterisation(crystal) # Find some reflections close to the Ewald sphere from dials.algorithms.spot_prediction.reeke import reeke_model U = crystal.get_U() B = crystal.get_B() UB = U * B dmin = 4 hkl = reeke_model(UB, UB, axis, s0, dmin, margin=1).generate_indices() # choose first reflection for now, calc quantities relating to q h = matrix.col(hkl[0]) q = UB * h q0 = q.normalize() q_scalar = q.length() qq = q_scalar * q_scalar # calculate the axis of closest rotation e1 = q0.cross(s0u).normalize() # calculate c0, a vector orthogonal to s0u and e1 c0 = s0u.cross(e1).normalize() # calculate q1 q1 = q0.cross(e1).normalize() # calculate DeltaPsi a = 0.5 * qq * wl b = sqrt(qq - a * a) r = -1.0 * a * s0u + b * c0 DeltaPsi = -1.0 * atan2(r.dot(q1), r.dot(q0)) # Checks on the reflection prediction from libtbx.test_utils import approx_equal # 1. check r is on the Ewald sphere s1 = s0 + r assert approx_equal(s1.length(), s0.length()) # 2. check DeltaPsi is correct tst = q.rotate_around_origin(e1, DeltaPsi) assert approx_equal(tst, r) # choose the derivative with respect to a particular parameter. if crystal_param < 3: dU_dp = xlop.get_ds_dp()[crystal_param] dq = dU_dp * B * h else: dB_dp = xlucp.get_ds_dp()[crystal_param - 3] dq = U * dB_dp * h # NKS method of calculating d[q0]/dp q_dot_dq = q.dot(dq) dqq = 2.0 * q_dot_dq dq_scalar = dqq / q_scalar dq0_dp = (q_scalar * dq - (q_dot_dq * q0)) / qq # orthogonal to q0, as expected. print "NKS [q0].(d[q0]/dp) = {0} (should be 0.0)".format(q0.dot(dq0_dp)) # intuitive method of calculating d[q0]/dp, based on the fact that # it must be orthogonal to q0, i.e. in the plane containing q1 and e1 scaled = dq / q.length() dq0_dp = scaled.dot(q1) * q1 + scaled.dot(e1) * e1 # orthogonal to q0, as expected. print "DGW [q0].(d[q0]/dp) = {0} (should be 0.0)".format(q0.dot(dq0_dp)) # So it doesn't matter which method I use to calculate d[q0]/dp, as # both methods give the same results # use the fact that -e1 == q0.cross(q1) to redefine the derivative d[e1]/dp # from Sauter et al. (2014) (A.22) de1_dp = -1.0 * dq0_dp.cross(q1) # this *is* orthogonal to e1, as expected. print "[e1].(d[e1]/dp) = {0} (should be 0.0)".format(e1.dot(de1_dp)) # calculate (d[r]/d[e1])(d[e1]/dp) analytically from scitbx.array_family import flex from dials_refinement_helpers_ext import dRq_de dr_de1 = matrix.sqr(dRq_de(flex.double([DeltaPsi]), flex.vec3_double([e1]), flex.vec3_double([q]))[0]) print "Analytical calculation for (d[r]/d[e1])(d[e1]/dp):" print dr_de1 * de1_dp # now calculate using finite differences. dp = 1.0e-8 del_e1 = de1_dp * dp e1f = e1 + del_e1 * 0.5 rfwd = q.rotate_around_origin(e1f, DeltaPsi) e1r = e1 - del_e1 * 0.5 rrev = q.rotate_around_origin(e1r, DeltaPsi) print "Finite difference estimate for (d[r]/d[e1])(d[e1]/dp):" print (rfwd - rrev) * (1 / dp) print "These are essentially the same :-)"
def tst_use_in_stills_parameterisation_for_crystal(crystal_param=0): # test use of analytical expression in stills prediction parameterisation from scitbx import matrix from math import pi, sqrt, atan2 import random print() print("Test use of analytical expressions in stills prediction " + "parameterisation for crystal parameters") # crystal model from dxtbx.model.crystal import crystal_model from dials.algorithms.refinement.parameterisation.crystal_parameters import ( CrystalOrientationParameterisation, CrystalUnitCellParameterisation, ) crystal = crystal_model((20, 0, 0), (0, 30, 0), (0, 0, 40), space_group_symbol="P 1") # random reorientation e = matrix.col( (random.random(), random.random(), random.random())).normalize() angle = random.random() * 180 crystal.rotate_around_origin(e, angle) wl = 1.1 s0 = matrix.col((0, 0, 1 / wl)) s0u = s0.normalize() # these are stills, but need a rotation axis for the Reeke algorithm axis = matrix.col((1, 0, 0)) # crystal parameterisations xlop = CrystalOrientationParameterisation(crystal) xlucp = CrystalUnitCellParameterisation(crystal) # Find some reflections close to the Ewald sphere from dials.algorithms.spot_prediction.reeke import reeke_model U = crystal.get_U() B = crystal.get_B() UB = U * B dmin = 4 hkl = reeke_model(UB, UB, axis, s0, dmin, margin=1).generate_indices() # choose first reflection for now, calc quantities relating to q h = matrix.col(hkl[0]) q = UB * h q0 = q.normalize() q_scalar = q.length() qq = q_scalar * q_scalar # calculate the axis of closest rotation e1 = q0.cross(s0u).normalize() # calculate c0, a vector orthogonal to s0u and e1 c0 = s0u.cross(e1).normalize() # calculate q1 q1 = q0.cross(e1).normalize() # calculate DeltaPsi a = 0.5 * qq * wl b = sqrt(qq - a * a) r = -1.0 * a * s0u + b * c0 DeltaPsi = -1.0 * atan2(r.dot(q1), r.dot(q0)) # Checks on the reflection prediction from libtbx.test_utils import approx_equal # 1. check r is on the Ewald sphere s1 = s0 + r assert approx_equal(s1.length(), s0.length()) # 2. check DeltaPsi is correct tst = q.rotate_around_origin(e1, DeltaPsi) assert approx_equal(tst, r) # choose the derivative with respect to a particular parameter. if crystal_param < 3: dU_dp = xlop.get_ds_dp()[crystal_param] dq = dU_dp * B * h else: dB_dp = xlucp.get_ds_dp()[crystal_param - 3] dq = U * dB_dp * h # NKS method of calculating d[q0]/dp q_dot_dq = q.dot(dq) dqq = 2.0 * q_dot_dq dq_scalar = dqq / q_scalar dq0_dp = (q_scalar * dq - (q_dot_dq * q0)) / qq # orthogonal to q0, as expected. print("NKS [q0].(d[q0]/dp) = {0} (should be 0.0)".format(q0.dot(dq0_dp))) # intuitive method of calculating d[q0]/dp, based on the fact that # it must be orthogonal to q0, i.e. in the plane containing q1 and e1 scaled = dq / q.length() dq0_dp = scaled.dot(q1) * q1 + scaled.dot(e1) * e1 # orthogonal to q0, as expected. print("DGW [q0].(d[q0]/dp) = {0} (should be 0.0)".format(q0.dot(dq0_dp))) # So it doesn't matter which method I use to calculate d[q0]/dp, as # both methods give the same results # use the fact that -e1 == q0.cross(q1) to redefine the derivative d[e1]/dp # from Sauter et al. (2014) (A.22) de1_dp = -1.0 * dq0_dp.cross(q1) # this *is* orthogonal to e1, as expected. print("[e1].(d[e1]/dp) = {0} (should be 0.0)".format(e1.dot(de1_dp))) # calculate (d[r]/d[e1])(d[e1]/dp) analytically from scitbx.array_family import flex from dials_refinement_helpers_ext import dRq_de dr_de1 = matrix.sqr( dRq_de(flex.double([DeltaPsi]), flex.vec3_double([e1]), flex.vec3_double([q]))[0]) print("Analytical calculation for (d[r]/d[e1])(d[e1]/dp):") print(dr_de1 * de1_dp) # now calculate using finite differences. dp = 1.0e-8 del_e1 = de1_dp * dp e1f = e1 + del_e1 * 0.5 rfwd = q.rotate_around_origin(e1f, DeltaPsi) e1r = e1 - del_e1 * 0.5 rrev = q.rotate_around_origin(e1r, DeltaPsi) print("Finite difference estimate for (d[r]/d[e1])(d[e1]/dp):") print((rfwd - rrev) * (1 / dp)) print("These are essentially the same :-)")