コード例 #1
0
ファイル: dx.py プロジェクト: dials/dials_scratch
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 :-)"
コード例 #2
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],
                                )
コード例 #3
0
class ModelState(object):
    """
    A class to keep track of the model state

    """
    def __init__(
        self,
        experiment,
        mosaicity_parameterisation,
        fix_mosaic_spread=False,
        fix_wavelength_spread=False,
        fix_unit_cell=False,
        fix_orientation=False,
    ):
        """
        Initialise the model state

        """

        # Save the crystal model
        self.experiment = experiment
        self.crystal = experiment.crystal

        # The U and P parameterisation
        self.U_parameterisation = CrystalOrientationParameterisation(
            self.crystal)
        self.B_parameterisation = CrystalUnitCellParameterisation(self.crystal)

        # The M, L and W parameterisations
        self.M_parameterisation = mosaicity_parameterisation

        # Set the flags to fix parameters
        self._is_mosaic_spread_fixed = fix_mosaic_spread
        self._is_wavelength_spread_fixed = fix_wavelength_spread
        self._is_unit_cell_fixed = fix_unit_cell
        self._is_orientation_fixed = fix_orientation

    def is_orientation_fixed(self):
        """
        Return whether the orientation is fixed

        """
        return self._is_orientation_fixed

    def is_unit_cell_fixed(self):
        """
        Return whether the unit cell is fixed

        """
        return self._is_unit_cell_fixed

    def is_mosaic_spread_fixed(self):
        """
        Return whether the mosaic spread is fixed

        """
        return self._is_mosaic_spread_fixed

    def is_mosaic_spread_angular(self):
        """
        Return whether the mosaic spread is angular

        """
        return self.M_parameterisation.is_angular()

    def is_wavelength_spread_fixed(self):
        """
        Return whether the wavelength spread is fixed

        """
        return self._is_wavelength_spread_fixed

    def get_unit_cell(self):
        """
        Get the crystal unit cell

        """
        return self.crystal.get_unit_cell()

    def get_U(self):
        """
        Get the crystal U matrix

        """
        return matrix.sqr(self.crystal.get_U())

    def get_B(self):
        """
        Get the crystal B matrix

        """
        return matrix.sqr(self.crystal.get_B())

    def get_A(self):
        """
        Get the crystal A matrix

        """
        return matrix.sqr(self.crystal.get_A())

    def get_M(self):
        """
        Get the Sigma M matrix

        """
        return self.M_parameterisation.sigma()

    def get_U_params(self):
        """
        Get the U parameters

        """
        return flex.double(self.U_parameterisation.get_param_vals())

    def get_B_params(self):
        """
        Get the B parameters

        """
        return flex.double(self.B_parameterisation.get_param_vals())

    def get_M_params(self):
        """
        Get the M parameters

        """
        return self.M_parameterisation.parameters()

    def set_U_params(self, params):
        """
        Set the U parameters

        """
        return self.U_parameterisation.set_param_vals(params)

    def set_B_params(self, params):
        """
        Set the B parameters

        """
        return self.B_parameterisation.set_param_vals(params)

    def set_M_params(self, params):
        """
        Set the M parameters

        """
        return self.M_parameterisation.set_parameters(params)

    def num_U_params(self):
        """
        Get the number of U parameters

        """
        return len(self.get_U_params())

    def num_B_params(self):
        """
        Get the number of B parameters

        """
        return len(self.get_B_params())

    def num_M_params(self):
        """
        Get the number of M parameters

        """
        return len(self.get_M_params())

    def get_dU_dp(self):
        """
        Get the first derivatives of U w.r.t its parameters

        """
        return flex.mat3_double(self.U_parameterisation.get_ds_dp())

    def get_dB_dp(self):
        """
        Get the first derivatives of B w.r.t its parameters

        """
        return flex.mat3_double(self.B_parameterisation.get_ds_dp())

    def get_dM_dp(self):
        """
        Get the first derivatives of M w.r.t its parameters

        """
        return self.M_parameterisation.first_derivatives()

    def get_active_parameters(self):
        """
        Get the active parameters in order: U, B, M, L, W

        """
        active_params = flex.double()
        if not self._is_orientation_fixed:
            active_params.extend(self.get_U_params())
        if not self._is_unit_cell_fixed:
            active_params.extend(self.get_B_params())
        if not self._is_mosaic_spread_fixed:
            active_params.extend(self.get_M_params())
        if not self._is_wavelength_spread_fixed:
            active_params.extend(self.get_L_params())
        assert len(active_params) > 0
        return active_params

    def set_active_parameters(self, params):
        """
        Set the active parameters in order: U, B, M, L, W

        """
        if not self._is_orientation_fixed:
            temp = params[:self.num_U_params()]
            params = params[self.num_U_params():]
            self.set_U_params(temp)
        if not self._is_unit_cell_fixed:
            temp = params[:self.num_B_params()]
            params = params[self.num_B_params():]
            self.set_B_params(temp)
        if not self._is_mosaic_spread_fixed:
            temp = params[:self.num_M_params()]
            params = params[self.num_M_params():]
            self.set_M_params(temp)
        if not self._is_wavelength_spread_fixed:
            temp = params[:self.num_L_params()]
            params = params[self.num_L_params():]
            self.set_L_params(temp)

    def get_labels(self):
        """
        Get the parameter labels

        """
        labels = []
        if not self._is_orientation_fixed:
            for i in range(len(self.get_U_params())):
                labels.append("Crystal_U_%d" % i)
        if not self._is_unit_cell_fixed:
            for i in range(len(self.get_B_params())):
                labels.append("Crystal_B_%d" % i)
        if not self._is_mosaic_spread_fixed:
            for i in range(len(self.get_M_params())):
                labels.append("Mosaicity_%d" % i)
        if not self._is_wavelength_spread_fixed:
            labels.append("Wavelength_Spread")
        assert len(labels) > 0
        return labels
コード例 #4
0
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 :-)")