fabio.cbfimage

Package fabio :: Module cbfimage |
[hide private]
[frames] | no frames]

# Source Code for Module fabio.cbfimage

      1  #!/usr/bin/env python
      2  # coding: utf8
      3  """
      4  Authors: Jérôme Kieffer, ESRF
      5           email:jerome.kieffer@esrf.fr
      6
      7  Cif Binary Files images are 2D images written by the Pilatus detector and others.
      8  They use a modified (simplified) byte-offset algorithm.
      9
     10  CIF is a library for manipulating Crystallographic information files and tries
     11  to conform to the specification of the IUCR
     12  """
     13  __author__ = "Jérôme Kieffer"
     14  __contact__ = "jerome.kieffer@esrf.eu"
     15  __license__ = "GPLv3+"
     16  __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
     17  __version__ = ["Generated by CIF.py: Jan 2005 - April 2012",
     18                "Written by Jerome Kieffer: Jerome.Kieffer@esrf.eu",
     19                "On-line data analysis / ISDD ", "ESRF Grenoble (France)"]
     20
     21
     22  import os, logging
     23  logger = logging.getLogger("cbfimage")
     24  import numpy
     25  from fabioimage import fabioimage
     26  from compression import decByteOffet_numpy, md5sum, compByteOffet_numpy
     27  #import time
     28
     29  DATA_TYPES = { "signed 8-bit integer"   : numpy.int8,
     30                 "signed 16-bit integer"  : numpy.int16,
     31                 "signed 32-bit integer"  : numpy.int32,
     32                 "signed 64-bit integer"  : numpy.int64
     33                  }
     34
     35  MINIMUM_KEYS = ["X-Binary-Size-Fastest-Dimension",
     36                  'ByteOrder',
     37                  'Data type',
     38                  'X dimension',
     39                  'Y dimension',
     40                  'Number of readouts']
     41
     42
     43  STARTER = "\x0c\x1a\x04\xd5"
     44  PADDING = 512
     45
     46 -class cbfimage(fabioimage):
     47      """
     48      Read the Cif Binary File data format
     49      """
     50 -    def __init__(self, data=None , header=None, fname=None):
     51          """
     52          Constructor of the class CIF Binary File reader.
     53
     54          @param _strFilename: the name of the file to open
     55          @type  _strFilename: string
     56          """
     57          fabioimage.__init__(self, data, header)
     58          self.cif = CIF()
     59          if fname is not None: #load the file)
     60              self.read(fname)
     61
     62      @staticmethod
     63 -    def checkData(data=None):
     64          if data is None:
     65              return None
     66          elif numpy.issubdtype(data.dtype, int):
     67              return data
     68          else:
     69              return data.astype(int)
     70
     71
     72 -    def _readheader(self, inStream):
     73          """
     74          Read in a header in some CBF format from a string representing binary stuff
     75
     76          @param inStream: file containing the Cif Binary part.
     77          @type inStream: opened file.
     78          """
     79          self.cif.loadCIF(inStream, _bKeepComment=True)
     80
     81  #        backport contents of the CIF data to the headers
     82          for key in self.cif:
     83              if key != "_array_data.data":
     84                  self.header_keys.append(key)
     85                  self.header[key] = self.cif[key].strip(" \"\n\r\t")
     86
     87          if not "_array_data.data" in self.cif:
     88              raise Exception("cbfimage: CBF file %s is corrupt, cannot find data block with '_array_data.data' key" % self.fname)
     89
     90          inStream2 = self.cif["_array_data.data"]
     91          sep = "\r\n"
     92          iSepPos = inStream2.find(sep)
     93          if iSepPos < 0 or iSepPos > 80:
     94              sep = "\n" #switch back to unix representation
     95
     96          lines = inStream2.split(sep)
     97          for oneLine in lines[1:]:
     98              if len(oneLine) < 10:
     99                  break
    100              try:
    101                  key, val = oneLine.split(':' , 1)
    102              except ValueError:
    103                  key, val = oneLine.split('=' , 1)
    104              key = key.strip()
    105              self.header_keys.append(key)
    106              self.header[key] = val.strip(" \"\n\r\t")
    107          missing = []
    108          for item in MINIMUM_KEYS:
    109              if item not in self.header_keys:
    110                  missing.append(item)
    111          if len(missing) > 0:
    112              logger.debug("CBF file misses the keys " + " ".join(missing))
    113
    114
    115 -    def read(self, fname, frame=None):
    116          """
    117          Read in header into self.header and
    118              the data   into self.data
    119          """
    120          self.filename = fname
    121          self.header = {}
    122          self.resetvals()
    123
    124          infile = self._open(fname, "rb")
    125          self._readheader(infile)
    126          # Compute image size
    127          try:
    128              self.dim1 = int(self.header['X-Binary-Size-Fastest-Dimension'])
    129              self.dim2 = int(self.header['X-Binary-Size-Second-Dimension'])
    130          except:
    131              raise Exception(IOError, "CBF file %s is corrupt, no dimensions in it" % fname)
    132          try:
    133              bytecode = DATA_TYPES[self.header['X-Binary-Element-Type']]
    134              self.bpp = len(numpy.array(0, bytecode).tostring())
    135          except KeyError:
    136              bytecode = numpy.int32
    137              self.bpp = 32
    138              logger.warning("Defaulting type to int32")
    139          if self.header["conversions"] == "x-CBF_BYTE_OFFSET":
    140              self.data = self._readbinary_byte_offset(self.cif["_array_data.data"]).astype(bytecode).reshape((self.dim2, self.dim1))
    141          else:
    142              raise Exception(IOError, "Compression scheme not yet supported, please contact FABIO development team")
    143
    144          self.bytecode = self.data.dtype.type
    145          self.resetvals()
    146  #        # ensure the PIL image is reset
    147          self.pilimage = None
    148          return self
    149
    150
    151
    152 -    def _readbinary_byte_offset(self, inStream):
    153          """
    154          Read in a binary part of an x-CBF_BYTE_OFFSET compressed image
    155
    156          @param inStream: the binary image (without any CIF decorators)
    157          @type inStream: python string.
    158          @return: a linear numpy array without shape and dtype set
    159          @rtype: numpy array
    160          """
    161          startPos = inStream.find(STARTER) + 4
    162          data = inStream[ startPos: startPos + int(self.header["X-Binary-Size"])]
    163          try:
    164              import byte_offset
    165          except ImportError:
    166              logger.warning("Error in byte_offset part: Falling back to Numpy implementation")
    167              myData = decByteOffet_numpy(data, size=self.dim1 * self.dim2)
    168          else:
    169              myData = byte_offset.analyseCython(data, size=self.dim1 * self.dim2)
    170
    171          assert len(myData) == self.dim1 * self.dim2
    172          return myData
    173
    174
    175 -    def write(self, fname):
    176          """
    177          write the file in CBF format
    178          @param fname: name of the file
    179          @type: string
    180          """
    181          if self.data is not None:
    182              self.dim2, self.dim1 = self.data.shape
    183          else:
    184              raise RuntimeError("CBF image contains no data")
    185          binary_blob = compByteOffet_numpy(self.data)
    186  #        l = len(binary_blob)
    187  #        if (l % PADDING) != 0:
    188  #            rem = PADDING - (l % PADDING)
    189  #            binary_blob += "\x00" * rem
    190          dtype = "Unknown"
    191          for key, value in DATA_TYPES.iteritems():
    192              if value == self.data.dtype:
    193                  dtype = key
    194          binary_block = [
    195                          "--CIF-BINARY-FORMAT-SECTION--",
    196                          "Content-Type: application/octet-stream;",
    197                          '     conversions="x-CBF_BYTE_OFFSET"',
    198                          'Content-Transfer-Encoding: BINARY',
    199                          "X-Binary-Size: %d" % (len(binary_blob)),
    200                          "X-Binary-ID: 1",
    201                          'X-Binary-Element-Type: "%s"' % (dtype),
    202                          "X-Binary-Element-Byte-Order: LITTLE_ENDIAN" ,
    203                          "Content-MD5: %s" % md5sum(binary_blob),
    204                          "X-Binary-Number-of-Elements: %s" % (self.dim1 * self.dim2),
    205                          "X-Binary-Size-Fastest-Dimension: %d" % self.dim1,
    206                          "X-Binary-Size-Second-Dimension: %d" % self.dim2,
    207                          "X-Binary-Size-Padding: %d" % 1,
    208                          "",
    209                          STARTER + binary_blob,
    210                          "",
    211                          "--CIF-BINARY-FORMAT-SECTION----"
    212                          ]
    213
    214          if "_array_data.header_contents" not in self.header:
    215              nonCifHeaders = []
    216          else:
    217              nonCifHeaders = [i.strip()[2:] for i in self.header["_array_data.header_contents"].split("\n") if i.find("# ") >= 0]
    218
    219          for key in self.header:
    220              if (key not in self.header_keys):
    221                  self.header_keys.append(key)
    222          for key in self.header_keys:
    223              if key.startswith("_") :
    224                  if key not in self.cif or self.cif[key] != self.header[key]:
    225                      self.cif[key] = self.header[key]
    226              elif key.startswith("X-Binary-"):
    227                  pass
    228              elif key.startswith("Content-"):
    229                  pass
    230              elif key.startswith("conversions"):
    231                  pass
    232              elif key.startswith("filename"):
    233                  pass
    234              elif key in self.header:
    235                  nonCifHeaders.append("%s %s" % (key, self.header[key]))
    236          if len(nonCifHeaders) > 0:
    237              self.cif["_array_data.header_contents"] = "\r\n".join(["# %s" % i for i in nonCifHeaders])
    238
    239          self.cif["_array_data.data"] = "\r\n".join(binary_block)
    240          self.cif.saveCIF(fname, linesep="\r\n", binary=True)
    241
    242  ################################################################################
    243  # CIF class
    244  ################################################################################
    245 -class CIF(dict):
    246      """
    247      This is the CIF class, it represents the CIF dictionary;
    248      and as a a python dictionary thus inherits from the dict built in class.
    249      """
    250      EOL = ["\r", "\n", "\r\n", "\n\r"]
    251      BLANK = [" ", "\t"] + EOL
    252      START_COMMENT = ["\"", "\'"]
    253      BINARY_MARKER = "--CIF-BINARY-FORMAT-SECTION--"
    254
    255 -    def __init__(self, _strFilename=None):
    256          """
    257          Constructor of the class.
    258
    259          @param _strFilename: the name of the file to open
    260          @type  _strFilename: filename (str) or file object
    261          """
    262          dict.__init__(self)
    263          self._ordered = []
    264          if _strFilename is not None: #load the file)
    265              self.loadCIF(_strFilename)
    266
    267 -    def __setitem__(self, key, value):
    268          if key not in self._ordered:
    269              self._ordered.append(key)
    270          return dict.__setitem__(self, key, value)
    271
    272 -    def pop(self, key):
    273          if key  in self._ordered:
    274              self._ordered.remove(key)
    275          return dict.pop(self, key)
    276
    277 -    def popitem(self, key):
    278          if key  in self._ordered:
    279              self._ordered.remove(key)
    280          return dict.popitem(self, key)
    281
    282
    283 -    def loadCIF(self, _strFilename, _bKeepComment=False):
    284          """Load the CIF file and populates the CIF dictionary into the object
    285          @param _strFilename: the name of the file to open
    286          @type  _strFilename: string
    287          @param _strFilename: the name of the file to open
    288          @type  _strFilename: string
    289          @return: None
    290          """
    291
    292          if isinstance(_strFilename, (str, unicode)):
    293              if os.path.isfile(_strFilename):
    294                  infile = open(_strFilename, "rb")
    295              else:
    296                  raise RuntimeError("CIF.loadCIF: No such file to open: %s" % _strFilename)
    297          #elif isinstance(_strFilename, file, bz2.BZ2File, ):
    298          elif "read" in dir(_strFilename):
    299              infile = _strFilename
    300          else:
    301              raise RuntimeError("CIF.loadCIF: what is %s type %s" % (_strFilename, type(_strFilename)))
    302          if _bKeepComment:
    303              self._parseCIF(infile.read())
    304          else:
    305              self._parseCIF(CIF._readCIF(infile))
    306      readCIF = loadCIF
    307
    308      @staticmethod
    309 -    def isAscii(_strIn):
    310          """
    311          Check if all characters in a string are ascii,
    312
    313          @param _strIn: input string
    314          @type _strIn: python string
    315          @return: boolean
    316          @rtype: boolean
    317          """
    318          bIsAcii = True
    319          for i in _strIn:
    320              if ord(i) > 127:
    321                  bIsAcii = False
    322                  break
    323          return bIsAcii
    324
    325
    326      @staticmethod
    327 -    def _readCIF(_instream):
    328          """
    329          - Check if the filename containing the CIF data exists
    330          - read the cif file
    331          - removes the comments
    332
    333          @param _instream: the file containing the CIF data
    334          @type _instream: open file in read mode
    335          @return: a string containing the raw data
    336          @rtype: string
    337          """
    338          if not "readlines" in dir(_instream):
    339              raise RuntimeError("CIF._readCIF(instream): I expected instream to be an opened file,\
    340               here I got %s type %s" % (_instream, type(_instream)))
    341          lLinesRead = _instream.readlines()
    342          sText = ""
    343          for sLine in lLinesRead:
    344              iPos = sLine.find("#")
    345              if iPos >= 0:
    346                  if CIF.isAscii(sLine):
    347                      sText += sLine[:iPos] + os.linesep
    348
    349                  if iPos > 80 :
    350                      logger.warning("This line is too long and could cause problems in PreQuest: %s", sLine)
    351              else :
    352                  sText += sLine
    353                  if len(sLine.strip()) > 80 :
    354                      logger.warning("This line is too long and could cause problems in PreQues: %s", sLine)
    355          return sText
    356
    357
    358 -    def _parseCIF(self, sText):
    359          """
    360          - Parses the text of a CIF file
    361          - Cut it in fields
    362          - Find all the loops and process
    363          - Find all the keys and values
    364
    365          @param sText: the content of the CIF - file
    366          @type sText: string
    367          @return: Nothing, the data are incorporated at the CIF object dictionary
    368          @rtype: None
    369          """
    370          loopidx = []
    371          looplen = []
    372          loop = []
    373          #first of all : separate the cif file in fields
    374          lFields = CIF._splitCIF(sText.strip())
    375          #Then : look for loops
    376          for i in range(len(lFields)):
    377              if lFields[i].lower() == "loop_":
    378                  loopidx.append(i)
    379          if len(loopidx) > 0:
    380              for i in loopidx:
    381                  loopone, length, keys = CIF._analyseOneLoop(lFields, i)
    382                  loop.append([keys, loopone])
    383                  looplen.append(length)
    384
    385
    386              for i in range(len(loopidx) - 1, -1, -1):
    387                  f1 = lFields[:loopidx[i]] + lFields[loopidx[i] + looplen[i]:]
    388                  lFields = f1
    389
    390              self["loop_"] = loop
    391
    392          for i in range(len(lFields) - 1):
    393      #        print lFields[i], lFields[i+1]
    394              if len(lFields[i + 1]) == 0 : lFields[i + 1] = "?"
    395              if lFields[i][0] == "_" and lFields[i + 1][0] != "_":
    396                  self[lFields[i]] = lFields[i + 1]
    397
    398      @staticmethod
    399 -    def _splitCIF(sText):
    400          """
    401          Separate the text in fields as defined in the CIF
    402
    403          @param sText: the content of the CIF - file
    404          @type sText: string
    405          @return: list of all the fields of the CIF
    406          @rtype: list
    407          """
    408          lFields = []
    409          while True:
    410              if len(sText) == 0:
    411                  break
    412              elif sText[0] == "'":
    413                  idx = 0
    414                  bFinished = False
    415                  while not  bFinished:
    416                      idx += 1 + sText[idx + 1:].find("'")
    417      ##########debuging    in case we arrive at the end of the text
    418                      if idx >= len(sText) - 1:
    419      #                    print sText,idx,len(sText)
    420                          lFields.append(sText[1:-1].strip())
    421                          sText = ""
    422                          bFinished = True
    423                          break
    424
    425                      if sText[idx + 1] in CIF.BLANK:
    426                          lFields.append(sText[1:idx].strip())
    427                          sText1 = sText[idx + 1:]
    428                          sText = sText1.strip()
    429                          bFinished = True
    430
    431              elif sText[0] == '"':
    432                  idx = 0
    433                  bFinished = False
    434                  while not  bFinished:
    435                      idx += 1 + sText[idx + 1:].find('"')
    436      ##########debuging    in case we arrive at the end of the text
    437                      if idx >= len(sText) - 1:
    438      #                    print sText,idx,len(sText)
    439                          lFields.append(sText[1:-1].strip())
    440  #                        print lFields[-1]
    441                          sText = ""
    442                          bFinished = True
    443                          break
    444
    445                      if sText[idx + 1] in CIF.BLANK:
    446                          lFields.append(sText[1:idx].strip())
    447  #                        print lFields[-1]
    448                          sText1 = sText[idx + 1:]
    449                          sText = sText1.strip()
    450                          bFinished = True
    451              elif sText[0] == ';':
    452                  if sText[1:].strip().find(CIF.BINARY_MARKER) == 0:
    453                      idx = sText[32:].find(CIF.BINARY_MARKER)
    454                      if idx == -1:
    455                          idx = 0
    456                      else:
    457                          idx += 32 + len(CIF.BINARY_MARKER)
    458                  else:
    459                      idx = 0
    460                  bFinished = False
    461                  while not  bFinished:
    462                      idx += 1 + sText[idx + 1:].find(';')
    463                      if sText[idx - 1] in CIF.EOL:
    464                          lFields.append(sText[1:idx - 1].strip())
    465                          sText1 = sText[idx + 1:]
    466                          sText = sText1.strip()
    467                          bFinished = True
    468              else:
    469                  f = sText.split(None, 1)[0]
    470                  lFields.append(f)
    471  #                print lFields[-1]
    472                  sText1 = sText[len(f):].strip()
    473                  sText = sText1
    474          return lFields
    475
    476
    477      @staticmethod
    478 -    def _analyseOneLoop(lFields, iStart):
    479          """Processes one loop in the data extraction of the CIF file
    480          @param lFields: list of all the words contained in the cif file
    481          @type lFields: list
    482          @param iStart: the starting index corresponding to the "loop_" key
    483          @type iStart: integer
    484          @return: the list of loop dictionaries, the length of the data
    485              extracted from the lFields and the list of all the keys of the loop.
    486          @rtype: tuple
    487          """
    488      #    in earch loop we first search the length of the loop
    489      #    print lFields
    490  #        curloop = {}
    491          loop = []
    492          keys = []
    493          i = iStart + 1
    494          bFinished = False
    495          while not bFinished:
    496              if lFields[i][0] == "_":
    497                  keys.append(lFields[i])#.lower())
    498                  i += 1
    499              else:
    500                  bFinished = True
    501          data = []
    502          while True:
    503              if i >= len(lFields):
    504                  break
    505              elif len(lFields[i]) == 0:
    506                  break
    507              elif lFields[i][0] == "_":
    508                  break
    509              elif lFields[i] in ["loop_", "stop_", "global_", "data_", "save_"]:
    510                  break
    511              else:
    512                  data.append(lFields[i])
    513                  i += 1
    514          #print len(keys), len(data)
    515          k = 0
    516
    517          if len(data) < len(keys):
    518              element = {}
    519              for j in keys:
    520                  if k < len(data):
    521                      element[j] = data[k]
    522                  else :
    523                      element[j] = "?"
    524                  k += 1
    525              #print element
    526              loop.append(element)
    527
    528          else:
    529              #print data
    530              #print keys
    531              for i in range(len(data) / len(keys)):
    532                  element = {}
    533                  for j in keys:
    534                      element[j] = data[k]
    535                      k += 1
    536      #            print element
    537                  loop.append(element)
    538      #    print loop
    539          return loop, 1 + len(keys) + len(data), keys
    540
    541
    542
    543
    544
    545
    546  #############################################################################################
    547  ########     everything needed to  write a cif file #########################################
    548  #############################################################################################
    549
    550 -    def saveCIF(self, _strFilename="test.cif", linesep=os.linesep, binary=False):
    551          """Transforms the CIF object in string then write it into the given file
    552          @param _strFilename: the of the file to be written
    553          @param linesep: line separation used (to force compatibility with windows/unix)
    554          @param binary: Shall we write the data as binary (True only for imageCIF/CBF)
    555          @type param: string
    556          """
    557          if binary:
    558              mode = "wb"
    559          else:
    560              mode = "w"
    561          try:
    562              fFile = open(_strFilename, mode)
    563          except IOError:
    564              print("Error during the opening of file for write: %s" %
    565                                                              _strFilename)
    566              return
    567          fFile.write(self.tostring(_strFilename, linesep))
    568          try:
    569              fFile.close()
    570          except IOError:
    571              print("Error during the closing of file for write: %s" %
    572                                                               _strFilename)
    573
    574
    575 -    def tostring(self, _strFilename=None, linesep=os.linesep):
    576          """
    577          Converts a cif dictionnary to a string according to the CIF syntax
    578
    579          @param _strFilename: the name of the filename to be appended in the header of the CIF file
    580          @type _strFilename: string
    581          @return: a sting that corresponds to the content of the CIF - file.
    582
    583          """
    584  #        sCifText = ""
    585          lstStrCif = ["# " + i for i in __version__]
    586          if "_chemical_name_common" in self:
    587              t = self["_chemical_name_common"].split()[0]
    588          elif _strFilename is not None:
    589              t = os.path.splitext(os.path.split(str(_strFilename).strip())[1])[0]
    590          else:
    591              t = ""
    592          lstStrCif.append("data_%s" % (t))
    593          #first of all get all the keys :
    594          lKeys = self.keys()
    595          lKeys.sort()
    596          for key in lKeys[:]:
    597              if key in self._ordered:
    598                  lKeys.remove(key)
    599          self._ordered += lKeys
    600
    601          for sKey in self._ordered:
    602              if sKey == "loop_":
    603                  continue
    604              if sKey not in self:
    605                  self._ordered.remove(sKey)
    606                  logger.debug("Skipping key %s from ordered list as no more present in dict")
    607                  continue
    608              sValue = str(self[sKey])
    609              if sValue.find("\n") > -1: #should add value  between ;;
    610                  lLine = [sKey, ";", sValue, ";", ""]
    611              elif len(sValue.split()) > 1: #should add value between ''
    612                  sLine = "%s        '%s'" % (sKey, sValue)
    613                  if len(sLine) > 80:
    614                      lLine = [str(sKey), sValue]
    615                  else:
    616                      lLine = [sLine]
    617              else:
    618                  sLine = "%s        %s" % (sKey, sValue)
    619                  if len(sLine) > 80:
    620                      lLine = [str(sKey), sValue]
    621                  else:
    622                      lLine = [sLine]
    623              lstStrCif += lLine
    624          if self.has_key("loop_"):
    625              for loop in self["loop_"]:
    626                  lstStrCif.append("loop_ ")
    627                  lKeys = loop[0]
    628                  llData = loop[1]
    629                  lstStrCif += [" %s" % (sKey) for sKey in lKeys]
    630                  for lData in llData:
    631                      sLine = " "
    632                      for key in lKeys:
    633                          sRawValue = lData[key]
    634                          if sRawValue.find("\n") > -1: #should add value  between ;;
    635                              lstStrCif += [sLine, ";", str(sRawValue), ";"]
    636                              sLine = " "
    637                          else:
    638                              if len(sRawValue.split()) > 1: #should add value between ''
    639                                  value = "'%s'" % (sRawValue)
    640                              else:
    641                                  value = str(sRawValue)
    642                              if len(sLine) + len(value) > 78:
    643                                  lstStrCif += [sLine]
    644                                  sLine = " " + value
    645                              else:
    646                                  sLine += " " + value
    647                      lstStrCif.append(sLine)
    648                  lstStrCif.append("")
    649          return linesep.join(lstStrCif)
    650
    651
    652 -    def exists(self, sKey):
    653          """
    654          Check if the key exists in the CIF and is non empty.
    655          @param sKey: CIF key
    656          @type sKey: string
    657          @param cif: CIF dictionary
    658          @return: True if the key exists in the CIF dictionary and is non empty
    659          @rtype: boolean
    660          """
    661          bExists = False
    662          if self.has_key(sKey):
    663              if len(self[sKey]) >= 1:
    664                  if self[sKey][0] not in ["?", "."]:
    665                      bExists = True
    666          return bExists
    667
    668
    669 -    def existsInLoop(self, sKey):
    670          """
    671          Check if the key exists in the CIF dictionary.
    672          @param sKey: CIF key
    673          @type sKey: string
    674          @param cif: CIF dictionary
    675          @return: True if the key exists in the CIF dictionary and is non empty
    676          @rtype: boolean
    677          """
    678          if not self.exists("loop_"):
    679              return False
    680          bExists = False
    681          if not bExists:
    682              for i in self["loop_"]:
    683                  for j in i[0]:
    684                      if j == sKey:
    685                          bExists = True
    686          return bExists
    687
    688
    689 -    def loadCHIPLOT(self, _strFilename):
    690          """
    691          Load the powder diffraction CHIPLOT file and returns the
    692          pd_CIF dictionary in the object
    693
    694          @param _strFilename: the name of the file to open
    695          @type  _strFilename: string
    696          @return: the CIF object corresponding to the powder diffraction
    697          @rtype: dictionary
    698          """
    699          if not os.path.isfile(_strFilename):
    700              print "I cannot find the file %s" % _strFilename
    701              raise
    702          lInFile = open(_strFilename, "r").readlines()
    703          self["_audit_creation_method"] = 'From 2-D detector using FIT2D and CIFfile'
    704          self["_pd_meas_scan_method"] = "fixed"
    705          self["_pd_spec_description"] = lInFile[0].strip()
    706          try:
    707              iLenData = int(lInFile[3])
    708          except ValueError:
    709              iLenData = None
    710          lOneLoop = []
    711          try:
    712              f2ThetaMin = float(lInFile[4].split()[0])
    713              last = ""
    714              for sLine in lInFile[-20:]:
    715                  if sLine.strip() != "":
    716                      last = sLine.strip()
    717              f2ThetaMax = float(last.split()[0])
    718              limitsOK = True
    719
    720          except (ValueError, IndexError):
    721              limitsOK = False
    722              f2ThetaMin = 180.0
    723              f2ThetaMax = 0
    724  #        print "limitsOK:", limitsOK
    725          for sLine in lInFile[4:]:
    726              sCleaned = sLine.split("#")[0].strip()
    727              data = sCleaned.split()
    728              if len(data) == 2 :
    729                  if not limitsOK:
    730                      f2Theta = float(data[0])
    731                      if f2Theta < f2ThetaMin :
    732                          f2ThetaMin = f2Theta
    733                      if f2Theta > f2ThetaMax :
    734                          f2ThetaMax = f2Theta
    735                  lOneLoop.append({ "_pd_meas_intensity_total": data[1] })
    736          if not iLenData:
    737              iLenData = len(lOneLoop)
    738          assert (iLenData == len(lOneLoop))
    739          self[ "_pd_meas_2theta_range_inc" ] = "%.4f" % ((f2ThetaMax - f2ThetaMin) / (iLenData - 1))
    740          if self[ "_pd_meas_2theta_range_inc" ] < 0:
    741              self[ "_pd_meas_2theta_range_inc" ] = abs (self[ "_pd_meas_2theta_range_inc" ])
    742              tmp = f2ThetaMax
    743              f2ThetaMax = f2ThetaMin
    744              f2ThetaMin = tmp
    745          self[ "_pd_meas_2theta_range_max" ] = "%.4f" % f2ThetaMax
    746          self[ "_pd_meas_2theta_range_min" ] = "%.4f" % f2ThetaMin
    747          self[ "_pd_meas_number_of_points" ] = str(iLenData)
    748          self["loop_"] = [ [ ["_pd_meas_intensity_total" ], lOneLoop ] ]
    749
    750
    751      @staticmethod
    752 -    def LoopHasKey(loop, key):
    753          "Returns True if the key (string) exist in the array called loop"""
    754          try:
    755              loop.index(key)
    756              return True
    757          except ValueError:
    758              return False
    759