def exercise():

  # find acceptable trial vectors
  while True:
    u = matrix.col.random(3,-1,1)
    v = matrix.col.random(3,-1,1)
    tst = u.accute_angle(v)
    # skip vectors of zero length
    if tst is None: continue
    # skip vectors less than about 1 degrees from collinear
    if tst < 0.017: continue
    # otherwise accept the trial
    break

  dtheta_du, dtheta_dv = angle_derivative_wrt_vectors(u,v)
  fd_dtheta_du, fd_dtheta_dv = finite_difference_approx(u,v)

  try:
    assert approx_equal(dtheta_du, fd_dtheta_du)
    assert approx_equal(dtheta_dv, fd_dtheta_dv)
  except AssertionError:
    print "problem at angle", u.angle(v)

  return
 def dangle(u, v):
     return [matrix.col(e) for e in angle_derivative_wrt_vectors(u, v)]
Example #3
0
    def _calc_cell_parameter_sd(self):

        # self._cov_B is the covariance matrix of elements of the B matrix. We
        # need to construct the covariance matrix of elements of the
        # transpose of B. The vector of elements of B is related to the
        # vector of elements of its transpose by a permutation, P.
        P = matrix.sqr((
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            1,
        ))

        # We can use P to replace var_cov with the covariance matrix of
        # elements of the transpose of B.
        var_cov = P * self._cov_B * P

        # From B = (O^-1)^T we can convert this
        # to the covariance matrix of the real space orthogonalisation matrix
        Bt = self._B.transpose()
        O = Bt.inverse()  # noqa: E741, is name of the matrix
        cov_O = matrix_inverse_error_propagation(Bt, var_cov)

        # The real space unit cell vectors are given by
        vec_a = O * matrix.col((1, 0, 0))
        vec_b = O * matrix.col((0, 1, 0))
        vec_c = O * matrix.col((0, 0, 1))

        # So the unit cell parameters are
        a, b, c = vec_a.length(), vec_b.length(), vec_c.length()
        # alpha = acos(vec_b.dot(vec_c) / (b * c))
        # beta = acos(vec_a.dot(vec_c) / (a * c))
        # gamma = acos(vec_a.dot(vec_b) / (a * b))

        # The estimated errors are calculated by error propagation from cov_O. In
        # each case we define a function F(O) that converts the matrix O into the
        # unit cell parameter of interest. To do error propagation to get the
        # variance of that cell parameter we need the Jacobian of the function.
        # This is a 1*9 matrix of derivatives in the order of the elements of O
        #
        #     / dF   dF   dF   dF   dF   dF   dF   dF   dF  \
        # J = | ---, ---, ---, ---, ---, ---, ---, ---, --- |
        #     \ da1  db1  dc1  da2  db2  dc2  da3  db3  dc3 /
        #

        # For cell length |a|, F = sqrt(a1^2 + a2^2 + a3^2)
        jacobian = matrix.rec(
            (vec_a[0] / a, 0, 0, vec_a[1] / a, 0, 0, vec_a[2] / a, 0, 0),
            (1, 9))
        var_a = (jacobian * cov_O * jacobian.transpose())[0]

        # For cell length |b|, F = sqrt(b1^2 + b2^2 + b3^2)
        jacobian = matrix.rec(
            (0, vec_b[0] / b, 0, 0, vec_b[1] / b, 0, 0, vec_b[2] / b, 0),
            (1, 9))
        var_b = (jacobian * cov_O * jacobian.transpose())[0]

        # For cell length |c|, F = sqrt(c1^2 + c2^2 + c3^2)
        jacobian = matrix.rec(
            (0, 0, vec_c[0] / c, 0, 0, vec_c[1] / c, 0, 0, vec_c[2] / c),
            (1, 9))
        var_c = (jacobian * cov_O * jacobian.transpose())[0]

        # For cell volume (a X b).c,
        # F = c1(a2*b3 - b2*a3) + c2(a3*b1 - b3*a1) + c3(a1*b2 - b1*a2)
        a1, a2, a3 = vec_a
        b1, b2, b3 = vec_b
        c1, c2, c3 = vec_c
        jacobian = matrix.rec(
            (
                c3 * b2 - c2 * b3,
                c1 * b3 - c3 * b1,
                c2 * b1 - c1 * b2,
                c2 * a3 - c3 * a2,
                c3 * a1 - c1 * a3,
                c1 * a2 - c2 * a1,
                a2 * b3 - b2 * a3,
                a3 * b1 - b3 * a1,
                a1 * b2 - b1 * a2,
            ),
            (1, 9),
        )
        var_V = (jacobian * cov_O * jacobian.transpose())[0]
        self._cell_volume_sd = sqrt(var_V)

        # For the unit cell angles we need to calculate derivatives of the angles
        # with respect to the elements of O
        dalpha_db, dalpha_dc = angle_derivative_wrt_vectors(vec_b, vec_c)
        dbeta_da, dbeta_dc = angle_derivative_wrt_vectors(vec_a, vec_c)
        dgamma_da, dgamma_db = angle_derivative_wrt_vectors(vec_a, vec_b)

        # For angle alpha, F = acos( b.c / |b||c|)
        jacobian = matrix.rec(
            (
                0,
                dalpha_db[0],
                dalpha_dc[0],
                0,
                dalpha_db[1],
                dalpha_dc[1],
                0,
                dalpha_db[2],
                dalpha_dc[2],
            ),
            (1, 9),
        )
        var_alpha = (jacobian * cov_O * jacobian.transpose())[0]

        # For angle beta, F = acos( a.c / |a||c|)
        jacobian = matrix.rec(
            (
                dbeta_da[0],
                0,
                dbeta_dc[0],
                dbeta_da[1],
                0,
                dbeta_dc[1],
                dbeta_da[2],
                0,
                dbeta_dc[2],
            ),
            (1, 9),
        )
        var_beta = (jacobian * cov_O * jacobian.transpose())[0]

        # For angle gamma, F = acos( a.b / |a||b|)
        jacobian = matrix.rec(
            (
                dgamma_da[0],
                dgamma_db[0],
                0,
                dgamma_da[1],
                dgamma_db[1],
                0,
                dgamma_da[2],
                dgamma_db[2],
                0,
            ),
            (1, 9),
        )
        var_gamma = (jacobian * cov_O * jacobian.transpose())[0]

        # Symmetry constraints may mean variances of the angles should be zero.
        # Floating point error could lead to negative variances. Ensure these are
        # caught before taking their square root!
        var_alpha = max(0, var_alpha)
        var_beta = max(0, var_beta)
        var_gamma = max(0, var_gamma)

        rad2deg = 180.0 / pi
        self._cell_sd = (
            sqrt(var_a),
            sqrt(var_b),
            sqrt(var_c),
            sqrt(var_alpha) * rad2deg,
            sqrt(var_beta) * rad2deg,
            sqrt(var_gamma) * rad2deg,
        )
