예제 #1
0
def sequence_to_stills(experiments, reflections, params):
    assert len(reflections) == 1
    reflections = reflections[0]

    new_experiments = ExperimentList()
    new_reflections = flex.reflection_table()

    # This is the subset needed to integrate
    for key in [
            "id",
            "imageset_id",
            "shoebox",
            "bbox",
            "intensity.sum.value",
            "intensity.sum.variance",
            "entering",
            "flags",
            "miller_index",
            "panel",
            "xyzobs.px.value",
            "xyzobs.px.variance",
    ]:
        if key in reflections:
            new_reflections[key] = type(reflections[key])()
        elif key == "imageset_id":
            assert len(experiments.imagesets()) == 1
            reflections["imageset_id"] = flex.int(len(reflections), 0)
            new_reflections["imageset_id"] = flex.int()
        elif key == "entering":
            reflections["entering"] = flex.bool(len(reflections), False)
            new_reflections["entering"] = flex.bool()
        else:
            raise RuntimeError(
                f"Expected key not found in reflection table: {key}")

    for expt_id, experiment in enumerate(experiments):
        # Get the goniometr setting matrix
        goniometer_setting_matrix = matrix.sqr(
            experiment.goniometer.get_setting_rotation())
        goniometer_axis = matrix.col(experiment.goniometer.get_rotation_axis())
        step = experiment.scan.get_oscillation()[1]

        refls = reflections.select(reflections["id"] == expt_id)
        _, _, _, _, z1, z2 = refls["bbox"].parts()

        # Create an experiment for each scanpoint
        for i_scan_point in range(*experiment.scan.get_array_range()):
            if params.max_scan_points and i_scan_point >= params.max_scan_points:
                break
            # The A matrix is the goniometer setting matrix for this scan point
            # times the scan varying A matrix at this scan point. Note, the
            # goniometer setting matrix for scan point zero will be the identity
            # matrix and represents the beginning of the oscillation.
            # For stills, the A matrix needs to be positioned in the midpoint of an
            # oscillation step. Hence, here the goniometer setting matrixis rotated
            # by a further half oscillation step.
            A = (goniometer_axis.axis_and_angle_as_r3_rotation_matrix(
                angle=experiment.scan.get_angle_from_array_index(i_scan_point)
                + (step / 2),
                deg=True,
            ) * goniometer_setting_matrix * matrix.sqr(
                experiment.crystal.get_A_at_scan_point(i_scan_point)))
            crystal = MosaicCrystalSauter2014(experiment.crystal)
            crystal.set_A(A)

            # Copy in mosaic parameters if available
            if params.output.domain_size_ang is None and hasattr(
                    experiment.crystal, "get_domain_size_ang"):
                crystal.set_domain_size_ang(
                    experiment.crystal.get_domain_size_ang())
            elif params.output.domain_size_ang is not None:
                crystal.set_domain_size_ang(params.output.domain_size_ang)

            if params.output.half_mosaicity_deg is None and hasattr(
                    experiment.crystal, "get_half_mosaicity_deg"):
                crystal.set_half_mosaicity_deg(
                    experiment.crystal.get_half_mosaicity_deg())
            elif params.output.half_mosaicity_deg is not None:
                crystal.set_half_mosaicity_deg(
                    params.output.half_mosaicity_deg)

            new_experiment = Experiment(
                detector=experiment.detector,
                beam=experiment.beam,
                crystal=crystal,
                imageset=experiment.imageset.as_imageset()
                [i_scan_point:i_scan_point + 1],
            )
            new_experiments.append(new_experiment)

            # Each reflection in a 3D shoebox can be found on multiple images.
            # Slice the reflections such that any reflection on this scan point
            # is included with this image
            new_id = len(new_experiments) - 1
            subrefls = refls.select((i_scan_point >= z1) & (i_scan_point < z2))
            for refl in subrefls.rows():
                assert i_scan_point in range(*refl["bbox"][4:6])

                new_sb = Shoebox()
                start = i_scan_point - refl["bbox"][4]  # z1
                new_sb.data = refl["shoebox"].data[start:start + 1, :, :]
                new_sb.background = refl["shoebox"].background[start:start +
                                                               1, :, :]
                new_sb.mask = refl["shoebox"].mask[start:start + 1, :, :]
                intensity = new_sb.summed_intensity()
                new_sb.bbox = tuple(
                    list(refl["bbox"])[0:4] +
                    [0, 1])  # keep the original shoebox but reset the z values
                new_sb.panel = refl["panel"]
                new_refl = {}
                new_refl["id"] = new_refl["imageset_id"] = new_id
                new_refl["shoebox"] = new_sb
                new_refl["bbox"] = new_sb.bbox
                new_refl["intensity.sum.value"] = intensity.observed.value
                new_refl[
                    "intensity.sum.variance"] = intensity.observed.variance
                for key in ["entering", "flags", "miller_index", "panel"]:
                    new_refl[key] = refl[key]
                centroid = new_sb.centroid_foreground_minus_background()
                new_refl["xyzobs.px.value"] = centroid.px.position
                new_refl["xyzobs.px.variance"] = centroid.px.variance
                new_reflections.append({})
                for key in new_refl:
                    new_reflections[key][-1] = new_refl[key]

    # Re-predict using the reflection slices and the stills predictors
    ref_predictor = ExperimentsPredictorFactory.from_experiments(
        new_experiments, force_stills=new_experiments.all_stills())
    new_reflections = ref_predictor(new_reflections)

    return (new_experiments, new_reflections)
