Exemple #1
0
def pycifrw_from_cif(datablocks, loops=None, names=None):
    """
    Constructs PyCifRW's CifFile from an array of CIF datablocks.

    :param datablocks: an array of CIF datablocks
    :param loops: optional list of lists of CIF tag loops.
    :param names: optional list of datablock names
    :return: CifFile
    """
    import CifFile
    from CifFile import CifBlock

    if loops is None:
        loops = dict()

    cif = CifFile.CifFile()
    try:
        cif.set_grammar("1.1")
    except AttributeError:
        # if no grammar can be set, we assume it's 1.1 (widespread standard)
        pass

    if names and len(names) < len(datablocks):
        raise ValueError("Not enough names supplied for "
                         "datablocks: {} (names) < "
                         "{} (datablocks)".format(len(names), len(datablocks)))
    for i, values in enumerate(datablocks):
        name = str(i)
        if names:
            name = names[i]
        datablock = CifBlock()
        cif[name] = datablock
        for loopname in loops.keys():
            loopdata = ([[]], [[]])
            row_size = None
            for tag in loops[loopname]:
                if tag in values:
                    tag_values = values.pop(tag)
                    if not isinstance(tag_values, list):
                        tag_values = [tag_values]
                    if row_size is None:
                        row_size = len(tag_values)
                    elif row_size != len(tag_values):
                        raise ValueError("Number of values for tag "
                                         "'{}' is different from "
                                         "the others in the same "
                                         "loop".format(tag))
                    loopdata[0][0].append(tag)
                    loopdata[1][0].append(tag_values)
            if row_size is not None and row_size > 0:
                datablock.AddCifItem(loopdata)
        for tag in sorted(values.keys()):
            datablock[tag] = values[tag]
            # create automatically a loop for non-scalar values
            if isinstance(values[tag],
                          (tuple, list)) and tag not in loops.keys():
                datablock.CreateLoop([tag])
    return cif
Exemple #2
0
def _make_cif_block(structure, wrap=False):
    cb = CifBlock()
    _add_header(structure, cb)
    _add_cell_loop(structure, cb)
    _add_symmetry_loop(structure, cb, wrap=wrap)
    _add_comp_loop(structure, cb)
    return cb
Exemple #3
0
    def values(self):
        """
        PyCifRW structure, representing the CIF datablocks.

        .. note:: requires PyCifRW module.
        """
        if self._values is None:
            import CifFile
            from CifFile import CifBlock  # pylint: disable=no-name-in-module

            with self.open() as handle:
                c = CifFile.ReadCif(handle, scantype=self.get_attribute('scan_type', CifData._SCAN_TYPE_DEFAULT))  # pylint: disable=no-member
            for k, v in c.items():
                c.dictionary[k] = CifBlock(v)
            self._values = c
        return self._values
Exemple #4
0
    def values(self):
        """
        PyCifRW structure, representing the CIF datablocks.

        .. note:: requires PyCifRW module.
        """
        if self._values is None:
            try:
                import CifFile
                from CifFile import CifBlock
            except ImportError as e:
                raise ImportError(
                    str(e) + '. You need to install the PyCifRW package.')

            c = CifFile.ReadCif(self.get_file_abs_path(),
                                scantype=self.get_attr('scan_type'))
            for k, v in c.items():
                c.dictionary[k] = CifBlock(v)
            self._values = c
        return self._values
Exemple #5
0
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
Exemple #6
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)
Exemple #7
0
def pycifrw_from_cif(datablocks, loops=None, names=None):
    """
    Constructs PyCifRW's CifFile from an array of CIF datablocks.

    :param datablocks: an array of CIF datablocks
    :param loops: optional dict of lists of CIF tag loops.
    :param names: optional list of datablock names
    :return: CifFile
    """
    try:
        import CifFile
        from CifFile import CifBlock
    except ImportError as exc:
        raise ImportError(str(exc) + '. You need to install the PyCifRW package.')

    if loops is None:
        loops = dict()

    cif = CifFile.CifFile()  # pylint: disable=no-member
    try:
        cif.set_grammar('1.1')
    except AttributeError:
        # if no grammar can be set, we assume it's 1.1 (widespread standard)
        pass

    if names and len(names) < len(datablocks):
        raise ValueError(
            'Not enough names supplied for '
            'datablocks: {} (names) < '
            '{} (datablocks)'.format(len(names), len(datablocks))
        )
    for i, values in enumerate(datablocks):
        name = str(i)
        if names:
            name = names[i]
        datablock = CifBlock()
        cif[name] = datablock
        tags_in_loops = []
        for loopname in loops.keys():
            row_size = None
            tags_seen = []
            for tag in loops[loopname]:
                if tag in values:
                    tag_values = values.pop(tag)
                    if not isinstance(tag_values, list):
                        tag_values = [tag_values]
                    if row_size is None:
                        row_size = len(tag_values)
                    elif row_size != len(tag_values):
                        raise ValueError(
                            'Number of values for tag '
                            "'{}' is different from "
                            'the others in the same '
                            'loop'.format(tag)
                        )
                    if row_size == 0:
                        continue
                    datablock.AddItem(tag, tag_values)
                    tags_seen.append(tag)
                    tags_in_loops.append(tag)
            if row_size is not None and row_size > 0:
                datablock.CreateLoop(datanames=tags_seen)
        for tag in sorted(values.keys()):
            if not tag in tags_in_loops:
                datablock.AddItem(tag, values[tag])
                # create automatically a loop for non-scalar values
                if isinstance(values[tag], (tuple, list)) and tag not in loops.keys():
                    datablock.CreateLoop([tag])
    return cif