コード例 #1
0
ファイル: cif.py プロジェクト: zhuruijie16/qmpy
def write(structures, filename=None, wrap=False, **kwargs):
    """
    Write a structure or list of structures to a cif file.

    Arguments:
        structures: 
            A :mod:`~qmpy.Structure` object or list of objects. If supplied
            with several Structures, they will all be written to the same cif.

    Keyword Arguments:
        filename:
            If supplied with a file name, will write to the file. If no
            filename is given, will return a string.
        wrap:
            If True, will include atoms at fractional coordinates 0 and 1.
            Useful only for visualizing structures.

    Examples::

        >>> io.cif.write(structure, "test.cif")
        >>> io.cif.write([structure1, structure2], "test.cif")

    """
    cif = CifFile()
    if not isinstance(structures, list):
        structures = [structures]
    for i, structure in enumerate(structures):
        cif['structure_%d' % i] = _make_cif_block(structure, wrap=wrap)

    if filename:
        open(filename, 'w').write(str(cif))
    else:
        return str(cif)
コード例 #2
0
def main():
    #read filename
    f = 'CHA.cif'  #input("Filename: ")
    # open and parse our cif
    cf = CifFile(f)
    F = f[:3]
    cb = cf[F]
    Crystal = Crysdata(F, cb)
    Crystal.printout()
コード例 #3
0
    def read(self, stream, default_type='A'):
        """Read text stream and return a trajectory instance.

        :param stream: The stream, which contains the ciffile.
        :type stream: A file-like textstream.
        :param default_type: The default particle type for
                             ciffile dialects without type definition.
        :type default_type: str
        """
        parsed_file = CifFile(stream)
        keys = list(sorted(parsed_file.keys()))

        # Index the stream
        frames = list(self._scan(parsed_file, keys, default_type))
        if len(frames) == 0:
            raise ParserError("Did not read a single complete frame.")
        logger.info("Read {} frames.".format(len(frames)))
        return Trajectory(frames)
コード例 #4
0
ファイル: __init__.py プロジェクト: JarritB/Thesis
def drawCrystal(file):
    # Check if file is file:
    S = time.time()
    global user_feedback
    ext = file[len(file)-4:]
    if(ext.lower() != ".cif"):
        print("Only cif files can be visualised")
        user_feedback = "Not a cif file"
        return
    # Check OpenBabel installation
    try:
        # Convert the cif file to its P1 symmetry notation as a temporary cif file
        print('Converting %s to P1' %file)
        obabel_fill_unit_cell(file, "temp.CIF")
        cf = CifFile("temp.CIF")
    except:
        print("No OpenBabel installation found, install it from http://openbabel.org/wiki/Category:Installation")
        user_feedback = "OpenBabel not installed"
        #cf = CifFile(file)         CifFile apparently can't read in long filepaths
        return
    # Open and parse our cif
    f = file.rsplit(dir_sep, 1)[-1]
    F = f[:3]
    print(f)
    cb = cf.first_block()
    Crystal = Crysdata(F,cb)

    # Print crystal data in terminal if checked
    if(print_data):
        Crystal.printout()

    print("Crystal data read after "+ str(time.time() - S) + " seconds")

    # Draw crystal if in Blender environment
    if(Blender_env):
        clearWS()
        Crystal.drawCrystal()
        bpy.ops.object.select_all(action='DESELECT')
        if(add_camera):
            addCamera(Crystal.cell.alen,Crystal.cell.blen,Crystal.cell.clen)
コード例 #5
0
ファイル: p_cif.py プロジェクト: samrmayers/diffpy.structure
    def _parseCifDataSource(self, datasource):
        """\
        Open and process CIF data from the specified `datasource`.


        Parameters
        ----------
        datasource : str or a file-like object
            This is used as an argument to the CifFile class.  The CifFile
            instance is stored in `ciffile` attribute of this Parser.

        Returns
        -------
        Structure
            The Structure object loaded from the specified data source.

        Raises
        ------
        StructureFormatError
            When the data do not constitute a valid CIF format.
        """
        from CifFile import CifFile, StarError
        self.stru = None
        try:
            with _suppressCifParserOutput():
                # Use `grammar` option to digest values with curly-brackets.
                # Ref: https://bitbucket.org/jamesrhester/pycifrw/issues/19
                self.ciffile = CifFile(datasource, grammar='auto')
                for blockname in self.ciffile.keys():
                    self._parseCifBlock(blockname)
                    # stop after reading the first structure
                    if self.stru is not None:
                        break
        except (StarError, ValueError, IndexError) as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err).strip()
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        return self.stru
コード例 #6
0
ファイル: p_cif.py プロジェクト: diffpy/diffpy.Structure
    def _parseCifDataSource(self, datasource):
        """\
        Open and process CIF data from the specified `datasource`.


        Parameters
        ----------
        datasource : str or a file-like object
            This is used as an argument to the CifFile class.  The CifFile
            instance is stored in `ciffile` attribute of this Parser.

        Returns
        -------
        Structure
            The Structure object loaded from the specified data source.

        Raises
        ------
        StructureFormatError
            When the data do not constitute a valid CIF format.
        """
        from CifFile import CifFile, StarError
        self.stru = None
        try:
            with _suppressCifParserOutput():
                self.ciffile = CifFile(datasource)
                for blockname in self.ciffile.keys():
                    self._parseCifBlock(blockname)
                    # stop after reading the first structure
                    if self.stru is not None:
                        break
        except (StarError, ValueError, IndexError) as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err).strip()
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        return self.stru
コード例 #7
0
def prepareGulpInput(cifFile,
                     gulpFile,
                     jobName,
                     verbose=False,
                     auto_fractions=False):
    """
    Example file:
    mgh2
    cell
    4.5168 4.5168 3.0205 90.0 90.0 90.0
    frac
    Mg 0.0 0.0 0.0
    H 0.306 0.306 0.0
    space
    136
    output xyz mgh2
    """
    from CifFile import CifFile

    if not os.path.exists(cifFile):
        raise IOError("CIF file '%s' was not found!" % (cifFile))

    cf = CifFile(cifFile)
    if verbose:
        print(
            "------------------------------------------------------------------"
        )
    if len(cf) != 1:
        raise StandardError(
            "The cif file contains %i data blocks, while one was expected")
        # A cif file can contain several "datablocks" that each start
        # with "data_".

    if verbose:
        print("Reading data block '%s'..." % (cf.keys()[0]))
    cb = cf[cf.keys()[0]]  # open the first block
    AA = float(re.match('([0-9.e]*)', cb['_cell_length_a']).group(0))
    BB = float(re.match('([0-9.e]*)', cb['_cell_length_b']).group(0))
    CC = float(re.match('([0-9.e]*)', cb['_cell_length_c']).group(0))
    alpha = float(cb['_cell_angle_alpha'])
    beta = float(cb['_cell_angle_beta'])
    gamma = float(cb['_cell_angle_gamma'])

    # Spacegroup number (1-230)
    # '_symmetry_Int_Tables_number' has been superseded by '_space_group_IT_number'

    if '_space_group_IT_number' in cb.keys():
        SG = int(cb['_space_group_IT_number'])
    elif '_symmetry_Int_Tables_number' in cb.keys():
        SG = int(cb['_symmetry_Int_Tables_number'])
    else:
        #print "WARNING: No space group specified. Assuming P1."
        SG = 1

    # CCTBX:
    #unit_cell = uctbx.unit_cell([AA,BB,CC,alpha,beta,gamma])
    #space_group_info = sgtbx.space_group_info(symbol=cb['_symmetry_space_group_name_H-M'])
    #crystal_symmetry = crystal.symmetry(unit_cell=unit_cell,space_group_info=space_group_info)
    ##print "CIF file read successfully:"
    #crystal_symmetry.show_summary()

    if verbose:
        print("  Space group:", SG)
        print("  a=%s, b=%s, c=%s, alpha=%s, beta=%s, gamma=%s" %
              (AA, BB, CC, alpha, beta, gamma))

    atomTypes = []
    atoms = ''
    fracOccFound = False
    firstAtom = True
    atoms = ""

    # The coordinates of the atom (_atom_site_fract_x/y/z) may have
    # a last digit in parenthesis, like "-0.6636(7)". Therefore we
    # extract the part consisting of only digits and a decimal separator:
    coordsMatch = re.compile('[0-9.e-]*')

    for atom in cb.GetLoop('_atom_site_label'):
        atomKeys = dir(atom)
        if '_atom_site_type_symbol' in atomKeys:
            m = re.match('[a-z]*', atom._atom_site_type_symbol, re.I)
            atomType = m.group(0)
        else:
            m = re.match('[a-z]*', atom._atom_site_label, re.I)
            atomType = m.group(0)

        if '_atom_site_occupancy' in atomKeys:
            occ = float(coordsMatch.match(atom._atom_site_occupancy).group())
            if not occ == 1.0:
                if not fracOccFound:
                    #print " "
                    #print "  WARNING: Fractional occupancy (" + str(occ) +") " \
                    + "found for atom of type " + atomType + "."
                fracOccFound = True
        else:
            occ = 1.0

        # Some crystal structures obtained by neutron diffraction use D for H:
        if atomType == 'D':
            atomType = 'H'

        if '_atom_site_symmetry_multiplicity' in atomKeys and '_atom_site_Wyckoff_symbol' in atomKeys:
            atomTypes.append(atomType + ' at ' +
                             atom._atom_site_symmetry_multiplicity +
                             atom._atom_site_Wyckoff_symbol)
        else:
            atomTypes.append(atomType)

        atomX = coordsMatch.match(atom._atom_site_fract_x).group()
        atomY = coordsMatch.match(atom._atom_site_fract_y).group()
        atomZ = coordsMatch.match(atom._atom_site_fract_z).group()

        atomPos = [atomX, atomY, atomZ]
        for i in range(3):
            pp = atomPos[i].split(".")
            if len(pp) is 2:
                decimals = pp[1]
                if len(decimals) > 3 and len(decimals) < 6 and decimals[
                        0] == decimals[1] and decimals[0] == decimals[
                            2] and decimals[-1] != "0":
                    if auto_fractions:
                        oldPos = atomPos[i]
                        atomPos[i] = "%.6f" % (float(
                            eval('1.*' + float2fraction(atomPos[i]))))
                        #print "  Notice: Converted %s into %s" %(oldPos,atomPos[i])
                    else:
                        #print "\n"\
                        + "  ! Warning: The coordinate "+atomPos[i]+" looks similar to the fraction %s, but\n" % float2fraction(atomPos[i]) \
                        + "  !   has insufficient decimals to be recognized as so by GULP. If you want\n" \
                        + "  !   this coordinate to be recognized as a special high-symmetry position,\n" \
                        + "  !   you need to specify at least six digits. If you run cif2vasp with the \n" \
                        + "  !   -f switch, cif2vasp will try to add the necessary decimals automaticly."

        atoms += "%s %s %s %s %f %f\n" % (atomType, atomPos[0], atomPos[1],
                                          atomPos[2], 0.0, occ)
        firstAtom = False

    if fracOccFound:
        #print " "
        #print "ERROR: Fractional occupancies are not currently supported.\n"
        exit()

    if verbose:
        print("  Atom types: " + ', '.join(atomTypes))

    gulpFile = open(gulpFile, 'w')  #Create and write the GULP
    gulpFile.writelines([
        jobName + '\n', 'cell\n',
        '%s %s %s %s %s %s\n' % (AA, BB, CC, alpha, beta, gamma), 'frac\n',
        atoms, 'space\n',
        str(SG) + '\n', 'output xyz ' + jobName + '\n'
    ])
