Ejemplo n.º 1
0
  def _beam_derivatives(self, isel, parameterisation=None, reflections=None):
    """helper function to extend the derivatives lists by derivatives of the
    beam parameterisations"""

    # Get required data
    s0 = self._s0.select(isel)
    s0u = self._s0u.select(isel)
    wl = self._wavelength.select(isel)
    r = self._r.select(isel)
    e1 = self._e1.select(isel)
    q = self._q.select(isel)
    c0 = self._c0.select(isel)
    DeltaPsi = self._DeltaPsi.select(isel)
    D = self._D.select(isel)

    # get the derivatives of the beam vector wrt the parameters
    ds0_dbeam_p = parameterisation.get_ds_dp(use_none_as_null=True)

    dDeltaPsi_dp = []
    dpv_dp = []

    # loop through the parameters
    for der in ds0_dbeam_p:

      if der is None:
        dpv_dp.append(None)
        dDeltaPsi_dp.append(None)
        continue

      # repeat the derivative in an array
      ds0 = flex.vec3_double(len(s0u), der.elems)

      # we need the derivative of the unit beam direction too. This requires
      # scaling by the wavelength and projection onto the Ewald sphere
      scaled = ds0 * wl
      ds0u = scaled.dot(c0) * c0 + scaled.dot(e1) * e1

      # calculate the derivative of DeltaPsi for this parameter
      dDeltaPsi = -1.0 * (r.dot(ds0)) / (e1.cross(r).dot(s0))
      dDeltaPsi_dp.append(dDeltaPsi)

      # calculate (d[r]/d[e1])(d[e1]/dp)
      de1_dp = c0.cross(ds0u)
      dr_de1 = dRq_de(DeltaPsi, e1, q)
      drde_dedp = dr_de1 * de1_dp

      #dp = 1.e-8 # finite step size for the parameter
      #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)
      #drde_dedp = (rfwd - rrev) * (1 / dp)

      # calculate the derivative of pv for this parameter
      dpv_dp.append(D * (ds0 + e1.cross(r) * dDeltaPsi + drde_dedp))

    return dpv_dp, dDeltaPsi_dp
  def _beam_derivatives(self, isel, parameterisation=None, reflections=None):
    """helper function to extend the derivatives lists by derivatives of the
    beam parameterisations"""

    # Get required data
    s0 = self._s0.select(isel)
    s0u = self._s0u.select(isel)
    wl = self._wavelength.select(isel)
    r = self._r.select(isel)
    e1 = self._e1.select(isel)
    q = self._q.select(isel)
    c0 = self._c0.select(isel)
    DeltaPsi = self._DeltaPsi.select(isel)
    D = self._D.select(isel)

    # get the derivatives of the beam vector wrt the parameters
    ds0_dbeam_p = parameterisation.get_ds_dp(use_none_as_null=True)

    dDeltaPsi_dp = []
    dpv_dp = []

    # loop through the parameters
    for der in ds0_dbeam_p:

      if der is None:
        dpv_dp.append(None)
        dDeltaPsi_dp.append(None)
        continue

      # repeat the derivative in an array
      ds0 = flex.vec3_double(len(s0u), der.elems)

      # we need the derivative of the unit beam direction too. This requires
      # scaling by the wavelength and projection onto the Ewald sphere
      scaled = ds0 * wl
      ds0u = scaled.dot(c0) * c0 + scaled.dot(e1) * e1

      # calculate the derivative of DeltaPsi for this parameter
      dDeltaPsi = -1.0 * (r.dot(ds0)) / (e1.cross(r).dot(s0))
      dDeltaPsi_dp.append(dDeltaPsi)

      # calculate (d[r]/d[e1])(d[e1]/dp)
      de1_dp = c0.cross(ds0u)
      dr_de1 = dRq_de(DeltaPsi, e1, q)
      drde_dedp = dr_de1 * de1_dp

      #dp = 1.e-8 # finite step size for the parameter
      #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)
      #drde_dedp = (rfwd - rrev) * (1 / dp)

      # calculate the derivative of pv for this parameter
      dpv_dp.append(D * (ds0 + e1.cross(r) * dDeltaPsi + drde_dedp))

    return dpv_dp, dDeltaPsi_dp
  def _xl_unit_cell_derivatives(self, isel, parameterisation=None,
    reflections=None):
    """helper function to extend the derivatives lists by derivatives of the
    crystal unit cell parameterisations"""

    # Get required data
    U = self._U.select(isel)
    h = self._h.select(isel)
    e1 = self._e1.select(isel)
    DeltaPsi = self._DeltaPsi.select(isel)
    s1 = self._s1.select(isel)
    q = self._q.select(isel)
    q_scalar = self._q_scalar.select(isel)
    qq = self._qq.select(isel)
    q0 = self._q0.select(isel)
    r = self._r.select(isel)
    s0 = self._s0.select(isel)
    s0u = self._s0u.select(isel)
    D = self._D.select(isel)

    # get derivatives of the B matrix wrt the parameters
    dB_dxluc_p = parameterisation.get_ds_dp(use_none_as_null=True)

    dDeltaPsi_dp = []
    dpv_dp = []

    # loop through the parameters
    for der in dB_dxluc_p:

      if der is None:
        dpv_dp.append(None)
        dDeltaPsi_dp.append(None)
        continue

      der_mat = flex.mat3_double(len(U), der.elems)

      # calculate the derivative of q for this parameter
      dq = U * der_mat * h

      # calculate the derivative of r for this parameter
      dr = dq.rotate_around_origin(e1, DeltaPsi)

      # calculate the derivative of DeltaPsi for this parameter
      dDeltaPsi = -1.0 * (dr.dot(s1)) / (e1.cross(r).dot(s0))
      dDeltaPsi_dp.append(dDeltaPsi)

      # derivative of the axis e1
      q_dot_dq = q.dot(dq)
      dq0 = (q_scalar * dq - (q_dot_dq * q0)) / qq
      de1_dp = dq0.cross(s0u)

      # calculate (d[r]/d[e1])(d[e1]/dp)
      dr_de1 = dRq_de(DeltaPsi, e1, q)
      drde_dedp = dr_de1 * de1_dp

      # calculate the partial derivative of r wrt change in rotation
      # axis e1 by finite differences
      dp = 1.e-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)
      drde_dedp = (rfwd - rrev) * (1 / dp)

      # calculate the derivative of pv for this parameter
      dpv = D * (dr + e1.cross(r) * dDeltaPsi + drde_dedp)
      dpv_dp.append(dpv)

    return dpv_dp, dDeltaPsi_dp
