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
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
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
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
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
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)
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