コード例 #8
0
def readCifFile(cifFile):
    from CifFile import CifFile

    if not os.path.exists(cifFile):
        raise IOError("CIF file '%s' was not found!" % (cifFile))

    cf = CifFile(cifFile)
    #print "------------------------------------------------------------------"
    if len(cf) != 1:
        print("xx")
#        raise(tandardError("The cif file contains %i data blocks, while one was expected"))
# A cif file can contain several "datablocks" that each start
# with "data_".

    cb = cf[cf.keys()[0]]  # open the first block
    AA = float(re.match('([0-9.]*)', cb['_cell_length_a']).group(0))
    BB = float(re.match('([0-9.]*)', cb['_cell_length_b']).group(0))
    CC = float(re.match('([0-9.]*)', cb['_cell_length_c']).group(0))
    alpha = float(cb['_cell_angle_alpha'])
    beta = float(cb['_cell_angle_beta'])
    gamma = float(cb['_cell_angle_gamma'])
    SG = int(cb['_symmetry_Int_Tables_number'])  # spacegroup

    atomTypes = []
    atoms = ''
    fracOccFound = False
    firstAtom = True
    atoms = []
    for atom in cb.GetLoop('_atom_site_label'):
        atomKeys = dir(atom)
        if '_atom_site_type_symbol' in atomKeys:
            m = re.match('[a-z]*', atom._atom_site_type_symbol, re.I)
            atomType = m.group(0)
        else:
            m = re.match('[a-z]*', atom._atom_site_label, re.I)
            atomType = m.group(0)

        atomLabel = atom._atom_site_label

        if '_atom_site_occupancy' in atomKeys:
            occ = float(atom._atom_site_occupancy)
            if not occ == 1.0:
                if not fracOccFound:
                    #print " "
                    #print "  WARNING: Fractional occupancy (" + str(occ) +") " \
                    + "found for atom of type " + atomType + "."
                fracOccFound = True
        else:
            occ = 1.0

        # Some crystal structures obtained by neutron diffraction use D for H:
        if atomType == 'D':
            atomType = 'H'
            atomLabel.replace('H', 'D')

        if '_atom_site_symmetry_multiplicity' in atomKeys and '_atom_site_Wyckoff_symbol' in atomKeys:
            atomTypes.append(atomType + ' at ' +
                             atom._atom_site_symmetry_multiplicity +
                             atom._atom_site_Wyckoff_symbol)
        else:
            atomTypes.append(atomType)

        atomPos = [
            atom._atom_site_fract_x, atom._atom_site_fract_y,
            atom._atom_site_fract_z
        ]
        for p in atomPos:
            pp = p.split(".")
            if len(pp) is 2:
                decimals = p.split(".")[1]
            # if len(decimals) > 3 and len(decimals) < 6 and decimals[0] == decimals[1] and decimals[-1] != "0":
            #print "\n  ---------------------\n"\
            #    + "  Warning: If the fractional coordinate "+p+" is a recurring decimal, such as 1/3,\n" \
            #    + "    then it is necessary to specify this value to six decimal places to be sure of \n" \
            #    + "    it being recognised correctly as a spcecial position.\n  ------------------"

# The coordinates of the atom (_atom_site_fract_x/y/z) may have
# a last digit in parenthesis, like "0.6636(7)". Therefore we
# extract the part consisting of only digits and a decimal separator:
        p = re.compile('[0-9.]*')
        atomX = float(p.match(atom._atom_site_fract_x).group())
        atomY = float(p.match(atom._atom_site_fract_y).group())
        atomZ = float(p.match(atom._atom_site_fract_z).group())

        #atoms += "%s %f %f %f %f %f\n" % (atomType, atomX, atomY, atomZ, 0.0, occ)
        atoms.append({
            'label': atomLabel,
            'type': atomType,
            'pos': (atomX, atomY, atomZ)
        })
        firstAtom = False

    if fracOccFound:
        #print " "
        #print "ERROR: Fractional occupancies are not currently supported.\n"
        exit()

    #print "  Atom types: " + ', '.join(atomTypes)

    return {
        'spacegroup': SG,
        'unit_cell': [AA, BB, CC, alpha, beta, gamma],
        'scatterers': atoms
    }
