예제 #1
0
    def __init__(self,
                 address,
                 detz_offset=575,
                 check_beam_status=True,
                 verbose=False,
                 delta_k=0.0,
                 override_energy=None,
                 **kwds):
        """The mod_event_info class constructor stores the
    parameters passed from the pyana configuration file in instance
    variables.

    @param address           Full data source address of the DAQ
                             device
    @param detz_offset       The distance from the interaction region
                             to the back of the detector stage, in mm
    @param check_beam_status Flag used to skip checking the beam
                             parameters
    @param delta_k           Correction to the K value used when calculating
                             wavelength
    @param override_energy   Use this energy instead of what is in the
                             XTC stream
    """
        logging.basicConfig()
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.setLevel(logging.INFO)

        # The subclasses accept keyword arguments; warn about any
        # unhandled arguments at the end of the inheritance chain.
        if len(kwds) > 0:
            self.logger.warning("Ignored unknown arguments: " +
                                ", ".join(kwds))

        # This is for messages that are picked up by Nat's monitoring program
        self.stats_logger = logging.getLogger("stats logger")
        handler = logging.StreamHandler()
        formatter = logging.Formatter('%(message)s')
        handler.setFormatter(formatter)
        self.stats_logger.addHandler(handler)
        self.stats_logger.removeHandler(self.stats_logger.handlers[0])
        self.stats_logger.setLevel(logging.INFO)

        self._detz_offset = cspad_tbx.getOptFloat(detz_offset)
        self.delta_k = cspad_tbx.getOptFloat(delta_k)
        self.override_energy = cspad_tbx.getOptFloat(override_energy)

        self.address = cspad_tbx.getOptString(address)
        self.verbose = cspad_tbx.getOptBool(verbose)
        self.check_beam_status = cspad_tbx.getOptBool(check_beam_status)
        self.distance = None
        self.sifoil = None
        self.wavelength = None  # The current wavelength - set by self.event()
        self.laser_1_status = laser_status(laser_id=1)
        self.laser_4_status = laser_status(laser_id=4)
예제 #2
0
    def __init__(self,
                 address,
                 n_collate=None,
                 n_update=120,
                 common_mode_correction="none",
                 wait=None,
                 photon_counting=False,
                 sigma_scaling=False,
                 **kwds):
        """The mod_view class constructor XXX.

    @param address         Full data source address of the DAQ device
    @param calib_dir       Directory with calibration information
    @param common_mode_correction The type of common mode correction to apply
    @param dark_path       Path to input average dark image
    @param dark_stddev     Path to input standard deviation dark
                           image, required if @p dark_path is given
    @param wait            Minimum time (in seconds) to wait on the current
                           image before moving on to the next
    @param n_collate       Number of shots to average, or <= 0 to
                           average all shots
    @param n_update        Number of shots between updates
    """

        super(mod_view,
              self).__init__(address=address,
                             common_mode_correction=common_mode_correction,
                             **kwds)

        self.detector = cspad_tbx.address_split(address)[0]
        self.nvalid = 0
        self.ncollate = cspad_tbx.getOptInteger(n_collate)
        self.nupdate = cspad_tbx.getOptInteger(n_update)
        self.photon_counting = cspad_tbx.getOptBool(photon_counting)
        self.sigma_scaling = cspad_tbx.getOptBool(sigma_scaling)
        if (self.ncollate is None):
            self.ncollate = self.nupdate
        if (self.ncollate > self.nupdate):
            self.ncollate = self.nupdate
            self.logger.warning("n_collate capped to %d" % self.nupdate)

        linger = True  # XXX Make configurable
        wait = cspad_tbx.getOptFloat(wait)

        # Create a managed FIFO queue shared between the viewer and the
        # current process.  The current process will produce images, while
        # the viewer process will consume them.
        manager = multiprocessing.Manager()
        self._queue = manager.Queue()
        self._proc = multiprocessing.Process(target=_xray_frame_process,
                                             args=(self._queue, linger, wait))
        self._proc.start()

        self.n_shots = 0
