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
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)