コード例 #9
0
ファイル: p_cif.py プロジェクト: pavoljuhas/diffpy.structure
class P_cif(StructureParser):
    """Simple parser for CIF structure format.
    Reads Structure from the first block containing _atom_site_label key.
    Following blocks, if any are ignored.

    Data members:

    format      -- structure format name
    ciffile     -- instance of CifFile from PyCifRW
    stru        -- Structure instance used for cif input or output

    Data members used for input only:

    spacegroup  -- instance of SpaceGroup used for symmetry expansion
    eps         -- resolution in fractional coordinates for non-equal
                   positions.  Use for expansion of asymmetric unit.
    eau         -- instance of ExpandAsymmetricUnit from SymmetryUtilities
    asymmetric_unit -- list of atom instances for the original asymmetric
                   unit in the CIF file
    labelindex  -- dictionary mapping unique atom label to index of atom
                   in self.asymmetric_unit
    cif_sgname  -- space group name obtained by looking up the value of
                   _space_group_name_Hall, _symmetry_space_group_name_Hall,
                   _space_group_name_H-M_alt, _symmetry_space_group_name_H-M
                   items.  None when neither is defined.
    """

    # static data and methods ------------------------------------------------

    # dictionary set of class methods for translating CIF values
    # to Atom attributes

    _atom_setters = dict.fromkeys((
        '_tr_ignore',
        '_tr_atom_site_label',
        '_tr_atom_site_type_symbol',
        '_tr_atom_site_fract_x',
        '_tr_atom_site_fract_y',
        '_tr_atom_site_fract_z',
        '_tr_atom_site_cartn_x',
        '_tr_atom_site_cartn_y',
        '_tr_atom_site_cartn_z',
        '_tr_atom_site_U_iso_or_equiv',
        '_tr_atom_site_B_iso_or_equiv',
        '_tr_atom_site_adp_type', '_tr_atom_site_thermal_displace_type',
        '_tr_atom_site_occupancy',
        '_tr_atom_site_aniso_U_11',
        '_tr_atom_site_aniso_U_22',
        '_tr_atom_site_aniso_U_33',
        '_tr_atom_site_aniso_U_12',
        '_tr_atom_site_aniso_U_13',
        '_tr_atom_site_aniso_U_23',
        '_tr_atom_site_aniso_B_11',
        '_tr_atom_site_aniso_B_22',
        '_tr_atom_site_aniso_B_33',
        '_tr_atom_site_aniso_B_12',
        '_tr_atom_site_aniso_B_13',
        '_tr_atom_site_aniso_B_23',
        ))
    # make _atom_setters case insensitive
    for k in list(_atom_setters.keys()):
        _atom_setters[k] = _atom_setters[k.lower()] = k
    del k

    BtoU = 1.0/(8 * numpy.pi**2)

    def _tr_ignore(a, value):
        return
    _tr_ignore = staticmethod(_tr_ignore)

    def _tr_atom_site_label(a, value):
        a.label = str(value)
        # set element when not specified by _atom_site_type_symbol
        if not a.element:
            P_cif._tr_atom_site_type_symbol(a, value)
    _tr_atom_site_label = staticmethod(_tr_atom_site_label)

    # 3 regexp groups for nucleon number, atom symbol, and oxidation state
    _psymb = re.compile(r'(\d+-)?([a-zA-Z]+)(\d[+-])?')

    def _tr_atom_site_type_symbol(a, value):
        rx = P_cif._psymb.match(value)
        smbl = rx and rx.group(0) or value
        smbl = str(smbl)
        a.element = smbl[:1].upper() + smbl[1:].lower()
    _tr_atom_site_type_symbol = staticmethod(_tr_atom_site_type_symbol)

    def _tr_atom_site_fract_x(a, value):
        a.xyz[0] = leading_float(value)
    _tr_atom_site_fract_x = staticmethod(_tr_atom_site_fract_x)

    def _tr_atom_site_fract_y(a, value):
        a.xyz[1] = leading_float(value)
    _tr_atom_site_fract_y = staticmethod(_tr_atom_site_fract_y)

    def _tr_atom_site_fract_z(a, value):
        a.xyz[2] = leading_float(value)
    _tr_atom_site_fract_z = staticmethod(_tr_atom_site_fract_z)

    def _tr_atom_site_cartn_x(a, value):
        a.xyz_cartn[0] = leading_float(value)
    _tr_atom_site_cartn_x = staticmethod(_tr_atom_site_cartn_x)

    def _tr_atom_site_cartn_y(a, value):
        a.xyz_cartn[1] = leading_float(value)
    _tr_atom_site_cartn_y = staticmethod(_tr_atom_site_cartn_y)

    def _tr_atom_site_cartn_z(a, value):
        a.xyz_cartn[2] = leading_float(value)
    _tr_atom_site_cartn_z = staticmethod(_tr_atom_site_cartn_z)

    def _tr_atom_site_U_iso_or_equiv(a, value):
        a.Uisoequiv = leading_float(value)
    _tr_atom_site_U_iso_or_equiv = staticmethod(_tr_atom_site_U_iso_or_equiv)

    def _tr_atom_site_B_iso_or_equiv(a, value):
        a.Uisoequiv = P_cif.BtoU * leading_float(value)
    _tr_atom_site_B_iso_or_equiv = staticmethod(_tr_atom_site_B_iso_or_equiv)

    def _tr_atom_site_adp_type(a, value):
        a.anisotropy = value not in ("Uiso", "Biso")
    _tr_atom_site_adp_type = staticmethod(_tr_atom_site_adp_type)
    _tr_atom_site_thermal_displace_type = _tr_atom_site_adp_type

    def _tr_atom_site_occupancy(a, value):
        a.occupancy = leading_float(value, 1.0)
    _tr_atom_site_occupancy = staticmethod(_tr_atom_site_occupancy)

    def _tr_atom_site_aniso_U_11(a, value):
        a.U11 = leading_float(value)
    _tr_atom_site_aniso_U_11 = staticmethod(_tr_atom_site_aniso_U_11)

    def _tr_atom_site_aniso_U_22(a, value):
        a.U22 = leading_float(value)
    _tr_atom_site_aniso_U_22 = staticmethod(_tr_atom_site_aniso_U_22)

    def _tr_atom_site_aniso_U_33(a, value):
        a.U33 = leading_float(value)
    _tr_atom_site_aniso_U_33 = staticmethod(_tr_atom_site_aniso_U_33)

    def _tr_atom_site_aniso_U_12(a, value):
        a.U12 = leading_float(value)
    _tr_atom_site_aniso_U_12 = staticmethod(_tr_atom_site_aniso_U_12)

    def _tr_atom_site_aniso_U_13(a, value):
        a.U13 = leading_float(value)
    _tr_atom_site_aniso_U_13 = staticmethod(_tr_atom_site_aniso_U_13)

    def _tr_atom_site_aniso_U_23(a, value):
        a.U23 = leading_float(value)
    _tr_atom_site_aniso_U_23 = staticmethod(_tr_atom_site_aniso_U_23)

    def _tr_atom_site_aniso_B_11(a, value):
        a.U11 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_11 = staticmethod(_tr_atom_site_aniso_B_11)

    def _tr_atom_site_aniso_B_22(a, value):
        a.U22 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_22 = staticmethod(_tr_atom_site_aniso_B_22)

    def _tr_atom_site_aniso_B_33(a, value):
        a.U33 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_33 = staticmethod(_tr_atom_site_aniso_B_33)

    def _tr_atom_site_aniso_B_12(a, value):
        a.U12 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_12 = staticmethod(_tr_atom_site_aniso_B_12)

    def _tr_atom_site_aniso_B_13(a, value):
        a.U13 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_13 = staticmethod(_tr_atom_site_aniso_B_13)

    def _tr_atom_site_aniso_B_23(a, value):
        a.U23 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_23 = staticmethod(_tr_atom_site_aniso_B_23)


    def _get_atom_setters(cifloop):
        """Find translators of CifLoop items to data in Atom instance.
        Static method.

        cifloop -- instance of CifLoop

        Return a list of setter functions in the order of cifloop.keys().
        """
        rv = []
        for p in cifloop.keys():
            lcname = "_tr" + p.lower()
            fncname = P_cif._atom_setters.get(lcname, '_tr_ignore')
            f = getattr(P_cif, fncname)
            rv.append(f)
        return rv
    _get_atom_setters = staticmethod(_get_atom_setters)

    # normal methods ---------------------------------------------------------

    def __init__(self, eps=None):
        """Initialize the parser for CIF structure files.

        eps  -- fractional coordinates cutoff for duplicate positions.
                When None use the default for ExpandAsymmetricUnit.
        """
        StructureParser.__init__(self)
        self.format = "cif"
        self.ciffile = None
        self.stru = None
        self.spacegroup = None
        self.eps = eps
        self.eau = None
        self.asymmetric_unit = None
        self.labelindex = {}
        self.cif_sgname = None
        pass


    def parse(self, s):
        """Create Structure instance from a string in CIF format.

        Return Structure instance or raise StructureFormatError.
        """
        self.ciffile = None
        self.filename = ''
        fp = six.StringIO(s)
        rv = self._parseCifDataSource(fp)
        return rv


    def parseLines(self, lines):
        """Parse list of lines in CIF format.

        lines -- list of strings stripped of line terminator

        Return Structure instance or raise StructureFormatError.
        """
        s = "\n".join(lines) + '\n'
        return self.parse(s)


    def parseFile(self, filename):
        """Create Structure from an existing CIF file.

        filename  -- path to structure file

        Return Structure object.
        Raise StructureFormatError or IOError.
        """
        self.ciffile = None
        self.filename = filename
        fileurl = _fixIfWindowsPath(filename)
        rv = self._parseCifDataSource(fileurl)
        # all good here
        return rv


    def _parseCifDataSource(self, datasource):
        """\
        Open and process CIF data from the specified `datasource`.


        Parameters
        ----------
        datasource : str or a file-like object
            This is used as an argument to the CifFile class.  The CifFile
            instance is stored in `ciffile` attribute of this Parser.

        Returns
        -------
        Structure
            The Structure object loaded from the specified data source.

        Raises
        ------
        StructureFormatError
            When the data do not constitute a valid CIF format.
        """
        from CifFile import CifFile, StarError
        self.stru = None
        try:
            with _suppressCifParserOutput():
                self.ciffile = CifFile(datasource)
                for blockname in self.ciffile.keys():
                    self._parseCifBlock(blockname)
                    # stop after reading the first structure
                    if self.stru is not None:
                        break
        except (StarError, ValueError, IndexError) as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err).strip()
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        return self.stru


    def _parseCifBlock(self, blockname):
        """Translate CIF file block, skip blocks without _atom_site_label.
        Updates data members stru, eau.

        blockname  -- name of top level block in self.ciffile

        No return value.
        """
        block = self.ciffile[blockname]
        if '_atom_site_label' not in block:   return
        # here block contains structure, initialize output data
        self.stru = Structure()
        self.labelindex.clear()
        # execute specialized block parsers
        self._parse_lattice(block)
        self._parse_atom_site_label(block)
        self._parse_atom_site_aniso_label(block)
        self._parse_space_group_symop_operation_xyz(block)
        return


    def _parse_lattice(self, block):
        """Obtain lattice parameters from a CifBlock.
        This method updates self.stru.lattice.

        block -- instance of CifBlock

        No return value.
        """
        if '_cell_length_a' not in block: return
        # obtain lattice parameters
        try:
            latpars = (
                leading_float(block['_cell_length_a']),
                leading_float(block['_cell_length_b']),
                leading_float(block['_cell_length_c']),
                leading_float(block['_cell_angle_alpha']),
                leading_float(block['_cell_angle_beta']),
                leading_float(block['_cell_angle_gamma']),
            )
        except KeyError as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err)
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        self.stru.lattice = Lattice(*latpars)
        return


    def _parse_atom_site_label(self, block):
        """Obtain atoms in asymmetric unit from a CifBlock.
        This method inserts Atom instances to self.stru and
        updates labelindex dictionary.

        block -- instance of CifBlock

        No return value.
        """
        # process _atom_site_label
        atom_site_loop = block.GetLoop('_atom_site_label')
        # get a list of setters for atom_site values
        prop_setters = P_cif._get_atom_setters(atom_site_loop)
        # index of the _atom_site_label item for the labelindex dictionary
        ilb = atom_site_loop.keys().index('_atom_site_label')
        # loop through the values and pass them to the setters
        sitedatalist = zip(*atom_site_loop.values())
        for values in sitedatalist:
            curlabel = values[ilb]
            # skip entries that have invalid label
            if curlabel == '?':
                continue
            self.labelindex[curlabel] = len(self.stru)
            self.stru.addNewAtom()
            a = self.stru.getLastAtom()
            for fset, val in zip(prop_setters, values):
                fset(a, val)
        return


    def _parse_atom_site_aniso_label(self, block):
        """Obtain value of anisotropic thermal displacements from a CifBlock.
        This method updates U members of Atom instances in self.stru.
        The labelindex dictionary has to be defined beforehand.

        block -- instance of CifBlock

        No return value.
        """
        if '_atom_site_aniso_label' not in block: return
        # was anisotropy processed in the _atom_site_label loop?
        isotropy_done = _hasAtomSiteADPType(block)
        # something to do here:
        adp_loop = block.GetLoop('_atom_site_aniso_label')
        # index of the _atom_site_label column
        ilb = adp_loop.keys().index('_atom_site_aniso_label')
        # get a list of setters for this loop
        prop_setters = P_cif._get_atom_setters(adp_loop)
        sitedatalist = zip(*adp_loop.values())
        for values in sitedatalist:
            idx = self.labelindex[values[ilb]]
            a = self.stru[idx]
            if not isotropy_done:
                a.anisotropy = True
            for fset, val in zip(prop_setters, values):
                fset(a, val)
        return


    def _parse_space_group_symop_operation_xyz(self, block):
        """Process symmetry operations from a CifBlock.  The method
        updates spacegroup and eau data according to symmetry
        operations defined in _space_group_symop_operation_xyz or
        _symmetry_equiv_pos_as_xyz items in CifBlock.

        block -- instance of CifBlock

        No return value.
        """
        from diffpy.structure.spacegroups import IsSpaceGroupIdentifier
        from diffpy.structure.spacegroups import SpaceGroup, GetSpaceGroup
        from diffpy.structure.spacegroups import FindSpaceGroup
        self.asymmetric_unit = list(self.stru)
        sym_synonyms = ('_space_group_symop_operation_xyz',
                        '_symmetry_equiv_pos_as_xyz')
        sym_loop_name = [n for n in sym_synonyms if n in block]
        # recover explicit list of symmetry operations
        symop_list = []
        if sym_loop_name:
            # sym_loop exists here and we know its cif name
            sym_loop_name = sym_loop_name[0]
            sym_loop = block.GetLoop(sym_loop_name)
            for eqxyz in sym_loop[sym_loop_name]:
                opcif = getSymOp(eqxyz)
                symop_list.append(opcif)
        # determine space group number
        sg_nameHall = (block.get('_space_group_name_Hall', '') or
                block.get('_symmetry_space_group_name_Hall', ''))
        sg_nameHM = (block.get('_space_group_name_H-M_alt', '') or
                block.get('_symmetry_space_group_name_H-M', ''))
        self.cif_sgname = (sg_nameHall or sg_nameHM or None)
        sgid = (int(block.get('_space_group_IT_number', '0')) or
                int(block.get('_symmetry_Int_Tables_number', '0')) or
                sg_nameHM)
        self.spacegroup = None
        # try to reuse existing space group from symmetry operations
        if symop_list:
            try:
                self.spacegroup = FindSpaceGroup(symop_list)
            except ValueError:
                pass
        # otherwise lookup the space group from its identifier
        if self.spacegroup is None and sgid and IsSpaceGroupIdentifier(sgid):
            self.spacegroup = GetSpaceGroup(sgid)
        # define new spacegroup when symmetry operations were listed, but
        # there is no match to an existing definition
        if symop_list and self.spacegroup is None:
            new_short_name = "CIF " + (sg_nameHall or 'data')
            new_crystal_system = (
                    block.get('_space_group_crystal_system') or
                    block.get('_symmetry_cell_setting') or
                    'TRICLINIC' ).upper()
            self.spacegroup = SpaceGroup(
                    short_name=new_short_name,
                    crystal_system=new_crystal_system,
                    symop_list=symop_list)
        if self.spacegroup is None:
            emsg = "CIF file has unknown space group identifier {!r}."
            raise StructureFormatError(emsg.format(sgid))
        self._expandAsymmetricUnit(block)
        return


    def _expandAsymmetricUnit(self, block):
        """Perform symmetry expansion of self.stru using self.spacegroup.

        This method updates data in stru and eau.

        Parameters
        ----------
        block : CifBlock
            The top-level block containing crystal structure data.
        """
        from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit
        corepos = [a.xyz for a in self.stru]
        coreUijs = [a.U for a in self.stru]
        self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs,
                                        eps=self.eps)
        # setup anisotropy according to symmetry requirements
        # was isotropy flag already processed
        isotropy_done = (_hasAtomSiteADPType(block) or
                         '_atom_site_aniso_label' in block)
        if not isotropy_done:
            for ca, uisotropy in zip(self.stru, self.eau.Uisotropy):
                ca.anisotropy = not uisotropy
        # build a nested list of new atoms:
        newatoms = []
        for i, ca in enumerate(self.stru):
            eca = []    # expanded core atom
            for j in range(self.eau.multiplicity[i]):
                a = Atom(ca)
                a.xyz = self.eau.expandedpos[i][j]
                if j > 0:
                    a.label += '_' + str(j + 1)
                if a.anisotropy:
                    a.U = self.eau.expandedUijs[i][j]
                eca.append(a)
            newatoms.append(eca)
        # insert new atoms where they belong
        self.stru[:] = sum(newatoms, [])
        return

    # conversion to CIF ------------------------------------------------------

    def toLines(self, stru):
        """Convert Structure stru to a list of lines in basic CIF format.

        Return list of strings.
        """
        import time
        lines = []
        # may be replaced with filtered Structure.title
        # for now, we can add the title as a comment
        if stru.title.strip() != "":
            title_lines = stru.title.split('\n')
            lines.extend([ "# " + line.strip() for line in title_lines ])
            lines.append("")
        lines.append("data_3D")
        iso_date =  "%04i-%02i-%02i" % time.gmtime()[:3]
        lines.extend([
            "%-31s %s" % ("_audit_creation_date", iso_date),
            "%-31s %s" % ("_audit_creation_method", "P_cif.py"),
            "",
            "%-31s %s" % ("_symmetry_space_group_name_H-M", "'P1'"),
            "%-31s %s" % ("_symmetry_Int_Tables_number", "1"),
            "%-31s %s" % ("_symmetry_cell_setting", "triclinic"),
            "" ])
        # there should be no need to specify equivalent positions for P1
        # _symmetry_equiv_posi_as_xyz x,y,z
        lines.extend([
            "%-31s %.6g" % ("_cell_length_a", stru.lattice.a),
            "%-31s %.6g" % ("_cell_length_b", stru.lattice.b),
            "%-31s %.6g" % ("_cell_length_c", stru.lattice.c),
            "%-31s %.6g" % ("_cell_angle_alpha", stru.lattice.alpha),
            "%-31s %.6g" % ("_cell_angle_beta", stru.lattice.beta),
            "%-31s %.6g" % ("_cell_angle_gamma", stru.lattice.gamma),
            "" ])
        # build a list of site labels and adp (displacement factor) types
        element_count = {}
        a_site_label = []
        a_adp_type = []
        for a in stru:
            cnt = element_count[a.element] = element_count.get(a.element,0)+1
            a_site_label.append( "%s%i" % (a.element, cnt) )
            if numpy.all(a.U == a.U[0,0]*numpy.identity(3)):
                a_adp_type.append("Uiso")
            else:
                a_adp_type.append("Uani")
        # list all atoms
        lines.extend([
            "loop_",
            "  _atom_site_label",
            "  _atom_site_type_symbol",
            "  _atom_site_fract_x",
            "  _atom_site_fract_y",
            "  _atom_site_fract_z",
            "  _atom_site_U_iso_or_equiv",
            "  _atom_site_adp_type",
            "  _atom_site_occupancy" ])
        for i in range(len(stru)):
            a = stru[i]
            line = "  %-5s %-3s %11.6f %11.6f %11.6f %11.6f %-5s %.4f" % (
                    a_site_label[i], a.element, a.xyz[0], a.xyz[1], a.xyz[2],
                    a.Uisoequiv, a_adp_type[i], a.occupancy  )
            lines.append(line)
        # find anisotropic atoms
        idx_aniso = [ i for i in range(len(stru)) if a_adp_type[i] != "Uiso" ]
        if idx_aniso != []:
            lines.extend([
                "loop_",
                "  _atom_site_aniso_label",
                "  _atom_site_aniso_U_11",
                "  _atom_site_aniso_U_22",
                "  _atom_site_aniso_U_33",
                "  _atom_site_aniso_U_12",
                "  _atom_site_aniso_U_13",
                "  _atom_site_aniso_U_23" ])
            for i in idx_aniso:
                a = stru[i]
                line = "  %-5s %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f" % (
                        a_site_label[i], a.U[0,0], a.U[1,1], a.U[2,2],
                        a.U[0,1], a.U[0,2], a.U[1,2] )
                lines.append(line)
        return lines