예제 #3
0
  def __init__(self,
               address,
               n_collate   = None,
               n_update    = 120,
               common_mode_correction = "none",
               wait=None,
               photon_counting=False,
               sigma_scaling=False,
               **kwds):
    """The mod_view class constructor XXX.

    @param address         Full data source address of the DAQ device
    @param calib_dir       Directory with calibration information
    @param common_mode_correction The type of common mode correction to apply
    @param dark_path       Path to input average dark image
    @param dark_stddev     Path to input standard deviation dark
                           image, required if @p dark_path is given
    @param wait            Minimum time (in seconds) to wait on the current
                           image before moving on to the next
    @param n_collate       Number of shots to average, or <= 0 to
                           average all shots
    @param n_update        Number of shots between updates
    """

    super(mod_view, self).__init__(
      address=address,
      common_mode_correction=common_mode_correction,
      **kwds)

    self.detector = cspad_tbx.address_split(address)[0]
    self.nvalid   = 0
    self.ncollate = cspad_tbx.getOptInteger(n_collate)
    self.nupdate  = cspad_tbx.getOptInteger(n_update)
    self.photon_counting = cspad_tbx.getOptBool(photon_counting)
    self.sigma_scaling = cspad_tbx.getOptBool(sigma_scaling)
    if (self.ncollate is None):
      self.ncollate = self.nupdate
    if (self.ncollate > self.nupdate):
      self.ncollate = self.nupdate
      self.logger.warning("n_collate capped to %d" % self.nupdate)

    linger = True # XXX Make configurable
    wait = cspad_tbx.getOptFloat(wait)

    # Create a managed FIFO queue shared between the viewer and the
    # current process.  The current process will produce images, while
    # the viewer process will consume them.
    manager = multiprocessing.Manager()
    self._queue = manager.Queue()
    self._proc = multiprocessing.Process(
      target=_xray_frame_process, args=(self._queue, linger, wait))
    self._proc.start()

    self.n_shots = 0
예제 #4
0
  def __init__(self, address, detz_offset=575, check_beam_status=True, verbose=False, delta_k=0.0, override_energy=None, **kwds):
    """The mod_event_info class constructor stores the
    parameters passed from the pyana configuration file in instance
    variables.

    @param address           Full data source address of the DAQ
                             device
    @param detz_offset       The distance from the interaction region
                             to the back of the detector stage, in mm
    @param check_beam_status Flag used to skip checking the beam
                             parameters
    @param delta_k           Correction to the K value used when calculating
                             wavelength
    @param override_energy   Use this energy instead of what is in the
                             XTC stream
    """
    logging.basicConfig()
    self.logger = logging.getLogger(self.__class__.__name__)
    self.logger.setLevel(logging.INFO)

    # The subclasses accept keyword arguments; warn about any
    # unhandled arguments at the end of the inheritance chain.
    if len(kwds) > 0:
      self.logger.warning("Ignored unknown arguments: " +
                          ", ".join(kwds.keys()))

    # This is for messages that are picked up by Nat's monitoring program
    self.stats_logger = logging.getLogger("stats logger")
    handler = logging.StreamHandler()
    formatter = logging.Formatter('%(message)s')
    handler.setFormatter(formatter)
    self.stats_logger.addHandler(handler)
    self.stats_logger.removeHandler(self.stats_logger.handlers[0])
    self.stats_logger.setLevel(logging.INFO)

    self._detz_offset = cspad_tbx.getOptFloat(detz_offset)
    self.delta_k = cspad_tbx.getOptFloat(delta_k)
    self.override_energy = cspad_tbx.getOptFloat(override_energy)

    self.address = cspad_tbx.getOptString(address)
    self.verbose = cspad_tbx.getOptBool(verbose)
    self.check_beam_status = cspad_tbx.getOptBool(check_beam_status)
    self.distance = None
    self.sifoil = None
    self.wavelength = None # The current wavelength - set by self.event()
    self.laser_1_status = laser_status(laser_id=1)
    self.laser_4_status = laser_status(laser_id=4)