Ejemplo n.º 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 :-)"
Ejemplo n.º 5
0
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 :-)"
Ejemplo n.º 6
0
def work():
    import random
    import math
    from scitbx import matrix

    # pick random rotation axis & angle - pick spherical coordinate basis to make
    # life easier in performing finite difference calculations

    t = 2 * math.pi * random.random()
    r = 2 * math.pi * random.random()
    s = math.pi * random.random()
    k = krs(r, s)

    # finite difference version
    ndRr, ndRs = dR_drs_fd(r, s, t)

    # now call on derivative calculation
    dRr, dRs = dR_drs_calc(r, s, t)

    # G&Y eqn 7
    dRk = dR_dki(t, k)

    # G&Y eqn 9
    dRk2 = gallego_yezzi_eqn9(t, k)

    dkdr = dk_dr(r, s)
    dkds = dk_ds(r, s)

    print "dR/dr"
    print "FD"
    print ndRr
    print "Analytical (G&Y 7)"
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkdr[i] * dRk[i]
    print m
    print "Analytical (G&Y 9)"
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkdr[i] * dRk2[i]
    print m
    print "Analytical (old)"
    print dRr

    print "dR/ds"
    print "FD"
    print ndRs
    print "Analytical (G&Y 7)"
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkds[i] * dRk[i]
    print m
    print "Analytical (G&Y 9)"
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkds[i] * dRk2[i]
    print m
    print "Analytical (old)"
    print dRs

    # Finally test the direct derivative of a rotated vector wrt the axis elements
    # versus finite differences

    # make a random vector to rotate
    u = matrix.col((random.random(), random.random(), random.random()))

    # calc derivatives of rotated vector (Gallego & Yezzi equn 8)
    from dials_refinement_helpers_ext import dRq_de
    from scitbx.array_family import flex

    dr_de = dRq_de(flex.double([t]), flex.vec3_double([k]), flex.vec3_double([u]))
    print
    print "d[r]/d[e], where [r] = [R][u] is a rotation about [e] (G&Y 8)"
    print matrix.sqr(dr_de[0])

    print "Compare with FD calculation"
    dr_de_FD = [dR_ki * u for dR_ki in dRk]
    dr_de_FD = [elt for vec in dr_de_FD for elt in vec]  # flatten list
    dr_de_FD = matrix.sqr(dr_de_FD).transpose()  # make a matrix
    print dr_de_FD
