Beispiel #1
0
def calc_angles():
    '''angles between input basis and candidate basis are calculated'''
    inp_a, inp_b, inp_c, cand = get_basis()
    res_a = flex.double()
    res_b = flex.double()
    res_c = flex.double()

    for i in range(len(cand)):
        for k in range(len(cand[i])):
            res_a.append(inp_a[i].dot(cand[i][k]) /
                         (inp_a[i].length() * cand[i][k].length()))
            res_b.append(inp_b[i].dot(cand[i][k]) /
                         (inp_b[i].length() * cand[i][k].length()))
            res_c.append(inp_c[i].dot(cand[i][k]) /
                         (inp_c[i].length() * cand[i][k].length()))

    #result is in radians convert to degrees
    a_angles = flex.acos(res_a) * 180 / math.pi
    b_angles = flex.acos(res_b) * 180 / math.pi
    c_angles = flex.acos(res_c) * 180 / math.pi

    #account for parallel or antiparallel candidate basis vectors
    for j in range(len(a_angles)):
        if a_angles[j] >= 90:
            a_angles[j] = 180 - a_angles[j]

    for j in range(len(b_angles)):
        if b_angles[j] >= 90:
            b_angles[j] = 180 - b_angles[j]

    for j in range(len(c_angles)):
        if c_angles[j] >= 90:
            c_angles[j] = 180 - c_angles[j]

    return a_angles, b_angles, c_angles