예제 #5
0
    def __init__(self, address, display, mat_path, table_path, template_path=None, **kwds):
        """The mod_average class constructor stores the parameters passed
    from the pyana configuration file in instance variables.  All
    parameters, except @p address are optional, and hence need not be
    defined in pyana.cfg.

    @param address Address string XXX Que?!
    """

        super(mod_ledge, self).__init__(address=address, **kwds)

        # XXX Should be forced to false if graphics unavailable (e.g. when
        # running on the cluster).
        self._display = cspad_tbx.getOptBool(display)

        # Use line buffering to allow tailing the output in real time.
        # XXX Make name configurable.
        self._mat_path = cspad_tbx.getOptString(mat_path)
        self._table_path = cspad_tbx.getOptString(table_path)

        # Ring buffers for history-keeping.
        self._history_injector_xyz = ring_buffer()
        self._history_spectrometer_xyz = ring_buffer()
        self._history_energy = ring_buffer()

        # Get the template for image registration.
        if template_path is not None:
            from libtbx import easy_pickle

            self._template = easy_pickle.load(template_path)
        else:
            self._template = None

        # Optionally, initialise the viewer with a custom callback, see
        # mod_view.  The regions of interest are communicated to the
        # viewer through a shared multiprocessing array.  XXX This needs a
        # bit more thought to come up with a sensible interface.
        if self._display:
            from mod_view import _xray_frame_process
            from multiprocessing import Array, Process, Manager
            from rstbx.viewer import display

            manager = Manager()
            self._queue = manager.Queue()
            self._proc = Process(target=_xray_frame_process, args=(self._queue, True, 0))
            self._roi = Array("i", 15 * 4, lock=False)  # XXX Make variable!
            display.user_callback = self._callback
            self._proc.start()
예제 #6
0
  def __init__(self,
               address,
               calib_dir=None,
               common_mode_correction="none",
               photon_threshold=None,
               two_photon_threshold=None,
               dark_path=None,
               dark_stddev=None,
               mask_path=None,
               gain_map_path=None,
               gain_map_level=None,
               cache_image=True,
               roi=None,
               laser_1_status=None,
               laser_4_status=None,
               laser_wait_time=None,
               override_beam_x=None,
               override_beam_y=None,
               bin_size=None,
               crop_rayonix=False,
               **kwds):
    """The common_mode_correction class constructor stores the
    parameters passed from the pyana configuration file in instance
    variables.

    @param address         Full data source address of the DAQ device
    @param calib_dir       Directory with calibration information
    @param common_mode_correction The type of common mode correction to apply
    @param dark_path       Path to input average dark image
    @param dark_stddev     Path to input standard deviation dark
                           image, required if @p dark_path is given
    @param mask_path       Path to input mask.  Pixels to mask out should be set to -2
    @param gain_map_path   Path to input gain map.  Multiplied times the image.
    @param gain_map_level  If set, all the '1' pixels in the gain_map are set to this multiplier
                           and all the '0' pixels in the gain_map are set to '1'.  If not set,
                           use the values in the gain_map directly
    @param laser_1_status  0 or 1 to indicate that the laser should be off or on respectively
    @param laser_4_status  0 or 1 to indicate that the laser should be off or on respectively
    @param laser_wait_time Length of time in milliseconds to wait after a laser
                           change of status to begin accepting images again.
                           (rejection of images occurs immediately after status
                           change).
    @param override_beam_x override value for x coordinate of beam center in pixels
    @param override_beam_y override value for y coordinate of beam center in pixels
    @param bin_size bin size for rayonix detector used to determin pixel size
    @param crop_rayonix    whether to crop rayonix images such that image center is the beam center
    """

    # Cannot use the super().__init__() construct here, because
    # common_mode_correction refers to the argument, and not the
    # class.
    mod_event_info.__init__(self, address=address, **kwds)

    # The paths will be substituted in beginjob(), where evt and env
    # are available.
    self._dark_path = cspad_tbx.getOptString(dark_path)
    self._dark_stddev_path = cspad_tbx.getOptString(dark_stddev)
    self._gain_map_path = cspad_tbx.getOptString(gain_map_path)
    self._mask_path = cspad_tbx.getOptString(mask_path)

    self.gain_map_level = cspad_tbx.getOptFloat(gain_map_level)
    self.common_mode_correction = cspad_tbx.getOptString(common_mode_correction)
    self.photon_threshold = cspad_tbx.getOptFloat(photon_threshold)
    self.two_photon_threshold = cspad_tbx.getOptFloat(two_photon_threshold)
    self.cache_image = cspad_tbx.getOptBool(cache_image)
    self.filter_laser_1_status = cspad_tbx.getOptInteger(laser_1_status)
    self.filter_laser_4_status = cspad_tbx.getOptInteger(laser_4_status)
    if self.filter_laser_1_status is not None:
      self.filter_laser_1_status = bool(self.filter_laser_1_status)
    if self.filter_laser_4_status is not None:
      self.filter_laser_4_status = bool(self.filter_laser_4_status)
    self.filter_laser_wait_time = cspad_tbx.getOptInteger(laser_wait_time)
    self.override_beam_x = cspad_tbx.getOptFloat(override_beam_x)
    self.override_beam_y = cspad_tbx.getOptFloat(override_beam_y)
    self.bin_size = cspad_tbx.getOptInteger(bin_size)
    self.crop_rayonix = cspad_tbx.getOptBool(crop_rayonix)

    self.cspad_img = None # The current image - set by self.event()
    self.sum_common_mode = 0
    self.sumsq_common_mode = 0
    self.roi = cspad_tbx.getOptROI(roi) # used to ignore the signal region in chebyshev fit

    assert self.common_mode_correction in \
        ("gaussian", "mean", "median", "mode", "none", "chebyshev")

    # Get and parse metrology.
    self.sections = None
    device = cspad_tbx.address_split(self.address)[2]
    if device == 'Andor':
      self.sections = [] # XXX FICTION
    elif device == 'Cspad':
      if self.address == 'XppGon-0|Cspad-0':
        self.sections = [] # Not used for XPP
      else:
        self.sections = calib2sections(cspad_tbx.getOptString(calib_dir))
    elif device == 'Cspad2x2':
      # There is no metrology information for the Sc1 detector, so
      # make it up.  The sections are rotated by 90 degrees with
      # respect to the "standing up" convention.
      self.sections = [[Section(90, (185 / 2 + 0,   (2 * 194 + 3) / 2)),
                        Section(90, (185 / 2 + 185, (2 * 194 + 3) / 2))]]
    elif device == 'marccd':
      self.sections = [] # XXX FICTION
    elif device == 'pnCCD':
      self.sections = [] # XXX FICTION
    elif device == 'Rayonix':
      self.sections = [] # XXX FICTION
    elif device == 'Opal1000':
      self.sections = [] # XXX FICTION
    if self.sections is None:
      raise RuntimeError("Failed to load metrology")