コード例 #10
0
ファイル: writers.py プロジェクト: LaurentRDC/crystals
def write_cif(crystal, fname):
    """
    Generate an atomic coordinates .cif file from a crystal structure.

    .. versionadded:: 1.2.0

    Parameters
    ----------
    crystal : crystals.Crystal
        Crystal to be converted.
    fname : path-like
        The CIF file will be written to this file. If the file already exists,
        it will be overwritten.
    comment : str or None, optional
        Comment to include at the second line of ``fname``.
    """
    cf = CifFile(strict=False)
    a, b, c, alpha, beta, gamma = crystal.lattice_parameters
    lattice_items = {
        "_cell_length_a": a,
        "_cell_length_b": b,
        "_cell_length_c": c,
        "_cell_angle_alpha": alpha,
        "_cell_angle_beta": beta,
        "_cell_angle_gamma": gamma,
    }

    sym = crystal.symmetry()
    symmetry_items = {
        "_symmetry_Int_Tables_number": sym["international_number"],
        "_symmetry_space_group_name_Hall": sym["hall_symbol"],
    }

    block = CifBlock()
    for key, val in lattice_items.items():
        block[key] = val

    for key, val in symmetry_items.items():
        block[key] = val

    # Note that we are using all atoms in the unit-cell,
    # and not the asymmetric unit cell + symmetry operators
    # This is valid CIF! And it is much simpler to implement
    # TODO: how to determine asymmetric cell + symmetry operations?
    atoms = list(crystal.primitive().unitcell)
    symbols = [atm.symbol for atm in atoms]
    xf = [atm.coords_fractional[0] for atm in atoms]
    yf = [atm.coords_fractional[1] for atm in atoms]
    zf = [atm.coords_fractional[2] for atm in atoms]

    block.CreateLoop(
        datanames=[
            "_atom_site_type_symbol",
            "_atom_site_fract_x",
            "_atom_site_fract_y",
            "_atom_site_fract_z",
        ],
        length_check=False,
    )
    block["_atom_site_type_symbol"] = symbols
    block["_atom_site_fract_x"] = xf
    block["_atom_site_fract_y"] = yf
    block["_atom_site_fract_z"] = zf

    # Name of the block cannot be empty!
    block_name = crystal.chemical_formula.replace(" ", "_")
    cf[block_name] = block

    # Converting to string writes to stdout for some reason
    with redirect_stdout(StringIO()):
        lines = str(cf).splitlines()

    with open(fname, "w", encoding="utf-8") as f:
        f.write(CIF_HEADER)
        f.write("\n".join(lines[13::]))  # Skip the fixed header