Beispiel #2
0
  def __call__(self, experiments, reflections, add_correction_column=False):
    result = flex.reflection_table()

    for expt_id, experiment in enumerate(experiments):
      refls = reflections.select(reflections['id'] == expt_id)
      beam = experiment.beam
      # Remove the need for pixel size within cxi.merge.  Allows multipanel detector with dissimilar panels.
      # Relies on new frame extractor code called by dials.stills_process that writes s0, s1 and polarization normal
      # vectors all to the integration pickle.  Future path (IE THIS CODE): use dials json and reflection file.
      s0_vec = matrix.col(beam.get_s0()).normalize()
      s0_polar_norm = beam.get_polarization_normal()
      s1_vec = refls['s1']
      Ns1 = len(s1_vec)
      # project the s1_vector onto the plane normal to s0.  Get result by subtracting the
      # projection of s1 onto s0, which is (s1.dot.s0_norm)s0_norm
      s0_norm = flex.vec3_double(Ns1,s0_vec)
      s1_proj = (s1_vec.dot(s0_norm))*s0_norm
      s1_in_normal_plane = s1_vec - s1_proj
      # Now want the polar angle between the projected s1 and the polarization normal
      s0_polar_norms = flex.vec3_double(Ns1,s0_polar_norm)
      dotprod = (s1_in_normal_plane.dot(s0_polar_norms))
      costheta = dotprod/(s1_in_normal_plane.norms())
      theta = flex.acos(costheta)
      cos_two_polar_angle = flex.cos(2.0*theta)
      # gives same as old answer to ~1% but not exact.  Not sure why, should not matter.

      tt_vec = experiment.crystal.get_unit_cell().two_theta(miller_indices = refls['miller_index'],
                                                            wavelength = beam.get_wavelength())
      cos_tt_vec = flex.cos(tt_vec)
      sin_tt_vec = flex.sin(tt_vec)
      cos_sq_tt_vec = cos_tt_vec * cos_tt_vec
      sin_sq_tt_vec = sin_tt_vec * sin_tt_vec
      P_nought_vec = 0.5 * (1. + cos_sq_tt_vec)

      F_prime = -1.0 # Hard-coded value defines the incident polarization axis
      P_prime = 0.5 * F_prime * cos_two_polar_angle * sin_sq_tt_vec

      # added as a diagnostic
      #prange=P_nought_vec - P_prime
      #other_F_prime = 1.0
      #otherP_prime = 0.5 * other_F_prime * cos_two_polar_angle * sin_sq_tt_vec
      #otherprange=P_nought_vec - otherP_prime
      #diff2 = flex.abs(prange - otherprange)
      #print >> out, "mean diff is",flex.mean(diff2), "range",flex.min(diff2), flex.max(diff2)
      # done

      correction = 1 / ( P_nought_vec - P_prime )
      refls['intensity.sum.value'] = refls['intensity.sum.value'] * correction
      refls['intensity.sum.variance'] = refls['intensity.sum.variance'] * correction**2 # propagated error
      # This corrects observations for polarization assuming 100% polarization on
      # one axis (thus the F_prime = -1.0 rather than the perpendicular axis, 1.0)
      # Polarization model as described by Kahn, Fourme, Gadet, Janin, Dumas & Andre
      # (1982) J. Appl. Cryst. 15, 330-337, equations 13 - 15.

      if add_correction_column:
        refls['polarization_correction'] = correction

      result.extend(refls)

    return result
    def run(self, experiments, reflections):

        self.logger.log_step_time("POLARIZATION_CORRECTION")

        result = flex.reflection_table()

        for experiment in experiments:
            refls = reflections.select(
                reflections['exp_id'] == experiment.identifier)
            if len(refls) == 0: continue
            beam = experiment.beam
            # Remove the need for pixel size within cxi.merge.  Allows multipanel detector with dissimilar panels.
            # Relies on new frame extractor code called by dials.stills_process that writes s0, s1 and polarization normal
            # vectors all to the integration pickle.  Future path (IE THIS CODE): use dials json and reflection file.
            s0_vec = matrix.col(beam.get_s0()).normalize()
            s0_polar_norm = beam.get_polarization_normal()
            s1_vec = refls['s1']
            Ns1 = len(s1_vec)
            # project the s1_vector onto the plane normal to s0.  Get result by subtracting the
            # projection of s1 onto s0, which is (s1.dot.s0_norm)s0_norm
            s0_norm = flex.vec3_double(Ns1, s0_vec)
            s1_proj = (s1_vec.dot(s0_norm)) * s0_norm
            s1_in_normal_plane = s1_vec - s1_proj
            # Now want the polar angle between the projected s1 and the polarization normal
            s0_polar_norms = flex.vec3_double(Ns1, s0_polar_norm)
            dotprod = (s1_in_normal_plane.dot(s0_polar_norms))
            costheta = dotprod / (s1_in_normal_plane.norms())
            theta = flex.acos(costheta)
            cos_two_polar_angle = flex.cos(2.0 * theta)
            # gives same as old answer to ~1% but not exact.  Not sure why, should not matter.

            tt_vec = experiment.crystal.get_unit_cell().two_theta(
                miller_indices=refls['miller_index'],
                wavelength=beam.get_wavelength())
            cos_tt_vec = flex.cos(tt_vec)
            sin_tt_vec = flex.sin(tt_vec)
            cos_sq_tt_vec = cos_tt_vec * cos_tt_vec
            sin_sq_tt_vec = sin_tt_vec * sin_tt_vec
            P_nought_vec = 0.5 * (1. + cos_sq_tt_vec)

            F_prime = -1.0  # Hard-coded value defines the incident polarization axis
            P_prime = 0.5 * F_prime * cos_two_polar_angle * sin_sq_tt_vec

            # added as a diagnostic
            #prange=P_nought_vec - P_prime
            #other_F_prime = 1.0
            #otherP_prime = 0.5 * other_F_prime * cos_two_polar_angle * sin_sq_tt_vec
            #otherprange=P_nought_vec - otherP_prime
            #diff2 = flex.abs(prange - otherprange)
            #print >> out, "mean diff is",flex.mean(diff2), "range",flex.min(diff2), flex.max(diff2)
            # done

            correction = 1 / (P_nought_vec - P_prime)
            refls['intensity.sum.value'] = refls[
                'intensity.sum.value'] * correction
            refls['intensity.sum.variance'] = refls[
                'intensity.sum.variance'] * correction**2  # propagated error
            # This corrects observations for polarization assuming 100% polarization on
            # one axis (thus the F_prime = -1.0 rather than the perpendicular axis, 1.0)
            # Polarization model as described by Kahn, Fourme, Gadet, Janin, Dumas & Andre
            # (1982) J. Appl. Cryst. 15, 330-337, equations 13 - 15.

            result.extend(refls)

        if len(reflections) > 0:
            self.logger.log(
                "Applied polarization correction. Mean intensity changed from %.2f to %.2f"
                % (flex.mean(reflections['intensity.sum.value']),
                   flex.mean(result['intensity.sum.value'])))

        self.logger.log_step_time("POLARIZATION_CORRECTION", True)
        self.logger.log("Memory usage: %d MB" % get_memory_usage())

        # Remove 's1' column from the reflection table
        from xfel.merging.application.reflection_table_utils import reflection_table_utils
        reflections = reflection_table_utils.prune_reflection_table_keys(
            reflections=result, keys_to_delete=['s1'])
        self.logger.log("Pruned reflection table")
        self.logger.log("Memory usage: %d MB" % get_memory_usage())

        return experiments, reflections
