<commit_before>#!/usr/bin/env python
#
# dials.two_theta_offset.py
#
#  Copyright (C) 2016 Diamond Light Source
#
#  Author: <NAME>
#
#  This code is distributed under the BSD license, a copy of which is
#  included in the root directory of this package.

from __future__ import division
from libtbx.phil import parse

help_message = '''

dials.two_theta_offset experiment_one.json experiment_two.json

'''

phil_scope = parse('''
''', process_includes=True)

class Script(object):
  '''A class for running the script.'''

  def __init__(self):
    '''Initialise the script.'''
    from dials.util.options import OptionParser
    from libtbx.phil import parse
    import libtbx.load_env

    # The script usage
    usage = "usage: %s [options] experiment_one.json experiment_two.json" \
            % libtbx.env.dispatcher_name

    # Create the parser
    self.parser = OptionParser(
      usage=usage,
      phil=phil_scope,
      epilog=help_message,
      check_format=False,
      read_experiments=True)

  def run(self):
    '''Execute the script.'''
    from dials.util.command_line import Command
    from dials.array_family import flex
    from dials.util.options import flatten_experiments

    # Parse the command line
    params, options = self.parser.parse_args(show_diff_phil=True)

    # Check the number of experiments
    experiments = flatten_experiments(params.input.experiments)
    if len(experiments) < 2:
      self.parser.print_help()
      return

    detectors = [experiment.detector[0] for experiment in experiments]

    from scitbx import matrix

    # FIXME make this a phil parameter in x, y
    offset = 100

    # FIXME iterate over pairs of experiments, if close in two-theta discard

    # pick two positions, at nominal origin offset in fast, slow

    x1 = matrix.col(detectors[0].get_origin())
    y1 = matrix.col(detectors[0].get_origin()) + \
      offset * matrix.col(detectors[0].get_fast_axis()) + \
      offset * matrix.col(detectors[0].get_slow_axis())

    x2 = matrix.col(detectors[1].get_origin())
    y2 = matrix.col(detectors[1].get_origin()) + \
      offset * matrix.col(detectors[1].get_fast_axis()) + \
      offset * matrix.col(detectors[1].get_slow_axis())

    centre, axis = find_centre_of_rotation(x1, x2, y1, y2)

    # compute "true" two-theta from these

    two_theta = component(x2 - centre, axis).angle(
      component(x1 - centre, axis), deg=True)

    print 'Centre: %7.4f %7.4f %7.4f' % centre.elems, \
      '  axis: %7.4f %7.4f %7.4f' % axis.elems, 'angle: %.2f' % two_theta

def component(a, n):
  return a - a.dot(n) * n

def find_centre_of_rotation(x1, x2, y1, y2):
  '''Find centre of rotation which takes postion x1 -> x2 and y1 -> y2'''

  # chords of rotation of x, y

  cx = x2 - x1
  cy = y2 - y1

  # know axis is perpendicular to both of these -> is cross product

  axis = cx.cross(cy).normalize()

  # normal vector to y chord

  ny = component(cy, axis).normalize().cross(axis)

  # origin of normal vectors, centre of x, y chords

  ox = component(x1 + 0.5 * cx, axis)
  oy = component(y1 + 0.5 * cy, axis)

  # determine true origin of rotation - normal vector of x chord, construct
  # right-angle-triangle with hypotenuse from unknown origin of rotation
  # to central point of y chord oy, and adjacent the vector parallel to
  # reversed x chord => opposite is on vector from unknown origin of rotation
  # to ox

  ncx = cx.normalize()
  h = (oy - ox).dot(ncx)
  d = h / (ny).dot(-ncx)
  return oy + d * ny, axis

if __name__ == '__main__':
  from dials.util import halraiser
  try:
    script = Script()
    script.run()
  except Exception as e:
    halraiser(e)
<commit_msg>Address FIXME's - phil parameters for offsets; now treats combinations of two experiments from list i.e. you can pass *lots* and get all results<commit_after>#!/usr/bin/env python
#
# dials.two_theta_offset.py
#
#  Copyright (C) 2016 Diamond Light Source
#
#  Author: <NAME>
#
#  This code is distributed under the BSD license, a copy of which is
#  included in the root directory of this package.

from __future__ import division
from libtbx.phil import parse