コード例 #11
0
    def get_cif(self, find_symmetry: bool = False):
        """ Get CIF format string

        :params find_symmetry: if use Spglib to find symmetry. Default: False
        """

        lattice = np.array(self.cell) * self.lat0 * BOHR_TO_A  # in Cartesian
        positions = []
        numbers = []
        magmoms = []
        label = []
        type_symbol = []
        for index, elem in enumerate(self.elements):
            num = self.numbers[elem]
            for n in range(num):
                numbers.append(index)
                label.append(f"{elem}{n+1}")
                type_symbol.append(f"{elem}")
                # fractional atomic positions
                positions.append(self.scaled_positions[elem][n])
                magmoms.append(self.magmoms[elem])
        if sum(magmoms):
            spgcell = (lattice, positions, numbers, magmoms)
        else:
            spgcell = (lattice, positions, numbers)

        from CifFile import CifBlock, CifFile
        cf = CifFile()
        cb = CifBlock()
        cb['_cell_length_a'], cb['_cell_length_b'], cb['_cell_length_c'], cb[
            '_cell_angle_alpha'], cb['_cell_angle_beta'], cb[
                '_cell_angle_gamma'] = self.cellpar

        from abacuskit.postprocess.symmetry import Spacegroup
        if find_symmetry:
            from spglib import get_symmetry_dataset
            dataset = get_symmetry_dataset(spgcell)
            if dataset:
                cb['_atom_site_label'] = [
                    self.elements[i] for i in dataset['std_types']
                ]
                cb['_atom_site_type_symbol'] = [
                    f"{self.elements[i]}{i}" for i in dataset['std_types']
                ]
                number = dataset['number']
                cb['_symmetry_cell_setting'] = Spacegroup().get_crystal_system(
                    number)
                cb['_space_group_IT_number'] = number
                cb['_space_group_name_H-M_alt'] = dataset['international']
                cb['_space_group_name_Hall'] = dataset['hall']
                x, y, z = np.split(dataset["std_positions"], 3, axis=1)
            else:
                cb['_atom_site_label'] = label
                cb['_atom_site_type_symbol'] = type_symbol
                cb['_symmetry_cell_setting'] = Spacegroup().get_crystal_system(
                    1)
                cb['_space_group_IT_number'] = 1
                cb['_space_group_name_H-M_alt'] = "P1"
                cb['_space_group_name_Hall'] = "P 1"
                x, y, z = np.split(np.array(positions), 3, axis=1)
        else:
            cb['_atom_site_label'] = label
            cb['_atom_site_type_symbol'] = type_symbol
            cb['_symmetry_cell_setting'] = Spacegroup().get_crystal_system(1)
            cb['_space_group_IT_number'] = 1
            cb['_space_group_name_H-M_alt'] = "P1"
            cb['_space_group_name_Hall'] = "P 1"
            x, y, z = np.split(np.array(positions), 3, axis=1)

        cb['_atom_site_fract_x'], cb['_atom_site_fract_y'], cb[
            '_atom_site_fract_z'] = x.flatten(), y.flatten(), z.flatten()
        cb.CreateLoop([
            '_atom_site_label', '_atom_site_type_symbol', '_atom_site_fract_x',
            '_atom_site_fract_y', '_atom_site_fract_z'
        ])

        cf['stru_to_cif'] = cb

        return str(cf)
コード例 #12
0
ファイル: read_cif.py プロジェクト: vitroid/cif2ice
def read_cif(fNameIn):
    logger = logging.getLogger()

    data = {}


    # Open the CIF file and read all the lines into a list of strings.
    try:
        f = open(fNameIn, 'r')
        lines = []
        oko_fix = False
        for line in f:
            stripped = line.strip()
            if (len(stripped) > 0):
                #fix for OKO
                #http://stackoverflow.com/questions/79968/split-a-string-by-spaces-preserving-quoted-substrings-in-python
                import shlex
                if stripped[0] == '_':
                    cols = shlex.split(stripped) #shlex protects the quotation
                    if len(cols) > 2:
                        stripped = "{0} '{1}'".format(cols[0]," ".join(cols[1:]))
                        logger.debug("Line (Fixed): {0}".format(stripped))
                        oko_fix = True
                lines.append(stripped)
        if oko_fix:
            fixedfilename = fNameIn + ".fix"
            fixedfile = open(fixedfilename,"w")
            fixedfile.write("\n".join(lines)+"\n")
            fixedfile.close()
            fNameIn = fixedfilename
    except:
        logger.error("Failed to open CIF file '{0}'".format(fNameIn))
        sys.exit()

    # Use the CifFile parser to extract the data.  Although there might be
    # multiple data blocks, we'll only use the first one.
    cif_file = CifFile(fNameIn)
    
    for db in cif_file:
        data_block = db
        break


    try:

        # Extract some parameters, and convert them to floats.
        data['_cell_length_a']    = float_with_error(data_block['_cell_length_a'])
        data['_cell_length_b']    = float_with_error(data_block['_cell_length_b'])
        data['_cell_length_c']    = float_with_error(data_block['_cell_length_c'])
        data['_cell_angle_alpha'] = float_with_error(data_block['_cell_angle_alpha'])
        data['_cell_angle_beta']  = float_with_error(data_block['_cell_angle_beta'])
        data['_cell_angle_gamma'] = float_with_error(data_block['_cell_angle_gamma'])
        if data_block.has_key('_cell_volume'):
            data['_cell_volume']      = float_with_error(data_block['_cell_volume'])

        # Get the symbolic operations that define the space group.  In a CIF file
        # that's the part that looks like:
        #
        # loop_
        # _symmetry_equiv_pos_as_xyz
        #   'x,y,z'
        #   'y,x,2/3-z'
        #   '-y,x-y,2/3+z'
        #   '-x,-x+y,1/3-z'
        #   '-x+y,-x,1/3+z'
        #   'x-y,-y,-z'
        #
        # In some cases it's called "_space_group_symop_operation_xyz" apparently?!?!
        data['_symmetry_equiv_pos_as_xyz'] = []

        try:
            xyz = data_block["_symmetry_equiv_pos_as_xyz"]

        except KeyError:
            try:
                xyz = data_block["_space_group_symop_operation_xyz"]
            except KeyError:
                logger.error("Missing item in CIF file: need either '_symmetry_equiv_pos_as_xyz' or '_space_group_symop_operation_xyz'.")
                sys.exit()


        # Copy the x,y,z symmetry group operations.  Remove the quotes if there
        # are any.
        for op_xyz in xyz:

            if (op_xyz[0] == '\''):
                data['_symmetry_equiv_pos_as_xyz'].append(op_xyz[1:-1])
            else:
                data['_symmetry_equiv_pos_as_xyz'].append(op_xyz)


        # Add x,y,z of the atoms to "data", but make sure to convert
        # e.g. "0.1549(8)" to "0.1549".
        data['_atom_site_label'] = data_block['_atom_site_label']

        data['_atom_site_fract_x'] = []
        for str_x in data_block['_atom_site_fract_x']:
            data['_atom_site_fract_x'].append( float_with_error(str_x))

        data['_atom_site_fract_y'] = []
        for str_y in data_block['_atom_site_fract_y']:
            data['_atom_site_fract_y'].append( float_with_error(str_y))

        data['_atom_site_fract_z'] = []
        for str_z in data_block['_atom_site_fract_z']:
            data['_atom_site_fract_z'].append( float_with_error(str_z))

    
    except KeyError as e:
        logger.error("Error!  Missing item in file.")
        logger.error(e)
        sys.exit()


    #print "ALL DATA:"
    #print data
    #print

    # Return the extracted data.
    return data