def wavelengths_from_gaussians(experiments, reflections, mosaic_parameters):
    """
    Given a set of mosaic parameters, use gaussian bandpass and mosaicity to estimate a
    wavelength for each reflection.

    Details:
    For a given reflection, the reciprocal lattice point vector q = Ah, where A is the reciprocal
    A matrix and h is the reflection's miller index.  Construct a vector e1 orthagonal to s0 and q.

    @param experiments ExperimentList. If crystal.band_pass is set, use it. Otherwise, estimate the
    band pass using estimate_bandpass
    @param reflections flex.reflection_table Needs to contain the column
    reflection_wavelength_from_pixels
    @param mosaic_parameters Tuple of domain size (angstroms) and half mosaic angle (degrees). If
    None, use the mosaic parameters from each crystal model.
    """

    if "reflection_wavelength_from_pixels" not in reflections:
        return reflections

    if mosaic_parameters is not None:
        domain_size_ang, half_mosaicity_deg = mosaic_parameters
        print(
            "Computing per-reflection wavelengths from domain size",
            domain_size_ang,
            "(ang), half mosaic angle",
            half_mosaicity_deg,
            "(deg), and bandpass derived from each image",
        )

    table = flex.reflection_table()
    new_wavelengths = flex.double()

    def gaussian_product(mean1, sigma1, mean2, sigma2):
        """Jiffy function to multiply two gaussians. Formula from
        P. Bromiley, "Products and convolutions of Gaussian distributions,"
        Medical School, Univ. Manchester, Manchester, UK, Tech. Rep, vol. 3,
        p. 2003, 2003.
        """
        ssq1 = sigma1**2
        ssq2 = sigma2**2
        mean = ((mean1 * ssq2) + (mean2 * ssq1)) / (ssq1 + ssq2)
        sigma = flex.sqrt((ssq1 * ssq2) / (ssq1 + ssq2))
        return mean, sigma

    for expt_id, expt in enumerate(experiments):
        refls = reflections.select(reflections["id"] == expt_id)
        table.extend(refls)

        if mosaic_parameters is None:
            domain_size_ang = expt.crystal.get_domain_size_ang()
            half_mosaicity_deg = expt.crystal.get_half_mosaicity_deg()
            print(
                "Computing per-reflection wavelengths from domain size",
                domain_size_ang,
                "(ang), half mosaic angle",
                half_mosaicity_deg,
                "(deg), and bandpass derived from each image",
            )

        # Determine how to obtain the bandpass
        if hasattr(expt.crystal, "bandpass"):
            wavelength_min, wavelength_max = expt.crystal.bandpass
        else:
            wavelength_min, wavelength_max = estimate_bandpass(refls)
            expt.crystal.bandpass = wavelength_min, wavelength_max

        unit_s0 = flex.vec3_double(len(refls),
                                   expt.beam.get_s0()).each_normalize()
        wavelength = expt.beam.get_wavelength()

        q = (flex.mat3_double(len(refls), expt.crystal.get_A()) *
             refls["miller_index"].as_vec3_double())
        e1 = q.cross(unit_s0).each_normalize()

        # length of an arc l = 2pir * angle/2pi = r*angle. So angle = l/r
        combined_mosaic_angle_approximation = (
            (2 / domain_size_ang) / q.norms()) + (half_mosaicity_deg *
                                                  math.pi / 180)

        # Compute z angles
        # Angle between s0 and q
        z_mosaicity = math.pi - q.angle(unit_s0)
        # Angle between s0 and q rotated to be on the center of bandpass
        z_wavelength = flex.acos(wavelength * q.norms() / 2)

        # Angles between s0 and q vectors rotated to the extreme ends of the bandpass
        z_wavelength_min = flex.acos(wavelength_min * q.norms() / 2)
        z_wavelength_max = flex.acos(wavelength_max * q.norms() / 2)

        # Now assume two gaussians on a polar notation (IE x is angle between a q vector and s0).  One gaussian for the
        # bandpass (assuming the width of the bandpass is one sigma) and one for the mosaicity (assuming the width of
        # the mosaic spread is one sigma). The product of the two gaussians is the illuminated volume, and the center
        # of that gaussian angle a q vector would take with s0 such that it maximizes the illuminated volume.
        mean_z, sigma_z = gaussian_product(
            z_wavelength,
            z_wavelength - z_wavelength_max,
            z_mosaicity,
            combined_mosaic_angle_approximation,
        )

        # Compute the wavelengths given the illuminated volume angle mean_z
        lmbda = flex.cos(mean_z) / (q.norms() / 2)
        new_wavelengths.extend(lmbda)

    assert (new_wavelengths <= 0).count(True) == 0
    reflections[
        "reflection_wavelength_from_gaussian_mosaicity_and_bandpass"] = new_wavelengths

    return reflections