help_message = '''

dials.two_theta_offset experiment_one.json experiment_two.json

'''

phil_scope = parse('''
offset_fast = 100.0
  .type = float
  .help = 'How far to move in the detector plane (fast direction)'
offset_slow = 100.0
  .type = float
  .help = 'How far to move in the detector plane (slow direction)'
min_distance = 10.0
  .type = float
  .help = 'Minimum shift in detector position'
''', process_includes=True)

class Script(object):
  '''A class for running the script.'''

  def __init__(self):
    '''Initialise the script.'''
    from dials.util.options import OptionParser
    from libtbx.phil import parse
    import libtbx.load_env

    # The script usage
    usage = "usage: %s [options] experiment_one.json experiment_two.json" \
            % libtbx.env.dispatcher_name

    # Create the parser
    self.parser = OptionParser(
      usage=usage,
      phil=phil_scope,
      epilog=help_message,
      check_format=False,
      read_experiments=True)

  def run(self):
    '''Execute the script.'''
    from dials.util.command_line import Command
    from dials.array_family import flex
    from dials.util.options import flatten_experiments

    # Parse the command line
    params, options = self.parser.parse_args(show_diff_phil=True)

    # Check the number of experiments is at least 2
    experiments = flatten_experiments(params.input.experiments)
    if len(experiments) < 2:
      self.parser.print_help()
      return

    detectors = [experiment.detector[0] for experiment in experiments]

    from itertools import combinations

    for pair in combinations(detectors, 2):
      determine_axis(pair, params)

def determine_axis(detectors, params):
  from scitbx import matrix

  offset_fast = params.offset_fast
  offset_slow = params.offset_slow
  min_distance = params.min_distance

  # pick two positions, at nominal origin offset in fast, slow

  x1 = matrix.col(detectors[0].get_origin())
  y1 = matrix.col(detectors[0].get_origin()) + \
    offset_fast * matrix.col(detectors[0].get_fast_axis()) + \
    offset_slow * matrix.col(detectors[0].get_slow_axis())

  x2 = matrix.col(detectors[1].get_origin())
  y2 = matrix.col(detectors[1].get_origin()) + \
    offset_fast * matrix.col(detectors[1].get_fast_axis()) + \
    offset_slow * matrix.col(detectors[1].get_slow_axis())

  # only allow this calculation if the detector has been moved a "significant"
  # amount

  if (x2 - x1).length() < min_distance:
    return

  centre, axis = find_centre_of_rotation(x1, x2, y1, y2)

  # compute "true" two-theta from these

  two_theta = component(x2 - centre, axis).angle(
    component(x1 - centre, axis), deg=True)

  print 'Centre: %7.4f %7.4f %7.4f' % centre.elems, \
    '  axis: %7.4f %7.4f %7.4f' % axis.elems, 'angle: %.2f' % two_theta

def component(a, n):
  return a - a.dot(n) * n

def find_centre_of_rotation(x1, x2, y1, y2):
  '''Find centre of rotation which takes postion x1 -> x2 and y1 -> y2'''

  # chords of rotation of x, y

  cx = x2 - x1
  cy = y2 - y1

  # know axis is perpendicular to both of these -> is cross product

  axis = cx.cross(cy).normalize()

  # normal vector to y chord

  ny = component(cy, axis).normalize().cross(axis)

  # origin of normal vectors, centre of x, y chords

  ox = component(x1 + 0.5 * cx, axis)
  oy = component(y1 + 0.5 * cy, axis)

  # determine true origin of rotation - normal vector of x chord, construct
  # right-angle-triangle with hypotenuse from unknown origin of rotation
  # to central point of y chord oy, and adjacent the vector parallel to
  # reversed x chord => opposite is on vector from unknown origin of rotation
  # to ox

  ncx = cx.normalize()
  h = (oy - ox).dot(ncx)
  d = h / (ny).dot(-ncx)
  return oy + d * ny, axis

if __name__ == '__main__':
  from dials.util import halraiser
  try:
    script = Script()
    script.run()
  except Exception as e:
    halraiser(e)

{'fraction_non_alphanumeric': 0.10866043613707164, 'fraction_numerical': 0.015950155763239875, 'mean_word_length': 3.3011789924973205, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '646231', 'n_tokens_mistral': 2742, 'n_tokens_neox': 2622, 'n_words': 818}