def CIFopen(self, ciffile=None, cifblkname=None): from CifFile import ReadCif # part of the PycifRW module try: # the following is a trick to avoid that urllib.URLopen.open # used by ReadCif misinterprets the url when a drive:\ is # present (Win) ciffile = ciffile.replace(':','|') cf = ReadCif(ciffile) except: logging.error('File %s could not be accessed' %ciffile) if cifblkname == None: #Try to guess blockname blocks = cf.keys() if len(blocks) > 1: if len(blocks) == 2 and 'global' in blocks: cifblkname = blocks[abs(blocks.index('global') - 1)] else: logging.error('More than one possible data set:') logging.error('The following data block names are in the file:') for block in blocks: logging.error(block) raise Exception else: # Only one available cifblkname = blocks[0] #Extract block try: self.cifblk = cf[cifblkname] except: logging.error('Block - %s - not found in %s' % (blockname, ciffile)) raise IOError return self.cifblk
def CIFopen(self, ciffile=None, cifblkname=None): from CifFile import ReadCif # part of the PycifRW module try: # the following is a trick to avoid that urllib.URLopen.open # used by ReadCif misinterprets the url when a drive:\ is # present (Win) ciffile = ciffile.replace(':', '|') cf = ReadCif(ciffile) except: logging.error('File %s could not be accessed' % ciffile) if cifblkname == None: #Try to guess blockname blocks = list(cf.keys()) if len(blocks) > 1: if len(blocks) == 2 and 'global' in blocks: cifblkname = blocks[abs(blocks.index('global') - 1)] else: logging.error('More than one possible data set:') logging.error( 'The following data block names are in the file:') for block in blocks: logging.error(block) raise Exception else: # Only one available cifblkname = blocks[0] #Extract block try: self.cifblk = cf[cifblkname] except: logging.error('Block - %s - not found in %s' % (blockname, ciffile)) raise IOError return self.cifblk
def test_calculating_xrd_pattern_from_cif_file(self): fcc_cif = ReadCif(get_cif_url('fcc.cif')) cif_phase = CifPhase(fcc_cif[fcc_cif.keys()[0]]) cif_converter = CifConverter(0.31) jcpds_phase = cif_converter.convert_cif_phase_to_jcpds(cif_phase) self.assertAlmostEqual(jcpds_phase.reflections[0].intensity, 100, places=4) self.assertAlmostEqual(jcpds_phase.reflections[0].d0, 2.814, places=4) self.assertAlmostEqual(jcpds_phase.reflections[1].d0, 2.437, places=4)
def import_cif(file_path, occupancy_tolerance=100): """ Import cif with pymatgen. This is the current default. Aruments -------- occupancy_tolerance: int Occupancy tolerance for pymatgen.io.cif.CifParser. Files from the CSD can contain multiple atoms which sit on the same site when PBC are taken into account. Pymatgen correctly recognizes these as the same atom, however, it has a tolerance for the number of atoms which it will assume are the same. This is the occupancy tolerance. I don't believe this value should be limited and thus it's set at 100. """ try: pycif = CifParser(file_path, occupancy_tolerance=occupancy_tolerance) pstruct_list = pycif.get_structures(primitive=False) struct = Structure.from_pymatgen(pstruct_list[0]) except: struct = import_cif_ase(file_path) file_name = os.path.basename(file_path) struct.struct_id = file_name.replace('.cif', '') #### Read in data that may be stored as a CIF property if read_cif_data: ### Wrap in try except in case of PyCIFRW errors try: cif = ReadCif(file_path) except: print("Error in reading CIF data for {}".format(file_path)) return struct if len(cif) > 1: raise Exception("Must only have one structure per CIF file.") cif_struct = [x for x in cif.keys()] cif_data = cif[cif_struct[0]] for key, value in cif_data.items(): ## Skip over geometry information if "_atom_" == key[0:len("_atom_")]: continue elif "_cell_" == key[0:len("_cell_")]: continue elif "_symmetry_equiv" == key[0:len("_symmetry_equiv")]: continue else: pass ## Otherwise add info to structure property information struct.properties[key] = value return struct
def read_reference(cif_file): """ Read a cif file and return a :mod:`~qmpy.Reference`. """ cf = ReadCif(cif_file, grammar='1.1') cif_block = cf[cf.keys()[0]] reference = rx.Reference() reference.authors = _get_authors(cif_block) reference.journal = _get_journal(cif_block) reference.volume = _get_volume(cif_block) reference.year = _get_year(cif_block) reference.first_page = _get_first_page(cif_block) reference.last_page = _get_last_page(cif_block) reference.title = _get_title(cif_block) return reference
def convert_cif_to_jcpds(self, filename): """ Reads a cif file and returns a jcpds with correct reflection and intensities :param filename: cif filename :return: converted jcpds object :rtype: jcpds """ file_url = 'file:' + urllib.pathname2url(filename) cif_file = ReadCif(file_url) cif_phase = CifPhase(cif_file[cif_file.keys()[0]]) jcpds_phase = self.convert_cif_phase_to_jcpds(cif_phase) jcpds_phase.filename = filename jcpds_phase.name = os.path.splitext(os.path.basename(filename))[0] jcpds_phase.modified = False return jcpds_phase
def convert_cif_to_jcpds(self, filename): """ Reads a cif file and returns a jcpds with correct reflection and intensities :param filename: cif filename :return: converted jcpds object :rtype: jcpds """ file_url = 'file:' + pathname2url(filename) cif_file = ReadCif(file_url) cif_phase = CifPhase(cif_file[cif_file.keys()[0]]) jcpds_phase = self.convert_cif_phase_to_jcpds(cif_phase) jcpds_phase.filename = filename jcpds_phase.name = os.path.splitext(os.path.basename(filename))[0] jcpds_phase.modified = False return jcpds_phase
def test_reading_phase(self): fcc_cif = ReadCif(get_cif_url('fcc.cif')) cif_phase = CifPhase(fcc_cif[fcc_cif.keys()[0]]) self.assertEqual(cif_phase.a, 4.874) self.assertEqual(cif_phase.b, 4.874) self.assertEqual(cif_phase.c, 4.874) self.assertEqual(cif_phase.alpha, 90) self.assertEqual(cif_phase.beta, 90) self.assertEqual(cif_phase.gamma, 90) self.assertEqual(cif_phase.volume, 115.79) self.assertEqual(cif_phase.space_group_number, 225) self.assertEqual(len(cif_phase.atoms), 8) self.assertEqual(cif_phase.comments, 'HoN, Fm-3m - NaCl structure type, ICSD 44776')
def read(cif_file, grammar=None): """ Takes a CIF format file, and returns a Structure object. Applies all symmetry operations in the CIF to the atoms supplied with the structure. If these are not correct, the structure will not be either. If the CIF contains more than one file, the return will be a list. If not, the return will be a single structure (not in a len-1 list). Examples:: >>> s = io.cif.read(INSTALL_PATH+'io/files/fe3o4.cif') """ if grammar: cf = ReadCif(cif_file, grammar=grammar) else: cf = ReadCif(cif_file) structures = [] for key in cf.keys(): structures.append(_read_cif_block(cf[key])) if len(structures) == 1: return structures[0] else: return structures
def run(config, input, output): dir = config['xcDir'] # Copy cif2cell related input to cif2cellInput here. Later we will be overriding some settings, # so we want to keep the original input intact. cif2cellInput = { key: input[key] for key in input if key.startswith('cif2cell.') } # Generate any data that is needed from generic input and populate cif2cellInput with # global data (needed for all feff runs.) cif2cellInput['cif2cell.cif_input'] = input['cif_input'] # Set executable directory for this exchange cif2celldir = config['cif2cell'] # Set input file - cif2cell takes input from command line, so no input file, however, maybe we can take commands from input file? #inpf = os.path.join(dir, 'cif2cell.in') # Loop over targets in output. Not sure if there will ever be more than one output target here. if set(output.keys()).issubset( set([ 'cell_vectors', 'cell_struct_xyz_red', 'cell_scaling_iso', 'cell_scaling_abc', 'number_density', 'supercell' ])): # Set output and error files with open(os.path.join(dir, 'corvus.CIF2CELL.stdout'), 'wb') as out, open( os.path.join(dir, 'corvus.CIF2CELL.stderr'), 'wb') as err: # Copy necessary files to dir cif_file = cif2cellInput['cif2cell.cif_input'][0][0] shutil.copy(cif_file, dir) # Loop over executables: This is specific to feff. Other codes # will more likely have only one executable. executables = ['cif2cell'] args = [cif_file, '-p', 'cif', '-o', 'out.cif'] iExec = 0 for executable in executables: runExecutable(cif2celldir, dir, executable, args, out, err) # For now, I am only passing the directory. print('Setting output') outCif = os.path.join(dir, 'out.cif') cifFile = ReadCif(outCif) # cifFile now contains an object that acts like a dictionary # of dictionaries, with the outer keys the data for each # structure listed in the cif, next the normal cif data, like # '_cell_angle_alpha', and '_atom_site_fract_[xyz]'. # Note that as indicated above, the cif can hold multiple # structures. For now I will assume that there is only one. cif_dict = cifFile[list(cifFile.keys())[0]] # Now lets make all of the data structures that we want out of # this. We are using the CellData structure from cif2cell's # uctools package. Right now, I have cif2cell write another # cif file first, then we read it in and analyze it. This is # because we don't want to reproduce all of the sanity check # that cif2cell already has in it. cell_data = CellData() # Fill the cell data object from the cif file. cell_data.getFromCIF(cif_dict) # Calculate cell structure for the primitive cell for now. Supercells are # represented as P1, so this will not harm that. cell_data.primitive() # The cell_data structure now has all cell data that we need # in it, or a method to get that data. if 'cell_vectors' in output: output['cell_vectors'] = cell_data.latticevectors if 'cell_struct_xyz_red' in output: xred = [] for atom in cell_data.atomdata: if atom: xred = xred + [atom[0].position] output['cell_struct_xyz_red'] = xred if 'cell_scaling_iso' in output: output['cell_scaling_iso'] = [[cell_data.lengthscale]] if 'cell_scaling_abc' in output: # For now I will output cell_data_abc as 1, since cif2cell seems to put things into lengthscale # and a,b,c are for convenience. output['cell_scaling_abc'] = [[1.0, 1.0, 1.0]] elif 'cell_structure' in output: # Return path to cif file. output['cell_structure'] = [['Working']]
def _readCif(self, fcif=DFLT_NAME + '.cif'): """ >> @AUTHOR: Saransh Singh, Lawrence Livermore National Lab, [email protected] >> @DATE: 10/16/2019 SS 1.0 original >> @DETAILS: hexrd3 will have real structure factors and will require the overhaul of the crystallography. In this effort, we will have a cif reader and also the HDF5 format reader in the material class. We will be using pycifrw for i/o """ # make sure file exists etc. if (fcif == Material.DFLT_NAME + '.cif'): try: cif = ReadCif(fcif) except (OSError): raise RuntimeError( 'OS Error: No file name supplied and default file name not found.' ) else: try: cif = ReadCif(fcif) except (OSError): raise RuntimeError('OS Error: File not found') # read the file for k in cif.keys(): if ('_cell_length_a' in cif[k]): m = k break cifdata = cif[m] # cifdata = cif[cif.keys()[0]] # make sure the space group is present in the cif file, either as # international table number, hermann-maguain or hall symbol sgkey = [ '_space_group_IT_number', '_symmetry_space_group_name_h-m', '_symmetry_space_group_name_hall', '_symmetry_Int_Tables_number' ] sgdata = False for key in sgkey: sgdata = sgdata or (key in cifdata) if (sgdata): skey = key break if (not (sgdata)): raise RuntimeError(' No space group information in CIF file! ') sgnum = 0 if skey is sgkey[0]: sgnum = int(cifdata[sgkey[0]]) elif (skey is sgkey[1]): HM = cifdata[sgkey[1]] HM = HM.replace(" ", "") sgnum = HM_to_sgnum[HM] elif (skey is sgkey[2]): hall = cifdata[sgkey[2]] hall = hall.replace(" ", "") sgnum = Hall_to_sgnum[HM] elif (skey is sgkey[3]): sgnum = int(cifdata[sgkey[3]]) # lattice parameters lparms = [] lpkey = ['_cell_length_a', '_cell_length_b', \ '_cell_length_c', '_cell_angle_alpha', \ '_cell_angle_beta', '_cell_angle_gamma'] for key in lpkey: n = cifdata[key].find('(') if (n != -1): lparms.append(float(cifdata[key][:n])) else: lparms.append(float(cifdata[key])) for i in range(6): if (i < 3): lparms[i] = _angstroms(lparms[i]) else: lparms[i] = _degrees(lparms[i]) self._lparms = lparms self.sgnum = sgnum # fractional atomic site, occ and vibration amplitude fracsitekey = ['_atom_site_fract_x', '_atom_site_fract_y',\ '_atom_site_fract_z',] occ_U = ['_atom_site_occupancy',\ '_atom_site_u_iso_or_equiv','_atom_site_U_iso_or_equiv'] sitedata = True for key in fracsitekey: sitedata = sitedata and (key in cifdata) if (not (sitedata)): raise RuntimeError( ' fractional site position is not present or incomplete in the CIF file! ' ) atompos = [] for key in fracsitekey: slist = cifdata[key] pos = [] for p in slist: n = p.find('(') if (n != -1): pos.append(p[:n]) else: pos.append(p) ''' sometimes cif files have negative values so need to bring them back to fractional coordinates between 0-1 ''' pos = numpy.asarray(pos).astype(numpy.float64) pos, _ = numpy.modf(pos + 100.0) atompos.append(pos) """note that the vibration amplitude, U is just the amplitude (in A) to convert to the typical B which occurs in the debye-waller factor, we will use the following formula B = 8 * pi ^2 * < U_av^2 > this will be done here so we dont have to worry about it later """ pocc = (occ_U[0] in cifdata.keys()) pU = (occ_U[1] in cifdata.keys()) or (occ_U[2] in cifdata.keys()) if (not pocc): warn('occupation fraction not present. setting it to 1') occ = numpy.ones(atompos[0].shape) atompos.append(occ) else: slist = cifdata[occ_U[0]] occ = [] for p in slist: n = p.find('(') if (n != -1): occ.append(p[:n]) else: occ.append(p) atompos.append(numpy.asarray(occ).astype(numpy.float64)) if (not pU): warn( 'Debye-Waller factors not present. setting to same values for all atoms.' ) U = 1.0 / numpy.pi / 2. / numpy.sqrt(2.) * numpy.ones( atompos[0].shape) self._U = U else: if (occ_U[1] in cifdata.keys()): k = occ_U[1] else: k = occ_U[2] slist = cifdata[k] U = [] for p in slist: n = p.find('(') if (n != -1): U.append(p[:n]) else: U.append(p) self._U = numpy.asarray(U).astype(numpy.float64) ''' format everything in the right shape etc. ''' self._atominfo = numpy.asarray(atompos).T ''' get atome types here i.e. the atomic number of atoms at each site ''' atype = '_atom_site_type_symbol' patype = (atype in cifdata) if (not patype): raise RuntimeError('atom types not defined in cif file.') satype = cifdata[atype] atomtype = [] for s in satype: atomtype.append(ptable[s]) self._atomtype = numpy.asarray(atomtype).astype(numpy.int32) self._sgsetting = 0
# # Author: Andrezio # # Created: 23/07/2017 # Copyright: (c) Andrezio 2017 # Licence: <your licence> #------------------------------------------------------------------------------- from CifFile import ReadCif <<<<<<< HEAD cf = ReadCif('UO2_STAR_246851.cif') lambida=1.54 ======= cf = ReadCif('u308.cif') >>>>>>> f26a11b75e998002824160efa1c337672e8cb21b chave = cf.keys() ##for k,v in cf[chave[0]].items(): ## print k <<<<<<< HEAD value= cf[chave[0]]['_cell_length_a'] value=value.split('(')[0] ======= print cf[chave[0]]['_symmetry_equiv_pos_as_xyz'] >>>>>>> f26a11b75e998002824160efa1c337672e8cb21b
class CIFParser(AbstractStructureParser): """ Collection of methods that parses CIF files based on cif2cell. The preferred method of using this object is as a context manager. Parameters ---------- filename : str or path-like Location of the CIF file. """ def __init__(self, filename, **kwargs): # ReadCIF would get confused between local files and URLs # Therefore, more clear to pass an open file self._handle = open(filename, mode="r") self.file = ReadCif(self._handle, **kwargs) def __exit__(self, *args, **kwargs): self._handle.close() @property def filename(self): return self._handle.name @staticmethod def sym_ops_from_equiv(equiv_site): """ Parse a symmetry operator from an equivalent-site representation Parameters ---------- equiv_site : str or iterable of strings Either comma-separated string e.g. "+y, +x, -z + 1/2" or an iterable of the comma-separated values, e.g. ["+y", "+x", "-z + 1/2"] Returns ------- sym_ops : ndarray, shape (4,4) Symmetry operator as a 4x4 affine transformation on the FRACTIONAL coordinates. """ symmetry = np.zeros((3, 3)) translation = np.zeros((3, )) if isinstance(equiv_site, str): equiv_site = equiv_site.split(",") equiv_site = tuple(map(lambda s: s.strip().lower(), equiv_site)) for j in range(3): xyz = equiv_site[j].replace("+", " +").replace("-", " -").split() for i in xyz: if i.strip("+-") == "x": symmetry[0, j] = float(i.strip("x") + "1") elif i.strip("+-") == "y": symmetry[1, j] = float(i.strip("y") + "1") elif i.strip("+-") == "z": symmetry[2, j] = float(i.strip("z") + "1") if i.strip("+-xyz") != "": translation[j] = eval(i) symmetry[:] = np.transpose(symmetry) # Combination of transform and translation into a single matrix # is done in a 4x4 affine transform symmetry_operation = affine_map(symmetry) symmetry_operation[:3, 3] = translation return symmetry_operation @property def structure_block(self): """ Retrieve which CIF block has the appropriate structural information """ blocks = (self.file[key] for key in self.file.keys()) for block in blocks: try: _, _ = get_number_with_esd(block["_cell_length_a"]) except KeyError: continue else: return block @lru_cache(maxsize=1) def hall_symbol(self): """ Returns the Hall symbol """ block = self.structure_block hall_symbol = block.get("_symmetry_space_group_name_Hall" ) or block.get("_space_group_name_Hall") # In some rare cases, the given hall symbol in the file isn't standard, # otherwise it would be a key in SymOpsHall # Then, it is preferable to infer the conventional hall symbol from other info if (hall_symbol is None) or (hall_symbol not in SymOpsHall): h_m_symbol = block.get("_symmetry_space_group_name_H-M" ) or block.get("_space_group_name_H-M_alt") if h_m_symbol is not None: h_m_symbol = re.sub(r"\s+", "", h_m_symbol) with suppress( KeyError ): # Symbol could be meaningless, e.g. h_m_symbol = '?' (True story) hall_symbol = HM2Hall[h_m_symbol] # Again, if hall_symbol is still missing OR invalid if (hall_symbol is None) or (hall_symbol not in SymOpsHall): table_number = block.get("_symmetry_Int_Tables_number" ) or block.get("_space_group_IT_number") if table_number is not None: hall_symbol = Number2Hall[int(table_number)] if hall_symbol is None: raise ParseError("Hall symbol could not be inferred") if hall_symbol[0] == "-": hall_symbol = "-" + hall_symbol[1].upper() + hall_symbol[2:].lower( ) else: hall_symbol = hall_symbol[0].upper() + hall_symbol[1:].lower() return hall_symbol @lru_cache(maxsize=1) def lattice_parameters(self): """ Returns the lattice parameters associated to a CIF structure. Returns ---------- a, b, c : float Lengths of lattice vectors [Angstroms] alpha, beta, gamma : float Angles of lattice vectors [degrees]. """ block = self.structure_block try: a_with_err = block["_cell_length_a"] except KeyError: raise ParseError( f"No lattice information is present in {self.filename}") # In case where b and c are not listed, we use the value of a a, _ = get_number_with_esd(a_with_err) b, _ = get_number_with_esd(block.get("_cell_length_b", a_with_err)) c, _ = get_number_with_esd(block.get("_cell_length_c", a_with_err)) alpha, _ = get_number_with_esd(block["_cell_angle_alpha"]) beta, _ = get_number_with_esd(block["_cell_angle_beta"]) gamma, _ = get_number_with_esd(block["_cell_angle_gamma"]) return a, b, c, alpha, beta, gamma @lru_cache(maxsize=1) def lattice_vectors(self): """ Returns the lattice vectors associated to a CIF structure. Returns ------- lv : list of ndarrays, shape (3,) """ return Lattice.from_parameters( *self.lattice_parameters()).lattice_vectors def symmetry_operators(self): """ Returns the symmetry operators that map the fractional atomic positions in a CIF file to the crystal *conventional* unit cell. Returns ------- sym_ops : iterable of ndarray, shape (4,4) Transformation matrices. Since translations and rotation are combined, the transformation matrices are 4x4. """ block = self.structure_block equivalent_sites_str = None for tag in [ "_symmetry_equiv_pos_as_xyz", "_space_group_symop_operation_xyz" ]: with suppress(KeyError): equivalent_sites_str = block.GetLoop(tag).get(tag) # P1 space group only has a single equivalent site if isinstance(equivalent_sites_str, str): equivalent_sites_str = [equivalent_sites_str] with suppress(ParseError): if not equivalent_sites_str: equivalent_sites_str = SymOpsHall[self.hall_symbol()] elif len(equivalent_sites_str) != len( SymOpsHall[self.hall_symbol()]): warnings.warn( "The number of equivalent sites is not in line with the database. The file might be incomplete" ) return list(map(self.sym_ops_from_equiv, equivalent_sites_str)) def atoms(self): """ Asymmetric unit cell. Combine with CIFParser.symmetry_operators() for a full unit cell. Returns ------- atoms : iterable of Atom instance """ block = self.structure_block try: tmpdata = block.GetLoop("_atom_site_fract_x") cartesian = False except: try: tmpdata = block.GetLoop("_atom_site_Cartn_x") cartesian = True except: raise ParseError( "Atomic positions could not be found or inferred.") t11 = block.get("_atom_sites_Cartn_tran_matrix_11") t12 = block.get("_atom_sites_Cartn_tran_matrix_12") t13 = block.get("_atom_sites_Cartn_tran_matrix_13") t21 = block.get("_atom_sites_Cartn_tran_matrix_21") t22 = block.get("_atom_sites_Cartn_tran_matrix_22") t23 = block.get("_atom_sites_Cartn_tran_matrix_23") t31 = block.get("_atom_sites_Cartn_tran_matrix_13") t32 = block.get("_atom_sites_Cartn_tran_matrix_23") t33 = block.get("_atom_sites_Cartn_tran_matrix_33") cart_trans_matrix_inv = np.array([ [float(t11), float(t12), float(t13)], [float(t21), float(t22), float(t23)], [float(t31), float(t32), float(t33)], ]) cart_trans_matrix = inv(cart_trans_matrix_inv) if not all([t11, t12, t13, t21, t22, t23, t31, t32, t33]): raise ParseError( "Cartesian coordinates in CIF but no transformation matrix given" ) if cartesian: xs = tmpdata.get("_atom_site_Cartn_x") ys = tmpdata.get("_atom_site_Cartn_y") zs = tmpdata.get("_atom_site_Cartn_z") else: xs = tmpdata.get("_atom_site_fract_x") ys = tmpdata.get("_atom_site_fract_y") zs = tmpdata.get("_atom_site_fract_z") # TODO: handle wildcards like '?', '.' in xs, ys, zs elements = tmpdata.get("_atom_site_type_symbol") if not elements: elements = tmpdata.get("_atom_site_label") if not elements: raise ParseError( "Atom symbols could not be found or inferred.") elements = map(lambda s: s.strip(punctuation + digits).title(), elements) atoms = list() for e, x, y, z in zip(elements, xs, ys, zs): coords = np.array([ get_number_with_esd(x)[0], get_number_with_esd(y)[0], get_number_with_esd(z)[0], ]) # We normalize atom position to be within the unit cell # Therefore we need the fractional coordinates if cartesian: coords = transform(cart_trans_matrix, coords) coords[:] = frac_coords(coords, self.lattice_vectors()) atoms.append(Atom(element=e, coords=np.mod(coords, 1))) return atoms
def read_cif(filename: str_PathLike, pps: muti_Dict = [{}], orbitals: muti_Dict = [{}], masses: muti_Dict = [{}], magmoms: muti_Dict = [{}], move: muti_Dict = [{}], abfs: muti_Dict = [{}]) -> List[Stru]: """Read CIF(Crystallographic Information Framework) file and return `Stru` object :params filename: name of cif file """ raise UserWarning("Can only read direct positions, generating equivalent atoms based on site symmetry or Wackoff positions are not supported now") from CifFile import ReadCif strulist = [] def list_elem2split(l): def list_split(s): return s.split('(')[0] return list(map(list_split, l)) def get_elements(string): return re.search(r'([A-Z][a-z]?)', string).group(0) cf = ReadCif(filename) for index, name in enumerate(cf.keys()): data = cf[name] fract_x = list_elem2split(data["_atom_site_fract_x"]) fract_y = list_elem2split(data["_atom_site_fract_y"]) fract_z = list_elem2split(data["_atom_site_fract_z"]) if "_atom_site_type_symbol" in data.keys(): elements = list(map(get_elements, data["_atom_site_type_symbol"])) else: elements = list(map(get_elements, data["_atom_site_label"])) scaled_positions = defaultdict(list) for i, elem in enumerate(elements): scaled_positions[elem].append([np.array(fract_x[i], dtype=float), np.array( fract_y[i], dtype=float), np.array(fract_z[i], dtype=float)]) la = float(data["_cell_length_a"].split('(')[0]) lb = float(data["_cell_length_b"].split('(')[0]) lc = float(data["_cell_length_c"].split('(')[0]) alpha = float(data["_cell_angle_alpha"].split('(')[0]) beta = float(data["_cell_angle_beta"].split('(')[0]) gamma = float(data["_cell_angle_gamma"].split('(')[0]) if "_symmetry_cell_setting" in data.keys(): crystal_system = data["_symmetry_cell_setting"] elif "_symmetry_Int_Tables_number" in data.keys(): crystal_system = Spacegroup().get_crystal_system( data["_symmetry_Int_Tables_number"]) elif "_space_group_name_Hall" in data.keys(): crystal_system = Spacegroup().get_crystal_system( Hall2Number[data["_space_group_name_Hall"]]) elif "_symmetry_space_group_name_H-M" in data.keys(): crystal_system = Spacegroup().get_crystal_system( Hall2Number[HM2Hall[data["_symmetry_space_group_name_H-M "]]]) else: crystal_system = 'unknown' cell = conventional_cell( la, lb, lc, alpha, beta, gamma, crystal_system) strulist.append(Stru(lat0=1/BOHR_TO_A, cell=cell, pps=pps[index], scaled_positions=scaled_positions, orbitals=orbitals[index], masses=masses[index], magmoms=magmoms[index], move=move[index], abfs=abfs[index])) return strulist