def from_slabs( cls, substrate_slab: Slab, film_slab: Slab, in_plane_offset: Tuple[float, float] = (0, 0), gap: float = 1.6, vacuum_over_film: float = 0.0, interface_properties: Optional[Dict] = None, center_slab: bool = True, ) -> "Interface": """ Makes an interface structure by merging a substrate and film slabs The film a- and b-vectors will be forced to be the substrate slab's a- and b-vectors. For now, it's suggested to use a factory method that will ensure the appropriate interface structure is already met. Args: sub_slab: slab for the substrate film_slab: slab for the film in_plane_offset: fractional shift in plane for the film with respect to the substrate gap: gap between substrate and film in Angstroms vacuum_over_film: vacuum space above the film in Angstroms structure_properties: dictionary of misc properties for this structure center_slab: center the slab """ interface_properties = interface_properties or {} # Ensure c-axis is orthogonal to a/b plane if isinstance(substrate_slab, Slab): substrate_slab = substrate_slab.get_orthogonal_c_slab() if isinstance(film_slab, Slab): film_slab = film_slab.get_orthogonal_c_slab() assert np.allclose(film_slab.lattice.alpha, 90, 0.1) assert np.allclose(film_slab.lattice.beta, 90, 0.1) assert np.allclose(substrate_slab.lattice.alpha, 90, 0.1) assert np.allclose(substrate_slab.lattice.beta, 90, 0.1) # Ensure sub is right-handed # IE sub has surface facing "up" sub_vecs = substrate_slab.lattice.matrix.copy() if np.dot(np.cross(*sub_vecs[:2]), sub_vecs[2]) < 0: sub_vecs[2] *= -1.0 substrate_slab.lattice = Lattice(sub_vecs) # Find the limits of C-coords sub_coords = substrate_slab.frac_coords film_coords = film_slab.frac_coords sub_min_c = np.min(sub_coords[:, 2]) * substrate_slab.lattice.c sub_max_c = np.max(sub_coords[:, 2]) * substrate_slab.lattice.c film_min_c = np.min(film_coords[:, 2]) * film_slab.lattice.c film_max_c = np.max(film_coords[:, 2]) * film_slab.lattice.c min_height = np.abs(film_max_c - film_min_c) + np.abs(sub_max_c - sub_min_c) # construct new lattice abc = substrate_slab.lattice.abc[:2] + (min_height + gap + vacuum_over_film, ) angles = substrate_slab.lattice.angles lattice = Lattice.from_parameters(*abc, *angles) # Get the species species = substrate_slab.species + film_slab.species # Get the coords # Shift substrate to bottom in new lattice sub_coords = np.subtract(sub_coords, [0, 0, np.min(sub_coords[:, 2])]) sub_coords[:, 2] *= substrate_slab.lattice.c / lattice.c # Flip the film over film_coords[:, 2] *= -1.0 film_coords[:, 2] *= film_slab.lattice.c / lattice.c # Shift the film coords to right over the substrate + gap film_coords = np.subtract(film_coords, [0, 0, np.min(film_coords[:, 2])]) film_coords = np.add( film_coords, [0, 0, gap / lattice.c + np.max(sub_coords[:, 2])]) # Build coords coords = np.concatenate([sub_coords, film_coords]) # Shift coords to center if center_slab: coords = np.add(coords, [0, 0, 0.5 - np.average(coords[:, 2])]) # Only merge site properties in both slabs site_properties = {} site_props_in_both = set(substrate_slab.site_properties.keys()) & set( film_slab.site_properties.keys()) for key in site_props_in_both: site_properties[key] = [ *substrate_slab.site_properties[key], *film_slab.site_properties[key], ] site_properties["interface_label"] = ["substrate"] * len( substrate_slab) + ["film"] * len(film_slab) iface = cls( lattice=lattice, species=species, coords=coords, to_unit_cell=False, coords_are_cartesian=False, site_properties=site_properties, validate_proximity=False, in_plane_offset=in_plane_offset, gap=gap, vacuum_over_film=vacuum_over_film, interface_properties=interface_properties, ) iface.sort() return iface