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
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
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, _ = get_number_with_esd(block["_cell_length_a"]) b, _ = get_number_with_esd(block["_cell_length_b"]) c, _ = get_number_with_esd(block["_cell_length_c"]) 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"]) except: raise ParseError('Lattice vectors could not be determined.') else: return a, b, c, alpha, beta, gamma
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