def tophat_vector_wavelengths(experiments, reflections, mosaic_parameters):
    """
    Given a set of mosaic parameters, use vectors to estimate a wavelength for each reflection

    Details:
    For a given reflection, the reciprocal lattice point vector q = Ah, where A is the reciprocal
    A matrix and h is the reflection's miller index.  Construct a vector e1 orthagonal to s0 and q.
    Construct 4 more vectors of length equal to the magnitude of q, and that lie in the plane that
    is normal to e1:
    q_mos_inner and q_mos_outer: q vectors rotated into or out of the Ewald sphere by an angle equal
    to the half mosaic angle + the angle inscribed by adding an arc length equal to 2/domain size
    (angstroms). Call this angle the combined mosaic angle approximation.
    q_wavelength_min and q_wavelength_max: q vectors rotated on an ewald sphere with radius
    1/bandpass minimum or 1/bandpass maximum.

    Consider now the pairs of vectors wavelength min/max and q_mos inner/outer.  If neither q_mos
    vectors lies between the two wavelength vectors, then assign a refletion's wavelength to
    either wavelength min or max, depending on which is closest.  Otherwise, find two vectors
    that lie between wavelength min/max. For example if q_mos inner lies between them, but outer
    does not, the two vectors will be q_mos inner and q wavelength min.  If both q_mos inner and
    outer lie between wavelength min/max, then the two vetors will be q_mos inner and outer.

    Once the two vectors have been identified, define a new q vector which is the average of the
    two vectors.  Determine the wavelength that would allow this q vector to be in the diffracting
    condition.  Report that wavelength in the column reflection_wavelength_from_mosaicity_and_bandpass.

    Because these determinations involve hard cutoffs instead of gaussians, the wavelengths are
    determined in a manner similar to the overlap of tophat functions.

    @param experiments ExperimentList. If crystal.band_pass is set, use it. Otherwise, estimate the
    band pass using estimate_bandpass
    @param reflections flex.reflection_table Needs to contain the column
    reflection_wavelength_from_pixels
    @param mosaic_parameters Tuple of domain size (angstroms) and half mosaic angle (degrees)

    """

    if "reflection_wavelength_from_pixels" not in reflections:
        return reflections

    domain_size_ang, half_mosaicity_deg = mosaic_parameters
    print(
        "Computing per-reflection wavelengths from domain size",
        domain_size_ang,
        "(ang), half mosaic angle",
        half_mosaicity_deg,
        "(deg), and bandpass derived from each image",
    )

    table = flex.reflection_table()

    new_wavelengths = flex.double()

    # Keep track of the various cases
    case_0 = case_1 = case_2 = case_3 = case_4 = case_5 = 0

    for expt_id, expt in enumerate(experiments):
        refls = reflections.select(reflections["id"] == expt_id)
        table.extend(refls)

        # Determine how to obtain the bandpass
        if hasattr(expt.crystal, "bandpass"):
            wavelength_min, wavelength_max = expt.crystal.bandpass
        else:
            wavelength_min, wavelength_max = estimate_bandpass(refls)
            expt.crystal.bandpass = wavelength_min, wavelength_max

        unit_s0 = flex.vec3_double(len(refls),
                                   expt.beam.get_s0()).each_normalize()
        wavelength = expt.beam.get_wavelength()

        q = (flex.mat3_double(len(refls), expt.crystal.get_A()) *
             refls["miller_index"].as_vec3_double())
        e1 = q.cross(unit_s0).each_normalize()

        # length of an arc l = 2pir * angle/2pi = r*angle. So angle = l/r
        combined_mosaic_angle_approximation = (
            (2 / domain_size_ang) / q.norms()) + (half_mosaicity_deg *
                                                  math.pi / 180)
        q_mos_inner = q.rotate_around_origin(
            e1, -combined_mosaic_angle_approximation)
        q_mos_outer = q.rotate_around_origin(
            e1, combined_mosaic_angle_approximation)
        # Z: angle between q and s0
        z_mos_inner = math.pi - q_mos_inner.angle(unit_s0)
        z_mos_outer = math.pi - q_mos_outer.angle(unit_s0)
        lmbda_bigger = flex.cos(z_mos_inner) / (q.norms() / 2)
        lmbda_smaller = flex.cos(z_mos_outer) / (q.norms() / 2)

        z_wavelength_min = flex.acos(wavelength_min * q.norms() / 2)
        z_wavelength_max = flex.acos(wavelength_max * q.norms() / 2)
        q_wavelength_min = (
            unit_s0.rotate_around_origin(e1, math.pi + z_wavelength_min) *
            q.norms())
        q_wavelength_max = (
            unit_s0.rotate_around_origin(e1, math.pi + z_wavelength_max) *
            q.norms())

        assert (lmbda_smaller < lmbda_bigger).count(False) == 0
        sel = flex.bool(len(refls), True)
        image_wavelengths = flex.double(len(refls), 0)

        q_inner = flex.vec3_double()
        q_outer = flex.vec3_double()

        # Iterate through the reflections and sort each into one of the 6 cases
        for i in xrange(len(refls)):
            # Cases 0 and 1: both q_mos inner and outer are outside of the wavelength min/max vectors
            if lmbda_smaller[i] > wavelength_max:
                image_wavelengths[i] = wavelength_max
                sel[i] = False
                case_0 += 1
                continue
            elif lmbda_bigger[i] < wavelength_min:
                image_wavelengths[i] = wavelength_min
                sel[i] = False
                case_1 += 1
                continue

            sel[i] = True

            # Case 2: q_mos outer is between wavelengths min and max so use q_mos outer
            # Case 3: q_mos outer is outside of wavelength min so use wavelength min
            if lmbda_smaller[i] >= wavelength_min:
                q_outer.append(q_mos_outer[i])
                case_2 += 1
            else:
                q_outer.append(q_wavelength_min[i])
                case_3 += 1

            # Case 4: q_mos inner is between wavelengths min and max so use q_mos inner
            # Case 5: q_mos inner is outside of wavelength max so use wavelength max
            if lmbda_bigger[i] <= wavelength_max:
                q_inner.append(q_mos_inner[i])
                case_4 += 1
            else:
                q_inner.append(q_wavelength_max[i])
                case_5 += 1

        # Compute new reflection wavelengths
        new_q = (q_inner + q_outer) * 0.5
        z = math.pi - new_q.angle(unit_s0.select(sel))
        lmbda = flex.cos(z) / (new_q.norms() / 2)
        image_wavelengths.set_selected(sel, lmbda)
        new_wavelengths.extend(image_wavelengths)

    assert (new_wavelengths <= 0).count(True) == 0
    reflections[
        "reflection_wavelength_from_mosaicity_and_bandpass"] = new_wavelengths
    print("CASES", case_0, case_1, case_2, case_3, case_4, case_5)

    return reflections