예제 #2
0
    def __call__(self):
        """Determine optimal mosaicity and domain size model (monochromatic)"""
        if self.refinery is None:
            RR = self.reflections
        else:
            RR = self.refinery.predict_for_reflection_table(self.reflections)
        excursion_rad = RR["delpsical.rad"]
        delta_psi_deg = excursion_rad * 180. / math.pi
        print
        print flex.max(delta_psi_deg), flex.min(delta_psi_deg)
        mean_excursion = flex.mean(delta_psi_deg)
        print "The mean excursion is %7.3f degrees, r.m.s.d %7.3f" % (
            mean_excursion, math.sqrt(flex.mean(RR["delpsical2"])))

        from dxtbx.model import MosaicCrystalSauter2014
        crystal = MosaicCrystalSauter2014(self.experiments[0].crystal)
        self.experiments[0].crystal = crystal
        beam = self.experiments[0].beam
        miller_indices = self.reflections["miller_index"]

        # FIXME XXX revise this formula so as to use a different wavelength potentially for each reflection
        two_thetas = crystal.get_unit_cell().two_theta(miller_indices,
                                                       beam.get_wavelength(),
                                                       deg=True)
        dspacings = crystal.get_unit_cell().d(miller_indices)
        dspace_sq = dspacings * dspacings

        #  First -- try to get a reasonable envelope for the observed excursions.
        ## minimum of three regions; maximum of 50 measurements in each bin
        print "fitting parameters on %d spots" % len(excursion_rad)
        n_bins = min(max(3, len(excursion_rad) // 25), 50)
        bin_sz = len(excursion_rad) // n_bins
        print "nbins", n_bins, "bin_sz", bin_sz
        order = flex.sort_permutation(two_thetas)
        two_thetas_env = flex.double()
        dspacings_env = flex.double()
        excursion_rads_env = flex.double()
        for x in xrange(0, n_bins):
            subset = order[x * bin_sz:(x + 1) * bin_sz]
            two_thetas_env.append(flex.mean(two_thetas.select(subset)))
            dspacings_env.append(flex.mean(dspacings.select(subset)))
            excursion_rads_env.append(
                flex.max(flex.abs(excursion_rad.select(subset))))

        #  Second -- parameter fit
        ## solve the normal equations
        sum_inv_u_sq = flex.sum(dspacings_env * dspacings_env)
        sum_inv_u = flex.sum(dspacings_env)
        sum_te_u = flex.sum(dspacings_env * excursion_rads_env)
        sum_te = flex.sum(excursion_rads_env)
        Normal_Mat = sqr(
            (sum_inv_u_sq, sum_inv_u, sum_inv_u, len(dspacings_env)))
        Vector = col((sum_te_u, sum_te))
        solution = Normal_Mat.inverse() * Vector
        s_ang = 1. / (2 * solution[0])
        print "Best LSQ fit Scheerer domain size is %9.2f ang" % (s_ang)

        tan_phi_rad = dspacings / (2. * s_ang)
        tan_phi_deg = tan_phi_rad * 180. / math.pi
        k_degrees = solution[1] * 180. / math.pi
        print "The LSQ full mosaicity is %8.5f deg; half-mosaicity %9.5f" % (
            2 * k_degrees, k_degrees)
        tan_outer_deg = tan_phi_deg + k_degrees

        from xfel.mono_simulation.max_like import minimizer
        # coerce the estimates to be positive for max-likelihood
        lower_limit_domain_size = math.pow(
            crystal.get_unit_cell().volume(),
            1. / 3.) * 3  # params.refinement.domain_size_lower_limit

        d_estimate = max(s_ang, lower_limit_domain_size)
        M = minimizer(d_i=dspacings,
                      psi_i=excursion_rad,
                      eta_rad=abs(2. * solution[1]),
                      Deff=d_estimate)
        print "ML: mosaicity FW=%4.2f deg, Dsize=%5.0fA on %d spots" % (
            M.x[1] * 180. / math.pi, 2. / M.x[0], len(two_thetas))
        tan_phi_rad_ML = dspacings / (2. / M.x[0])
        tan_phi_deg_ML = tan_phi_rad_ML * 180. / math.pi
        tan_outer_deg_ML = tan_phi_deg_ML + 0.5 * M.x[1] * 180. / math.pi

        self.nv_acceptance_flags = flex.abs(delta_psi_deg) < tan_outer_deg_ML

        if self.graph_verbose:  #params.refinement.mosaic.enable_AD14F7B: # Excursion vs resolution fit
            AD1TF7B_MAX2T = 30.
            AD1TF7B_MAXDP = 1.
            from matplotlib import pyplot as plt
            plt.plot(two_thetas, delta_psi_deg, "bo")
            minplot = flex.min(two_thetas)
            plt.plot([0, minplot], [mean_excursion, mean_excursion], "k-")
            LR = flex.linear_regression(two_thetas, delta_psi_deg)
            model_y = LR.slope() * two_thetas + LR.y_intercept()
            plt.plot(two_thetas, model_y, "k-")

            plt.title("ML: mosaicity FW=%4.2f deg, Dsize=%5.0fA on %d spots" %
                      (M.x[1] * 180. / math.pi, 2. / M.x[0], len(two_thetas)))
            plt.plot(two_thetas, tan_phi_deg_ML, "r.")
            plt.plot(two_thetas, -tan_phi_deg_ML, "r.")
            plt.plot(two_thetas, tan_outer_deg_ML, "g.")
            plt.plot(two_thetas, -tan_outer_deg_ML, "g.")
            plt.xlim([0, AD1TF7B_MAX2T])
            plt.ylim([-AD1TF7B_MAXDP, AD1TF7B_MAXDP])
            plt.show()
            plt.close()

        from xfel.mono_simulation.util import green_curve_area
        self.green_curve_area = green_curve_area(two_thetas, tan_outer_deg_ML)
        print "The green curve area is ", self.green_curve_area

        crystal.set_half_mosaicity_deg(M.x[1] * 180. / (2. * math.pi))
        crystal.set_domain_size_ang(2. / M.x[0])
        self._ML_full_mosaicity_rad = M.x[1]
        self._ML_domain_size_ang = 2. / M.x[0]

        #params.refinement.mosaic.model_expansion_factor
        """The expansion factor should be initially set to 1, then expanded so that the # reflections matched becomes
    as close as possible to # of observed reflections input, in the last integration call.  Determine this by
    inspecting the output log file interactively.  Do not exceed the bare minimum threshold needed.
    The intention is to find an optimal value, global for a given dataset."""
        model_expansion_factor = 1.4
        crystal.set_half_mosaicity_deg(crystal.get_half_mosaicity_deg() *
                                       model_expansion_factor)
        crystal.set_domain_size_ang(crystal.get_domain_size_ang() /
                                    model_expansion_factor)

        return crystal
    def __init__(self, test_nave_model=False):
        # Set up experimental models with regular geometry
        from dxtbx.model import BeamFactory
        from dxtbx.model import GoniometerFactory
        from dxtbx.model import DetectorFactory

        # Beam along the Z axis
        self.beam = BeamFactory.make_beam(unit_s0=matrix.col((0, 0, 1)),
                                          wavelength=1.0)

        # Goniometer (used only for index generation) along X axis
        self.goniometer = GoniometerFactory.known_axis(matrix.col((1, 0, 0)))

        # Detector fast, slow along X, -Y; beam in the centre, 200 mm distance
        dir1 = matrix.col((1, 0, 0))
        dir2 = matrix.col((0, -1, 0))
        n = matrix.col((0, 0, 1))
        centre = matrix.col((0, 0, 200))
        npx_fast = npx_slow = 1000
        pix_size = 0.2
        origin = centre - (0.5 * npx_fast * pix_size * dir1 +
                           0.5 * npx_slow * pix_size * dir2)
        self.detector = DetectorFactory.make_detector(
            "PAD",
            dir1,
            dir2,
            origin,
            (pix_size, pix_size),
            (npx_fast, npx_slow),
            (0, 1.0e6),
        )

        # Cubic 100 A^3 crystal
        a = matrix.col((100, 0, 0))
        b = matrix.col((0, 100, 0))
        c = matrix.col((0, 0, 100))

        if test_nave_model:
            from dxtbx.model import MosaicCrystalSauter2014

            self.crystal = MosaicCrystalSauter2014(a,
                                                   b,
                                                   c,
                                                   space_group_symbol="P 1")
            self.crystal.set_half_mosaicity_deg(500)
            self.crystal.set_domain_size_ang(0.2)
        else:
            from dxtbx.model import Crystal

            self.crystal = Crystal(a, b, c, space_group_symbol="P 1")

        # Collect these models in an Experiment (ignoring the goniometer)
        from dxtbx.model.experiment_list import Experiment

        self.experiment = Experiment(
            beam=self.beam,
            detector=self.detector,
            goniometer=None,
            scan=None,
            crystal=self.crystal,
            imageset=None,
        )

        # Generate some reflections
        self.reflections = self.generate_reflections()