예제 #1
0
    def run(self):
        """Run the pychef analysis."""
        self.stats = Statistics(
            self.intensities,
            self.dose,
            n_bins=self.params.resolution_bins,
            range_min=self.params.range.min,
            range_max=self.params.range.max,
            range_width=self.params.range.width,
        )

        logger.debug(self.stats.completeness_vs_dose_str())
        logger.debug(self.stats.rcp_vs_dose_str())
        logger.debug(self.stats.scp_vs_dose_str())
        logger.debug(self.stats.rd_vs_dose_str())
    def radiation_damage_analysis(self):
        from dials.pychef import Statistics

        intensities_all, batches_all, _ = self._data_manager.reflections_as_miller_arrays(
            combined=False)

        intensities_combined = None
        dose_combined = None
        for i, (intensities,
                batches) in enumerate(zip(intensities_all, batches_all)):
            dose = batches.array(batches.data() -
                                 self._data_manager.experiments[i].scan.
                                 get_batch_offset()).set_info(batches.info())
            if intensities_combined is None:
                intensities_combined = intensities
                dose_combined = dose
            else:
                intensities_combined = intensities_combined.concatenate(
                    intensities, assert_is_similar_symmetry=False)
                dose_combined = dose_combined.concatenate(
                    dose, assert_is_similar_symmetry=False)

        stats = Statistics(intensities, dose.data())

        logger.debug(stats.completeness_vs_dose_str())
        logger.debug(stats.rcp_vs_dose_str())
        logger.debug(stats.scp_vs_dose_str())
        logger.debug(stats.rd_vs_dose_str())

        with open("chef.json", "wb") as f:
            json.dump(stats.to_dict(), f)

        self._chef_stats = stats
        return stats
