from math import pi import random from libtbx.test_utils import approx_equal from scitbx import matrix from dxtbx.model.experiment import beam_factory from dials.algorithms.refinement.parameterisation.beam_parameters import \ BeamParameterisation from dials.algorithms.refinement.refinement_helpers \ import get_fd_gradients, random_param_shift if __name__ == '__main__': # make a random beam vector and parameterise it bf = beam_factory() s0 = bf.make_beam(matrix.col.random(3, 0.5, 1.5), wavelength=1.2) s0p = BeamParameterisation(s0) # Let's do some basic tests. First, can we change parameter values and # update the modelled vector s0? s0_old = matrix.col(s0.get_s0()) s0p.set_param_vals([1000*0.1, 1000*0.1, 0.8]) assert(approx_equal(matrix.col(s0.get_s0()).angle(s0_old), 0.1413033)) assert(approx_equal(matrix.col(s0.get_s0()).length(), 0.8)) # random initial orientations and wavelengths with a random parameter shifts attempts = 1000 failures = 0 for i in range(attempts):
# set up a simple detector frame with directions aligned with # principal axes and sensor origin located on the z-axis at -110 d1 = matrix.col((1, 0, 0)) d2 = matrix.col((0, -1, 0)) #lim = (0,50) npx_fast = 1475 npx_slow = 1679 pix_size_f = pix_size_s = 0.172 detector = detector_factory.make_detector("PAD", d1, d2, matrix.col((0, 0, -110)), (pix_size_f, pix_size_s), (npx_fast, npx_slow), (0, 2e20)) dp = DetectorParameterisationSinglePanel(detector) beam = beam_factory().make_beam( sample_to_source=-1*(matrix.col((0, 0, -110)) + 10 * d1 + 10 * d2), wavelength=1.0) # Test change of parameters # ========================= # 1. shift detector plane so that the z-axis intercepts its centre # at a distance of 100 along the initial normal direction. As the # initial normal is along -z, we expect the frame to intercept the # z-axis at -100. p_vals = dp.get_param_vals() p_vals[0:3] = [100., 0., 0.] dp.set_param_vals(p_vals) detector = dp._model assert(len(detector) == 1)
def tst_use_in_stills_parameterisation_for_beam(beam_param=0): # test use of analytical expression in stills prediction parameterisation from scitbx import matrix from math import pi import random print print "Test use of analytical expressions in stills prediction " + "parameterisation for beam parameters" # beam model from dxtbx.model.experiment import beam_factory from dials.algorithms.refinement.parameterisation.beam_parameters import BeamParameterisation beam = beam_factory().make_beam(matrix.col((0, 0, 1)), wavelength=1.1) s0 = matrix.col(beam.get_s0()) s0u = matrix.col(beam.get_unit_s0()) # beam parameterisation bp = BeamParameterisation(beam) # choose the derivative with respect to a particular parameter. 0: mu1, # 1: mu2, 2: nu. ds0_dp = bp.get_ds_dp()[beam_param] # pick a random point on (the positive octant of) the Ewald sphere to rotate s1 = matrix.col((random.random(), random.random(), random.random())).normalize() * s0.length() r = s1 - s0 r0 = r.normalize() # calculate the axis of rotation e1 = r0.cross(s0u).normalize() # calculate c0, a vector orthogonal to s0u and e1 c0 = s0u.cross(e1).normalize() # convert to derivative of the unit beam direction. This involves scaling # by the wavelength, then projection back onto the Ewald sphere. scaled = ds0_dp * beam.get_wavelength() ds0u_dp = scaled.dot(c0) * c0 + scaled.dot(e1) * e1 # rotate relp off Ewald sphere a small angle (up to 1 deg) DeltaPsi = random.uniform(-pi / 180, pi / 180) q = r.rotate_around_origin(e1, -DeltaPsi) q0 = q.normalize() from libtbx.test_utils import approx_equal assert approx_equal(q0.cross(s0u).normalize(), e1) # use the fact that e1 == c0.cross(s0u) to redefine the derivative d[e1]/dp # from Sauter et al. (2014) (A.3) de1_dp = c0.cross(ds0u_dp) # unlike the previous definition 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 now the same :-)"
# set up a simple detector frame with directions aligned with # principal axes and sensor origin located on the z-axis at -110 d1 = matrix.col((1, 0, 0)) d2 = matrix.col((0, -1, 0)) #lim = (0,50) npx_fast = 1475 npx_slow = 1679 pix_size_f = pix_size_s = 0.172 detector = detector_factory.make_detector("PAD", d1, d2, matrix.col((0, 0, -110)), (pix_size_f, pix_size_s), (npx_fast, npx_slow), (0, 2e20)) dp = DetectorParameterisationSinglePanel(detector) beam = beam_factory().make_beam(sample_to_source=-1 * (matrix.col( (0, 0, -110)) + 10 * d1 + 10 * d2), wavelength=1.0) # Test change of parameters # ========================= # 1. shift detector plane so that the z-axis intercepts its centre # at a distance of 100 along the initial normal direction. As the # initial normal is along -z, we expect the frame to intercept the # z-axis at -100. p_vals = dp.get_param_vals() p_vals[0:3] = [100., 0., 0.] dp.set_param_vals(p_vals) detector = dp._model assert (len(detector) == 1)
def tst_use_in_stills_parameterisation_for_beam(beam_param=0): # test use of analytical expression in stills prediction parameterisation from scitbx import matrix from math import pi import random print() print("Test use of analytical expressions in stills prediction " + "parameterisation for beam parameters") # beam model from dxtbx.model.experiment import beam_factory from dials.algorithms.refinement.parameterisation.beam_parameters import ( BeamParameterisation, ) beam = beam_factory().make_beam(matrix.col((0, 0, 1)), wavelength=1.1) s0 = matrix.col(beam.get_s0()) s0u = matrix.col(beam.get_unit_s0()) # beam parameterisation bp = BeamParameterisation(beam) # choose the derivative with respect to a particular parameter. 0: mu1, # 1: mu2, 2: nu. ds0_dp = bp.get_ds_dp()[beam_param] # pick a random point on (the positive octant of) the Ewald sphere to rotate s1 = (matrix.col( (random.random(), random.random(), random.random())).normalize() * s0.length()) r = s1 - s0 r0 = r.normalize() # calculate the axis of rotation e1 = r0.cross(s0u).normalize() # calculate c0, a vector orthogonal to s0u and e1 c0 = s0u.cross(e1).normalize() # convert to derivative of the unit beam direction. This involves scaling # by the wavelength, then projection back onto the Ewald sphere. scaled = ds0_dp * beam.get_wavelength() ds0u_dp = scaled.dot(c0) * c0 + scaled.dot(e1) * e1 # rotate relp off Ewald sphere a small angle (up to 1 deg) DeltaPsi = random.uniform(-pi / 180, pi / 180) q = r.rotate_around_origin(e1, -DeltaPsi) q0 = q.normalize() from libtbx.test_utils import approx_equal assert approx_equal(q0.cross(s0u).normalize(), e1) # use the fact that e1 == c0.cross(s0u) to redefine the derivative d[e1]/dp # from Sauter et al. (2014) (A.3) de1_dp = c0.cross(ds0u_dp) # unlike the previous definition 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 now the same :-)")