예제 #7
0
파일: mod_filter.py 프로젝트: dials/cctbx
    def __init__(self,
                 timestamps_path=None,
                 timestamps_interval=None,
                 negate="False"):
        """Extracts timestamps from the file whose name is the string
    pointed to by @p timestamps_path.

    @param timestamps_path     Path to file containing timestamps
    @param timestamps_interval Comma-separated inclusive endpoints of
                               a timestamp interval.  The lower or the
                               upper endpoint may be omitted, in which
                               case it will be treated as -infinity or
                               +infinity.
    @param negate              Select shots not matching any of the
                               timestamps
    """

        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.setLevel(logging.INFO)

        if (not ((timestamps_path is None) ^ (timestamps_interval is None))):
            raise RuntimeError(
                "Must specify either timestamps_path or timestamps_interval")

        self.negate = cspad_tbx.getOptBool(negate)

        if (timestamps_path is not None):
            p_old = re.compile("\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\d{2}\.\d{3}")
            p_new = re.compile("\d{17}")
            f = open(timestamps_path, "r")
            self.timestamps_list = []
            for line in f.readlines():
                s = line.strip()
                if (len(s) == 0 or s[0] == "#"):
                    continue
                for t in s.split():
                    m = p_old.findall(t)
                    if len(m) == 0:
                        m = p_new.findall(t)
                    if (len(m) == 1):
                        self.timestamps_list.append(self._ts2sms(m[0]))
            f.close()
            self.timestamps_interval = None
        else:
            try:
                s = timestamps_interval.split(",")
                if (len(s) == 1 or len(s) == 2 and len(s[1]) == 0):
                    self.timestamps_interval = (self._ts2sms(s[0]), None)
                elif (len(s) == 2 and len(s[0]) == 0):
                    self.timestamps_interval = (None, self._ts2sms(s[1]))
                elif (len(s) == 2):
                    self.timestamps_interval = (self._ts2sms(s[0]),
                                                self._ts2sms(s[1]))
                else:
                    raise ValueError()

                # Ensure lower endpoint is earlier than upper endpoint.
                if (self.timestamps_interval[0] is not None
                        and self.timestamps_interval[1] is not None
                        and self._timestamp_compar(
                            self.timestamps_interval[0],
                            self.timestamps_interval[1]) > 0):
                    raise ValueError()

            except ValueError:
                raise RuntimeError("Failed to parse timestamp interval %s" %
                                   timestamps_interval)
            self.timestamps_list = None

        self.naccepted = 0
        self.nshots = 0