コード例 #13
0
ファイル: cif2vasp.py プロジェクト: danmichaelo/cif2vasp
def prepareGulpInput(cifFile, gulpFile, jobName, verbose = False, auto_fractions = False): 
    """
    Example file:
    mgh2
    cell
    4.5168 4.5168 3.0205 90.0 90.0 90.0
    frac
    Mg 0.0 0.0 0.0
    H 0.306 0.306 0.0
    space
    136
    output xyz mgh2
    """
    from CifFile import CifFile
    
    if not os.path.exists(cifFile):
        raise IOError("CIF file '%s' was not found!" % (cifFile))
    
    cf = CifFile(cifFile)
    if verbose:
        print "------------------------------------------------------------------"
    if len(cf) != 1:
        raise StandardError("The cif file contains %i data blocks, while one was expected")
        # A cif file can contain several "datablocks" that each start
        # with "data_".
    
    if verbose:
        print "Reading data block '%s'..." % (cf.keys()[0])
    cb = cf[cf.keys()[0]]                               # open the first block
    AA = float(re.match('([0-9.e]*)',cb['_cell_length_a']).group(0))
    BB = float(re.match('([0-9.e]*)',cb['_cell_length_b']).group(0))
    CC = float(re.match('([0-9.e]*)',cb['_cell_length_c']).group(0))
    alpha = float(cb['_cell_angle_alpha'])
    beta = float(cb['_cell_angle_beta'])
    gamma = float(cb['_cell_angle_gamma'])    
    
    # Spacegroup number (1-230)
    # '_symmetry_Int_Tables_number' has been superseded by '_space_group_IT_number'
    
    if '_space_group_IT_number' in cb.keys():
        SG = int(cb['_space_group_IT_number'])
    elif '_symmetry_Int_Tables_number' in cb.keys():
        SG = int(cb['_symmetry_Int_Tables_number'])
    else:
        print "WARNING: No space group specified. Assuming P1."
        SG = 1
           

    # CCTBX:
    #unit_cell = uctbx.unit_cell([AA,BB,CC,alpha,beta,gamma])
    #space_group_info = sgtbx.space_group_info(symbol=cb['_symmetry_space_group_name_H-M'])
    #crystal_symmetry = crystal.symmetry(unit_cell=unit_cell,space_group_info=space_group_info)    
    #print "CIF file read successfully:"
    #crystal_symmetry.show_summary()   

    if verbose:
        print "  Space group:",SG
        print "  a=%s, b=%s, c=%s, alpha=%s, beta=%s, gamma=%s" % (AA,BB,CC,alpha,beta,gamma)
    
    atomTypes = []
    atoms = ''
    fracOccFound = False
    firstAtom = True
    atoms = ""

    # The coordinates of the atom (_atom_site_fract_x/y/z) may have 
    # a last digit in parenthesis, like "-0.6636(7)". Therefore we
    # extract the part consisting of only digits and a decimal separator:
    coordsMatch = re.compile('[0-9.e-]*');

    for atom in cb.GetLoop('_atom_site_label'):
        atomKeys = dir(atom)
        if '_atom_site_type_symbol' in atomKeys:
            m = re.match('[a-z]*',atom._atom_site_type_symbol,re.I)
            atomType = m.group(0)
        else:
            m = re.match('[a-z]*',atom._atom_site_label,re.I)
            atomType = m.group(0)

        if '_atom_site_occupancy' in atomKeys:
            occ = float(coordsMatch.match(atom._atom_site_occupancy).group())
            if not occ == 1.0:
                if not fracOccFound: 
                    print " "
                print "  WARNING: Fractional occupancy (" + str(occ) +") " \
                    + "found for atom of type " + atomType + "."                
                fracOccFound = True
        else:
            occ = 1.0
        
        # Some crystal structures obtained by neutron diffraction use D for H:
        if atomType == 'D':
            atomType = 'H'
        
        if '_atom_site_symmetry_multiplicity' in atomKeys and '_atom_site_Wyckoff_symbol' in atomKeys:
            atomTypes.append(atomType+' at '+atom._atom_site_symmetry_multiplicity+atom._atom_site_Wyckoff_symbol)
        else:
            atomTypes.append(atomType)

        atomX = coordsMatch.match(atom._atom_site_fract_x).group()
        atomY = coordsMatch.match(atom._atom_site_fract_y).group()
        atomZ = coordsMatch.match(atom._atom_site_fract_z).group()
        
        atomPos = [atomX, atomY, atomZ]
        for i in range(3):
            pp = atomPos[i].split(".")
            if len(pp) is 2:
                decimals = pp[1]
                if len(decimals) > 3 and len(decimals) < 6 and decimals[0] == decimals[1] and decimals[0] == decimals[2] and decimals[-1] != "0":
                    if auto_fractions:
                        oldPos = atomPos[i]
                        atomPos[i] = "%.6f" % (float(eval('1.*'+float2fraction(atomPos[i]))))
                        print "  Notice: Converted %s into %s" %(oldPos,atomPos[i])
                    else:
                        print "\n"\
                            + "  ! Warning: The coordinate "+atomPos[i]+" looks similar to the fraction %s, but\n" % float2fraction(atomPos[i]) \
                            + "  !   has insufficient decimals to be recognized as so by GULP. If you want\n" \
                            + "  !   this coordinate to be recognized as a special high-symmetry position,\n" \
                            + "  !   you need to specify at least six digits. If you run cif2vasp with the \n" \
                            + "  !   -f switch, cif2vasp will try to add the necessary decimals automaticly."		
        
        atoms += "%s %s %s %s %f %f\n" % (atomType, atomPos[0], atomPos[1], atomPos[2], 0.0, occ)
        firstAtom = False

    if fracOccFound: 
        print " "
        print "ERROR: Fractional occupancies are not currently supported.\n"
        exit()
    
    if verbose:
        print "  Atom types: " + ', '.join(atomTypes)
    
    gulpFile = open(gulpFile,'w')       #Create and write the GULP  
    gulpFile.writelines([jobName+'\n',
        'cell\n',
        '%s %s %s %s %s %s\n' % (AA,BB,CC,alpha,beta,gamma),
        'frac\n',
        atoms,
        'space\n',
        str(SG)+'\n',
        'output xyz '+jobName+'\n'
    ])
コード例 #14
0
ファイル: cif2vasp.py プロジェクト: danmichaelo/cif2vasp
def readCifFile(cifFile):
    from CifFile import CifFile

    if not os.path.exists(cifFile):
        raise IOError("CIF file '%s' was not found!" % (cifFile))
    
    cf = CifFile(cifFile)
    print "------------------------------------------------------------------"
    if len(cf) != 1:
        raise StandardError("The cif file contains %i data blocks, while one was expected")
        # A cif file can contain several "datablocks" that each start
        # with "data_".
    
    cb = cf[cf.keys()[0]]                               # open the first block
    AA = float(re.match('([0-9.]*)',cb['_cell_length_a']).group(0))
    BB = float(re.match('([0-9.]*)',cb['_cell_length_b']).group(0))
    CC = float(re.match('([0-9.]*)',cb['_cell_length_c']).group(0))
    alpha = float(cb['_cell_angle_alpha'])
    beta = float(cb['_cell_angle_beta'])
    gamma = float(cb['_cell_angle_gamma'])
    SG = int(cb['_symmetry_Int_Tables_number'])              # spacegroup
  
    atomTypes = []
    atoms = ''
    fracOccFound = False
    firstAtom = True
    atoms = []
    for atom in cb.GetLoop('_atom_site_label'):
        atomKeys = dir(atom)
        if '_atom_site_type_symbol' in atomKeys:
            m = re.match('[a-z]*',atom._atom_site_type_symbol,re.I)
            atomType = m.group(0)
        else:
            m = re.match('[a-z]*',atom._atom_site_label,re.I)
            atomType = m.group(0)
        
        atomLabel = atom._atom_site_label

        if '_atom_site_occupancy' in atomKeys:
            occ = float(atom._atom_site_occupancy)
            if not occ == 1.0:
                if not fracOccFound: 
                    print " "
                print "  WARNING: Fractional occupancy (" + str(occ) +") " \
                    + "found for atom of type " + atomType + "."                
                fracOccFound = True
        else:
            occ = 1.0
        
        # Some crystal structures obtained by neutron diffraction use D for H:
        if atomType == 'D':
            atomType = 'H'
            atomLabel.replace('H','D')
        
        if '_atom_site_symmetry_multiplicity' in atomKeys and '_atom_site_Wyckoff_symbol' in atomKeys:
            atomTypes.append(atomType+' at '+atom._atom_site_symmetry_multiplicity+atom._atom_site_Wyckoff_symbol)
        else:
            atomTypes.append(atomType)
        
        atomPos = [atom._atom_site_fract_x, atom._atom_site_fract_y, atom._atom_site_fract_z]
        for p in atomPos:
            pp = p.split(".")
            if len(pp) is 2:
                decimals = p.split(".")[1]
                if len(decimals) > 3 and len(decimals) < 6 and decimals[0] == decimals[1] and decimals[-1] != "0":
                    print "\n  ---------------------\n"\
                        + "  Warning: If the fractional coordinate "+p+" is a recurring decimal, such as 1/3,\n" \
                        + "    then it is necessary to specify this value to six decimal places to be sure of \n" \
                        + "    it being recognised correctly as a spcecial position.\n  ------------------" 
		
		
		# The coordinates of the atom (_atom_site_fract_x/y/z) may have 
		# a last digit in parenthesis, like "0.6636(7)". Therefore we
		# extract the part consisting of only digits and a decimal separator:
		p = re.compile('[0-9.]*');
        atomX = float(p.match(atom._atom_site_fract_x).group())
        atomY = float(p.match(atom._atom_site_fract_y).group())
        atomZ = float(p.match(atom._atom_site_fract_z).group())
        
        #atoms += "%s %f %f %f %f %f\n" % (atomType, atomX, atomY, atomZ, 0.0, occ)
        atoms.append({'label': atomLabel, 'type': atomType, 'pos': (atomX,atomY,atomZ) })
        firstAtom = False

    if fracOccFound: 
        print " "
        print "ERROR: Fractional occupancies are not currently supported.\n"
        exit()
    
    print "  Atom types: " + ', '.join(atomTypes)
    
    return {'spacegroup': SG, 'unit_cell': [AA,BB,CC,alpha,beta,gamma], 'scatterers': atoms}
コード例 #15
0
ファイル: readCIF.py プロジェクト: cathyers/tsu_cod
# prepare a cursor object using cursor() method
cursor = db.cursor()

sql = "select file from data"
cursor.execute(sql)
allFiles = cursor.fetchall()

for i in range(0, len(allFiles)):
    fileNum = str(allFiles[i][0])
    fileName = "%s.cif" % fileNum

    dir_level1 = fileNum[0]
    dir_level2 = fileNum[1:3]
    dir_level3 = fileNum[3:5]

    cif_full_dir = os.path.join(cif_dir, dir_level1, dir_level2, dir_level3)
    cif_full_file = os.path.join(cif_full_dir, fileName)

    if (not os.path.exists(cif_full_file)):
        raise IOError("CIF file '%s' was not found!" % (fileName))
    else:
        cf = CifFile(cif_full_file)
        cb = cf[cf.keys()[0]]
        cb['_symmetry_cell_setting']
        cb['_symmetry_space_group_name_Hall']
        cb['_symmetry_space_group_name_H-M']
        cb['_atom_site_label']
        cb['_atom_site_fract_x']
        cb['_atom_site_fract_y']
        cb['_atom_site_fract_z']
