This documentation page refers to a previous release of DIALS (2.2).
Click here to go to the corresponding page for the latest version of DIALS

Source code for dials.algorithms.integration.integrator

from __future__ import absolute_import, division, print_function

import collections
import functools
import logging
import math
import random
from dials.util import tabulate

import six
import six.moves.cPickle as pickle
from dials_algorithms_integration_integrator_ext import (
    Executor,
    JobList,
    ReflectionManager,
)
from dials.algorithms.integration.processor import build_processor
from dials.algorithms.integration.processor import Processor3D
from dials.algorithms.integration.processor import ProcessorFlat3D
from dials.algorithms.integration.processor import Processor2D
from dials.algorithms.integration.processor import ProcessorSingle2D
from dials.algorithms.integration.processor import ProcessorStills
from dials.algorithms.integration.processor import job
from dials.array_family import flex
from dials.util import phil
from dials.util import Sorry

logger = logging.getLogger(__name__)

__all__ = [
    "Executor",
    "Integrator",
    "Integrator2D",
    "Integrator3D",
    "Integrator3DThreaded",
    "IntegratorExecutor",
    "IntegratorFactory",
    "IntegratorFlat3D",
    "IntegratorSingle2D",
    "IntegratorStills",
    "JobList",
    "Parameters",
    "Processor2D",
    "Processor3D",
    "ProcessorFlat3D",
    "ProcessorSingle2D",
    "ProcessorStills",
    "ProfileModellerExecutor",
    "ProfileValidatorExecutor",
    "ReflectionManager",
    "frame_hist",
    "generate_phil_scope",
    "hist",
    "job",
    "nframes_hist",
    "phil_scope",
]