예제 #8
0
  def __init__(
    self, timestamps_path=None, timestamps_interval=None, negate="False"):
    """Extracts timestamps from the file whose name is the string
    pointed to by @p timestamps_path.

    @param timestamps_path     Path to file containing timestamps
    @param timestamps_interval Comma-separated inclusive endpoints of
                               a timestamp interval.  The lower or the
                               upper endpoint may be omitted, in which
                               case it will be treated as -infinity or
                               +infinity.
    @param negate              Select shots not matching any of the
                               timestamps
    """

    self.logger = logging.getLogger(self.__class__.__name__)
    self.logger.setLevel(logging.INFO)

    if (not((timestamps_path is None) ^ (timestamps_interval is None))):
      raise RuntimeError(
        "Must specify either timestamps_path or timestamps_interval")

    self.negate = cspad_tbx.getOptBool(negate)

    if (timestamps_path is not None):
      p_old = re.compile("\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\d{2}\.\d{3}")
      p_new = re.compile("\d{17}")
      f = open(timestamps_path, "r")
      self.timestamps_list = []
      for line in f.readlines():
        s = line.strip()
        if (len(s) == 0 or s[0] == "#"):
          continue
        for t in s.split():
          m = p_old.findall(t)
          if len(m) == 0:
            m = p_new.findall(t)
          if (len(m) == 1):
            self.timestamps_list.append(self._ts2sms(m[0]))
      f.close()
      self.timestamps_interval = None
    else:
      try:
        s = timestamps_interval.split(",")
        if (len(s) == 1 or len(s) == 2 and len(s[1]) == 0):
          self.timestamps_interval = (self._ts2sms(s[0]), None)
        elif (len(s) == 2 and len(s[0]) == 0):
          self.timestamps_interval = (None, self._ts2sms(s[1]))
        elif (len(s) == 2):
          self.timestamps_interval = (self._ts2sms(s[0]), self._ts2sms(s[1]))
        else:
          raise ValueError()

        # Ensure lower endpoint is earlier than upper endpoint.
        if (self.timestamps_interval[0] is not None and
            self.timestamps_interval[1] is not None and
            self._timestamp_compar(self.timestamps_interval[0],
                                   self.timestamps_interval[1]) > 0):
          raise ValueError()

      except ValueError:
        raise RuntimeError(
          "Failed to parse timestamp interval %s" % timestamps_interval)
      self.timestamps_list = None

    self.naccepted = 0
    self.nshots = 0
