def del_atoms(self, index_or_atoms, isUpdatedInfo=False, isPersist=False, **kwargs): """ delete atoms from structure. Arguments: index_or_atoms: collection of atom's index, formated atom or object. i.e. [atom0, atom1, atom2,...] ['Na', 0.1, 0.0, 0.0, 'Direct'] ['Na', 0.1, 0.0, 0.0] ['Na', 5.234, 0.0, 0.0, 'Cartesian'] kwargs: symprec (default=1e-5): precision when to find the symmetry. angle_tolerance (default=-1.0): a experimental argument that controls angle tolerance between basis vectors. Returns: structureFactory's object. """ from utils.check import check_formated_atom from materials.atom import Atom structure = self.structure for atom in index_or_atoms: formated_atom = None if isinstance(atom, int): index = atom if index < 0 or index > structure.natoms: raise ValueError('beyond the range of atomic index') else: formated_atom = structure.atoms[index].to_formated() elif isinstance(atom, Atom): formated_atom = atom.to_formated() elif check_formated_atom(atom): formated_atom = atom else: raise ValueError('unrecognized atom') status = structure.del_atom(formated_atom) if not status: raise StructureFactoryError('failed') # default symprec = 1e-5 # symprec angle_tolerance = -1.0 # angle_tolerance if 'symprec' in kwargs: symprec = kwargs['symprec'] if 'angle_tolerance' in kwargs: angle_tolerance = kwargs['angle_tolerance'] if isPersist: structure.update(isPersist=isPersist, symprec=symprec, angle_tolerance=angle_tolerance) elif isUpdatedInfo and not isPersist: structure.update(isPersist=False, symprec=symprec, angle_tolerance=angle_tolerance) self.structure = structure return self
def del_atom(self, index_or_atom, isUpdatedInfo=False, isPersist=False, **kwargs): """ delete a atom from this species. Note that it will delete this atom's object from other related classes's objects. Arguments: index_or_atom: atom's index, formated atom and object. isUpdatedInfo (default=False): whether to update the composition and symmetry information (include the site, operation, wyckoffSite, spacegroup). isPersist (default=False): whether to save to the database. kwargs: isNormalizingCoordinate (default=True): whether to remove the periodic boundary condition, ensure the value of atomic coordinate is between 0 and 1 (i.e. 1.3 -> 0.3). precision (default=1e-3): used to determine whether the two atoms are overlapped. Note that, to determine whether this atom is in collection by comparing its distance from other atoms. symprec (default=1e-5): precision when to find the symmetry. angle_tolerance (default=-1.0): a experimental argument that controls angle tolerance between basis vectors. Returns: True if delete a atom successfully. Conversely, False. 从元素种类中删除一个原子。注意:当删除原子时,基于程序效率上的考虑(可能会多次对结构进行操作,可以在所有的操作完成后,更新内存中内建的结构关联信息以及同步数据库中的数据), 程序默认不会更新结构(更新化学式,更新空间群和WyckoffSite,等)。 参数: index_or_atom:其值可以是原子在结构的原子属性数组中的索引(index)、原子的格式化数组或原子对象。注意:原子的格式化数组的格式需要符合以下规则: 1.程序默认晶体结构中原子坐标的类型为分数坐标。如果原子坐标为分数坐标,可以不用指定坐标类型。如,['Na', 0.1, 0.0, 0.0]。 2.如果指定原子坐标类型,类型必须为‘Direct’或‘Cartesian’。如,['Na', 0.1, 0.0, 0.0, 'Direct']、['Na', 5.234, 0.0, 0.0, 'Cartesian']。 isUpdatedInfo (default=False):是否更新结构的其他关联数据信息(如,化学式、不等价位置,等)。 isPersist (default=False):是否持久化,即将结构保存到数据库中。 kwargs: isNormalizingCoordinate (default=True):当给定原子的类型为格式化数组时,默认移除原子坐标上的平移周期性,以保证其值在0.0~1.0之间。 precision (default=1e-3):比较原子是否重叠的精度。当“atom”参数为格式化数组时,此参数用于判断给定的原子是否在结构中(比较给定原子坐标与结构中的原子坐标之间的距离)。 symprec (default=1e-5):找结构对称性时,采用的精度。 angle_tolerance (default=-1.0):找结构对称性时,控制晶胞基矢之间的角度容差值。 返回: 布尔值(True/False)。 """ import warnings from utils.check import check_formated_atom from materials.atom import Atom atom = None if isinstance(index_or_atom, int): index = index_or_atom if index < 0 or index > len(self.atoms): warnings.warn('beyond the range of atomic index') return False atom = self.atoms[index] elif check_formated_atom(index_or_atom): # remove atomic translation periodicity isNormalizingCoordinate = True if 'isNormalizingCoordinate' in kwargs: isNormalizingCoordinate = kwargs['isNormalizingCoordinate'] precision = 1e-3 if 'precision' in kwargs: precision = kwargs['precision'] formated_atom = index_or_atom atom = self.get_atom( formated_atom, isNormalizingCoordinate=isNormalizingCoordinate, precision=precision) elif isinstance(index_or_atom, Atom): atom = index_or_atom else: warnings.warn('unrecognized index_or_atom') return False if not atom is None: if len(self.structures) != 1: warnings.warn( "exist more than one structure in element.structures array, don't know which structure the element belong to" ) return None structure = self.structures[0] structure.del_atom(atom, isUpdatedInfo=False, isPersist=False) # default symprec = 1e-5 # symprec angle_tolerance = -1.0 # angle_tolerance if 'symprec' in kwargs: symprec = kwargs['symprec'] if 'angle_tolerance' in kwargs: angle_tolerance = kwargs['angle_tolerance'] if isPersist: structure.update(isPersist=isPersist, symprec=symprec, angle_tolerance=angle_tolerance) elif isUpdatedInfo and not isPersist: structure.update(isPersist=False, symprec=symprec, angle_tolerance=angle_tolerance) return True else: return False
def get_entity_from_collection(entity, collection, entity_type, **kwargs): """ get a entity's object from collection. Arguments: entity: for atom: atom's object or formated string. For formated string, if type of coordinate is 'Direct', you can not specify the type. But, for 'Cartesian', must be given. the valid formation: ['Na', 0.1, 0.0, 0.0, 'Direct'] ['Na', 0.1, 0.0, 0.0] ['Na', 5.234, 0.0, 0.0, 'Cartesian'] for site: site's position. Note that type of coordinate is 'Direct', you can not specify the type. The valid formation: [0.1, 0.0, 0.0, 'Direct'] [0.1, 0.0, 0.0] [5.234, 0.0, 0.0, 'Cartesian'] kwargs: isNormalizingCoordinate (default=True): whether to remove the periodic boundary condition, ensure the value of atomic coordinate is between 0 and 1 (i.e. 1.3 -> 0.3). precision (default=1e-3): used to determine whether the two atoms are overlapped. Note that, to determine whether this atom is in collection by comparing its distance from other atoms. lattice: this structure's lattice parameter. Returns: entity's object if exist. Conversely, None. """ from utils.check import check_formated_atom, check_formated_atom_only_cartesian from utils.check import check_formated_position, check_formated_position_only_cartesian from utils.convert import any2direct, normalize_position result = None if entity_type == 'atom': # remove atomic translation periodicity isNormalizingCoordinate = True if 'isNormalizingCoordinate' in kwargs: isNormalizingCoordinate = kwargs['isNormalizingCoordinate'] precision = 1e-3 if 'precision' in kwargs: precision = kwargs['precision'] if check_formated_atom(entity): for i in xrange(0, len(collection)): if collection[i].element.symbol == entity[0]: position = None #if len(entity) == 5 and entity[-1].strip().lower().startswith('c'): # Cartesian if check_formated_atom_only_cartesian(entity): if not 'lattice' in kwargs: raise ValueError("need to be given the 'lattice'") lattice = kwargs['lattice'] position = any2direct(lattice, entity[1:5]) else: position = entity[1:] if isNormalizingCoordinate: position = normalize_position(position, 'Direct')[:-1] distance = collection[i].position - np.array(position) if np.linalg.norm(distance) <= precision: result = collection[i] elif entity_type == 'site': # remove atomic translation periodicity isNormalizingCoordinate = True if 'isNormalizingCoordinate' in kwargs: isNormalizingCoordinate = kwargs['isNormalizingCoordinate'] precision = 1e-3 if 'precision' in kwargs: precision = kwargs['precision'] position = entity if check_formated_position(position): for site in collection: if check_formated_position_only_cartesian(position): position = any2direct(site.structure.lattice, position) if isNormalizingCoordinate: position = normalize_position(position, 'Direct')[:-1] distance = site.position - np.array(position) if np.linalg.norm(distance) <= precision: result = site return result
def translation(self, index_or_atoms, direction, isUpdatedInfo=False, isPersist=False, **kwargs): """ translation given atoms. arguments: direction: direction vector to add the vacuum along lattice vector(a/b/c). The valid format is : [0.1, 0, 0, 'Direct'] [0.1, 0, 0] (for Direct, can not be specify) [5.234, 0, 0, 'Cartesian'] (for Cartesian, must be given) index_or_atoms: collection of atom's index, formated atom or object. i.e. [atom0, atom1, atom2,...] ['Na', 0.1, 0.0, 0.0, 'Direct'] ['Na', 0.1, 0.0, 0.0] ['Na', 5.234, 0.0, 0.0, 'Cartesian'] isUpdatedInfo (default=False): whether to update the composition and symmetry information (include the site, operation, wyckoffSite, spacegroup). isPersist (default=False): whether to save to the database. kwargs: symprec (default=1e-5): precision when to find the symmetry. angle_tolerance (default=-1.0): a experimental argument that controls angle tolerance between basis vectors. Returns: structureFactory's object. """ from utils.check import check_formated_position, check_formated_atom from utils.convert import any2direct, normalize_position, translation from materials.atom import Atom structure = self.structure # check if not check_formated_position(direction): raise StructureFactoryError('invalid direction') direction = any2direct(structure.lattice, direction) for atom in index_or_atoms: formated_atom = None if isinstance(atom, int): index = atom if index < 0 or index > structure.natoms: raise ValueError('beyond the range of atomic index') else: formated_atom = structure.atoms[index].to_formated() elif isinstance(atom, Atom): formated_atom = atom.to_formated() elif check_formated_atom(atom): formated_atom = atom else: raise ValueError('unrecognized atom') atom0 = structure.get_atom(formated_atom) atom0.position = normalize_position(translation( atom0.position, direction), dtype='d')[:-1] # default symprec = 1e-5 # symprec angle_tolerance = -1.0 # angle_tolerance if 'symprec' in kwargs: symprec = kwargs['symprec'] if 'angle_tolerance' in kwargs: angle_tolerance = kwargs['angle_tolerance'] if isPersist: structure.update(isPersist=isPersist, symprec=symprec, angle_tolerance=angle_tolerance) elif isUpdatedInfo and not isPersist: structure.update(isPersist=False, symprec=symprec, angle_tolerance=angle_tolerance) self.structure = structure return self
def rotation(self, index_or_atoms, axis, theta, isUpdatedInfo=False, isPersist=False, **kwargs): """ rotation given atoms. arguments: axis: rotation axis. Note that, for molecule, the type of axis is only 'Cartesian'. The valid format: [0.1, 0.0, 0.0, 'Direct'] [0.1, 0.0, 0.0] [5.234, 0.0, 0.0, 'Cartesian'] theta: rotation angle. The valid format: [30, 'Degree'] [0.2, 'Radian'] index_or_atoms: collection of atom's index, formated atom or object. i.e. [atom0, atom1, atom2,...] ['Na', 0.1, 0.0, 0.0, 'Direct'] ['Na', 0.1, 0.0, 0.0] ['Na', 5.234, 0.0, 0.0, 'Cartesian'] isUpdatedInfo (default=False): whether to update the composition and symmetry information (include the site, operation, wyckoffSite, spacegroup). isPersist (default=False): whether to save to the database. kwargs: symprec (default=1e-5): precision when to find the symmetry. angle_tolerance (default=-1.0): a experimental argument that controls angle tolerance between basis vectors. origin: rotation origin. The valid format: [0.1, 0.0, 0.0, 'Direct'] [0.1, 0.0, 0.0] [5.234, 0.0, 0.0, 'Cartesian'] Returns: structureFactory's object. """ from utils.check import check_formated_position, check_formated_position_only_cartesian, check_formated_angle, check_formated_atom from utils.convert import any2direct, normalize_position, rotation from materials.atom import Atom from materials.structure import Structure from materials.molStructure import MolStructure structure = self.structure # check # axis if not check_formated_position(axis): raise ValueError('unrecognized axis') if isinstance(structure, Structure): axis = any2direct(structure.lattice, axis) elif isinstance(structure, MolStructure): if not check_formated_position_only_cartesian(axis): raise ValueError('unrecognized axis') # theta if not check_formated_angle(theta): raise ValueError('unrecognized theta') # origin origin = None if 'origin' in kwargs: origin = kwargs['origin'] for atom in index_or_atoms: formated_atom = None if isinstance(atom, int): index = atom if index < 0 or index > structure.natoms: raise ValueError('beyond the range of atomic index') else: formated_atom = structure.atoms[index].to_formated() elif isinstance(atom, Atom): formated_atom = atom.to_formated() elif check_formated_atom(atom): formated_atom = atom else: raise ValueError('unrecognized atom') atom0 = structure.get_atom(formated_atom) if origin is None: atom0.position = normalize_position(rotation( atom0.position, axis, theta), dtype='d')[:-1] else: atom0.position = normalize_position(rotation(atom0.position, axis, theta, origin=origin), dtype='d')[:-1] # default symprec = 1e-5 # symprec angle_tolerance = -1.0 # angle_tolerance if 'symprec' in kwargs: symprec = kwargs['symprec'] if 'angle_tolerance' in kwargs: angle_tolerance = kwargs['angle_tolerance'] if isPersist: structure.update(isPersist=isPersist, symprec=symprec, angle_tolerance=angle_tolerance) elif isUpdatedInfo and not isPersist: structure.update(isPersist=False, symprec=symprec, angle_tolerance=angle_tolerance) self.structure = structure return self
def constraint(self, index_or_atoms, isPersist=False, **kwargs): """ selected dynamics. assign the constraint information to given atoms. Meanwhile, the constraint of remainder atoms are set to the default [False, False, False] if don't give the value of 'constraint_of_remainder'. Arguments: index_or_atoms: collection of atom contain constraint information. The valid formation: [[1, True, True, False], ['Na', 0.1, 0.0, 0.0, True, True, False], ['Na', 0.1, 0.0, 0.0, 'Direct, True, True, False], ['Na', 5.234, 0.0, 0.0, 'Cartesian', True, True, False], [atom1, True, True, True]] isPersist (default=False): whether to save to the database. kwarges: constraint_of_remainder (default=[False, False, False]): constraint of remainder atoms. Returns: structureFactory's object. 用于VASP的选择动力学,指定给定原子的束缚信息。同时,对于没有给出原子束缚信息的原子: 1)没有指定束缚条件的情况下,按照默认值设置([False, False, False])。 2)给出剩余原子束缚条件时,剩余原子的束缚信息全部设置为指定的值。 参数: index_or_atom:其值可以是原子在结构的原子属性数组中的索引(index)、原子的格式化数组或原子对象。注意:原子的格式化数组的格式需要符合以下规则: 1.程序默认晶体结构中原子坐标的类型为分数坐标。如果原子坐标为分数坐标,可以不用指定坐标类型。如,['Na', 0.1, 0.0, 0.0]。 2.如果指定原子坐标类型,类型必须为‘Direct’或‘Cartesian’。如,['Na', 0.1, 0.0, 0.0, 'Direct']、['Na', 5.234, 0.0, 0.0, 'Cartesian']。 isPersist (default=False):是否持久化,即将结构保存到数据库中。 kwargs: constraint_of_remainder (default=[False, False, False]):剩余原子的束缚值。 返回: 结构操作对象。 """ import warnings from utils.check import check_constraint, check_formated_atom, compare_with_memory from materials.atom import Atom structure = self.structure atoms = [] constraints = [] for index_or_atom in index_or_atoms: constraint = index_or_atom[-3:] index_or_atom = index_or_atom[:-3] if len(index_or_atom) == 1: index_or_atom = index_or_atom[0] if not check_constraint(constraint): warnings.warn('invalid constrain in index_or_atoms') return False # get given atom from structure atom = None if isinstance(index_or_atom, int): index = index_or_atom if index < 0 or index > structure.natoms: warnings.warn('beyond the range of atomic index') return False atom = structure.atoms[index] elif check_formated_atom(index_or_atom): # remove atomic translation periodicity isNormalizingCoordinate = True if 'isNormalizingCoordinate' in kwargs: isNormalizingCoordinate = kwargs['isNormalizingCoordinate'] precision = 1e-3 if 'precision' in kwargs: precision = kwargs['precision'] formated_atom = index_or_atom atom = structure.get_atom( formated_atom, isNormalizingCoordinate=isNormalizingCoordinate, precision=precision) elif isinstance(index_or_atom, Atom): atom = index_or_atom if not atom in structure.atoms: warnings.warn("atom doesn't belong to this structure") return False else: warnings.warn('unrecognized index_or_atom') return False if atom in atoms: warnings.warn('exist duplication in index_of_atoms') return False atoms.append(atom) constraints.append(constraint) for i in xrange(0, len(atoms)): atoms[i].constraint = constraints[i] result = compare_with_memory(atoms, structure, 'atom') remainders = result['memory'] constraint_of_remainder = [False, False, False] if 'constraint_of_remaider' in kwargs: constraint_of_remainder = kwargs['constraint_of_remaider'] if not check_constraint(constraint_of_remainder): warnings.warn('invalid constrain in index_or_atoms') return False for atom in remainders: atom.constraint = constraint_of_remainder if isPersist: structure._persist() self.structure = structure return self
def substitute_atoms(self, index_or_atoms, symbol_of_elements, isUpdatedInfo=False, isPersist=False, **kwargs): """ delete atoms from structure. Arguments: index_or_atoms: collection of atom's index, formated atom or object. i.e. [atom0, atom1, atom2,...] ['Na', 0.1, 0.0, 0.0, 'Direct'] ['Na', 0.1, 0.0, 0.0] ['Na', 5.234, 0.0, 0.0, 'Cartesian'] symbol_of_elements: element's symbol. If replacing by an element for all atom, you can only specify the a element' symbol. i.e. 'Na', ['Na', 'Na', 'Na'] kwargs: symprec (default=1e-5): precision when to find the symmetry. angle_tolerance (default=-1.0): a experimental argument that controls angle tolerance between basis vectors. Returns: structureFactory's object. """ from utils.check import check_formated_atom from materials.atom import Atom structure = self.structure for i in xrange(0, len(index_or_atoms)): formated_atom = None atom = index_or_atoms[i] if isinstance(atom, int): index = atom if index < 0 or index > structure.natoms: raise ValueError('beyond the range of atomic index') formated_atom = structure.atoms[index].to_formated() elif isinstance(atom, Atom): formated_atom = atom.to_formated() elif check_formated_atom(atom): formated_atom = atom else: raise ValueError('unrecognized atom') if isinstance(symbol_of_elements, str): symbol_of_element = symbol_of_elements structure.substitute_atom(formated_atom, symbol_of_element) elif isinstance(symbol_of_elements, list) or isinstance( symbol_of_element, np.ndarray): symbol_of_element = symbol_of_elements[i] structure.substitute_atom(formated_atom, symbol_of_element) # default symprec = 1e-5 # symprec angle_tolerance = -1.0 # angle_tolerance if 'symprec' in kwargs: symprec = kwargs['symprec'] if 'angle_tolerance' in kwargs: angle_tolerance = kwargs['angle_tolerance'] if isPersist: structure.update(isPersist=isPersist, symprec=symprec, angle_tolerance=angle_tolerance) elif isUpdatedInfo and not isPersist: structure.update(isPersist=False, symprec=symprec, angle_tolerance=angle_tolerance) self.structure = structure return self