예제 #3
0
class PychefRunner:
    """Class to prepare input data and run the pychef algorithm."""
    def __init__(self, intensities, dose, params):
        self.params = params
        self.stats = None
        sel = dose >= 0
        self.intensities = intensities.select(sel)
        self.dose = dose.select(sel)
        self.resolution_filter()

    def resolution_filter(self):
        """Filter arrays on resolution."""
        if not self.params.d_min and self.params.min_completeness:
            # Update self.params.d_min using resolution estimate
            params = resolution_analysis.phil_defaults.extract().resolution
            params.nbins = self.params.resolution_bins
            r = resolution_analysis.Resolutionizer(self.intensities, params)
            self.params.d_min = r.resolution(
                resolution_analysis.metrics.COMPLETENESS,
                limit=self.params.min_completeness,
            ).d_min
            logger.info("Estimated d_min: %.2f", self.params.d_min)

        if self.params.d_min or self.params.d_max:
            sel = flex.bool(self.intensities.size(), True)
            d_spacings = self.intensities.d_spacings().data()
            if self.params.d_min:
                sel &= d_spacings >= self.params.d_min
            if self.params.d_max:
                sel &= d_spacings <= self.params.d_max
            self.intensities = self.intensities.select(sel)
            self.dose = self.dose.select(sel)

    @classmethod
    def from_mtz(cls, params, mtz_object):
        """Initialise the class from mtz input.

        Args:
            params: A damage-analysis phil params object
            mtz_object: An iotbx.mtz.object.
        """
        miller_arrays = mtz_object.as_miller_arrays(merge_equivalents=False,
                                                    anomalous=params.anomalous)

        intensities = None
        batches = None
        dose = None

        for ma in miller_arrays:
            if ma.info().labels == ["BATCH"]:
                batches = ma
            elif ma.info().labels == ["DOSE"]:
                dose = ma
            elif ma.info().labels == ["I", "SIGI"]:
                intensities = ma
            elif ma.info().labels == ["I(+)", "SIGI(+)", "I(-)", "SIGI(-)"]:
                intensities = ma
        if not intensities:
            raise KeyError("Intensity array not found in mtz file")
        if not batches:
            raise KeyError("Batch array not found in mtz file")

        indices = mtz_object.extract_original_index_miller_indices()
        intensities = intensities.customized_copy(indices=indices)
        batches = batches.customized_copy(indices=indices)

        if params.anomalous:
            intensities = intensities.as_anomalous_array()
            batches = batches.as_anomalous_array()

        if dose is None:
            dose = batches_to_dose(batches.data(), params.dose)
        else:
            dose = dose.data()

        return cls(intensities, dose, params)

    @classmethod
    def from_dials_data_files(cls, params, experiments, reflection_table):
        """Initialise the class from an experiment list and reflection table.

        Args:
            params: A damage-analysis phil params object
            experiments: An ExperimentList
            reflection_table: A reflection table.
        """
        reflection_table = filter_reflection_table(reflection_table,
                                                   intensity_choice=["scale"],
                                                   partiality_threshold=0.4)

        # get scaled intensities
        intensities = miller.array(
            miller.set(
                crystal.symmetry(
                    unit_cell=median_unit_cell(experiments),
                    space_group=experiments[0].crystal.get_space_group(),
                ),
                indices=reflection_table["miller_index"],
                anomalous_flag=params.anomalous,
            ),
            data=reflection_table["intensity.scale.value"],
            sigmas=flex.sqrt(reflection_table["intensity.scale.variance"]),
        )
        intensities.set_observation_type_xray_intensity()

        doses = flex.double()
        start_doses, doses_per_image = interpret_images_to_doses_options(
            experiments,
            params.dose.experiments.dose_per_image,
            params.dose.experiments.starting_doses,
            params.dose.experiments.shared_crystal,
        )
        logger.info(
            "Interpreting data using:\n  starting_doses=%s\n  dose_per_image=%s",
            ", ".join("%s" % i for i in start_doses)
            if len(set(start_doses)) > 1 else f" all {start_doses[0]}",
            ", ".join("%s" % i for i in doses_per_image)
            if len(set(doses_per_image)) > 1 else f" all {doses_per_image[0]}",
        )

        for expt, starting_dose, dose_per_img in zip(experiments, start_doses,
                                                     doses_per_image):
            refls = reflection_table.select(expt)
            imgno = flex.ceil(refls["xyzobs.px.value"].parts()[2])
            dose = (imgno * dose_per_img) + starting_dose
            doses.extend(dose)

        doses = doses.iround()

        return cls(intensities, doses, params)

    def run(self):
        """Run the pychef analysis."""
        self.stats = Statistics(
            self.intensities,
            self.dose,
            n_bins=self.params.resolution_bins,
            range_min=self.params.range.min,
            range_max=self.params.range.max,
            range_width=self.params.range.width,
        )

        logger.debug(self.stats.completeness_vs_dose_str())
        logger.debug(self.stats.rcp_vs_dose_str())
        logger.debug(self.stats.scp_vs_dose_str())
        logger.debug(self.stats.rd_vs_dose_str())

    def make_html_report(self, html_filename=None, json_filename=None):
        """Generate html report from pychef stats."""
        data = {"dose_plots": self.stats.to_dict()}
        if html_filename:
            logger.info("Writing html report to: %s", html_filename)
            loader = ChoiceLoader([
                PackageLoader("dials", "templates"),
                PackageLoader("dials", "static", encoding="utf-8"),
            ])
            env = Environment(loader=loader)
            template = env.get_template("simple_report.html")
            html = template.render(
                page_title="Damage analysis report",
                panel_title="Damage analysis plots",
                panel_id="dose_plots",
                graphs=data["dose_plots"],
            )
            with open(html_filename, "wb") as f:
                f.write(html.encode("utf-8", "xmlcharrefreplace"))
        if json_filename:
            logger.info("Writing html report data to: %s", json_filename)
            with open(json_filename, "w") as outfile:
                json.dump(data, outfile)