예제 #9
0
    def __init__(self,
                 address,
                 dispatch=None,
                 integration_dirname=None,
                 integration_basename=None,
                 out_dirname=None,
                 out_basename=None,
                 roi=None,
                 distl_min_peaks=None,
                 distl_flags=None,
                 threshold=None,
                 xtal_target=None,
                 negate_hits=False,
                 trial_id=None,
                 db_logging=False,
                 progress_logging=False,
                 sql_buffer_size=1,
                 db_host=None,
                 db_name=None,
                 db_table_name=None,
                 db_experiment_tag=None,
                 db_user=None,
                 db_password=None,
                 db_tags=None,
                 trial=None,
                 rungroup_id=None,
                 db_version='v1',
                 **kwds):
        """The mod_hitfind class constructor stores the parameters passed
    from the pyana configuration file in instance variables.  All
    parameters, except @p address are optional, and hence need not be
    defined in pyana.cfg.

    @param address      Full data source address of the DAQ device
    @param dispatch     Function to call
    @param integration_dirname
                        Directory portion of output integration file
                        pathname
    @param integration_basename
                        Filename prefix of output integration file
                        pathname
    @param out_dirname  Directory portion of output image pathname
    @param out_basename Filename prefix of output image pathname
    @param roi          Region of interest for thresholding, on the
                        form fast_low:fast_high,slow_low:slow_high
    @param threshold    Minimum value in region of interest to pass
    """

        super(mod_hitfind, self).__init__(address=address, **kwds)

        self.m_dispatch = cspad_tbx.getOptString(dispatch)
        self.m_integration_basename = cspad_tbx.getOptString(
            integration_basename)
        self.m_integration_dirname = cspad_tbx.getOptString(
            integration_dirname)
        self.m_out_basename = cspad_tbx.getOptString(out_basename)
        self.m_out_dirname = cspad_tbx.getOptString(out_dirname)
        self.m_distl_min_peaks = cspad_tbx.getOptInteger(distl_min_peaks)
        self.m_distl_flags = cspad_tbx.getOptStrings(distl_flags)
        self.m_threshold = cspad_tbx.getOptInteger(threshold)
        self.m_xtal_target = cspad_tbx.getOptString(xtal_target)
        self.m_negate_hits = cspad_tbx.getOptBool(negate_hits)
        self.m_trial_id = cspad_tbx.getOptInteger(trial_id)
        self.m_db_logging = cspad_tbx.getOptBool(db_logging)
        self.m_progress_logging = cspad_tbx.getOptBool(progress_logging)
        self.m_sql_buffer_size = cspad_tbx.getOptInteger(sql_buffer_size)
        self.m_db_host = cspad_tbx.getOptString(db_host)
        self.m_db_name = cspad_tbx.getOptString(db_name)
        self.m_db_table_name = cspad_tbx.getOptString(db_table_name)
        self.m_db_experiment_tag = cspad_tbx.getOptString(db_experiment_tag)
        self.m_db_user = cspad_tbx.getOptString(db_user)
        self.m_db_password = cspad_tbx.getOptString(db_password)
        self.m_db_tags = cspad_tbx.getOptString(db_tags)
        self.m_trial = cspad_tbx.getOptInteger(trial)
        self.m_rungroup_id = cspad_tbx.getOptInteger(rungroup_id)
        self.m_db_version = cspad_tbx.getOptString(db_version)
        # A ROI should not contain any ASIC boundaries, as these are
        # noisy.  Hence circular ROI:s around the beam centre are probably
        # not such a grand idea.
        self.m_roi = cspad_tbx.getOptROI(roi)

        # Verify that dist_min_peaks is either "restrictive" or
        # "permissive", but not both.  ^ is the logical xor operator
        if self.m_distl_min_peaks is not None:
            if (not (('permissive' in self.m_distl_flags) ^
                     ('restrictive' in self.m_distl_flags))):
                raise RuntimeError("""Sorry, with the distl_min_peaks option,
          distl_flags must be set to 'permissive' or 'restrictive'.""")
            if (self.m_roi is not None):
                raise RuntimeError("""Sorry, either specify region of interest
          (roi) or distl_min_peaks, but not both.""")

        if self.m_db_logging:
            self.buffered_sql_entries = []
            assert self.m_sql_buffer_size >= 1

        if self.m_progress_logging:
            if self.m_db_version == 'v1':
                self.buffered_progress_entries = []
                assert self.m_sql_buffer_size >= 1
                self.isoforms = {}
            elif self.m_db_version == 'v2':
                from xfel.ui import db_phil_str
                from libtbx.phil import parse
                extra_phil = """
        input {
          trial = None
            .type = int
          trial_id = None
            .type = int
          rungroup = None
            .type = int
        }
        """
                self.db_params = parse(db_phil_str + extra_phil).extract()
                self.db_params.experiment_tag = self.m_db_experiment_tag
                self.db_params.db.host = self.m_db_host
                self.db_params.db.name = self.m_db_name
                self.db_params.db.user = self.m_db_user
                self.db_params.db.password = self.m_db_password
                self.db_params.input.trial = self.m_trial
                self.db_params.input.rungroup = self.m_rungroup_id

        if self.m_db_tags is None:
            self.m_db_tags = ""