[docs]def generate_phil_scope(): """ Generate the integration phil scope. :return: The phil scope """ import dials.extensions phil_scope = phil.parse( """ integration { include scope dials.data.lookup.phil_scope block { size = auto .type = float .help = "The block size in rotation angle (degrees)." units = *degrees radians frames .type = choice .help = "The units of the block size" threshold = 0.95 .type = float(value_min=0.0, value_max=1.0) .help = "For block size auto the block size is calculated by sorting" "reflections by the number of frames they cover and then" "selecting the block size to be 2*nframes[threshold] such" "that 100*threshold % of reflections are guarenteed to be" "fully contained in 1 block" force = False .type = bool .help = "If the number of processors is 1 and force is False, then the" "number of blocks may be set to 1. If force is True then the" "block size is always calculated." max_memory_usage = 0.90 .type = float(value_min=0.0,value_max=1.0) .help = "The maximum percentage of available memory to use for" "allocating shoebox arrays." } use_dynamic_mask = True .type = bool .help = "Use dynamic mask if available" debug { reference { filename = "reference_profiles.refl" .type = str .help = "The filename for the reference profiles" output = False .type = bool .help = "Save the reference profiles" } during = modelling *integration .type = choice .help = "Do debugging during modelling or integration" output = False .type = bool .help = "Save shoeboxes after each processing task." separate_files = True .type = bool .help = "If this is true, the shoeboxes are saved in separate files" "from the output integrated.refl file. This is necessary" "in most cases since the amount of memory used by the" "shoeboxes is typically greater than the available system" "memory. If, however, you know that memory is not an issue," "you can saved the shoeboxes in the integrated.refl file" "by setting this option to False. This only works if the debug" "output is during integrated and not modelling." delete_shoeboxes = False .type = bool .help = "Delete shoeboxes immediately before saving files. This option" "in combination with debug.output=True enables intermediate" "processing steps to make use of shoeboxes." select = None .type = reflection_table_selector .help = "A string specifying the selection. The string should be of the" "form: select=${COLUMN}[<|<=|==|!=|>=|>]${VALUE}. In addition" "to the items in the reflection table, the following implicit" "columns are defined if the necessary data is there:" " intensity.sum.i_over_sigma" " intensity.prf.i_over_sigma" split_experiments = True .type = bool .help = "Split shoeboxes into different files" } integrator = *auto 3d flat3d 2d single2d stills 3d_threaded .type = choice .help = "The integrator to use." .expert_level=3 profile { fitting = True .type = bool .help = "Use profile fitting if available" validation { number_of_partitions = 1 .type = int(value_min=1) .help = "The number of subsamples to take from the reference spots." "If the value is 1, then no validation is performed." min_partition_size = 100 .type = int(value_min=1) .help = "The minimum number of spots to use in each subsample." } } filter .expert_level = 1 { min_zeta = 0.05 .help = "Filter the reflections by the value of zeta. A value of less" "than or equal to zero indicates that this will not be used. A" "positive value is used as the minimum permissable value." .type = float(value_min=0.0, value_max=1.0) max_shoebox_overlap = 1.0 .type = float(value_min=0.0, value_max=1.0) .help = "Filter reflections whose shoeboxes are overlapped by greater" "than the requested amount. Note that this is not the" "percentage of the peak that is overlapped but rather the" "percentage of the shoebox (background and foreground). This" "can be useful when the detector is too close and many" "overlapping reflections are predicted at high resolution" "causing memory issues." ice_rings = False .help = "Set the ice ring flags" .type = bool } include scope dials.algorithms.integration.overlaps_filter.phil_scope mp { method = *multiprocessing drmaa sge lsf pbs .type = choice .help = "The multiprocessing method to use" njobs = 1 .type = int(value_min=1) .help = "The number of cluster jobs to use" nproc = 1 .type = int(value_min=1) .help = "The number of processes to use per cluster job" } summation { detector_gain = 1 .type = float .help = "Multiplier for variances after integration of still images." "See Leslie 1999." } } """, process_includes=True, ) main_scope = phil_scope.get_without_substitution("integration") assert len(main_scope) == 1 main_scope = main_scope[0] main_scope.adopt_scope(dials.extensions.Background.phil_scope()) main_scope.adopt_scope(dials.extensions.Centroid.phil_scope()) return phil_scope
# The integration phil scope phil_scope = generate_phil_scope()
[docs]def hist(data, width=80, symbol="#", prefix=""): """ A utility function to print a histogram of reflections on frames. :param data: The data to histogram :param width: The number of characters in each line :param symbol: The plot symbol :param prefix: String to prefix to each line :return: The histogram string """ assert len(data) > 0, "Need > 0 reflections" assert width > 0, "Width should be > 0" count = collections.Counter(data) count = sorted(count.items()) frame, count = zip(*count) max_frame = max(frame) min_count = min(count) max_count = max(count) assert max_count > 0, "Max should be > 0" assert min_count >= 0, "Min should be >= 0" if max_frame == 0: num_frame_zeros = 1 else: num_frame_zeros = int(math.floor(math.log10(max_frame))) + 1 num_count_zeros = int(math.floor(math.log10(max_count))) + 1 assert num_frame_zeros > 0, "Num should be > 0" assert num_count_zeros > 0, "Num should be > 0" num_hist = width - (num_frame_zeros + num_count_zeros + 5) - len(prefix) assert num_hist > 0, "num_hist should be > 0" fmt = "%s%%-%dd [%%-%dd]: %%s" % (prefix, num_frame_zeros, num_count_zeros) scale = float(num_hist) / max_count return "\n".join( ( fmt % (key, value, int(value * scale) * symbol) for key, value in zip(frame, count) ) )
[docs]def frame_hist(bbox, width=80, symbol="#", prefix=""): """ A utility function to print a histogram of reflections on frames. :param bbox: The bounding boxes :param width: The width of each line :param symbol: The histogram symbol :param prefix: A string to prefix to each line :return: The histogram string """ return hist( [(z + 1) for b in bbox for z in range(b[4], b[5])], width=width, symbol=symbol, prefix=prefix, )
[docs]def nframes_hist(bbox, width=80, symbol="#", prefix=""): """ A utility function to print a histogram of number of frames. :param bbox: The bounding boxes :param width: The width of each line :param symbol: The histogram symbol :param prefix: A string to prefix to each line :return: The histogram string """ return hist([b[5] - b[4] for b in bbox], width=width, symbol=symbol, prefix=prefix)
[docs]class Parameters(object): """ A class to represent the integration parameters """
[docs] class Filter(object): """ Filter parameters """ def __init__(self): self.min_zeta = 0.05 self.powder_filter = None
[docs] class Profile(object): """ Profile parameters """
[docs] class Validation(object): def __init__(self): self.number_of_partitions = 2 self.min_partition_size = 100
def __init__(self): self.fitting = True self.validation = Parameters.Profile.Validation()
def __init__(self): """ Initialize """ from dials.algorithms.integration import processor self.modelling = processor.Parameters() self.integration = processor.Parameters() self.filter = Parameters.Filter() self.profile = Parameters.Profile() self.debug_reference_filename = "reference_profiles.refl" self.debug_reference_output = False
[docs] @staticmethod def from_phil(params): """ Convert the phil parameters """ from dials.algorithms.integration import processor from dials.algorithms.integration.filtering import IceRingFilter # Init the parameters result = Parameters() # Create the multi processing parameters mp = processor.MultiProcessing() mp.method = params.mp.method mp.nproc = params.mp.nproc mp.njobs = params.mp.njobs # Set the lookup parameters lookup = processor.Lookup() lookup.mask = params.lookup.mask # Set the block parameters block = processor.Block() block.size = params.block.size block.units = params.block.units block.threshold = params.block.threshold block.force = params.block.force block.max_memory_usage = params.block.max_memory_usage # Set the modelling processor parameters result.modelling.mp = mp result.modelling.lookup = lookup result.modelling.block = block if params.debug.during == "modelling": result.modelling.debug.output = params.debug.output result.modelling.debug.select = params.debug.select result.modelling.debug.separate_files = True # Set the integration processor parameters result.integration.mp = mp result.integration.lookup = lookup result.integration.block = block if params.debug.during == "integration": result.integration.debug.output = params.debug.output result.integration.debug.select = params.debug.select result.integration.debug.separate_files = params.debug.separate_files result.integration.summation = params.summation result.debug_reference_filename = params.debug.reference.filename result.debug_reference_output = params.debug.reference.output # Get the min zeta filter result.filter.min_zeta = params.filter.min_zeta if params.filter.ice_rings is True: result.filter.powder_filter = IceRingFilter() # Get post-integration overlap filtering parameters result.integration.overlaps_filter = params.overlaps_filter # Set the profile fitting parameters result.profile.fitting = params.profile.fitting result.profile.validation.number_of_partitions = ( params.profile.validation.number_of_partitions ) result.profile.validation.min_partition_size = ( params.profile.validation.min_partition_size ) # Return the result return result
def _initialize_rotation(experiments, params, reflections): """ A pre-processing class for oscillation data. """ # Compute some reflection properties reflections.compute_zeta_multi(experiments) reflections.compute_d(experiments) reflections.compute_bbox(experiments) # Filter the reflections by zeta mask = flex.abs(reflections["zeta"]) < params.filter.min_zeta reflections.set_flags(mask, reflections.flags.dont_integrate) # Filter the reflections by powder ring if params.filter.powder_filter is not None: mask = params.filter.powder_filter(reflections["d"]) reflections.set_flags(mask, reflections.flags.in_powder_ring) def _initialize_stills(experiments, params, reflections): """ A pre-processing class for stills data. """ # Compute some reflection properties reflections.compute_d(experiments) reflections.compute_bbox(experiments) # Check the bounding boxes are all 1 frame in width z0, z1 = reflections["bbox"].parts()[4:6] assert (z1 - z0).all_eq(1), "bbox is invalid" # Filter the reflections by powder ring if params.filter.powder_filter is not None: mask = params.filter.powder_filter(reflections["d"]) reflections.set_flags(mask, reflections.flags.in_powder_ring) def _finalize(reflections, experiments, params): """ A generic post-processing function. """ overlaps_scope = params.integration.overlaps_filter if True in [ overlaps_scope.foreground_foreground.enable, overlaps_scope.foreground_background.enable, ]: from dials.algorithms.integration.overlaps_filter import OverlapsFilterMultiExpt overlaps_filter = OverlapsFilterMultiExpt(reflections, experiments) if overlaps_scope.foreground_foreground.enable: overlaps_filter.remove_foreground_foreground_overlaps() if overlaps_scope.foreground_background.enable: overlaps_filter.remove_foreground_background_overlaps() reflections = overlaps_filter.refl return reflections, experiments def _finalize_rotation(reflections, experiments, params): """ A post-processing function for oscillation data. """ reflections, experiments = _finalize(reflections, experiments, params) # Compute the corrections reflections.compute_corrections(experiments) return reflections, experiments def _finalize_stills(reflections, experiments, params): """ A post-processing function for stills data. """ reflections, experiments = _finalize(reflections, experiments, params) integrated = reflections # Select only those reflections which were integrated if "intensity.prf.variance" in integrated: selection = integrated.get_flags(integrated.flags.integrated, all=True) else: selection = integrated.get_flags(integrated.flags.integrated_sum) integrated = integrated.select(selection) len_all = len(integrated) integrated = integrated.select( ~integrated.get_flags(integrated.flags.foreground_includes_bad_pixels) ) logger.info( "Filtering %d reflections with at least one bad foreground pixel out of %d" % (len_all - len(integrated), len_all) ) # verify sigmas are sensible if "intensity.prf.value" in integrated: if (integrated["intensity.prf.variance"] <= 0).count(True) > 0: raise Sorry( "Found negative variances (prf). Are bad pixels properly masked out?" ) if "intensity.sum.value" in integrated: if (integrated["intensity.sum.variance"] <= 0).count(True) > 0: if (integrated["intensity.sum.variance"] < 0).count(True) > 0: raise Sorry( "Found negative variances (sum). Are bad pixels properly masked out?" ) n = (integrated["intensity.sum.variance"] == 0).count(True) sel = (integrated["intensity.sum.variance"] == 0) & ( integrated["intensity.sum.value"] == 0 ) if n == sel.count(True): logger.info( "Filtering %d reflections with no integrated signal (sum and variance = 0) out of %d" % (n, len(integrated)) ) integrated = integrated.select(integrated["intensity.sum.variance"] > 0) else: raise Sorry( "Found reflections with variances == 0 but summed signal != 0" ) # apply detector gain to summation variances integrated[ "intensity.sum.variance" ] *= params.integration.summation.detector_gain if "background.sum.value" in integrated: if (integrated["background.sum.variance"] < 0).count(True) > 0: raise Sorry( "Found negative variances (background sum). Are bad pixels properly masked out?" ) if (integrated["background.sum.variance"] == 0).count(True) > 0: logger.info( "Filtering %d reflections with zero background variance" % ((integrated["background.sum.variance"] == 0).count(True)) ) integrated = integrated.select(integrated["background.sum.variance"] > 0) # apply detector gain to background summation variances integrated[ "background.sum.variance" ] *= params.integration.summation.detector_gain reflections = integrated return reflections, experiments
[docs]class ProfileModellerExecutor(Executor): """ The class to do profile modelling calculations """ def __init__(self, experiments, profile_fitter): """ Initialise the executor :param experiments: The experiment list """ self.experiments = experiments self.profile_fitter = profile_fitter super(ProfileModellerExecutor, self).__init__()
[docs] def initialize(self, frame0, frame1, reflections): """ Initialise the processing for a job :param frame0: The first frame in the job :param frame1: The last frame in the job :param reflections: The reflections that will be processed """ # Get some info EPS = 1e-7 full_value = 0.997300203937 - EPS fully_recorded = reflections["partiality"] > full_value npart = fully_recorded.count(False) nfull = fully_recorded.count(True) nice = reflections.get_flags(reflections.flags.in_powder_ring).count(True) ntot = len(reflections) # Write some output logger.info(" Beginning modelling job %d" % job.index) logger.info("") logger.info(" Frames: %d -> %d" % (frame0, frame1)) logger.info("") logger.info(" Number of reflections") logger.info(" Partial: %d" % npart) logger.info(" Full: %d" % nfull) logger.info(" In ice ring: %d" % nice) logger.info(" Total: %d" % ntot) logger.info("") # Print a histogram of reflections on frames if frame1 - frame0 > 1: logger.info( " The following histogram shows the number of reflections predicted" ) logger.info(" to have all or part of their intensity on each frame.") logger.info("") logger.info(frame_hist(reflections["bbox"], prefix=" ", symbol="*")) logger.info("")
[docs] def process(self, frame, reflections): """ Process the data :param frame: The frame being processed :param reflections: The reflections to process """ # Check if pixels are overloaded reflections.is_overloaded(self.experiments) # Compute the shoebox mask reflections.compute_mask(self.experiments) # Process the data reflections.compute_background(self.experiments) reflections.compute_centroid(self.experiments) reflections.compute_summed_intensity() # Do the profile modelling self.profile_fitter.model(reflections) # Print some info fmt = " Modelled % 5d / % 5d reflection profiles on image %d" nmod = reflections.get_flags(reflections.flags.used_in_modelling).count(True) ntot = len(reflections) logger.info(fmt % (nmod, ntot, frame))
[docs] def finalize(self): """ Finalize the processing """ pass
[docs] def data(self): """ :return: the modeller """ return self.profile_fitter
def __getinitargs__(self): """ Support for pickling """ return (self.experiments, self.profile_fitter)
[docs]class ProfileValidatorExecutor(Executor): """ The class to do profile validation calculations """ def __init__(self, experiments, profile_fitter): """ Initialise the executor :param experiments: The experiment list """ self.experiments = experiments self.profile_fitter = profile_fitter super(ProfileValidatorExecutor, self).__init__()
[docs] def initialize(self, frame0, frame1, reflections): """ Initialise the processing for a job :param frame0: The first frame in the job :param frame1: The last frame in the job :param reflections: The reflections that will be processed """ # Get some info EPS = 1e-7 full_value = 0.997300203937 - EPS fully_recorded = reflections["partiality"] > full_value npart = fully_recorded.count(False) nfull = fully_recorded.count(True) nice = reflections.get_flags(reflections.flags.in_powder_ring).count(True) ntot = len(reflections) # Write some output logger.info(" Beginning modelling job %d" % job.index) logger.info("") logger.info(" Frames: %d -> %d" % (frame0, frame1)) logger.info("") logger.info(" Number of reflections") logger.info(" Partial: %d" % npart) logger.info(" Full: %d" % nfull) logger.info(" In ice ring: %d" % nice) logger.info(" Total: %d" % ntot) logger.info("") # Print a histogram of reflections on frames if frame1 - frame0 > 1: logger.info( " The following histogram shows the number of reflections predicted" ) logger.info(" to have all or part of their intensity on each frame.") logger.info("") logger.info(frame_hist(reflections["bbox"], prefix=" ", symbol="*")) logger.info("") self.results = None
[docs] def process(self, frame, reflections): """ Process the data :param frame: The frame being processed :param reflections: The reflections to process """ # Check if pixels are overloaded reflections.is_overloaded(self.experiments) # Compute the shoebox mask reflections.compute_mask(self.experiments) # Process the data reflections.compute_background(self.experiments) reflections.compute_centroid(self.experiments) reflections.compute_summed_intensity() # Do the profile validation self.results = self.profile_fitter.validate(reflections) # Print some info fmt = " Validated % 5d / % 5d reflection profiles on image %d" nmod = reflections.get_flags(reflections.flags.used_in_modelling).count(True) ntot = len(reflections) logger.info(fmt % (nmod, ntot, frame))
[docs] def finalize(self): """ Finalize the processing """ pass
[docs] def data(self): """ :return: the modeller """ return self.results
def __getinitargs__(self): """ Support for pickling """ return (self.experiments, self.profile_fitter)
[docs]class IntegratorExecutor(Executor): """ The class to process the integration data """ def __init__(self, experiments, profile_fitter=None): """ Initialize the executor :param experiments: The experiment list """ self.experiments = experiments self.overlaps = None self.profile_fitter = profile_fitter super(IntegratorExecutor, self).__init__()
[docs] def initialize(self, frame0, frame1, reflections): """ Initialize the processing for the job :param frame0: The first frame to process :param frame1: The last frame to process :param reflections: The reflections to process """ # Get some info EPS = 1e-7 full_value = 0.997300203937 - EPS fully_recorded = reflections["partiality"] > full_value npart = fully_recorded.count(False) nfull = fully_recorded.count(True) nice = reflections.get_flags(reflections.flags.in_powder_ring).count(True) nint = reflections.get_flags(reflections.flags.dont_integrate).count(False) ntot = len(reflections) # Write some output logger.info(" Beginning integration job %d" % job.index) logger.info("") logger.info(" Frames: %d -> %d" % (frame0, frame1)) logger.info("") logger.info(" Number of reflections") logger.info(" Partial: %d" % npart) logger.info(" Full: %d" % nfull) logger.info(" In ice ring: %d" % nice) logger.info(" Integrate: %d" % nint) logger.info(" Total: %d" % ntot) logger.info("") # Print a histogram of reflections on frames if frame1 - frame0 > 1: logger.info( " The following histogram shows the number of reflections predicted" ) logger.info(" to have all or part of their intensity on each frame.") logger.info("") logger.info(frame_hist(reflections["bbox"], prefix=" ", symbol="*")) logger.info("") # Find any overlaps self.overlaps = reflections.find_overlaps(self.experiments)
[docs] def process(self, frame, reflections): """ Process the reflections on a frame :param frame: The frame to process :param reflections: The reflections to process """ from dials.algorithms.shoebox import MaskCode # Check if pixels are overloaded reflections.is_overloaded(self.experiments) # Compute the shoebox mask reflections.compute_mask(self.experiments) # Check for invalid pixels in foreground/background reflections.contains_invalid_pixels() # Process the data reflections.compute_background(self.experiments) reflections.compute_centroid(self.experiments) reflections.compute_summed_intensity() if self.profile_fitter: reflections.compute_fitted_intensity(self.profile_fitter) # from matplotlib import pylab # from dials.array_family import flex # I = reflections['intensity.sum.value'] # F = reflections.get_flags(reflections.flags.integrated_sum) # A = flex.size_t(range(len(reflections))) # A = A.select(F) # I = I.select(F) # index = flex.max_index(I) # index = A[index] # sbox = reflections['shoebox'][index] # zmin, zmax = sbox.bbox[4:6] # zind = int((zmin + zmax) / 2) # image1 = sbox.background.as_numpy_array().sum(axis=0) # image2 = sbox.data.as_numpy_array().sum(axis=0) # vmin=image1.min()#min([image1.min(), image2.min()]) # vmax=image1.max()#min([image1.max(), image2.max()]) # print image1.min(), image1.max(), image2.min(), image2.max() # pylab.subplot(121) # pylab.imshow(image1, interpolation='none', vmin=vmin, vmax=vmax) # pylab.subplot(122) # pylab.imshow(image2, interpolation='none', vmin=vmin, vmax=vmax) # pylab.show() # Compute the number of background/foreground pixels sbox = reflections["shoebox"] code1 = MaskCode.Valid code2 = MaskCode.Background | code1 code3 = MaskCode.BackgroundUsed | code2 code4 = MaskCode.Foreground | code1 reflections["num_pixels.valid"] = sbox.count_mask_values(code1) reflections["num_pixels.background"] = sbox.count_mask_values(code2) reflections["num_pixels.background_used"] = sbox.count_mask_values(code3) reflections["num_pixels.foreground"] = sbox.count_mask_values(code4) # Print some info fmt = " Integrated % 5d (sum) + % 5d (prf) /% 5d reflections on image %d" nsum = reflections.get_flags(reflections.flags.integrated_sum).count(True) nprf = reflections.get_flags(reflections.flags.integrated_prf).count(True) ntot = len(reflections) logger.info(fmt % (nsum, nprf, ntot, frame))
[docs] def finalize(self): """ Finalize the processing """ pass
[docs] def data(self): """ Return data """ return None
def __getinitargs__(self): """ Support for pickling """ return (self.experiments, self.profile_fitter)
[docs]class Integrator(object): """ The integrator class """ def __init__(self, experiments, reflections, params): """ Initialize the integrator :param experiments: The experiment list :param reflections: The reflections to process :param params: The parameters to use """ # Save some stuff self.experiments = experiments self.reflections = reflections self.params = Parameters.from_phil(params.integration) self.profile_model_report = None self.integration_report = None
[docs] def integrate(self): """ Integrate the data """ from dials.algorithms.integration.report import IntegrationReport from dials.algorithms.integration.report import ProfileModelReport from dials.algorithms.integration.report import ProfileValidationReport from dials.util.command_line import heading from dials.util import pprint from dials.algorithms.profile_model.modeller import MultiExpProfileModeller from dials.algorithms.integration.validation import ( ValidatedMultiExpProfileModeller, ) # Ensure we get the same random sample each time random.seed(0) # Init the report self.profile_model_report = None self.integration_report = None # Heading logger.info("=" * 80) logger.info("") logger.info(heading("Processing reflections")) logger.info("") # Create summary format fmt = ( " Processing the following experiments:\n" "\n" " Experiments: %d\n" " Beams: %d\n" " Detectors: %d\n" " Goniometers: %d\n" " Scans: %d\n" " Crystals: %d\n" " Imagesets: %d\n" ) # Print the summary logger.info( fmt % ( len(self.experiments), len(self.experiments.beams()), len(self.experiments.detectors()), len(self.experiments.goniometers()), len(self.experiments.scans()), len(self.experiments.crystals()), len(self.experiments.imagesets()), ) ) # Initialize the reflections self.initialize_reflections(self.experiments, self.params, self.reflections) # Check if we want to do some profile fitting fitting_class = [e.profile.fitting_class() for e in self.experiments] fitting_avail = all(c is not None for c in fitting_class) if self.params.profile.fitting and fitting_avail: profile_fitting = True profile_fitter = None else: profile_fitting = False profile_fitter = None # Do profile modelling if profile_fitting: logger.info("=" * 80) logger.info("") logger.info(heading("Modelling reflection profiles")) logger.info("") # Get the selection selection = self.reflections.get_flags( self.reflections.flags.reference_spot ) # Get the reference spots reference = self.reflections.select(selection) # Check if we need to skip if len(reference) == 0: logger.info( "** Skipping profile modelling - no reference profiles given **" ) else: # Try to set up the validation if self.params.profile.validation.number_of_partitions > 1: n = len(reference) k_max = int( math.floor( n / self.params.profile.validation.min_partition_size ) ) if k_max < self.params.profile.validation.number_of_partitions: num_folds = k_max else: num_folds = self.params.profile.validation.number_of_partitions if num_folds > 1: indices = ( list(range(num_folds)) * int(math.ceil(n / num_folds)) )[0:n] random.shuffle(indices) reference["profile.index"] = flex.size_t(indices) if num_folds < 1: num_folds = 1 else: num_folds = 1 # Create the profile fitter profile_fitter = ValidatedMultiExpProfileModeller() for i in range(num_folds): profile_fitter_single = MultiExpProfileModeller() # (num_folds) for expr in self.experiments: profile_fitter_single.add(expr.profile.fitting_class()(expr)) profile_fitter.add(profile_fitter_single) # Create the data processor executor = ProfileModellerExecutor(self.experiments, profile_fitter) processor = build_processor( self.ProcessorClass, self.experiments, reference, self.params.modelling, ) processor.executor = executor # Process the reference profiles reference, profile_fitter_list, time_info = processor.process() # Set the reference spots info # self.reflections.set_selected(selection, reference) # Finalize the profile models for validation assert len(profile_fitter_list) > 0, "No profile fitters" profile_fitter = None for pf in profile_fitter_list.values(): if pf is None: continue if profile_fitter is None: profile_fitter = pf else: profile_fitter.accumulate(pf) profile_fitter.finalize() # Get the finalized modeller finalized_profile_fitter = profile_fitter.finalized_model() # Print profiles if self.params.debug_reference_output: reference_debug = [] for i in range(len(finalized_profile_fitter)): m = finalized_profile_fitter[i] p = [] for j in range(len(m)): try: p.append((m.data(j), m.mask(j))) except Exception: p.append(None) reference_debug.append(p) with open(self.params.debug_reference_filename, "wb") as outfile: pickle.dump(reference_debug, outfile) for i in range(len(finalized_profile_fitter)): m = finalized_profile_fitter[i] logger.debug("") logger.debug("Profiles for experiment %d" % i) for j in range(len(m)): logger.debug("Profile %d" % j) try: logger.debug(pprint.profile3d(m.data(j))) except Exception: logger.debug("** NO PROFILE **") # Print the modeller report self.profile_model_report = ProfileModelReport( self.experiments, finalized_profile_fitter, reference ) logger.info("") logger.info(self.profile_model_report.as_str(prefix=" ")) # Print the time info logger.info("") logger.info("Timing information for reference profile formation") logger.info(str(time_info)) logger.info("") # If we have more than 1 fold then do the validation if num_folds > 1: # Create the data processor executor = ProfileValidatorExecutor( self.experiments, profile_fitter ) processor = build_processor( self.ProcessorClass, self.experiments, reference, self.params.modelling, ) processor.executor = executor # Process the reference profiles reference, validation, time_info = processor.process() # Print the modeller report self.profile_validation_report = ProfileValidationReport( self.experiments, profile_fitter, reference, num_folds ) logger.info("") logger.info(self.profile_validation_report.as_str(prefix=" ")) # Print the time info logger.info("") logger.info("Timing information for reference profile validation") logger.info(str(time_info)) logger.info("") # Set to the finalized fitter profile_fitter = finalized_profile_fitter logger.info("=" * 80) logger.info("") logger.info(heading("Integrating reflections")) logger.info("") # Create the data processor executor = IntegratorExecutor(self.experiments, profile_fitter) processor = build_processor( self.ProcessorClass, self.experiments, self.reflections, self.params.integration, ) processor.executor = executor # Process the reflections self.reflections, _, time_info = processor.process() # Finalize the reflections self.reflections, self.experiments = self.finalize_reflections( self.reflections, self.experiments, self.params ) # Create the integration report self.integration_report = IntegrationReport(self.experiments, self.reflections) logger.info("") logger.info(self.integration_report.as_str(prefix=" ")) # Print the time info logger.info("Timing information for integration") logger.info(str(time_info)) logger.info("") # Return the reflections return self.reflections
[docs] def report(self): """ Return the report of the processing """ from dials.util.report import Report result = Report() if self.profile_model_report is not None: result.combine(self.profile_model_report) result.combine(self.integration_report) return result
[docs] def summary(self, block_size, block_size_units): """Print a summary of the integration stuff.""" # Compute the task table if self._experiments.all_stills(): rows = [["#", "Group", "Frame From", "Frame To"]] for i in range(len(self)): job = self._manager.job(i) group = job.index() f0, f1 = job.frames() rows.append([str(i), str(group), str(f0), str(f1)]) elif self._experiments.all_sequences(): rows = [["#", "Group", "Frame From", "Frame To", "Angle From", "Angle To"]] for i in range(len(self)): job = self._manager.job(i) group = job.index() expr = job.expr() f0, f1 = job.frames() scan = self._experiments[expr[0]].scan p0 = scan.get_angle_from_array_index(f0) p1 = scan.get_angle_from_array_index(f1) rows.append([str(i), str(group), str(f0), str(f1), str(p0), str(p1)]) else: raise RuntimeError("Experiments must be all sequences or all stills") return tabulate(rows, headers="firstrow")
[docs]class Integrator3D(Integrator): """ Integrator for 3D algorithms """ initialize_reflections = staticmethod(_initialize_rotation) ProcessorClass = Processor3D finalize_reflections = staticmethod(_finalize_rotation)
[docs]class IntegratorFlat3D(Integrator): """ Integrator for flattened 3D algorithms """ initialize_reflections = staticmethod(_initialize_rotation) ProcessorClass = ProcessorFlat3D finalize_reflections = staticmethod(_finalize_rotation)
[docs]class Integrator2D(Integrator): """ Integrator for 2D algorithms """ initialize_reflections = staticmethod(_initialize_rotation) ProcessorClass = Processor2D finalize_reflections = staticmethod(_finalize_rotation)
[docs]class IntegratorSingle2D(Integrator): """ Integrator for 2D algorithms on a single image """ initialize_reflections = staticmethod(_initialize_rotation) ProcessorClass = ProcessorSingle2D finalize_reflections = staticmethod(_finalize_rotation)
[docs]class IntegratorStills(Integrator): """ Integrator for still algorithms """ initialize_reflections = staticmethod(_initialize_stills) ProcessorClass = ProcessorStills finalize_reflections = staticmethod(_finalize_stills)
[docs]class Integrator3DThreaded(object): """ Integrator for 3D algorithms """ def __init__(self, experiments, reflections, params): """ Initialize the integrator :param experiments: The experiment list :param reflections: The reflections to process :param params: The parameters to use """ # Save some stuff self.experiments = experiments self.reflections = reflections self.params = params self.profile_model_report = None self.integration_report = None
[docs] def initialise(self): """ Initialise the integrator """ # Compute some reflection properties self.reflections.compute_zeta_multi(self.experiments) self.reflections.compute_d(self.experiments) self.reflections.compute_bbox(self.experiments) # Filter the reflections by zeta mask = ( flex.abs(self.reflections["zeta"]) < self.params.integration.filter.min_zeta ) self.reflections.set_flags(mask, self.reflections.flags.dont_integrate)
# Filter the reflections by powder ring # if self.params.integration.filter.powder_filter is not None: # mask = self.params.integration.filter.powder_filter(self.reflections['d']) # self.reflections.set_flags(mask, self.reflections.flags.in_powder_ring)
[docs] def finalise(self): """ Finalise the integrator """ # Compute the corrections self.reflections.compute_corrections(self.experiments)
[docs] def integrate(self): """ Integrate the data """ from dials.algorithms.integration.parallel_integrator import ( ReferenceCalculatorProcessor, ) from dials.algorithms.integration.parallel_integrator import IntegratorProcessor from dials.algorithms.integration.report import IntegrationReport from dials.util.command_line import heading # Init the report self.profile_model_report = None self.integration_report = None # Heading logger.info("=" * 80) logger.info("") logger.info(heading("Processing reflections")) logger.info("") # Create summary format fmt = ( " Processing the following experiments:\n" "\n" " Experiments: %d\n" " Beams: %d\n" " Detectors: %d\n" " Goniometers: %d\n" " Scans: %d\n" " Crystals: %d\n" " Imagesets: %d\n" ) # Print the summary logger.info( fmt % ( len(self.experiments), len(self.experiments.beams()), len(self.experiments.detectors()), len(self.experiments.goniometers()), len(self.experiments.scans()), len(self.experiments.crystals()), len(self.experiments.imagesets()), ) ) # Do the initialisation self.initialise() # Do profile modelling if self.params.integration.profile.fitting: logger.info("=" * 80) logger.info("") logger.info(heading("Modelling reflection profiles")) logger.info("") # Compute the reference profiles reference_calculator = ReferenceCalculatorProcessor( experiments=self.experiments, reflections=self.reflections, params=self.params, ) # Get the reference profiles self.reference_profiles = reference_calculator.profiles() else: self.reference_profiles = None logger.info("=" * 80) logger.info("") logger.info(heading("Integrating reflections")) logger.info("") integrator = IntegratorProcessor( experiments=self.experiments, reflections=self.reflections, reference=self.reference_profiles, params=self.params, ) # Process the reflections self.reflections = integrator.reflections() # Do the finalisation self.finalise() # Create the integration report self.integration_report = IntegrationReport(self.experiments, self.reflections) logger.info("") logger.info(self.integration_report.as_str(prefix=" ")) # Print the time info # logger.info("Timing information for integration") # logger.info(str(time_info)) # logger.info("") # Return the reflections return self.reflections
[docs] def report(self): """ Return the report of the processing """ from dials.util.report import Report result = Report() if self.profile_model_report is not None: result.combine(self.profile_model_report) result.combine(self.integration_report) return result
[docs] def summary(self, block_size, block_size_units): """Print a summary of the integration stuff.""" # Compute the task table if self._experiments.all_stills(): rows = [["#", "Group", "Frame From", "Frame To"]] for i in range(len(self)): job = self._manager.job(i) group = job.index() f0, f1 = job.frames() rows.append([str(i), str(group), str(f0), str(f1)]) elif self._experiments.all_sequences(): rows = [["#", "Group", "Frame From", "Frame To", "Angle From", "Angle To"]] for i in range(len(self)): job = self._manager.job(i) group = job.index() expr = job.expr() f0, f1 = job.frames() scan = self._experiments[expr[0]].scan p0 = scan.get_angle_from_array_index(f0) p1 = scan.get_angle_from_array_index(f1) rows.append([str(i), str(group), str(f0), str(f1), str(p0), str(p1)]) else: raise RuntimeError("Experiments must be all sequences or all stills") return tabulate(rows, headers="firstrow")
[docs]class IntegratorFactory(object): """ A factory for creating integrators. """
[docs] @staticmethod def create(params, experiments, reflections): """ Create the integrator from the input configuration. :param params: The input phil parameters :param experiments: The list of experiments :param reflections: The reflections to integrate :return: The integrator class """ import dials.extensions from dials.util import Sorry # Check each experiment has an imageset for exp in experiments: if exp.imageset is None: raise Sorry( """ One or more experiment does not contain an imageset. Access to the image data is crucial for integration. """ ) # Read the mask in if necessary if params.integration.lookup.mask and isinstance( params.integration.lookup.mask, str ): with open(params.integration.lookup.mask, "rb") as infile: if six.PY3: params.integration.lookup.mask = pickle.load( infile, encoding="bytes" ) else: params.integration.lookup.mask = pickle.load(infile) # Set algorithms as reflection table defaults BackgroundAlgorithm = dials.extensions.Background.load( params.integration.background.algorithm ) flex.reflection_table.background_algorithm = functools.partial( BackgroundAlgorithm, params ) CentroidAlgorithm = dials.extensions.Centroid.load( params.integration.centroid.algorithm ) flex.reflection_table.centroid_algorithm = functools.partial( CentroidAlgorithm, params ) # Get the classes we need if params.integration.integrator == "auto": if experiments.all_stills(): params.integration.integrator = "stills" else: params.integration.integrator = "3d" if params.integration.integrator == "3d": IntegratorClass = Integrator3D elif params.integration.integrator == "flat3d": IntegratorClass = IntegratorFlat3D elif params.integration.integrator == "2d": IntegratorClass = Integrator2D elif params.integration.integrator == "single2d": IntegratorClass = IntegratorSingle2D elif params.integration.integrator == "stills": IntegratorClass = IntegratorStills elif params.integration.integrator == "3d_threaded": IntegratorClass = Integrator3DThreaded else: raise RuntimeError("Unknown integration type") # Remove scan if stills if experiments.all_stills(): for experiment in experiments: experiment.scan = None # Return an instantiation of the class return IntegratorClass(experiments, reflections, params)