Skip to content

autocat.surface

generate_surface_structures(species_list, crystal_structures=None, facets=None, supercell_dim=None, default_lat_param_lib=None, a_dict=None, c_dict=None, set_magnetic_moments=None, magnetic_moments=None, vacuum=10.0, n_fixed_layers=0, write_to_disk=False, write_location='.', dirs_exist_ok=False)

Generates mono-element slabs and writes them to separate directories, if specified.

Parameters:

Name Type Description Default
species_list List[str]

List of chemical symbols of the slabs to be built.

required

crystal_structures: Dictionary with crystal structure to be used for each species. These will be passed on as input to ase.build.bulk. So, must be one of sc, fcc, bcc, tetragonal, bct, hcp, rhombohedral, orthorhombic, diamond, zincblende, rocksalt, cesiumchloride, fluorite or wurtzite. If not specified, the default reference crystal structure for each species from ase.data will be used.

facets: Dictionary with the surface facets to be considered for each species.

If not specified for a given species, the following
defaults will be used based on the crystal structure:
fcc/bcc: 100, 111, 110
hcp: 0001

supercell_dim: List specifying the size of the supercell to be generated in the format (nx, ny, nz).

default_lat_param_lib: String indicating which library the lattice constants should be pulled from if not specified in either a_dict or c_dict. Defaults to lattice constants defined in ase.data.

Options:
pbe_fd: parameters calculated using xc=PBE and finite-difference
beefvdw_fd: parameters calculated using xc=BEEF-vdW and finite-difference
pbe_pw: parameters calculated using xc=PBE and a plane-wave basis set
beefvdw_fd: parameters calculated using xc=BEEF-vdW and a plane-wave basis set

N.B. if there is a species present in species_list that is NOT in the
reference library specified, it will be pulled from `ase.data`

a_dict: Dictionary with lattice parameters to be used for each species. If not specified, defaults from default_lat_param_lib are used.

c_dict: Dictionary with lattice parameters to be used for each species. If not specified, defaults from default_lat_param_lib are used.

set_magnetic_moments: List of species for which magnetic moments need to be set. If not specified, magnetic moments will be set only for Fe, Co, Ni (the ferromagnetic elements).

magnetic_moments: Dictionary with the magnetic moments to be set for the chemical species listed previously. If not specified, default ground state magnetic moments from ase.data are used.

vacuum: Float specifying the amount of vacuum (in Angstrom) to be added to the slab (the slab is placed at the center of the supercell).

n_fixed_layers: Integer giving the number of layers of the slab to be fixed starting from the bottom up (e.g. a value of 2 will fix the bottom 2 layers).

write_to_disk: Boolean specifying whether the surface structures generated should be written to disk. Defaults to False.

write_location: String with the location where the per-species/per-crystal structure directories must be constructed and structure files written to disk.

In the specified write_location, the following directory structure
will be created:
[species]/[crystal_structure + facet]/substrate/input.traj
Defaults to the current working directory.

dirs_exist_ok: Boolean specifying whether existing directories/files should be overwritten or not. This is passed on to the os.makedirs builtin. Defaults to False (raises an error if directories corresponding the species and crystal structure already exist).

Returns:

Type Description
Dictionary with surface structures (as
write-location (if any) for each
each input species.

Example:

{ "Pt": { "fcc111": { "structure": Pt_surface_obj, "traj_file_path": "/path/to/Pt/fcc111/surface/traj/file" }, "fcc100": ... , "Cu": ... } }

Source code in autocat/surface.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
def generate_surface_structures(
    species_list: List[str],
    crystal_structures: Dict[str, str] = None,
    facets: Dict[str, str] = None,
    supercell_dim: List[int] = None,
    default_lat_param_lib: str = None,
    a_dict: Dict[str, float] = None,
    c_dict: Dict[str, float] = None,
    set_magnetic_moments: List[str] = None,
    magnetic_moments: Dict[str, float] = None,
    vacuum: float = 10.0,
    n_fixed_layers: int = 0,
    write_to_disk: bool = False,
    write_location: str = ".",
    dirs_exist_ok: bool = False,
) -> Dict[str, Dict[str, Dict[str, Any]]]:
    """
    Generates mono-element slabs and writes them to separate directories,
    if specified.

    Parameters
    ----------

    species_list (REQUIRED):
        List of chemical symbols of the slabs to be built.

    crystal_structures:
        Dictionary with crystal structure to be used for each species.
        These will be passed on as input to `ase.build.bulk`. So, must be one
        of sc, fcc, bcc, tetragonal, bct, hcp, rhombohedral, orthorhombic,
        diamond, zincblende, rocksalt, cesiumchloride, fluorite or wurtzite.
        If not specified, the default reference crystal structure for each
        species from `ase.data` will be used.

    facets:
        Dictionary with the surface facets to be considered for each
        species.

        If not specified for a given species, the following
        defaults will be used based on the crystal structure:
        fcc/bcc: 100, 111, 110
        hcp: 0001

    supercell_dim:
        List specifying the size of the supercell to be generated in the
        format (nx, ny, nz).

    default_lat_param_lib:
        String indicating which library the lattice constants should be pulled
        from if not specified in either a_dict or c_dict.
        Defaults to lattice constants defined in `ase.data`.

        Options:
        pbe_fd: parameters calculated using xc=PBE and finite-difference
        beefvdw_fd: parameters calculated using xc=BEEF-vdW and finite-difference
        pbe_pw: parameters calculated using xc=PBE and a plane-wave basis set
        beefvdw_fd: parameters calculated using xc=BEEF-vdW and a plane-wave basis set

        N.B. if there is a species present in species_list that is NOT in the
        reference library specified, it will be pulled from `ase.data`

    a_dict:
        Dictionary with lattice parameters <a> to be used for each species.
        If not specified, defaults from `default_lat_param_lib` are used.

    c_dict:
        Dictionary with lattice parameters <c> to be used for each species.
        If not specified, defaults from `default_lat_param_lib` are used.

    set_magnetic_moments:
        List of species for which magnetic moments need to be set.
        If not specified, magnetic moments will be set only for Fe, Co, Ni
        (the ferromagnetic elements).

    magnetic_moments:
        Dictionary with the magnetic moments to be set for the chemical
        species listed previously.
        If not specified, default ground state magnetic moments from
        `ase.data` are used.

    vacuum:
        Float specifying the amount of vacuum (in Angstrom) to be added to
        the slab (the slab is placed at the center of the supercell).

    n_fixed_layers:
        Integer giving the number of layers of the slab to be fixed
        starting from the bottom up (e.g. a value of 2 will fix the
        bottom 2 layers).

    write_to_disk:
        Boolean specifying whether the surface structures generated should be
        written to disk.
        Defaults to False.

    write_location:
        String with the location where the per-species/per-crystal structure
        directories must be constructed and structure files written to disk.

        In the specified write_location, the following directory structure
        will be created:
        [species]/[crystal_structure + facet]/substrate/input.traj
        Defaults to the current working directory.

    dirs_exist_ok:
        Boolean specifying whether existing directories/files should be
        overwritten or not. This is passed on to the `os.makedirs` builtin.
        Defaults to False (raises an error if directories corresponding the
        species and crystal structure already exist).

    Returns
    -------

    Dictionary with surface structures (as `ase.Atoms` objects) and
    write-location (if any) for each {crystal structure and facet} specified for
    each input species.

    Example:

    {
        "Pt": {
            "fcc111": {
                "structure": Pt_surface_obj,
                "traj_file_path": "/path/to/Pt/fcc111/surface/traj/file"
                },
            "fcc100": ...
            ,
        "Cu": ...
        }
    }

    """

    lpl = {
        "pbe_fd": BULK_PBE_FD,
        "beefvdw_fd": BULK_BEEFVDW_FD,
        "pbe_pw": BULK_PBE_PW,
        "beefvdw_pw": BULK_BEEFVDW_PW,
    }
    ase_build_funcs = {
        "fcc100": ase.build.fcc100,
        "fcc110": ase.build.fcc110,
        "fcc111": ase.build.fcc111,
        "bcc100": ase.build.bcc100,
        "bcc110": ase.build.bcc110,
        "bcc111": ase.build.bcc111,
        "hcp0001": ase.build.hcp0001,
    }

    if crystal_structures is None:
        crystal_structures = {}
    if facets is None:
        facets = {}
    if supercell_dim is None:
        supercell_dim = [3, 3, 4]
    if a_dict is None:
        a_dict = {}
    if c_dict is None:
        c_dict = {}
    if set_magnetic_moments is None:
        set_magnetic_moments = ["Fe", "Co", "Ni"]
    if magnetic_moments is None:
        magnetic_moments = {}

    # load crystal structure defaults from `ase.data`, override with user input
    cs_library = {
        species: reference_states[atomic_numbers[species]].get("symmetry")
        for species in species_list
    }
    cs_library.update(crystal_structures)

    # load lattice params <a>, <c> from reference library, override with user input
    a_library = {}
    c_library = {}
    if default_lat_param_lib is not None:
        a_library.update(
            {
                species: lpl[default_lat_param_lib].get(species, {}).get("a")
                for species in species_list
            }
        )
        c_library.update(
            {
                species: lpl[default_lat_param_lib].get(species, {}).get("c")
                for species in species_list
            }
        )
    a_library.update(a_dict)
    c_library.update(c_dict)

    # load magnetic moment defaults from `ase.data`, override with user input
    mm_library = {
        species: ground_state_magnetic_moments[atomic_numbers[species]]
        for species in species_list
    }
    mm_library.update(magnetic_moments)

    # set default facets for each crystal structure, override with user input
    ft_defaults = {
        "fcc": ["100", "111", "110"],
        "bcc": ["100", "111", "110"],
        "hcp": ["0001"],
    }

    ft_library = {species: ft_defaults[cs_library[species]] for species in species_list}
    ft_library.update(facets)

    surface_structures = {}
    for species in species_list:
        cs = cs_library.get(species)
        a = a_library.get(species)
        c = c_library.get(species)

        surf = {}
        for facet in ft_library[species]:
            if c is not None:
                struct = ase_build_funcs[f"{cs}{facet}"](
                    species, size=supercell_dim, vacuum=vacuum, a=a, c=c
                )
            else:
                struct = ase_build_funcs[f"{cs}{facet}"](
                    species, size=supercell_dim, vacuum=vacuum, a=a
                )

            if n_fixed_layers > 0:
                f = FixAtoms(
                    mask=[
                        atom.tag > (supercell_dim[-1] - n_fixed_layers)
                        for atom in struct
                    ]
                )
                struct.set_constraint([f])

            if species in set_magnetic_moments:
                struct.set_initial_magnetic_moments([mm_library[species]] * len(struct))

            traj_file_path = None
            if write_to_disk:
                dir_path = os.path.join(
                    write_location, f"{species}", f"{cs}{facet}", "substrate"
                )
                os.makedirs(dir_path, exist_ok=dirs_exist_ok)
                traj_file_path = os.path.join(dir_path, "input.traj")
                struct.write(traj_file_path)
                print(f"{species}_{cs}{facet} structure written to {traj_file_path}")

            surf[f"{cs}{facet}"] = {
                "structure": struct,
                "traj_file_path": traj_file_path,
            }

        surface_structures[species] = surf
    return surface_structures