Example #4
0
  def _calc_cell_parameter_sd(self):

    from scitbx.math.lefebvre import matrix_inverse_error_propagation
    from scitbx.math import angle_derivative_wrt_vectors
    from math import pi, acos, sqrt

    # self._cov_B is the covariance matrix of elements of the B matrix. We
    # need to construct the covariance matrix of elements of the
    # transpose of B. The vector of elements of B is related to the
    # vector of elements of its transpose by a permutation, P.
    P = matrix.sqr((1,0,0,0,0,0,0,0,0,
                    0,0,0,1,0,0,0,0,0,
                    0,0,0,0,0,0,1,0,0,
                    0,1,0,0,0,0,0,0,0,
                    0,0,0,0,1,0,0,0,0,
                    0,0,0,0,0,0,0,1,0,
                    0,0,1,0,0,0,0,0,0,
                    0,0,0,0,0,1,0,0,0,
                    0,0,0,0,0,0,0,0,1))

    # We can use P to replace var_cov with the covariance matrix of
    # elements of the transpose of B.
    var_cov = P*self._cov_B*P

    # From B = (O^-1)^T we can convert this
    # to the covariance matrix of the real space orthogonalisation matrix
    Bt = self._B.transpose()
    O = Bt.inverse()
    cov_O = matrix_inverse_error_propagation(Bt, var_cov)

    # The real space unit cell vectors are given by
    vec_a = (O * matrix.col((1,0,0)))
    vec_b = (O * matrix.col((0,1,0)))
    vec_c = (O * matrix.col((0,0,1)))

    # So the unit cell parameters are
    a, b, c = vec_a.length(), vec_b.length(), vec_c.length()
    alpha = acos(vec_b.dot(vec_c) / (b*c))
    beta =  acos(vec_a.dot(vec_c) / (a*c))
    gamma = acos(vec_a.dot(vec_b) / (a*b))

    # The estimated errors are calculated by error propagation from cov_O. In
    # each case we define a function F(O) that converts the matrix O into the
    # unit cell parameter of interest. To do error propagation to get the
    # variance of that cell parameter we need the Jacobian of the function.
    # This is a 1*9 matrix of derivatives in the order of the elements of O
    #
    #     / dF   dF   dF   dF   dF   dF   dF   dF   dF  \
    # J = | ---, ---, ---, ---, ---, ---, ---, ---, --- |
    #     \ da1  db1  dc1  da2  db2  dc2  da3  db3  dc3 /
    #

    # For cell length |a|, F = sqrt(a1^2 + a2^2 + a3^2)
    jacobian = matrix.rec(
      (vec_a[0]/a, 0, 0, vec_a[1]/a, 0, 0, vec_a[2]/a, 0, 0), (1, 9))
    var_a = (jacobian * cov_O * jacobian.transpose())[0]

    # For cell length |b|, F = sqrt(b1^2 + b2^2 + b3^2)
    jacobian = matrix.rec(
      (0, vec_b[0]/b, 0, 0, vec_b[1]/b, 0, 0, vec_b[2]/b, 0), (1, 9))
    var_b = (jacobian * cov_O * jacobian.transpose())[0]

    # For cell length |c|, F = sqrt(c1^2 + c2^2 + c3^2)
    jacobian = matrix.rec(
      (0, 0, vec_c[0]/c, 0, 0, vec_c[1]/c, 0, 0, vec_c[2]/c), (1, 9))
    jacobian_t = jacobian.transpose()
    var_c = (jacobian * cov_O * jacobian.transpose())[0]

    # For cell volume (a X b).c,
    # F = c1(a2*b3 - b2*a3) + c2(a3*b1 - b3*a1) + c3(a1*b2 - b1*a2)
    a1, a2, a3 = vec_a
    b1, b2, b3 = vec_b
    c1, c2, c3 = vec_c
    jacobian = matrix.rec((c3*b2 - c2*b3,
                           c1*b3 - c3*b1,
                           c2*b1 - c1*b2,
                           c2*a3 - c3*a2,
                           c3*a1 - c1*a3,
                           c1*a2 - c2*a1,
                           a2*b3 - b2*a3,
                           a3*b1 - b3*a1,
                           a1*b2 - b1*a2), (1,9))
    jacobian_t = jacobian.transpose()
    var_V = (jacobian * cov_O * jacobian.transpose())[0]
    self._cell_volume_sd = sqrt(var_V)

    # For the unit cell angles we need to calculate derivatives of the angles
    # with respect to the elements of O
    dalpha_db, dalpha_dc = angle_derivative_wrt_vectors(vec_b, vec_c)
    dbeta_da, dbeta_dc = angle_derivative_wrt_vectors(vec_a, vec_c)
    dgamma_da, dgamma_db = angle_derivative_wrt_vectors(vec_a, vec_b)

    # For angle alpha, F = acos( b.c / |b||c|)
    jacobian = matrix.rec(
      (0, dalpha_db[0], dalpha_dc[0], 0, dalpha_db[1], dalpha_dc[1], 0,
      dalpha_db[2], dalpha_dc[2]), (1, 9))
    var_alpha = (jacobian * cov_O * jacobian.transpose())[0]

    # For angle beta, F = acos( a.c / |a||c|)
    jacobian = matrix.rec(
      (dbeta_da[0], 0, dbeta_dc[0], dbeta_da[1], 0, dbeta_dc[1], dbeta_da[2],
      0, dbeta_dc[2]), (1, 9))
    var_beta = (jacobian * cov_O * jacobian.transpose())[0]

    # For angle gamma, F = acos( a.b / |a||b|)
    jacobian = matrix.rec(
      (dgamma_da[0], dgamma_db[0], 0, dgamma_da[1], dgamma_db[1], 0,
      dgamma_da[2], dgamma_db[2], 0), (1, 9))
    var_gamma = (jacobian * cov_O * jacobian.transpose())[0]

    # Symmetry constraints may mean variances of the angles should be zero.
    # Floating point error could lead to negative variances. Ensure these are
    # caught before taking their square root!
    var_alpha = max(0, var_alpha)
    var_beta = max(0, var_beta)
    var_gamma = max(0, var_gamma)

    from math import pi
    rad2deg = 180. / pi
    self._cell_sd = (sqrt(var_a),
                     sqrt(var_b),
                     sqrt(var_c),
                     sqrt(var_alpha) * rad2deg,
                     sqrt(var_beta) * rad2deg,
                     sqrt(var_gamma) * rad2deg)
    return