#!/usr/bin/env python
# Cellparm.py
#   Copyright (C) 2006 CCLRC, Graeme Winter
#
#   This code is distributed under the BSD license, a copy of which is
#   included in the root directory of this package.
#
# A wrapper for the XDS jiffy program CELLPARM for computing average unit
# cell parameters, from the results of multiple CORRECT runs for input into
# a single XSCALE run.
#

import os
import sys
import math

if not os.environ.has_key('XIA2CORE_ROOT'):
  raise RuntimeError, 'XIA2CORE_ROOT not defined'

if not os.environ.has_key('XIA2_ROOT'):
  raise RuntimeError, 'XIA2_ROOT not defined'

if not os.path.join(os.environ['XIA2CORE_ROOT'],
                    'Python') in sys.path:
  sys.path.append(os.path.join(os.environ['XIA2CORE_ROOT'],
                               'Python'))

if not os.environ['XIA2_ROOT'] in sys.path:
  sys.path.append(os.environ['XIA2_ROOT'])

from Driver.DriverFactory import DriverFactory

from Handlers.Flags import Flags

def Cellparm(DriverType = None):

  DriverInstance = DriverFactory.Driver(DriverType)

  class CellparmWrapper(DriverInstance.__class__):
    '''A wrapper for wrapping CELLPARM.'''

    def __init__(self):

      # set up the object ancestors...

      DriverInstance.__class__.__init__(self)

      self.set_executable('cellparm')

      # input parameters
      self._cells = []
      self._n_refs = []

      return

    def add_cell(self, cell, n_ref):
      '''Add a unit cell which belongs to n_ref reflections.'''

      self._cells.append(cell)
      self._n_refs.append(n_ref)

      return

    def get_cell(self):
      '''Compute an average cell.'''

      if len(self._cells) < 1:
        raise RuntimeError, 'no input unit cell parameters'

      # check that the input cells are reasonably uniform -
      # be really relaxed and allow 5% variation!

      average_cell = [self._cells[0][j] for j in range(6)]
      number_cells = 1

      for j in range(1, len(self._cells)):
        cell = self._cells[j]
        for k in range(6):
          average = average_cell[k] / number_cells
          if math.fabs((cell[k] - average) / average) > 0.05 \
                 and not Flags.get_relax():
            raise RuntimeError, 'incompatible unit cells'
          average = average_cell[k] / number_cells
          if math.fabs((cell[k] - average) / average) > 0.2:
            raise RuntimeError, 'very incompatible unit cells'

        # it was ok to remember for later on..
        for k in range(6):
          average_cell[k] += cell[k]
        number_cells += 1

      cellparm_inp = open(os.path.join(
          self.get_working_directory(), 'CELLPARM.INP'), 'w')

      for j in range(len(self._cells)):
        cell = self._cells[j]
        n_ref = self._n_refs[j]
        cellparm_inp.write('UNIT_CELL_CONSTANTS=')
        cellparm_inp.write(
            '%.3f %.3f %.3f %.3f %.3f %.3f WEIGHT=%d\n' % \
            (cell[0], cell[1], cell[2], cell[3], cell[4], cell[5],
             n_ref))

      cellparm_inp.close()

      self.start()

      self.close_wait()

      # FIXME need to look for errors in here

      cellparm_lp = open(os.path.join(
          self.get_working_directory(), 'CELLPARM.LP'), 'r')
      data = cellparm_lp.readlines()

      return map(float, data[-1].split()[:6])

  return CellparmWrapper()

if __name__ == '__main__':
  cellparm = Cellparm()

  cellparm.add_cell(
      (64.585, 30.911, 34.886, 90.000, 105.822, 90.000), 284516)

  cellparm.add_cell(
      (64.584, 30.932, 34.879, 90.000, 105.832, 90.000), 32106)

  cellparm.add_cell(
      (64.658, 30.972, 34.918, 90.000, 105.823, 90.000), 1966)

  print cellparm.get_cell()
