def test_beam_parameters(): from scitbx import matrix from dxtbx.model import BeamFactory from dials.algorithms.refinement.parameterisation.beam_parameters import ( BeamParameterisation, ) from dials.algorithms.refinement.refinement_helpers import ( get_fd_gradients, random_param_shift, ) # make a random beam vector and parameterise it bf = BeamFactory() 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 matrix.col(s0.get_s0()).angle(s0_old) == pytest.approx(0.1413033, abs=1e-6) assert matrix.col(s0.get_s0()).length() == pytest.approx(0.8, abs=1e-6) # random initial orientations and wavelengths with a random parameter shifts attempts = 1000 failures = 0 for i in range(attempts): # make a random beam vector and parameterise it s0 = bf.make_beam( matrix.col.random(3, 0.5, 1.5), wavelength=random.uniform(0.8, 1.5) ) s0p = BeamParameterisation(s0) # apply a random parameter shift p_vals = s0p.get_param_vals() p_vals = random_param_shift(p_vals, [1000 * pi / 9, 1000 * pi / 9, 0.01]) s0p.set_param_vals(p_vals) # compare analytical and finite difference derivatives an_ds_dp = s0p.get_ds_dp() fd_ds_dp = get_fd_gradients(s0p, [1.0e-5 * pi / 180, 1.0e-5 * pi / 180, 1.0e-6]) for j in range(3): try: assert list(fd_ds_dp[j] - an_ds_dp[j]) == pytest.approx( (0, 0, 0), abs=1e-6 ) except Exception: print("for try", i) print("failure for parameter number", j) print("with fd_ds_dp = ") print(fd_ds_dp[j]) print("and an_ds_dp = ") print(an_ds_dp[j]) print("so that difference fd_ds_dp - an_ds_dp =") print(fd_ds_dp[j] - an_ds_dp[j]) raise
attempts = 1000 failures = 0 for i in range(attempts): # make a random beam vector and parameterise it s0 = bf.make_beam(matrix.col.random(3, 0.5, 1.5), wavelength=random.uniform(0.8,1.5)) s0p = BeamParameterisation(s0) # apply a random parameter shift p_vals = s0p.get_param_vals() p_vals = random_param_shift(p_vals, [1000*pi/9, 1000*pi/9, 0.01]) s0p.set_param_vals(p_vals) # compare analytical and finite difference derivatives an_ds_dp = s0p.get_ds_dp() fd_ds_dp = get_fd_gradients(s0p, [1.e-5 * pi/180, 1.e-5 * pi/180, 1.e-6]) for j in range(3): try: assert(approx_equal((fd_ds_dp[j] - an_ds_dp[j]), matrix.col((0., 0., 0.)), eps = 1.e-6)) except Exception: failures += 1 print "for try", i print "failure for parameter number", j print "with fd_ds_dp = " print fd_ds_dp[j] print "and an_ds_dp = " print an_ds_dp[j] print "so that difference fd_ds_dp - an_ds_dp ="
def test_beam_parameters(): from dxtbx.model import BeamFactory from dials.algorithms.refinement.parameterisation.beam_parameters import ( BeamParameterisation, ) from dials.algorithms.refinement.refinement_helpers import ( get_fd_gradients, random_param_shift, ) # make a random beam vector and parameterise it bf = BeamFactory() 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 matrix.col(s0.get_s0()).angle(s0_old) == pytest.approx(0.1413033, abs=1e-6) assert matrix.col(s0.get_s0()).length() == pytest.approx(0.8, abs=1e-6) # random initial orientations and wavelengths with a random parameter shifts attempts = 1000 for i in range(attempts): # make a random beam vector and parameterise it sample_to_source = matrix.col.random(3, 0.5, 1.5).normalize() beam = bf.make_beam(sample_to_source, wavelength=random.uniform(0.8, 1.5)) # Ensure consistent polarization (https://github.com/cctbx/dxtbx/issues/454) beam.set_polarization_normal(sample_to_source.ortho().normalize()) s0p = BeamParameterisation(beam) # apply a random parameter shift p_vals = s0p.get_param_vals() p_vals = random_param_shift(p_vals, [1000 * pi / 9, 1000 * pi / 9, 0.01]) s0p.set_param_vals(p_vals) # compare analytical and finite difference derivatives an_ds_dp = s0p.get_ds_dp() fd_ds_dp = get_fd_gradients(s0p, [1.0e-5 * pi / 180, 1.0e-5 * pi / 180, 1.0e-6]) for j in range(3): try: assert list(fd_ds_dp[j] - an_ds_dp[j]) == pytest.approx( (0, 0, 0), abs=1e-6 ) except Exception: print("for try", i) print("failure for parameter number", j) print("with fd_ds_dp = ") print(fd_ds_dp[j]) print("and an_ds_dp = ") print(an_ds_dp[j]) print("so that difference fd_ds_dp - an_ds_dp =") print(fd_ds_dp[j] - an_ds_dp[j]) raise # Ensure the polarization normal vector remains orthogonal to the beam # (https://github.com/dials/dials/issues/1939) assert ( abs( matrix.col(beam.get_unit_s0()).dot( matrix.col(beam.get_polarization_normal()) ) ) < 1e-10 )
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 :-)"
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 :-)")