예제 #10
0
  def __init__(self,
               address,
               dispatch               = None,
               integration_dirname    = None,
               integration_basename   = None,
               out_dirname            = None,
               out_basename           = None,
               roi                    = None,
               distl_min_peaks        = None,
               distl_flags            = None,
               threshold              = None,
               xtal_target            = None,
               negate_hits            = False,
               trial_id               = None,
               db_logging             = False,
               progress_logging       = False,
               sql_buffer_size        = 1,
               db_host                = None,
               db_name                = None,
               db_table_name          = None,
               db_experiment_tag      = None,
               db_user                = None,
               db_password            = None,
               db_tags                = None,
               rungroup_id            = None,
               **kwds):
    """The mod_hitfind class constructor stores the parameters passed
    from the pyana configuration file in instance variables.  All
    parameters, except @p address are optional, and hence need not be
    defined in pyana.cfg.

    @param address      Full data source address of the DAQ device
    @param dispatch     Function to call
    @param integration_dirname
                        Directory portion of output integration file
                        pathname
    @param integration_basename
                        Filename prefix of output integration file
                        pathname
    @param out_dirname  Directory portion of output image pathname
    @param out_basename Filename prefix of output image pathname
    @param roi          Region of interest for thresholding, on the
                        form fast_low:fast_high,slow_low:slow_high
    @param threshold    Minimum value in region of interest to pass
    """

    super(mod_hitfind, self).__init__(address=address, **kwds)

    self.m_dispatch             = cspad_tbx.getOptString(dispatch)
    self.m_integration_basename = cspad_tbx.getOptString(integration_basename)
    self.m_integration_dirname  = cspad_tbx.getOptString(integration_dirname)
    self.m_out_basename         = cspad_tbx.getOptString(out_basename)
    self.m_out_dirname          = cspad_tbx.getOptString(out_dirname)
    self.m_distl_min_peaks      = cspad_tbx.getOptInteger(distl_min_peaks)
    self.m_distl_flags          = cspad_tbx.getOptStrings(distl_flags)
    self.m_threshold            = cspad_tbx.getOptInteger(threshold)
    self.m_xtal_target          = cspad_tbx.getOptString(xtal_target)
    self.m_negate_hits          = cspad_tbx.getOptBool(negate_hits)
    self.m_trial_id             = cspad_tbx.getOptInteger(trial_id)
    self.m_db_logging           = cspad_tbx.getOptBool(db_logging)
    self.m_progress_logging     = cspad_tbx.getOptBool(progress_logging)
    self.m_sql_buffer_size      = cspad_tbx.getOptInteger(sql_buffer_size)
    self.m_db_host              = cspad_tbx.getOptString(db_host)
    self.m_db_name              = cspad_tbx.getOptString(db_name)
    self.m_db_table_name        = cspad_tbx.getOptString(db_table_name)
    self.m_db_experiment_tag    = cspad_tbx.getOptString(db_experiment_tag)
    self.m_db_user              = cspad_tbx.getOptString(db_user)
    self.m_db_password          = cspad_tbx.getOptString(db_password)
    self.m_db_tags              = cspad_tbx.getOptString(db_tags)
    self.m_rungroup_id          = cspad_tbx.getOptInteger(rungroup_id)
    # A ROI should not contain any ASIC boundaries, as these are
    # noisy.  Hence circular ROI:s around the beam centre are probably
    # not such a grand idea.
    self.m_roi = cspad_tbx.getOptROI(roi)

    # Verify that dist_min_peaks is either "restrictive" or
    # "permissive", but not both.  ^ is the logical xor operator
    if self.m_distl_min_peaks is not None:
      if (not (('permissive'  in self.m_distl_flags) ^
               ('restrictive' in self.m_distl_flags))):
        raise RuntimeError("""Sorry, with the distl_min_peaks option,
          distl_flags must be set to 'permissive' or 'restrictive'.""")
      if (self.m_roi is not None):
        raise RuntimeError("""Sorry, either specify region of interest
          (roi) or distl_min_peaks, but not both.""")

    if self.m_db_logging:
      self.buffered_sql_entries = []
      assert self.m_sql_buffer_size >= 1

    if self.m_progress_logging:
      self.buffered_progress_entries = []
      assert self.m_sql_buffer_size >= 1
      self.isoforms = {}

    if self.m_db_tags is None:
      self.m_db_tags = ""