def test_num_intervals(self, nintervals): """Test a range of different numbers of intervals""" # Parameterise the detector with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50 det_p = TestDetectorModel(50, self.detector, self.image_range, nintervals) # How many parameters? num_param = det_p.num_free() # apply a random parameter shift to the detector, on order of 2% of # the initial values p_vals = det_p.get_param_vals() sigmas = [0.02 * p for p in p_vals] new_vals = random_param_shift(p_vals, sigmas) det_p.set_param_vals(new_vals) # calculate state and gradients at image 50 det_p.compose() null_mat = matrix.sqr((0., 0., 0., 0., 0., 0., 0., 0., 0.)) # compare analytical and finite difference derivatives at image 50 an_ds_dp = det_p.get_ds_dp() fd_ds_dp = get_fd_gradients(det_p, [1.e-7] * num_param) param_names = det_p.get_param_names() for e, f in zip(an_ds_dp, fd_ds_dp): assert(approx_equal((e - f), null_mat, eps = 1.e-6)) print "OK" return
def test_ScanVaryingDetectorParameterisation(nintervals, plots=False): """Basic test of a ScanVaryingDetectorParameterisationSinglePanel with a range of different numbers of intervals""" vmp = _TestScanVaryingModelParameterisation() # Parameterise the detector with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50 det_p = _TestDetectorModel(50, vmp.detector, vmp.image_range, nintervals) # How many parameters? num_param = det_p.num_free() # apply a random parameter shift to the detector, on order of 2% of # the initial values p_vals = det_p.get_param_vals() sigmas = [0.02 * p for p in p_vals] new_vals = random_param_shift(p_vals, sigmas) det_p.set_param_vals(new_vals) # calculate state and gradients at image 50 det_p.compose() null_mat = matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) # compare analytical and finite difference derivatives at image 50 an_ds_dp = det_p.get_ds_dp() fd_ds_dp = get_fd_gradients(det_p, [1.0e-7] * num_param) for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.0e-6)
def test_ScanVaryingCrystalOrientationParameterisation_random(plots=False): """Test a ScanVaryingCrystalOrientationParameterisation with random initial orientations, random parameter shifts and random times""" vmp = _TestScanVaryingModelParameterisation() attempts = 100 null_mat = matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) for i in range(attempts): # make a new P1 random crystal and parameterise it a = random.uniform(10, 50) * vmp.random_direction_close_to( matrix.col((1, 0, 0))) b = random.uniform(10, 50) * vmp.random_direction_close_to( matrix.col((0, 1, 0))) c = random.uniform(10, 50) * vmp.random_direction_close_to( matrix.col((0, 0, 1))) xl = Crystal(a, b, c, space_group_symbol="P 1") xl_op = _TestOrientationModel(50, xl, vmp.image_range, 5) # How many parameters? num_param = xl_op.num_free() # apply random parameter shifts to the orientation (2.0 mrad each # checkpoint) p_vals = xl_op.get_param_vals() sigmas = [2.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xl_op.set_param_vals(new_vals) # select random time point at which to make comparisons t = random.uniform(*vmp.image_range) xl_op.set_time_point(t) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.0e-6 * math.pi / 180] * num_param) for j in range(num_param): assert approx_equal((xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j]), null_mat, eps=1.0e-6), textwrap.dedent("""\ Failure in try {i} failure for parameter number {j} of the orientation parameterisation with fd_ds_dp = {fd} and an_ds_dp = {an} so that difference fd_ds_dp - an_ds_dp = {diff} """).format( i=i, j=j, fd=xl_op_fd_ds_dp[j], an=xl_op_an_ds_dp[j], diff=xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j], )
def test_ScanVaryingBeamParameterisation(nintervals, plots=False): """Basic test of a ScanVaryingBeamParameterisation with a range of different numbers of intervals""" vmp = _TestScanVaryingModelParameterisation() # Parameterise the crystal with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50 beam_p = _TestBeamModel(50, vmp.beam, vmp.image_range, nintervals, vmp.goniometer) # How many parameters? num_param = beam_p.num_free() # apply a random parameter shift to the beam, on order of 2% of # the initial values p_vals = beam_p.get_param_vals() sigmas = [0.02 * p for p in p_vals] new_vals = random_param_shift(p_vals, sigmas) beam_p.set_param_vals(new_vals) # calculate state and gradients at image 50 beam_p.compose() null_vec = matrix.col((0., 0., 0.)) # compare analytical and finite difference derivatives at image 50 an_ds_dp = beam_p.get_ds_dp() fd_ds_dp = get_fd_gradients(beam_p, [1.e-7] * num_param) param_names = beam_p.get_param_names() for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_vec, eps=1.e-6)
def test_ScanVaryingCrystalUnitCellParameterisation_intervals( nintervals, plots=False): """Basic test of a ScanVaryingCrystalUnitCellParameterisation with a range of different numbers of intervals""" vmp = _TestScanVaryingModelParameterisation() # Parameterise the crystal with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50 xl_ucp = _TestUnitCellModel(50, vmp.xl, vmp.image_range, nintervals) # How many parameters? num_param = xl_ucp.num_free() # apply a random parameter shift to the unit cell, on order of 2% of # the initial metrical matrix parameters p_vals = xl_ucp.get_param_vals() sigmas = [0.02 * p for p in p_vals] new_vals = random_param_shift(p_vals, sigmas) xl_ucp.set_param_vals(new_vals) # calculate state and gradients at image 50 xl_ucp.compose() null_mat = matrix.sqr((0., 0., 0., 0., 0., 0., 0., 0., 0.)) # compare analytical and finite difference derivatives at image 50 an_ds_dp = xl_ucp.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_ucp, [1.e-7] * num_param) param_names = xl_ucp.get_param_names() for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.e-6)
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
def test_random(self): """Test random initial orientations, random parameter shifts and random times""" attempts = 100 failures = 0 null_mat = matrix.sqr((0., 0., 0., 0., 0., 0., 0., 0., 0.)) for i in range(attempts): # make a new P1 random crystal and parameterise it a = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((1, 0, 0))) b = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((0, 1, 0))) c = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((0, 0, 1))) xl = Crystal(a, b, c, space_group_symbol="P 1") xl_op = TestOrientationModel(50, xl, self.image_range, 5) # How many parameters? num_param = xl_op.num_free() # apply random parameter shifts to the orientation (2.0 mrad each # checkpoint) p_vals = xl_op.get_param_vals() sigmas = [2.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xl_op.set_param_vals(new_vals) # select random time point at which to make comparisons t = random.uniform(*self.image_range) xl_op.set_time_point(t) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.e-6 * pi/180] * \ num_param) for j in range(num_param): try: assert (approx_equal( (xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j]), null_mat, eps=1.e-6)) except Exception: failures += 1 print "for try", i print "failure for parameter number", j print "of the orientation parameterisation" print "with fd_ds_dp = " print xl_op_fd_ds_dp[j] print "and an_ds_dp = " print xl_op_an_ds_dp[j] print "so that difference fd_ds_dp - an_ds_dp =" print xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j] if failures == 0: print "OK"
def test_random(self): """Test random initial orientations, random parameter shifts and random times""" attempts = 100 failures = 0 null_mat = matrix.sqr((0., 0., 0., 0., 0., 0., 0., 0., 0.)) for i in range(attempts): # make a new P1 random crystal and parameterise it a = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((1, 0, 0))) b = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((0, 1, 0))) c = random.uniform(10,50) * \ self.random_direction_close_to(matrix.col((0, 0, 1))) xl = crystal_model(a, b, c, space_group_symbol="P 1") xl_op = TestOrientationModel(50, xl, self.image_range, 5) # How many parameters? num_param = xl_op.num_free() # apply random parameter shifts to the orientation (2.0 mrad each # checkpoint) p_vals = xl_op.get_param_vals() sigmas = [2.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xl_op.set_param_vals(new_vals) # select random time point at which to make comparisons t = random.uniform(*self.image_range) xl_op.set_time_point(t) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.e-6 * pi/180] * \ num_param) for j in range(num_param): try: assert(approx_equal((xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j]), null_mat, eps = 1.e-6)) except Exception: failures += 1 print "for try", i print "failure for parameter number", j print "of the orientation parameterisation" print "with fd_ds_dp = " print xl_op_fd_ds_dp[j] print "and an_ds_dp = " print xl_op_an_ds_dp[j] print "so that difference fd_ds_dp - an_ds_dp =" print xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j] if failures == 0: print "OK"
def test(): goniometer = random_gonio() gonp = GoniometerParameterisation(goniometer) # Let's do some basic tests. First, can we change parameter values and # update the laboratory frame rotation axis? e_lab = matrix.col(goniometer.get_rotation_axis()) gonp.set_param_vals([1000 * 0.1, 1000 * 0.1]) assert matrix.col(goniometer.get_rotation_axis()).angle( e_lab) == pytest.approx(0.1413033) # random goniometers and random parameter shifts attempts = 1000 for i in range(attempts): # make a random goniometer and parameterise it goniometer = random_gonio() gonp = GoniometerParameterisation(goniometer) # apply a random parameter shift p_vals = gonp.get_param_vals() p_vals = random_param_shift(p_vals, [1000 * math.pi / 9, 1000 * math.pi / 9]) gonp.set_param_vals(p_vals) # compare analytical and finite difference derivatives an_ds_dp = gonp.get_ds_dp() fd_ds_dp = get_fd_gradients( gonp, [1.0e-5 * math.pi / 180, 1.0e-5 * math.pi / 180]) null_mat = matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) for j in range(2): try: assert approx_equal((fd_ds_dp[j] - an_ds_dp[j]), null_mat, eps=1.0e-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
for i in range(attempts): # make a random P1 crystal and parameterise it a = random.uniform(10, 50) * random_direction_close_to( matrix.col((1, 0, 0))) b = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 1, 0))) c = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 0, 1))) xl = crystal_model(a, b, c, space_group_symbol="P 1") xl_op = CrystalOrientationParameterisation(xl) xl_uc = CrystalUnitCellParameterisation(xl) # apply a random parameter shift to the orientation p_vals = xl_op.get_param_vals() p_vals = random_param_shift( p_vals, [1000 * pi / 9, 1000 * pi / 9, 1000 * pi / 9]) xl_op.set_param_vals(p_vals) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.e-5 * pi / 180] * 3) # apply a random parameter shift to the unit cell. We have to # do this in a way that is respectful to metrical constraints, # so don't modify the parameters directly; modify the cell # constants and extract the new parameters cell_params = xl.get_unit_cell().parameters() cell_params = random_param_shift(cell_params, [1.] * 6) new_uc = unit_cell(cell_params) newB = matrix.sqr(new_uc.fractionalization_matrix()).transpose() S = symmetrize_reduce_enlarge(xl.get_space_group())
# random initial orientations with a random parameter shift at each attempts = 100 failures = 0 for i in range(attempts): # make a random P1 crystal and parameterise it a = random.uniform(10,50) * random_direction_close_to(matrix.col((1, 0, 0))) b = random.uniform(10,50) * random_direction_close_to(matrix.col((0, 1, 0))) c = random.uniform(10,50) * random_direction_close_to(matrix.col((0, 0, 1))) xl = crystal_model(a, b, c, space_group_symbol="P 1") xl_op = CrystalOrientationParameterisation(xl) xl_uc = CrystalUnitCellParameterisation(xl) # apply a random parameter shift to the orientation p_vals = xl_op.get_param_vals() p_vals = random_param_shift(p_vals, [1000*pi/9, 1000*pi/9, 1000*pi/9]) xl_op.set_param_vals(p_vals) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.e-5 * pi/180] * 3) # apply a random parameter shift to the unit cell. We have to # do this in a way that is respectful to metrical constraints, # so don't modify the parameters directly; modify the cell # constants and extract the new parameters cell_params = xl.get_unit_cell().parameters() cell_params = random_param_shift(cell_params, [1.] * 6) new_uc = unit_cell(cell_params) newB = matrix.sqr(new_uc.fractionalization_matrix()).transpose() S = symmetrize_reduce_enlarge(xl.get_space_group())
def test_num_intervals(self, nintervals): """Test a range of different numbers of intervals""" # Parameterise the crystal with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50, but actually # will try various time points in the test xl_op = TestOrientationModel(50, self.xl, self.image_range, nintervals) # How many parameters? num_param = xl_op.num_free() # shift the parameters away from zero p_vals = xl_op.get_param_vals() sigmas = [1.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xl_op.set_param_vals(new_vals) # recalc state and gradients at image 50 xl_op.compose() p_vals = xl_op.get_param_vals() #print "Shifted parameter vals", p_vals # compare analytical and finite difference derivatives at image 50 an_ds_dp = xl_op.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_op, [1.e-6 * pi/180] * num_param) param_names = xl_op.get_param_names() null_mat = matrix.sqr((0., 0., 0., 0., 0., 0., 0., 0., 0.)) for e, f in zip(an_ds_dp, fd_ds_dp): assert(approx_equal((e - f), null_mat, eps = 1.e-6)) # Now test gradients at equally spaced time points across the whole # range num_points = 50 smooth_at = [] phi1_data = [] phi2_data = [] phi3_data = [] step_size = (self.image_range[1] - self.image_range[0]) / num_points for t in [self.image_range[0] + e * step_size \ for e in range(num_points + 1)]: # collect data for plot smooth_at.append(t) phi1_data.append(xl_op._smoother.value_weight(t, xl_op._param[0])[0]) phi2_data.append(xl_op._smoother.value_weight(t, xl_op._param[1])[0]) phi3_data.append(xl_op._smoother.value_weight(t, xl_op._param[2])[0]) xl_op.set_time_point(t) an_ds_dp = xl_op.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_op, [1.e-6 * pi/180] * num_param) #print t #print "Gradients:" #for s, a, f in zip(param_names, an_ds_dp, fd_ds_dp): # print s # print a # print f # print "diff:", a-f # print # for e, f in zip(an_ds_dp, fd_ds_dp): assert(approx_equal((e - f), null_mat, eps = 1.e-6)) if self.do_plots: try: import matplotlib.pyplot as plt plt.ion() plt.clf() plt.subplot(311) plt.cla() plt.scatter(smooth_at, phi1_data) plt.title("Phi1") plt.xlabel("image number") plt.ylabel("Phi1 (mrad)") plt.subplot(312) plt.cla() plt.scatter(smooth_at, phi2_data) plt.title("Phi2") plt.xlabel("image number") plt.ylabel("Phi2 (mrad)") plt.subplot(313) plt.cla() plt.scatter(smooth_at, phi3_data) plt.title("Phi3") plt.xlabel("image number") plt.ylabel("Phi3 (mrad)") plt.suptitle("Parameter smoothing with %d intervals" % nintervals) plt.draw() except ImportError as e: print "pyplot not available", e print "OK"
def test_ScanVaryingCrystalOrientationParameterisation_intervals( nintervals, plots=False): """Test a ScanVaryingCrystalOrientationParameterisation with a range of different numbers of intervals""" vmp = _TestScanVaryingModelParameterisation() # Parameterise the crystal with the image range and five intervals. Init # TestOrientationModel to explore gradients at image 50, but actually # will try various time points in the test xl_op = _TestOrientationModel(50, vmp.xl, vmp.image_range, nintervals) # How many parameters? num_param = xl_op.num_free() # shift the parameters away from zero p_vals = xl_op.get_param_vals() sigmas = [1.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xl_op.set_param_vals(new_vals) # recalc state and gradients at image 50 xl_op.compose() p_vals = xl_op.get_param_vals() # print "Shifted parameter vals", p_vals # compare analytical and finite difference derivatives at image 50 an_ds_dp = xl_op.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_op, [1.0e-6 * math.pi / 180] * num_param) null_mat = matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.0e-6) # Now test gradients at equally spaced time points across the whole # range num_points = 50 smooth_at = [] phi1_data = [] phi2_data = [] phi3_data = [] step_size = (vmp.image_range[1] - vmp.image_range[0]) / num_points for t in [ vmp.image_range[0] + e * step_size for e in range(num_points + 1) ]: # collect data for plot smooth_at.append(t) phi1_data.append(xl_op._smoother.value_weight(t, xl_op._param[0])[0]) phi2_data.append(xl_op._smoother.value_weight(t, xl_op._param[1])[0]) phi3_data.append(xl_op._smoother.value_weight(t, xl_op._param[2])[0]) xl_op.set_time_point(t) an_ds_dp = xl_op.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_op, [1.0e-6 * math.pi / 180] * num_param) for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.0e-6) if plots: import matplotlib.pyplot as plt plt.ion() plt.clf() plt.subplot(311) plt.cla() plt.scatter(smooth_at, phi1_data) plt.title("Phi1") plt.xlabel("image number") plt.ylabel("Phi1 (mrad)") plt.subplot(312) plt.cla() plt.scatter(smooth_at, phi2_data) plt.title("Phi2") plt.xlabel("image number") plt.ylabel("Phi2 (mrad)") plt.subplot(313) plt.cla() plt.scatter(smooth_at, phi3_data) plt.title("Phi3") plt.xlabel("image number") plt.ylabel("Phi3 (mrad)") plt.suptitle("Parameter smoothing with %d intervals" % nintervals) plt.draw()
# Now using parameterisation in mrad # random initial orientations with a random parameter shift at each attempts = 100 failures = 0 for i in range(attempts): # create random initial position det = Detector(random_panel()) dp = DetectorParameterisationSinglePanel(det) # apply a random parameter shift p_vals = dp.get_param_vals() p_vals = random_param_shift( p_vals, [10, 10, 10, 1000. * pi / 18, 1000. * pi / 18, 1000. * pi / 18]) dp.set_param_vals(p_vals) # obtain current sensor state #state = dp.get_state() # compare analytical and finite difference derivatives. an_ds_dp = dp.get_ds_dp(multi_state_elt=0) fd_ds_dp = get_fd_gradients(dp, [1.e-6] * 3 + [1.e-4 * pi / 180] * 3) for j in range(6): try: assert (approx_equal((fd_ds_dp[j] - an_ds_dp[j]), matrix.sqr( (0., 0., 0., 0., 0., 0., 0., 0., 0.)),
def test(): import random import textwrap from cctbx.uctbx import unit_cell from libtbx.test_utils import approx_equal def random_direction_close_to(vector): return vector.rotate_around_origin( matrix.col((random.random(), random.random(), random.random())).normalize(), random.gauss(0, 1.0), deg=True, ) # make a random P1 crystal and parameterise it a = random.uniform(10, 50) * random_direction_close_to( matrix.col((1, 0, 0))) b = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 1, 0))) c = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 0, 1))) xl = Crystal(a, b, c, space_group_symbol="P 1") xl_op = CrystalOrientationParameterisation(xl) xl_ucp = CrystalUnitCellParameterisation(xl) null_mat = matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) # compare analytical and finite difference derivatives an_ds_dp = xl_op.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_op, [1.0e-6 * pi / 180] * 3) for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.0e-6) an_ds_dp = xl_ucp.get_ds_dp() fd_ds_dp = get_fd_gradients(xl_ucp, [1.0e-7] * xl_ucp.num_free()) for e, f in zip(an_ds_dp, fd_ds_dp): assert approx_equal((e - f), null_mat, eps=1.0e-6) # random initial orientations with a random parameter shift at each attempts = 100 for i in range(attempts): # make a random P1 crystal and parameterise it a = random.uniform(10, 50) * random_direction_close_to( matrix.col((1, 0, 0))) b = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 1, 0))) c = random.uniform(10, 50) * random_direction_close_to( matrix.col((0, 0, 1))) xl = Crystal(a, b, c, space_group_symbol="P 1") xl_op = CrystalOrientationParameterisation(xl) xl_uc = CrystalUnitCellParameterisation(xl) # apply a random parameter shift to the orientation p_vals = xl_op.get_param_vals() p_vals = random_param_shift( p_vals, [1000 * pi / 9, 1000 * pi / 9, 1000 * pi / 9]) xl_op.set_param_vals(p_vals) # compare analytical and finite difference derivatives xl_op_an_ds_dp = xl_op.get_ds_dp() xl_op_fd_ds_dp = get_fd_gradients(xl_op, [1.0e-5 * pi / 180] * 3) # apply a random parameter shift to the unit cell. We have to # do this in a way that is respectful to metrical constraints, # so don't modify the parameters directly; modify the cell # constants and extract the new parameters cell_params = xl.get_unit_cell().parameters() cell_params = random_param_shift(cell_params, [1.0] * 6) new_uc = unit_cell(cell_params) newB = matrix.sqr(new_uc.fractionalization_matrix()).transpose() S = symmetrize_reduce_enlarge(xl.get_space_group()) S.set_orientation(orientation=newB) X = S.forward_independent_parameters() xl_uc.set_param_vals(X) xl_uc_an_ds_dp = xl_ucp.get_ds_dp() # now doing finite differences about each parameter in turn xl_uc_fd_ds_dp = get_fd_gradients(xl_ucp, [1.0e-7] * xl_ucp.num_free()) for j in range(3): assert approx_equal((xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j]), null_mat, eps=1.0e-6), textwrap.dedent("""\ Failure in try {i} failure for parameter number {j} of the orientation parameterisation with fd_ds_dp = {fd} and an_ds_dp = {an} so that difference fd_ds_dp - an_ds_dp = {diff} """).format( i=i, j=j, fd=xl_op_fd_ds_dp[j], an=xl_op_an_ds_dp[j], diff=xl_op_fd_ds_dp[j] - xl_op_an_ds_dp[j], ) for j in range(xl_ucp.num_free()): assert approx_equal((xl_uc_fd_ds_dp[j] - xl_uc_an_ds_dp[j]), null_mat, eps=1.0e-6), textwrap.dedent("""\ Failure in try {i} failure for parameter number {j} of the unit cell parameterisation with fd_ds_dp = {fd} and an_ds_dp = {an} so that difference fd_ds_dp - an_ds_dp = {diff} """).format( i=i, j=j, fd=xl_uc_fd_ds_dp[j], an=xl_uc_an_ds_dp[j], diff=xl_uc_fd_ds_dp[j] - xl_uc_an_ds_dp[j], )
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): # 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 = "
# Create parameterisations of these models, with 5 samples for the # scan-varying crystal parameterisations det_param = DetectorParameterisationSinglePanel(mydetector) s0_param = BeamParameterisation(mybeam, mygonio) xlo_param = ScanVaryingCrystalOrientationParameterisation( mycrystal, myscan.get_array_range(), 5) xluc_param = ScanVaryingCrystalUnitCellParameterisation( mycrystal, myscan.get_array_range(), 5) #### Cause the crystal U and B to vary over the scan # Vary orientation angles by ~1.0 mrad each checkpoint p_vals = xlo_param.get_param_vals() sigmas = [1.0] * len(p_vals) new_vals = random_param_shift(p_vals, sigmas) xlo_param.set_param_vals(new_vals) # Vary unit cell parameters, on order of 1% of the initial metrical # matrix parameters p_vals = xluc_param.get_param_vals() sigmas = [0.01 * p for p in p_vals] new_vals = random_param_shift(p_vals, sigmas) xluc_param.set_param_vals(new_vals) # Generate an ExperimentList experiments = ExperimentList() experiments.append(Experiment( beam=mybeam, detector=mydetector, goniometer=mygonio, scan=myscan, crystal=mycrystal, imageset=None)) sweep_range = myscan.get_oscillation_range(deg=False)
# 5. Tests of the calculation of derivatives # Now using parameterisation in mrad # random initial orientations with a random parameter shift at each attempts = 100 failures = 0 for i in range(attempts): # create random initial position det = Detector(random_panel()) dp = DetectorParameterisationSinglePanel(det) # apply a random parameter shift p_vals = dp.get_param_vals() p_vals = random_param_shift(p_vals, [10, 10, 10, 1000.*pi/18, 1000.*pi/18, 1000.*pi/18]) dp.set_param_vals(p_vals) # obtain current sensor state #state = dp.get_state() # compare analytical and finite difference derivatives. an_ds_dp = dp.get_ds_dp(multi_state_elt=0) fd_ds_dp = get_fd_gradients(dp, [1.e-6] * 3 + [1.e-4 * pi/180] * 3) for j in range(6): try: assert(approx_equal((fd_ds_dp[j] - an_ds_dp[j]), matrix.sqr((0., 0., 0., 0., 0., 0.,
def test(): # set the random seed to make the test reproducible random.seed(1337) # 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 = DetectorFactory.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 = BeamFactory().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.0, 0.0] dp.set_param_vals(p_vals) detector = dp._model assert len(detector) == 1 panel = detector[0] v1 = matrix.col(panel.get_origin()) v2 = matrix.col((0.0, 0.0, 1.0)) assert approx_equal(v1.dot(v2), -100.0) # 2. rotate frame around its initial normal by +90 degrees. Only d1 # and d2 should change. As we rotate clockwise around the initial # normal (-z direction) then d1 should rotate onto the original # direction d2, and d2 should rotate to negative of the original # direction d1 p_vals[3] = 1000.0 * pi / 2 # set tau1 value dp.set_param_vals(p_vals) detector = dp._model assert len(detector) == 1 panel = detector[0] assert approx_equal( matrix.col(panel.get_fast_axis()).dot(dp._initial_state["d1"]), 0.0) assert approx_equal( matrix.col(panel.get_slow_axis()).dot(dp._initial_state["d2"]), 0.0) assert approx_equal( matrix.col(panel.get_normal()).dot(dp._initial_state["dn"]), 1.0) # 3. no rotation around initial normal, +10 degrees around initial # d1 direction and +10 degrees around initial d2. Check d1 and d2 # match paper calculation p_vals[3] = 0.0 # tau1 p_vals[4] = 1000.0 * pi / 18 # tau2 p_vals[5] = 1000.0 * pi / 18 # tau3 dp.set_param_vals(p_vals) # paper calculation values v1 = matrix.col((cos(pi / 18), 0, sin(pi / 18))) v2 = matrix.col(( sin(pi / 18)**2, -cos(pi / 18), sqrt((2 * sin(pi / 36) * sin(pi / 18))**2 - sin(pi / 18)**4) - sin(pi / 18), )) detector = dp._model assert len(detector) == 1 panel = detector[0] assert approx_equal(matrix.col(panel.get_fast_axis()).dot(v1), 1.0) assert approx_equal(matrix.col(panel.get_slow_axis()).dot(v2), 1.0) # 4. Test fixing and unfixing of parameters p_vals = [ 100.0, 0.0, 0.0, 1000.0 * pi / 18, 1000.0 * pi / 18, 1000.0 * pi / 18 ] dp.set_param_vals(p_vals) f = dp.get_fixed() f[0:3] = [True] * 3 dp.set_fixed(f) p_vals2 = [0.0, 0.0, 0.0] dp.set_param_vals(p_vals2) assert dp.get_param_vals(only_free=False) == [ 100.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] an_ds_dp = dp.get_ds_dp() assert len(an_ds_dp) == 3 f[0:3] = [False] * 3 dp.set_fixed(f) p_vals = dp.get_param_vals() p_vals2 = [a + b for a, b in zip(p_vals, [-10.0, 1.0, 1.0, 0.0, 0.0, 0.0])] dp.set_param_vals(p_vals2) assert dp.get_param_vals() == [90.0, 1.0, 1.0, 0.0, 0.0, 0.0] # 5. Tests of the calculation of derivatives # Now using parameterisation in mrad # random initial orientations with a random parameter shift at each attempts = 100 for i in range(attempts): # create random initial position det = Detector(random_panel()) dp = DetectorParameterisationSinglePanel(det) # apply a random parameter shift p_vals = dp.get_param_vals() p_vals = random_param_shift( p_vals, [10, 10, 10, 1000.0 * pi / 18, 1000.0 * pi / 18, 1000.0 * pi / 18]) dp.set_param_vals(p_vals) # compare analytical and finite difference derivatives. an_ds_dp = dp.get_ds_dp(multi_state_elt=0) fd_ds_dp = get_fd_gradients(dp, [1.0e-6] * 3 + [1.0e-4 * pi / 180] * 3) for j in range(6): assert approx_equal( (fd_ds_dp[j] - an_ds_dp[j]), matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)), eps=1.0e-6, ), textwrap.dedent("""\ Failure comparing analytical with finite difference derivatives. Failure in try {i} failure for parameter number {j} of the orientation parameterisation with fd_ds_dp = {fd} and an_ds_dp = {an} so that difference fd_ds_dp - an_ds_dp = {diff} """).format(i=i, j=j, fd=fd_ds_dp[j], an=an_ds_dp[j], diff=fd_ds_dp[j] - an_ds_dp[j]) # 5. Test a multi-panel detector with non-coplanar panels. # place a beam at the centre of the single panel detector (need a # beam to initialise the multi-panel detector parameterisation) lim = det[0].get_image_size_mm() shift1 = lim[0] / 2.0 shift2 = lim[1] / 2.0 beam_centre = (matrix.col(det[0].get_origin()) + shift1 * matrix.col(det[0].get_fast_axis()) + shift2 * matrix.col(det[0].get_slow_axis())) beam = BeamFactory().make_beam(sample_to_source=-1.0 * beam_centre, wavelength=1.0) multi_panel_detector = make_multi_panel(det) # parameterise this detector dp = DetectorParameterisationMultiPanel(multi_panel_detector, beam) # ensure the beam still intersects the central panel intersection = multi_panel_detector.get_ray_intersection(beam.get_s0()) assert intersection[0] == 4 # record the offsets and dir1s, dir2s offsets_before_shift = dp._offsets dir1s_before_shift = dp._dir1s dir2s_before_shift = dp._dir2s # apply a random parameter shift (~10 mm distances, ~50 mrad angles) p_vals = dp.get_param_vals() p_vals = random_param_shift(p_vals, [10, 10, 10, 50, 50, 50]) # reparameterise the detector dp = DetectorParameterisationMultiPanel(multi_panel_detector, beam) # record the offsets and dir1s, dir2s offsets_after_shift = dp._offsets dir1s_after_shift = dp._dir1s dir2s_after_shift = dp._dir2s # ensure the offsets, dir1s and dir2s are the same. This means that # each panel in the detector moved with the others as a rigid body for a, b in zip(offsets_before_shift, offsets_after_shift): assert approx_equal(a, b, eps=1.0e-10) for a, b in zip(dir1s_before_shift, dir1s_after_shift): assert approx_equal(a, b, eps=1.0e-10) for a, b in zip(dir2s_before_shift, dir2s_after_shift): assert approx_equal(a, b, eps=1.0e-10) attempts = 5 for i in range(attempts): multi_panel_detector = make_multi_panel(det) # parameterise this detector dp = DetectorParameterisationMultiPanel(multi_panel_detector, beam) p_vals = dp.get_param_vals() # apply a random parameter shift p_vals = random_param_shift( p_vals, [10, 10, 10, 1000.0 * pi / 18, 1000.0 * pi / 18, 1000.0 * pi / 18]) dp.set_param_vals(p_vals) # compare analytical and finite difference derivatives # get_fd_gradients will implicitly only get gradients for the # 1st panel in the detector, so explicitly get the same for the # analytical gradients for j in range(9): an_ds_dp = dp.get_ds_dp(multi_state_elt=j) fd_ds_dp = get_fd_gradients(dp, [1.0e-7] * dp.num_free(), multi_state_elt=j) for k in range(6): assert approx_equal( (fd_ds_dp[k] - matrix.sqr(an_ds_dp[k])), matrix.sqr((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)), eps=1.0e-5, out=None, ), textwrap.dedent("""\ Failure comparing analytical with finite difference derivatives. Failure in try {i} for panel number {j] failure for parameter number {k} of the orientation parameterisation with fd_ds_dp = {fd} and an_ds_dp = {an} so that difference fd_ds_dp - an_ds_dp = {diff} """).format( i=i, j=j, k=k, fd=fd_ds_dp[k], an=an_ds_dp[k], diff=fd_ds_dp[k] - matrix.sqr(an_ds_dp[k]), )
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 )