Ejemplo n.º 7
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 :-)")
Ejemplo n.º 8
0
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 :-)")
Ejemplo n.º 9
0
def work():
    import random
    import math
    from scitbx import matrix

    # pick random rotation axis & angle - pick spherical coordinate basis to make
    # life easier in performing finite difference calculations

    t = 2 * math.pi * random.random()
    r = 2 * math.pi * random.random()
    s = math.pi * random.random()
    k = krs(r, s)

    # finite difference version
    ndRr, ndRs = dR_drs_fd(r, s, t)

    # now call on derivative calculation
    dRr, dRs = dR_drs_calc(r, s, t)

    # G&Y eqn 7
    dRk = dR_dki(t, k)

    # G&Y eqn 9
    dRk2 = gallego_yezzi_eqn9(t, k)

    dkdr = dk_dr(r, s)
    dkds = dk_ds(r, s)

    print("dR/dr")
    print("FD")
    print(ndRr)
    print("Analytical (G&Y 7)")
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkdr[i] * dRk[i]
    print(m)
    print("Analytical (G&Y 9)")
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkdr[i] * dRk2[i]
    print(m)
    print("Analytical (old)")
    print(dRr)

    print("dR/ds")
    print("FD")
    print(ndRs)
    print("Analytical (G&Y 7)")
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkds[i] * dRk[i]
    print(m)
    print("Analytical (G&Y 9)")
    m = matrix.sqr((0, 0, 0, 0, 0, 0, 0, 0, 0))
    for i in range(3):
        m += dkds[i] * dRk2[i]
    print(m)
    print("Analytical (old)")
    print(dRs)

    # Finally test the direct derivative of a rotated vector wrt the axis elements
    # versus finite differences

    # make a random vector to rotate
    u = matrix.col((random.random(), random.random(), random.random()))

    # calc derivatives of rotated vector (Gallego & Yezzi equn 8)
    from dials_refinement_helpers_ext import dRq_de
    from scitbx.array_family import flex

    dr_de = dRq_de(flex.double([t]), flex.vec3_double([k]),
                   flex.vec3_double([u]))
    print()
    print("d[r]/d[e], where [r] = [R][u] is a rotation about [e] (G&Y 8)")
    print(matrix.sqr(dr_de[0]))

    print("Compare with FD calculation")
    dr_de_FD = [dR_ki * u for dR_ki in dRk]
    dr_de_FD = [elt for vec in dr_de_FD for elt in vec]  # flatten list
    dr_de_FD = matrix.sqr(dr_de_FD).transpose()  # make a matrix
    print(dr_de_FD)
Ejemplo n.º 10
0
  def _xl_unit_cell_derivatives(self, isel, parameterisation=None,
    reflections=None):
    """helper function to extend the derivatives lists by derivatives of the
    crystal unit cell parameterisations"""

    # Get required data
    U = self._U.select(isel)
    h = self._h.select(isel)
    e1 = self._e1.select(isel)
    DeltaPsi = self._DeltaPsi.select(isel)
    s1 = self._s1.select(isel)
    q = self._q.select(isel)
    q_scalar = self._q_scalar.select(isel)
    qq = self._qq.select(isel)
    q0 = self._q0.select(isel)
    r = self._r.select(isel)
    s0 = self._s0.select(isel)
    s0u = self._s0u.select(isel)
    D = self._D.select(isel)

    # get derivatives of the B matrix wrt the parameters
    dB_dxluc_p = parameterisation.get_ds_dp(use_none_as_null=True)

    dDeltaPsi_dp = []
    dpv_dp = []

    # loop through the parameters
    for der in dB_dxluc_p:

      if der is None:
        dpv_dp.append(None)
        dDeltaPsi_dp.append(None)
        continue

      der_mat = flex.mat3_double(len(U), der.elems)

      # calculate the derivative of q for this parameter
      dq = U * der_mat * h

      # calculate the derivative of r for this parameter
      dr = dq.rotate_around_origin(e1, DeltaPsi)

      # calculate the derivative of DeltaPsi for this parameter
      dDeltaPsi = -1.0 * (dr.dot(s1)) / (e1.cross(r).dot(s0))
      dDeltaPsi_dp.append(dDeltaPsi)

      # derivative of the axis e1
      q_dot_dq = q.dot(dq)
      dq0 = (q_scalar * dq - (q_dot_dq * q0)) / qq
      de1_dp = dq0.cross(s0u)

      # calculate (d[r]/d[e1])(d[e1]/dp)
      dr_de1 = dRq_de(DeltaPsi, e1, q)
      drde_dedp = dr_de1 * de1_dp

      # calculate the partial derivative of r wrt change in rotation
      # axis e1 by finite differences
      dp = 1.e-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)
      drde_dedp = (rfwd - rrev) * (1 / dp)

      # calculate the derivative of pv for this parameter
      dpv = D * (dr + e1.cross(r) * dDeltaPsi + drde_dedp)
      dpv_dp.append(dpv)

    return dpv_dp, dDeltaPsi_dp