コード例 #16
0
ファイル: p_cif.py プロジェクト: diffpy/diffpy.Structure
class P_cif(StructureParser):
    """Simple parser for CIF structure format.
    Reads Structure from the first block containing _atom_site_label key.
    Following blocks, if any are ignored.

    Data members:

    format      -- structure format name
    ciffile     -- instance of CifFile from PyCifRW
    stru        -- Structure instance used for cif input or output

    Data members used for input only:

    spacegroup  -- instance of SpaceGroup used for symmetry expansion
    eps         -- resolution in fractional coordinates for non-equal
                   positions.  Use for expansion of asymmetric unit.
    eau         -- instance of ExpandAsymmetricUnit from SymmetryUtilities
    asymmetric_unit -- list of atom instances for the original asymmetric
                   unit in the CIF file
    labelindex  -- dictionary mapping unique atom label to index of atom
                   in self.asymmetric_unit
    cif_sgname  -- space group name obtained by looking up the value of
                   _space_group_name_Hall, _symmetry_space_group_name_Hall,
                   _space_group_name_H-M_alt, _symmetry_space_group_name_H-M
                   items.  None when neither is defined.
    """

    # static data and methods ------------------------------------------------

    # dictionary set of class methods for translating CIF values
    # to Atom attributes

    _atom_setters = dict.fromkeys((
        '_tr_ignore',
        '_tr_atom_site_label',
        '_tr_atom_site_type_symbol',
        '_tr_atom_site_fract_x',
        '_tr_atom_site_fract_y',
        '_tr_atom_site_fract_z',
        '_tr_atom_site_cartn_x',
        '_tr_atom_site_cartn_y',
        '_tr_atom_site_cartn_z',
        '_tr_atom_site_U_iso_or_equiv',
        '_tr_atom_site_B_iso_or_equiv',
        '_tr_atom_site_adp_type', '_tr_atom_site_thermal_displace_type',
        '_tr_atom_site_occupancy',
        '_tr_atom_site_aniso_U_11',
        '_tr_atom_site_aniso_U_22',
        '_tr_atom_site_aniso_U_33',
        '_tr_atom_site_aniso_U_12',
        '_tr_atom_site_aniso_U_13',
        '_tr_atom_site_aniso_U_23',
        '_tr_atom_site_aniso_B_11',
        '_tr_atom_site_aniso_B_22',
        '_tr_atom_site_aniso_B_33',
        '_tr_atom_site_aniso_B_12',
        '_tr_atom_site_aniso_B_13',
        '_tr_atom_site_aniso_B_23',
        ))
    # make _atom_setters case insensitive
    for k in list(_atom_setters.keys()):
        _atom_setters[k] = _atom_setters[k.lower()] = k
    del k

    BtoU = 1.0/(8 * numpy.pi**2)

    def _tr_ignore(a, value):
        return
    _tr_ignore = staticmethod(_tr_ignore)

    def _tr_atom_site_label(a, value):
        a.label = str(value)
        # set element when not specified by _atom_site_type_symbol
        if not a.element:
            P_cif._tr_atom_site_type_symbol(a, value)
    _tr_atom_site_label = staticmethod(_tr_atom_site_label)

    # 3 regexp groups for nucleon number, atom symbol, and oxidation state
    _psymb = re.compile(r'(\d+-)?([a-zA-Z]+)(\d[+-])?')

    def _tr_atom_site_type_symbol(a, value):
        rx = P_cif._psymb.match(value)
        smbl = rx and rx.group(0) or value
        smbl = str(smbl)
        a.element = smbl[:1].upper() + smbl[1:].lower()
    _tr_atom_site_type_symbol = staticmethod(_tr_atom_site_type_symbol)

    def _tr_atom_site_fract_x(a, value):
        a.xyz[0] = leading_float(value)
    _tr_atom_site_fract_x = staticmethod(_tr_atom_site_fract_x)

    def _tr_atom_site_fract_y(a, value):
        a.xyz[1] = leading_float(value)
    _tr_atom_site_fract_y = staticmethod(_tr_atom_site_fract_y)

    def _tr_atom_site_fract_z(a, value):
        a.xyz[2] = leading_float(value)
    _tr_atom_site_fract_z = staticmethod(_tr_atom_site_fract_z)

    def _tr_atom_site_cartn_x(a, value):
        a.xyz_cartn[0] = leading_float(value)
    _tr_atom_site_cartn_x = staticmethod(_tr_atom_site_cartn_x)

    def _tr_atom_site_cartn_y(a, value):
        a.xyz_cartn[1] = leading_float(value)
    _tr_atom_site_cartn_y = staticmethod(_tr_atom_site_cartn_y)

    def _tr_atom_site_cartn_z(a, value):
        a.xyz_cartn[2] = leading_float(value)
    _tr_atom_site_cartn_z = staticmethod(_tr_atom_site_cartn_z)

    def _tr_atom_site_U_iso_or_equiv(a, value):
        a.Uisoequiv = leading_float(value)
    _tr_atom_site_U_iso_or_equiv = staticmethod(_tr_atom_site_U_iso_or_equiv)

    def _tr_atom_site_B_iso_or_equiv(a, value):
        a.Uisoequiv = P_cif.BtoU * leading_float(value)
    _tr_atom_site_B_iso_or_equiv = staticmethod(_tr_atom_site_B_iso_or_equiv)

    def _tr_atom_site_adp_type(a, value):
        a.anisotropy = value not in ("Uiso", "Biso")
    _tr_atom_site_adp_type = staticmethod(_tr_atom_site_adp_type)
    _tr_atom_site_thermal_displace_type = _tr_atom_site_adp_type

    def _tr_atom_site_occupancy(a, value):
        a.occupancy = leading_float(value, 1.0)
    _tr_atom_site_occupancy = staticmethod(_tr_atom_site_occupancy)

    def _tr_atom_site_aniso_U_11(a, value):
        a.U11 = leading_float(value)
    _tr_atom_site_aniso_U_11 = staticmethod(_tr_atom_site_aniso_U_11)

    def _tr_atom_site_aniso_U_22(a, value):
        a.U22 = leading_float(value)
    _tr_atom_site_aniso_U_22 = staticmethod(_tr_atom_site_aniso_U_22)

    def _tr_atom_site_aniso_U_33(a, value):
        a.U33 = leading_float(value)
    _tr_atom_site_aniso_U_33 = staticmethod(_tr_atom_site_aniso_U_33)

    def _tr_atom_site_aniso_U_12(a, value):
        a.U12 = leading_float(value)
    _tr_atom_site_aniso_U_12 = staticmethod(_tr_atom_site_aniso_U_12)

    def _tr_atom_site_aniso_U_13(a, value):
        a.U13 = leading_float(value)
    _tr_atom_site_aniso_U_13 = staticmethod(_tr_atom_site_aniso_U_13)

    def _tr_atom_site_aniso_U_23(a, value):
        a.U23 = leading_float(value)
    _tr_atom_site_aniso_U_23 = staticmethod(_tr_atom_site_aniso_U_23)

    def _tr_atom_site_aniso_B_11(a, value):
        a.U11 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_11 = staticmethod(_tr_atom_site_aniso_B_11)

    def _tr_atom_site_aniso_B_22(a, value):
        a.U22 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_22 = staticmethod(_tr_atom_site_aniso_B_22)

    def _tr_atom_site_aniso_B_33(a, value):
        a.U33 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_33 = staticmethod(_tr_atom_site_aniso_B_33)

    def _tr_atom_site_aniso_B_12(a, value):
        a.U12 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_12 = staticmethod(_tr_atom_site_aniso_B_12)

    def _tr_atom_site_aniso_B_13(a, value):
        a.U13 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_13 = staticmethod(_tr_atom_site_aniso_B_13)

    def _tr_atom_site_aniso_B_23(a, value):
        a.U23 = P_cif.BtoU * leading_float(value)
    _tr_atom_site_aniso_B_23 = staticmethod(_tr_atom_site_aniso_B_23)


    def _get_atom_setters(cifloop):
        """Find translators of CifLoop items to data in Atom instance.
        Static method.

        cifloop -- instance of CifLoop

        Return a list of setter functions in the order of cifloop.keys().
        """
        rv = []
        for p in cifloop.keys():
            lcname = "_tr" + p.lower()
            fncname = P_cif._atom_setters.get(lcname, '_tr_ignore')
            f = getattr(P_cif, fncname)
            rv.append(f)
        return rv
    _get_atom_setters = staticmethod(_get_atom_setters)

    # normal methods ---------------------------------------------------------

    def __init__(self, eps=None):
        """Initialize the parser for CIF structure files.

        eps  -- fractional coordinates cutoff for duplicate positions.
                When None use the default for ExpandAsymmetricUnit.
        """
        StructureParser.__init__(self)
        self.format = "cif"
        self.ciffile = None
        self.stru = None
        self.spacegroup = None
        self.eps = eps
        self.eau = None
        self.asymmetric_unit = None
        self.labelindex = {}
        self.cif_sgname = None
        pass


    def parse(self, s):
        """Create Structure instance from a string in CIF format.

        Return Structure instance or raise StructureFormatError.
        """
        self.ciffile = None
        self.filename = ''
        fp = six.StringIO(s)
        rv = self._parseCifDataSource(fp)
        return rv


    def parseLines(self, lines):
        """Parse list of lines in CIF format.

        lines -- list of strings stripped of line terminator

        Return Structure instance or raise StructureFormatError.
        """
        s = "\n".join(lines) + '\n'
        return self.parse(s)


    def parseFile(self, filename):
        """Create Structure from an existing CIF file.

        filename  -- path to structure file

        Return Structure object.
        Raise StructureFormatError or IOError.
        """
        self.ciffile = None
        self.filename = filename
        fileurl = _fixIfWindowsPath(filename)
        rv = self._parseCifDataSource(fileurl)
        # all good here
        return rv


    def _parseCifDataSource(self, datasource):
        """\
        Open and process CIF data from the specified `datasource`.


        Parameters
        ----------
        datasource : str or a file-like object
            This is used as an argument to the CifFile class.  The CifFile
            instance is stored in `ciffile` attribute of this Parser.

        Returns
        -------
        Structure
            The Structure object loaded from the specified data source.

        Raises
        ------
        StructureFormatError
            When the data do not constitute a valid CIF format.
        """
        from CifFile import CifFile, StarError
        self.stru = None
        try:
            with _suppressCifParserOutput():
                self.ciffile = CifFile(datasource)
                for blockname in self.ciffile.keys():
                    self._parseCifBlock(blockname)
                    # stop after reading the first structure
                    if self.stru is not None:
                        break
        except (StarError, ValueError, IndexError) as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err).strip()
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        return self.stru


    def _parseCifBlock(self, blockname):
        """Translate CIF file block, skip blocks without _atom_site_label.
        Updates data members stru, eau.

        blockname  -- name of top level block in self.ciffile

        No return value.
        """
        block = self.ciffile[blockname]
        if '_atom_site_label' not in block:   return
        # here block contains structure, initialize output data
        self.stru = Structure()
        self.labelindex.clear()
        # execute specialized block parsers
        self._parse_lattice(block)
        self._parse_atom_site_label(block)
        self._parse_atom_site_aniso_label(block)
        self._parse_space_group_symop_operation_xyz(block)
        return


    def _parse_lattice(self, block):
        """Obtain lattice parameters from a CifBlock.
        This method updates self.stru.lattice.

        block -- instance of CifBlock

        No return value.
        """
        if '_cell_length_a' not in block: return
        # obtain lattice parameters
        try:
            latpars = (
                leading_float(block['_cell_length_a']),
                leading_float(block['_cell_length_b']),
                leading_float(block['_cell_length_c']),
                leading_float(block['_cell_angle_alpha']),
                leading_float(block['_cell_angle_beta']),
                leading_float(block['_cell_angle_gamma']),
            )
        except KeyError as err:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            emsg = str(err)
            e = StructureFormatError(emsg)
            six.reraise(StructureFormatError, e, exc_traceback)
        self.stru.lattice = Lattice(*latpars)
        return


    def _parse_atom_site_label(self, block):
        """Obtain atoms in asymmetric unit from a CifBlock.
        This method inserts Atom instances to self.stru and
        updates labelindex dictionary.

        block -- instance of CifBlock

        No return value.
        """
        # process _atom_site_label
        atom_site_loop = block.GetLoop('_atom_site_label')
        # get a list of setters for atom_site values
        prop_setters = P_cif._get_atom_setters(atom_site_loop)
        # index of the _atom_site_label item for the labelindex dictionary
        ilb = atom_site_loop.keys().index('_atom_site_label')
        # loop through the values and pass them to the setters
        sitedatalist = zip(*atom_site_loop.values())
        for values in sitedatalist:
            curlabel = values[ilb]
            # skip entries that have invalid label
            if curlabel == '?':
                continue
            self.labelindex[curlabel] = len(self.stru)
            self.stru.addNewAtom()
            a = self.stru.getLastAtom()
            for fset, val in zip(prop_setters, values):
                fset(a, val)
        return


    def _parse_atom_site_aniso_label(self, block):
        """Obtain value of anisotropic thermal displacements from a CifBlock.
        This method updates U members of Atom instances in self.stru.
        The labelindex dictionary has to be defined beforehand.

        block -- instance of CifBlock

        No return value.
        """
        if '_atom_site_aniso_label' not in block: return
        # was anisotropy set in the _atom_site_label loop?
        atom_site_loop = block.GetLoop('_atom_site_label')
        anisotropy_already_set = (
            '_atom_site_adp_type' in atom_site_loop or
            '_atom_site_thermal_displace_type' in atom_site_loop)
        # something to do here:
        adp_loop = block.GetLoop('_atom_site_aniso_label')
        # index of the _atom_site_label column
        ilb = adp_loop.keys().index('_atom_site_aniso_label')
        # get a list of setters for this loop
        prop_setters = P_cif._get_atom_setters(adp_loop)
        sitedatalist = zip(*adp_loop.values())
        for values in sitedatalist:
            idx = self.labelindex[values[ilb]]
            a = self.stru[idx]
            if not anisotropy_already_set:
                a.anisotropy = True
            for fset, val in zip(prop_setters, values):
                fset(a, val)
        return


    def _parse_space_group_symop_operation_xyz(self, block):
        """Process symmetry operations from a CifBlock.  The method
        updates spacegroup and eau data according to symmetry
        operations defined in _space_group_symop_operation_xyz or
        _symmetry_equiv_pos_as_xyz items in CifBlock.

        block -- instance of CifBlock

        No return value.
        """
        from diffpy.structure.spacegroups import IsSpaceGroupIdentifier
        from diffpy.structure.spacegroups import SpaceGroup, GetSpaceGroup
        self.asymmetric_unit = list(self.stru)
        sym_synonyms = ('_space_group_symop_operation_xyz',
                        '_symmetry_equiv_pos_as_xyz')
        sym_loop_name = [n for n in sym_synonyms if n in block]
        # recover explicit list of symmetry operations
        symop_list = []
        if sym_loop_name:
            # sym_loop exists here and we know its cif name
            sym_loop_name = sym_loop_name[0]
            sym_loop = block.GetLoop(sym_loop_name)
            for eqxyz in sym_loop[sym_loop_name]:
                opcif = getSymOp(eqxyz)
                symop_list.append(opcif)
        # determine space group number
        sg_nameHall = (block.get('_space_group_name_Hall', '') or
                block.get('_symmetry_space_group_name_Hall', ''))
        sg_nameHM = (block.get('_space_group_name_H-M_alt', '') or
                block.get('_symmetry_space_group_name_H-M', ''))
        self.cif_sgname = (sg_nameHall or sg_nameHM or None)
        sgid = (int(block.get('_space_group_IT_number', '0')) or
                int(block.get('_symmetry_Int_Tables_number', '0')) or
                sg_nameHM)
        # try to reuse existing space group
        self.spacegroup = None
        if sgid and IsSpaceGroupIdentifier(sgid):
            sgstd = GetSpaceGroup(sgid)
            oprep_std = [str(op) for op in sgstd.iter_symops()]
            oprep_std.sort()
            oprep_cif = [str(op) for op in symop_list]
            oprep_cif.sort()
            # make sure symmetry operations have the same order
            if oprep_std == oprep_cif:
                self.spacegroup = copy.copy(sgstd)
                self.spacegroup.symop_list = symop_list
            # use standard definition when symmetry operations were not listed
            elif not symop_list:
                self.spacegroup = sgstd
        # define new spacegroup when symmetry operations were listed, but
        # there is no match to an existing definition
        if symop_list and self.spacegroup is None:
            new_short_name = "CIF " + (sg_nameHall or 'data')
            new_crystal_system = (
                    block.get('_space_group_crystal_system') or
                    block.get('_symmetry_cell_setting') or
                    'TRICLINIC' ).upper()
            self.spacegroup = SpaceGroup(
                    short_name=new_short_name,
                    crystal_system=new_crystal_system,
                    symop_list=symop_list)
        if self.spacegroup is None:
            emsg = "CIF file has unknown space group identifier {!r}."
            raise StructureFormatError(emsg.format(sgid))
        self._expandAsymmetricUnit()
        return


    def _expandAsymmetricUnit(self):
        """Perform symmetry expansion of self.stru using self.spacegroup.
        This method updates data in stru and eau.

        No return value.
        """
        from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit
        # get reverse-ordered unique indices
        corepos = [a.xyz for a in self.stru]
        coreUijs = [a.U for a in self.stru]
        self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs,
                                        eps=self.eps)
        # build a nested list of new atoms:
        newatoms = []
        for i, ca in enumerate(self.stru):
            eca = []    # expanded core atom
            for j in range(self.eau.multiplicity[i]):
                a = Atom(ca)
                a.xyz = self.eau.expandedpos[i][j]
                if j > 0:
                    a.label += '_' + str(j + 1)
                if a.anisotropy:
                    a.U = self.eau.expandedUijs[i][j]
                eca.append(a)
            newatoms.append(eca)
        # insert new atoms where they belong
        self.stru[:] = sum(newatoms, [])
        return

    # conversion to CIF ------------------------------------------------------

    def toLines(self, stru):
        """Convert Structure stru to a list of lines in basic CIF format.

        Return list of strings.
        """
        import time
        lines = []
        # may be replaced with filtered Structure.title
        # for now, we can add the title as a comment
        if stru.title.strip() != "":
            title_lines = stru.title.split('\n')
            lines.extend([ "# " + line.strip() for line in title_lines ])
            lines.append("")
        lines.append("data_3D")
        iso_date =  "%04i-%02i-%02i" % time.gmtime()[:3]
        lines.extend([
            "%-31s %s" % ("_audit_creation_date", iso_date),
            "%-31s %s" % ("_audit_creation_method", "P_cif.py"),
            "",
            "%-31s %s" % ("_symmetry_space_group_name_H-M", "'P1'"),
            "%-31s %s" % ("_symmetry_Int_Tables_number", "1"),
            "%-31s %s" % ("_symmetry_cell_setting", "triclinic"),
            "" ])
        # there should be no need to specify equivalent positions for P1
        # _symmetry_equiv_posi_as_xyz x,y,z
        lines.extend([
            "%-31s %.6g" % ("_cell_length_a", stru.lattice.a),
            "%-31s %.6g" % ("_cell_length_b", stru.lattice.b),
            "%-31s %.6g" % ("_cell_length_c", stru.lattice.c),
            "%-31s %.6g" % ("_cell_angle_alpha", stru.lattice.alpha),
            "%-31s %.6g" % ("_cell_angle_beta", stru.lattice.beta),
            "%-31s %.6g" % ("_cell_angle_gamma", stru.lattice.gamma),
            "" ])
        # build a list of site labels and adp (displacement factor) types
        element_count = {}
        a_site_label = []
        a_adp_type = []
        for a in stru:
            cnt = element_count[a.element] = element_count.get(a.element,0)+1
            a_site_label.append( "%s%i" % (a.element, cnt) )
            if numpy.all(a.U == a.U[0,0]*numpy.identity(3)):
                a_adp_type.append("Uiso")
            else:
                a_adp_type.append("Uani")
        # list all atoms
        lines.extend([
            "loop_",
            "  _atom_site_label",
            "  _atom_site_type_symbol",
            "  _atom_site_fract_x",
            "  _atom_site_fract_y",
            "  _atom_site_fract_z",
            "  _atom_site_U_iso_or_equiv",
            "  _atom_site_adp_type",
            "  _atom_site_occupancy" ])
        for i in range(len(stru)):
            a = stru[i]
            line = "  %-5s %-3s %11.6f %11.6f %11.6f %11.6f %-5s %.4f" % (
                    a_site_label[i], a.element, a.xyz[0], a.xyz[1], a.xyz[2],
                    a.Uisoequiv, a_adp_type[i], a.occupancy  )
            lines.append(line)
        # find anisotropic atoms
        idx_aniso = [ i for i in range(len(stru)) if a_adp_type[i] != "Uiso" ]
        if idx_aniso != []:
            lines.extend([
                "loop_",
                "  _atom_site_aniso_label",
                "  _atom_site_aniso_U_11",
                "  _atom_site_aniso_U_22",
                "  _atom_site_aniso_U_33",
                "  _atom_site_aniso_U_12",
                "  _atom_site_aniso_U_13",
                "  _atom_site_aniso_U_23" ])
            for i in idx_aniso:
                a = stru[i]
                line = "  %-5s %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f" % (
                        a_site_label[i], a.U[0,0], a.U[1,1], a.U[2,2],
                        a.U[0,1], a.U[0,2], a.U[1,2] )
                lines.append(line)
        return lines