def setUpClass(cls): cls.al_fcc = ase_to_pyiron(bulk("Al", cubic=True)) cls.fe_bcc = ase_to_pyiron(bulk("Fe", cubic=True)) cls.ti_hcp = ase_to_pyiron(bulk("Ti", orthorhombic=True)) cls.si_dia = ase_to_pyiron(bulk("Si", cubic=True)) cls.al_fcc_4 = CrystalStructure(element="Al", bravais_basis="fcc", lattice_constants=4 ).repeat(4)
def test_check_diamond(self): al_fcc = ase_to_pyiron(bulk("Al", cubic=True)) fe_bcc = ase_to_pyiron(bulk("Fe", cubic=True)) ti_hcp = ase_to_pyiron(bulk("Ti", orthorhombic=True)) si_dia = ase_to_pyiron(bulk("Si", cubic=True)) self.assertFalse(check_diamond(structure=al_fcc)) self.assertFalse(check_diamond(structure=fe_bcc)) self.assertFalse(check_diamond(structure=ti_hcp)) self.assertTrue(check_diamond(structure=si_dia))
def surface_hkl(lattice, hkl, layers, vacuum=1.0, center=False, pbc=True): """ Use ase.build.surface to build a surface with surface normal (hkl). Args: lattice (pyiron_atomistics.atomistics.structure.atoms.Atoms/str): bulk Atoms instance or str, e.g. "Fe", from which to build the surface hkl (list): miller indices of surface to be created layers (int): # of atomic layers in the surface vacuum (float): vacuum spacing center (bool): shift all positions to center the surface in the cell Returns: pyiron_atomistics.atomistics.structure.atoms.Atoms instance: Required surface """ # https://gitlab.com/ase/ase/blob/master/ase/lattice/surface.py state.publications.add(publication_ase()) surface = ase_surf(lattice, hkl, layers) z_max = np.max(surface.positions[:, 2]) surface.cell[2, 2] = z_max + vacuum if center: surface.positions += 0.5 * surface.cell[2] - [0, 0, z_max / 2] surface.pbc = pbc return ase_to_pyiron(surface)
def build(self): """ Call Atomsk with the options accumulated so far. Returns: :class:`.Atoms`: new structure """ self._options.append("- exyz") # output to stdout as exyz format with tempfile.TemporaryDirectory() as temp_dir: if self._structure is not None: write( os.path.join(temp_dir, "input.exyz"), self._structure, format="extxyz", ) proc = subprocess.run( ["atomsk", *" ".join(self._options).split()], capture_output=True, cwd=temp_dir, ) output = proc.stdout.decode("utf8") for l in output.split("\n"): if l.strip().startswith("X!X ERROR:"): raise AtomskError(f"atomsk returned error: {output}") return ase_to_pyiron(read(io.StringIO(output), format="extxyz"))
def test_strain(self): bulk = StructureFactory().ase.bulk('Fe', cubic=True) a_0 = bulk.cell[0, 0] b = 0.5 * np.sqrt(3) * a_0 structure = ase_to_pyiron( BodyCenteredCubic(symbol='Fe', directions=[[-1, 0, 1], [1, -2, 1], [1, 1, 1]], latticeconstant=a_0)) L = 100 structure = structure.repeat( (*np.rint(L / structure.cell.diagonal()[:2]).astype(int), 1)) voro = Voronoi(structure.positions[:, :2]) center = voro.vertices[np.linalg.norm( voro.vertices - structure.cell.diagonal()[:2] * 0.5, axis=-1).argmin()] structure.positions[:, 2] += b / (2 * np.pi) * np.arctan2( *(structure.positions[:, :2] - center).T[::-1]) structure.center_coordinates_in_unit_cell() r_0 = 0.9 * L / 2 r = np.linalg.norm(structure.positions[:, :2] - center, axis=-1) core_region = (r < r_0) * (r > 10) strain = structure.analyse.get_strain(bulk, num_neighbors=8) strain = strain[core_region] positions = structure.positions[core_region, :2] x = positions - center eps_yz = b / (4 * np.pi) * x[:, 0] / np.linalg.norm(x, axis=-1)**2 eps_xz = -b / (4 * np.pi) * x[:, 1] / np.linalg.norm(x, axis=-1)**2 self.assertLess(np.absolute(eps_yz - strain[:, 1, 2]).max(), 0.01) self.assertLess(np.absolute(eps_xz - strain[:, 0, 2]).max(), 0.01)
def high_index_surface( self, element, crystal_structure, lattice_constant, terrace_orientation=None, step_orientation=None, kink_orientation=None, step_down_vector=None, length_step=3, length_terrace=3, length_kink=1, layers=60, vacuum=10, ): """ Gives a slab positioned at the bottom with the high index surface computed by high_index_surface_info(). Args: element (str): The parent element eq. "N", "O", "Mg" etc. crystal_structure (str): The crystal structure of the lattice lattice_constant (float): The lattice constant terrace_orientation (list): The miller index of the terrace. default: [1,1,1] step_orientation (list): The miller index of the step. default: [1,1,0] kink_orientation (list): The miller index of the kink. default: [1,1,1] step_down_vector (list): The direction for stepping down from the step to next terrace. default: [1,1,0] length_terrace (int): The length of the terrace along the kink direction in atoms. default: 3 length_step (int): The length of the step along the step direction in atoms. default: 3 length_kink (int): The length of the kink along the kink direction in atoms. default: 1 layers (int): Number of layers of the high_index_surface. default: 60 vacuum (float): Thickness of vacuum on the top of the slab. default:10 Returns: slab: pyiron_atomistics.atomistics.structure.atoms.Atoms instance Required surface """ basis = self.crystal( element=element, bravais_basis=crystal_structure, lattice_constant=lattice_constant, ) high_index_surface, _, _ = self.high_index_surface_info( element=element, crystal_structure=crystal_structure, lattice_constant=lattice_constant, terrace_orientation=terrace_orientation, step_orientation=step_orientation, kink_orientation=kink_orientation, step_down_vector=step_down_vector, length_step=length_step, length_terrace=length_terrace, length_kink=length_kink, ) surf = ase_surf(basis, high_index_surface, layers, vacuum) sga = SpacegroupAnalyzer(pyiron_to_pymatgen(ase_to_pyiron(surf))) pmg_refined = sga.get_refined_structure() slab = pymatgen_to_pyiron(pmg_refined) slab.positions[:, 2] = slab.positions[:, 2] - np.min(slab.positions[:, 2]) slab.set_pbc(True) return slab
def db_entry(self): """ Generate the initial database entry Returns: (dict): db_dict """ db_dict = super(InteractiveWrapper, self).db_entry() if self.structure: if isinstance(self.structure, PAtoms): parent_structure = self.structure.get_parent_basis() else: parent_structure = ase_to_pyiron( self.structure).get_parent_basis() db_dict["ChemicalFormula"] = parent_structure.get_chemical_formula( ) return db_dict
def from_ase(ase_atoms): return ase_to_pyiron(ase_atoms)
def surface( element, surface_type, size=(1, 1, 1), vacuum=1.0, center=False, pbc=True, **kwargs, ): """ Generate a surface based on the ase.build.surface module. Args: element (str): Element name surface_type (str): The string specifying the surface type generators available through ase (fcc111, hcp0001 etc.) size (tuple): Size of the surface vacuum (float): Length of vacuum layer added to the surface along the z direction center (bool): Tells if the surface layers have to be at the center or at one end along the z-direction pbc (list/numpy.ndarray): List of booleans specifying the periodic boundary conditions along all three directions. **kwargs: Additional, arguments you would normally pass to the structure generator like 'a', 'b', 'orthogonal' etc. Returns: pyiron_atomistics.atomistics.structure.atoms.Atoms instance: Required surface """ # https://gitlab.com/ase/ase/blob/master/ase/lattice/surface.py if pbc is None: pbc = True state.publications.add(publication_ase()) for surface_class in [ add_adsorbate, add_vacuum, bcc100, bcc110, bcc111, diamond100, diamond111, fcc100, fcc110, fcc111, fcc211, hcp0001, hcp10m10, mx2, hcp0001_root, fcc111_root, bcc111_root, root_surface, root_surface_analysis, ase_surf, ]: if surface_type == surface_class.__name__: surface_type = surface_class break if isinstance(surface_type, types.FunctionType): if center: surface = surface_type(symbol=element, size=size, vacuum=vacuum, **kwargs) else: surface = surface_type(symbol=element, size=size, **kwargs) z_max = np.max(surface.positions[:, 2]) surface.cell[2, 2] = z_max + vacuum surface.pbc = pbc return ase_to_pyiron(surface) else: raise ValueError(f"Surface type {surface_type} not recognized.")
def _final_struct_to_hdf(self): with self._hdf5.open("output") as hdf5: structure = self.get_structure(frame=-1) if not isinstance(structure, Atoms): structure = ase_to_pyiron(structure) structure.to_hdf(hdf5)
def read(self, *args, **kwargs): return ase_to_pyiron(ase_read(*args, **kwargs))
def crystal(self, *args, **kwargs): return ase_to_pyiron(ase_crystal(*args, **kwargs))
def bulk(self, *args, **kwargs): return ase_to_pyiron(ase_bulk(*args, **kwargs))
def pymatgen_to_pyiron(structure): return ase_to_pyiron(AseAtomsAdaptor.get_atoms(structure))