Exemple #1
0
def get_hull_distance(competing_phase_directory='../competing_phases'):
    """
    Calculate the material's distance to the thermodynamic hull,
    based on species in the Materials Project database.

    Args:
        competing_phase_directory (str): absolute or relative path
            to the location where your competing phases have been
            relaxed. The default expectation is that they are stored
            in a directory named 'competing_phases' at the same level
            as your material's relaxation directory.
    Returns:
        float. distance (eV/atom) between the material and the
            hull.
    """

    finished_competitors = {}
    original_directory = os.getcwd()
    # Determine which competing phases have been relaxed in the current
    # framework and store them in a dictionary ({formula: entry}).
    if os.path.isdir(competing_phase_directory):
        os.chdir(competing_phase_directory)
        for comp_dir in [
            dir for dir in os.listdir(os.getcwd()) if os.path.isdir(dir) and
            is_converged(dir)
                ]:
            vasprun = Vasprun('{}/vasprun.xml'.format(comp_dir))
            composition = vasprun.final_structure.composition
            energy = vasprun.final_energy
            finished_competitors[comp_dir] = ComputedEntry(composition, energy)
        os.chdir(original_directory)
    else:
        raise ValueError('Competing phase directory does not exist.')

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        raise ValueError('This directory does not have a converged vasprun.xml')
    my_entry = ComputedEntry(composition, energy)  # 2D material
    entries = MPR.get_entries_in_chemsys(
        [elt.symbol for elt in composition]
        )

    # If the energies of competing phases have been calculated in
    # the current framework, put them in the phase diagram instead
    # of the MP energies.
    for i in range(len(entries)):
        formula = entries[i].composition.reduced_formula
        if formula in finished_competitors:
            entries[i] = finished_competitors[formula]
        else:
            entries[i] = ComputedEntry(entries[i].composition, 100)

    entries.append(my_entry)  # 2D material

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)

    return decomp[1]
def get_hull_distance(competing_phase_directory='../competing_phases'):
    """
    Calculate the material's distance to the thermodynamic hull,
    based on species in the Materials Project database.

    Args:
        competing_phase_directory (str): absolute or relative path
            to the location where your competing phases have been
            relaxed. The default expectation is that they are stored
            in a directory named 'competing_phases' at the same level
            as your material's relaxation directory.
    Returns:
        float. distance (eV/atom) between the material and the
            hull.
    """

    finished_competitors = {}
    original_directory = os.getcwd()
    # Determine which competing phases have been relaxed in the current
    # framework and store them in a dictionary ({formula: entry}).
    if os.path.isdir(competing_phase_directory):
        os.chdir(competing_phase_directory)
        for comp_dir in [
                dir for dir in os.listdir(os.getcwd())
                if os.path.isdir(dir) and is_converged(dir)
        ]:
            vasprun = Vasprun('{}/vasprun.xml'.format(comp_dir))
            composition = vasprun.final_structure.composition
            energy = vasprun.final_energy
            finished_competitors[comp_dir] = ComputedEntry(composition, energy)
        os.chdir(original_directory)
    else:
        raise ValueError('Competing phase directory does not exist.')

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        raise ValueError(
            'This directory does not have a converged vasprun.xml')
    my_entry = ComputedEntry(composition, energy)  # 2D material
    entries = MPR.get_entries_in_chemsys([elt.symbol for elt in composition])

    # If the energies of competing phases have been calculated in
    # the current framework, put them in the phase diagram instead
    # of the MP energies.
    for i in range(len(entries)):
        formula = entries[i].composition.reduced_formula
        if formula in finished_competitors:
            entries[i] = finished_competitors[formula]
        else:
            entries[i] = ComputedEntry(entries[i].composition, 100)

    entries.append(my_entry)  # 2D material

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)

    return decomp[1]
Exemple #3
0
def relax(dim=2, submit=True, force_overwrite=False):
    """
    Writes input files and (optionally) submits a self-consistent
    relaxation. Should be run before pretty much anything else, in
    order to get the right energy and structure of the material.

    Args:
        dim (int): 2 for relaxing a 2D material, 3 for a 3D material.
        submit (bool): Whether or not to submit the job.
        force_overwrite (bool): Whether or not to overwrite files
            if an already converged vasprun.xml exists in the
            directory.
    """

    if force_overwrite or not utl.is_converged(os.getcwd()):
        directory = os.getcwd().split('/')[-1]

        # vdw_kernel.bindat file required for VDW calculations.
        if VDW_KERNEL != '/path/to/vdw_kernel.bindat':
            os.system('cp {} .'.format(VDW_KERNEL))
        # KPOINTS
        Kpoints.automatic_density(Structure.from_file('POSCAR'),
                                  1000).write_file('KPOINTS')

        # INCAR
        INCAR_DICT.update({'MAGMOM': utl.get_magmom_string()})
        Incar.from_dict(INCAR_DICT).write_file('INCAR')
        # POTCAR
        utl.write_potcar()

        # Special tasks only performed for 2D materials.
        if dim == 2:
            # Ensure 20A interlayer vacuum
            utl.add_vacuum(20 - utl.get_spacing(), 0.9)
            # Remove all z k-points.
            kpts_lines = open('KPOINTS').readlines()
            with open('KPOINTS', 'w') as kpts:
                for line in kpts_lines[:3]:
                    kpts.write(line)
                kpts.write(kpts_lines[3].split()[0] + ' '
                           + kpts_lines[3].split()[1] + ' 1')

        # Submission script
        if QUEUE == 'pbs':
            utl.write_pbs_runjob(directory, 1, 16, '800mb', '6:00:00',
                                 VASP_2D)
            submission_command = 'qsub runjob'

        elif QUEUE == 'slurm':
            utl.write_slurm_runjob(directory, 16, '800mb', '6:00:00',
                                   VASP_2D)
            submission_command = 'sbatch runjob'

        if submit:
            os.system(submission_command)
Exemple #4
0
def relax(dim=2, submit=True, force_overwrite=False):
    """
    Writes input files and (optionally) submits a self-consistent
    relaxation. Should be run before pretty much anything else, in
    order to get the right energy and structure of the material.

    Args:
        dim (int): 2 for relaxing a 2D material, 3 for a 3D material.
        submit (bool): Whether or not to submit the job.
        force_overwrite (bool): Whether or not to overwrite files
            if an already converged vasprun.xml exists in the
            directory.
    """

    if force_overwrite or not utl.is_converged(os.getcwd()):
        directory = os.getcwd().split('/')[-1]

        # vdw_kernel.bindat file required for VDW calculations.
        if VDW_KERNEL != '/path/to/vdw_kernel.bindat':
            os.system('cp {} .'.format(VDW_KERNEL))
        # KPOINTS
        Kpoints.automatic_density(Structure.from_file('POSCAR'),
                                  1000).write_file('KPOINTS')

        # INCAR
        INCAR_DICT.update({'MAGMOM': utl.get_magmom_string()})
        Incar.from_dict(INCAR_DICT).write_file('INCAR')
        # POTCAR
        utl.write_potcar()

        # Special tasks only performed for 2D materials.
        if dim == 2:
            # Ensure 20A interlayer vacuum
            utl.add_vacuum(20 - utl.get_spacing(), 0.9)
            # Remove all z k-points.
            kpts_lines = open('KPOINTS').readlines()
            with open('KPOINTS', 'w') as kpts:
                for line in kpts_lines[:3]:
                    kpts.write(line)
                kpts.write(kpts_lines[3].split()[0] + ' ' +
                           kpts_lines[3].split()[1] + ' 1')

        # Submission script
        if QUEUE == 'pbs':
            utl.write_pbs_runjob(directory, 1, 16, '800mb', '6:00:00', VASP_2D)
            submission_command = 'qsub runjob'

        elif QUEUE == 'slurm':
            utl.write_slurm_runjob(directory, 16, '800mb', '6:00:00', VASP_2D)
            submission_command = 'sbatch runjob'

        if submit:
            os.system(submission_command)
    competing_species = get_competing_species(directories)
    relax_competing_species(competing_species)

    for directory in directories:
        os.chdir(directory)
        relax()
        os.chdir('../')

    loop = True
    while loop:
        print('>> Checking convergence')
        finished_2d, finished_3d = [], []

        for directory in directories:
            if is_converged(directory):
                finished_2d.append(directory)
        for directory in competing_species:
            if is_converged('all_competitors/{}'.format(directory[0])):
                finished_3d.append(directory[0])

        if len(finished_2d + finished_3d) == len(directories +
                                                 competing_species):
            print('>> Plotting hull distances')
            plot_hull_distances(get_hull_distances(finished_2d))
            loop = False
        else:
            print('>> Not all directories converged ({}/{})'.format(
                len(finished_2d + finished_3d),
                len(directories + competing_species)))
        os.chdir(directory)
        converged[directory] = False
        run_friction_calculations()
        os.chdir('../')

    loop = True

    while loop:
        time.sleep(INTERVAL)

        loop = False

        for directory in directories:
            os.chdir(directory)
            converged[directory] = True
            for subdirectory in [
                    dir for dir in os.listdir(os.getcwd())
                    if os.path.isdir(dir)]:
                if not is_converged(subdirectory):
                    converged[directory] = False
                    break

            if not converged[directory]:
                print('>> Not all directories converged')
                loop = True

            print('>> Plotting gamma surface for {}'.format(directory))
            plot_gamma_surface()

            os.chdir('../')
               and dir not in ['all_competitors']]

if __name__ == '__main__':

    for directory in directories:
        os.chdir(directory)
        run_linemode_calculation()
        os.chdir('../')

    loop = True
    while loop:
        print('>> Checking convergence')
        finished = []

        for directory in directories:
            if is_converged('{}/pbe_bands'.format(directory)):
                finished.append(directory)

        if len(finished) == len(directories):
            print('>> Plotting band structures')
            for directory in finished:
                os.chdir('{}/pbe_bands'.format(directory))
                plot_normal_band_structure()
                os.chdir('../../')
            loop = False
        else:
            print('>> Not all directories converged ({}/{})'.format(
                len(finished), len(directories)))

            time.sleep(INTERVAL)
]

if __name__ == '__main__':

    for directory in directories:
        os.chdir(directory)
        run_linemode_calculation()
        os.chdir('../')

    loop = True
    while loop:
        print('>> Checking convergence')
        finished = []

        for directory in directories:
            if is_converged('{}/pbe_bands'.format(directory)):
                finished.append(directory)

        if len(finished) == len(directories):
            print('>> Plotting band structures')
            for directory in finished:
                os.chdir('{}/pbe_bands'.format(directory))
                plot_normal_band_structure()
                os.chdir('../../')
            loop = False
        else:
            print('>> Not all directories converged ({}/{})'.format(
                len(finished), len(directories)))

            time.sleep(INTERVAL)
def plot_ion_hull_and_voltages(ion, fmt='pdf'):
    """
    Plots the phase diagram between the pure material and pure ion,
    Connecting the points on the convex hull of the phase diagram.

    Args:
        ion (str): name of atom that was intercalated, e.g. 'Li'.
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    # Calculated with the relax() function in
    # twod_materials.stability.startup. If you are using other input
    # parameters, you need to recalculate these values!
    ion_ev_fu = {'Li': -1.7540797, 'Mg': -1.31976062, 'Al': -3.19134607}

    energy = Vasprun('vasprun.xml').final_energy
    composition = Structure.from_file('POSCAR').composition

    # Get the formula (with single-digit integers preceded by a '_').
    twod_material = list(composition.reduced_formula)
    twod_formula = str()
    for i in range(len(twod_material)):
        try:
            int(twod_material[i])
            twod_formula += '_{}'.format(twod_material[i])
        except:
            twod_formula += twod_material[i]

    twod_ev_fu = energy / composition.get_reduced_composition_and_factor()[1]

    data = [(0, 0, 0, twod_ev_fu)]  # (at% ion, n_ions, E_F, abs_energy)
    for directory in [
            dir for dir in os.listdir(os.getcwd()) if os.path.isdir(dir)
    ]:
        if is_converged(directory):
            os.chdir(directory)
            energy = Vasprun('vasprun.xml').final_energy
            composition = Structure.from_file('POSCAR').composition
            ion_fraction = composition.get_atomic_fraction(ion)

            no_ion_comp_dict = composition.as_dict()
            no_ion_comp_dict.update({ion: 0})
            no_ion_comp = Composition.from_dict(no_ion_comp_dict)

            n_twod_fu = no_ion_comp.get_reduced_composition_and_factor()[1]
            n_ions = composition[ion] / n_twod_fu

            E_F = ((energy - composition[ion] * ion_ev_fu[ion] -
                    twod_ev_fu * n_twod_fu) / composition.num_atoms)

            data.append((ion_fraction, n_ions, E_F, energy / n_twod_fu))

            os.chdir('../')
    data.append((1, 1, 0, ion_ev_fu[ion]))  # Pure ion

    sorted_data = sorted(data, key=operator.itemgetter(0))

    # Determine which compositions are on the convex hull.
    energy_profile = np.array([[item[0], item[2]] for item in sorted_data
                               if item[2] <= 0])
    hull = ConvexHull(energy_profile)
    convex_ion_fractions = [
        energy_profile[vertex, 0] for vertex in hull.vertices
    ]
    convex_formation_energies = [
        energy_profile[vertex, 1] for vertex in hull.vertices
    ]

    convex_ion_fractions.append(convex_ion_fractions.pop(0))
    convex_formation_energies.append(convex_formation_energies.pop(0))

    concave_ion_fractions = [
        pt[0] for pt in sorted_data if pt[0] not in convex_ion_fractions
    ]
    concave_formation_energies = [
        pt[2] for pt in sorted_data if pt[0] not in convex_ion_fractions
    ]

    voltage_profile = []
    j = 0
    k = 0
    for i in range(1, len(sorted_data) - 1):
        if sorted_data[i][0] in convex_ion_fractions:
            voltage = -(
                ((sorted_data[i][3] - sorted_data[k][3]) -
                 (sorted_data[i][1] - sorted_data[k][1]) * ion_ev_fu[ion]) /
                (sorted_data[i][1] - sorted_data[k][1]))
            voltage_profile.append((sorted_data[k][0], voltage))
            voltage_profile.append((sorted_data[i][0], voltage))
            j += 1
            k = i

    voltage_profile.append((voltage_profile[-1][0], 0))
    voltage_profile.append((1, 0))

    voltage_profile_x = [tup[0] for tup in voltage_profile]
    voltage_profile_y = [tup[1] for tup in voltage_profile]

    ax = plt.figure(figsize=(14, 10)).gca()

    ax.plot([0, 1], [0, 0], 'k--')
    ax.plot(convex_ion_fractions,
            convex_formation_energies,
            'b-',
            marker='o',
            markersize=12,
            markeredgecolor='none')
    ax.plot(concave_ion_fractions,
            concave_formation_energies,
            'r',
            marker='o',
            linewidth=0,
            markersize=12,
            markeredgecolor='none')

    ax2 = ax.twinx()
    ax2.plot(voltage_profile_x, voltage_profile_y, 'k-', marker='o')

    ax.text(0, 0.002, r'$\mathrm{%s}$' % twod_formula, family='serif', size=24)
    ax.text(0.99,
            0.002,
            r'$\mathrm{%s}$' % ion,
            family='serif',
            size=24,
            horizontalalignment='right')

    ax.set_xticklabels(ax.get_xticks(), family='serif', size=20)
    ax.set_yticklabels(ax.get_yticks(), family='serif', size=20)
    ax2.set_yticklabels(ax2.get_yticks(), family='serif', size=20)

    ax.set_xlabel('at% {}'.format(ion), family='serif', size=28)
    ax.set_ylabel(r'$\mathrm{E_F\/(eV/atom)}$', size=28)

    ax2.yaxis.set_label_position('right')
    if ion == 'Li':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Li/Li^+\/(V)}$', size=28)
    elif ion == 'Mg':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Mg/Mg^{2+}\/(V)}$', size=28)
    elif ion == 'Al':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Al/Al^{3+}\/(V)}$', size=28)

    plt.savefig('{}_hull.{}'.format(ion, fmt), transparent=True)
Exemple #10
0
def plot_band_alignments(directories, run_type='PBE', fmt='pdf'):
    """
    Plot CBM's and VBM's of all compounds together, relative to the band
    edges of H2O.

    Args:
        directories (list): list of the directory paths for materials
            to include in the plot.
        run_type (str): 'PBE' or 'HSE', so that the function knows which
            subdirectory to go into (pbe_bands or hse_bands).
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    if run_type == 'HSE':
        subdirectory = 'hse_bands'
    else:
        subdirectory = 'pbe_bands'

    band_gaps = {}

    for directory in directories:
        if is_converged('{}/{}'.format(directory, subdirectory)):
            os.chdir('{}/{}'.format(directory, subdirectory))
            band_structure = Vasprun('vasprun.xml').get_band_structure()
            band_gap = band_structure.get_band_gap()

            # Vacuum level energy from LOCPOT.
            locpot = Locpot.from_file('LOCPOT')
            evac = max(locpot.get_average_along_axis(2))

            if not band_structure.is_metal():
                is_direct = band_gap['direct']
                cbm = band_structure.get_cbm()
                vbm = band_structure.get_vbm()

            else:
                cbm = None
                vbm = None
                is_metal = True
                is_direct = False

            band_gaps[directory] = {
                'CBM': cbm,
                'VBM': vbm,
                'Direct': is_direct,
                'Metal': band_structure.is_metal(),
                'E_vac': evac
            }

            os.chdir('../../')

    ax = plt.figure(figsize=(16, 10)).gca()

    x_max = len(band_gaps) * 1.315
    ax.set_xlim(0, x_max)

    # Rectangle representing band edges of water.
    ax.add_patch(
        plt.Rectangle((0, -5.67),
                      height=1.23,
                      width=len(band_gaps),
                      facecolor='#00cc99',
                      linewidth=0))
    ax.text(len(band_gaps) * 1.01,
            -4.44,
            r'$\mathrm{H+/H_2}$',
            size=20,
            verticalalignment='center')
    ax.text(len(band_gaps) * 1.01,
            -5.67,
            r'$\mathrm{O_2/H_2O}$',
            size=20,
            verticalalignment='center')

    x_ticklabels = []

    y_min = -8

    i = 0

    # Nothing but lies.
    are_directs, are_indirects, are_metals = False, False, False

    for compound in [cpd for cpd in directories if cpd in band_gaps]:
        x_ticklabels.append(compound)

        # Plot all energies relative to their vacuum level.
        evac = band_gaps[compound]['E_vac']
        if band_gaps[compound]['Metal']:
            cbm = -8
            vbm = -2
        else:
            cbm = band_gaps[compound]['CBM']['energy'] - evac
            vbm = band_gaps[compound]['VBM']['energy'] - evac

        # Add a box around direct gap compounds to distinguish them.
        if band_gaps[compound]['Direct']:
            are_directs = True
            linewidth = 5
        elif not band_gaps[compound]['Metal']:
            are_indirects = True
            linewidth = 0

        # Metals are grey.
        if band_gaps[compound]['Metal']:
            are_metals = True
            linewidth = 0
            color_code = '#404040'
        else:
            color_code = '#002b80'

        # CBM
        ax.add_patch(
            plt.Rectangle((i, cbm),
                          height=-cbm,
                          width=0.8,
                          facecolor=color_code,
                          linewidth=linewidth,
                          edgecolor="#e68a00"))
        # VBM
        ax.add_patch(
            plt.Rectangle((i, y_min),
                          height=(vbm - y_min),
                          width=0.8,
                          facecolor=color_code,
                          linewidth=linewidth,
                          edgecolor="#e68a00"))

        i += 1

    ax.set_ylim(y_min, 0)

    # Set tick labels
    ax.set_xticks([n + 0.4 for n in range(i)])
    ax.set_xticklabels(x_ticklabels, family='serif', size=20, rotation=60)
    ax.set_yticklabels(ax.get_yticks(), family='serif', size=20)

    # Add a legend
    height = y_min
    if are_directs:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#002b80',
                          edgecolor='#e68a00',
                          linewidth=5))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Direct',
                family='serif',
                color='w',
                size=20,
                horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_indirects:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#002b80',
                          linewidth=0))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Indirect',
                family='serif',
                size=20,
                color='w',
                horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_metals:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#404040',
                          linewidth=0))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Metal',
                family='serif',
                size=20,
                color='w',
                horizontalalignment='center',
                verticalalignment='center')

    # Who needs axes?
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')

    ax.set_ylabel('eV', family='serif', size=24)

    plt.savefig('band_alignments.{}'.format(fmt), transparent=True)
    plt.close()
Exemple #11
0
def plot_ion_hull_and_voltages(ion, fmt='pdf'):
    """
    Plots the phase diagram between the pure material and pure ion,
    Connecting the points on the convex hull of the phase diagram.

    Args:
        ion (str): name of atom that was intercalated, e.g. 'Li'.
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    # Calculated with the relax() function in
    # twod_materials.stability.startup. If you are using other input
    # parameters, you need to recalculate these values!
    ion_ev_fu = {'Li': -1.7540797, 'Mg': -1.31976062, 'Al': -3.19134607}

    energy = Vasprun('vasprun.xml').final_energy
    composition = Structure.from_file('POSCAR').composition

    # Get the formula (with single-digit integers preceded by a '_').
    twod_material = list(composition.reduced_formula)
    twod_formula = str()
    for i in range(len(twod_material)):
        try:
            int(twod_material[i])
            twod_formula += '_{}'.format(twod_material[i])
        except:
            twod_formula += twod_material[i]

    twod_ev_fu = energy / composition.get_reduced_composition_and_factor()[1]

    data = [(0, 0, 0, twod_ev_fu)]  # (at% ion, n_ions, E_F, abs_energy)
    for directory in [
            dir for dir in os.listdir(os.getcwd()) if os.path.isdir(dir)]:
        if is_converged(directory):
            os.chdir(directory)
            energy = Vasprun('vasprun.xml').final_energy
            composition = Structure.from_file('POSCAR').composition
            ion_fraction = composition.get_atomic_fraction(ion)

            no_ion_comp_dict = composition.as_dict()
            no_ion_comp_dict.update({ion: 0})
            no_ion_comp = Composition.from_dict(no_ion_comp_dict)

            n_twod_fu = no_ion_comp.get_reduced_composition_and_factor()[1]
            n_ions = composition[ion] / n_twod_fu

            E_F = (
                (energy - composition[ion] * ion_ev_fu[ion]
                 - twod_ev_fu * n_twod_fu)
                / composition.num_atoms
            )

            data.append((ion_fraction, n_ions, E_F, energy / n_twod_fu))

            os.chdir('../')
    data.append((1, 1, 0, ion_ev_fu[ion]))  # Pure ion

    sorted_data = sorted(data, key=operator.itemgetter(0))

    # Determine which compositions are on the convex hull.
    energy_profile = np.array([[item[0], item[2]]
                                for item in sorted_data if item[2] <= 0])
    hull = ConvexHull(energy_profile)
    convex_ion_fractions = [
        energy_profile[vertex, 0] for vertex in hull.vertices]
    convex_formation_energies = [
        energy_profile[vertex, 1] for vertex in hull.vertices]

    convex_ion_fractions.append(convex_ion_fractions.pop(0))
    convex_formation_energies.append(convex_formation_energies.pop(0))

    concave_ion_fractions = [
        pt[0] for pt in sorted_data if pt[0] not in convex_ion_fractions]
    concave_formation_energies = [
        pt[2] for pt in sorted_data if pt[0] not in convex_ion_fractions]

    voltage_profile = []
    j = 0
    k = 0
    for i in range(1, len(sorted_data) - 1):
        if sorted_data[i][0] in convex_ion_fractions:
            voltage = -(
                ((sorted_data[i][3] - sorted_data[k][3])
                 - (sorted_data[i][1] - sorted_data[k][1]) * ion_ev_fu[ion])
                / (sorted_data[i][1] - sorted_data[k][1])
                )
            voltage_profile.append((sorted_data[k][0], voltage))
            voltage_profile.append((sorted_data[i][0], voltage))
            j += 1
            k = i

    voltage_profile.append((voltage_profile[-1][0], 0))
    voltage_profile.append((1, 0))

    voltage_profile_x = [tup[0] for tup in voltage_profile]
    voltage_profile_y = [tup[1] for tup in voltage_profile]

    ax = plt.figure(figsize=(14, 10)).gca()

    ax.plot([0, 1], [0, 0], 'k--')
    ax.plot(convex_ion_fractions, convex_formation_energies, 'b-', marker='o',
            markersize=12, markeredgecolor='none')
    ax.plot(concave_ion_fractions, concave_formation_energies, 'r', marker='o',
            linewidth=0, markersize=12, markeredgecolor='none')

    ax2 = ax.twinx()
    ax2.plot(voltage_profile_x, voltage_profile_y, 'k-', marker='o')

    ax.text(0, 0.002, r'$\mathrm{%s}$' % twod_formula, family='serif', size=24)
    ax.text(0.99, 0.002, r'$\mathrm{%s}$' % ion, family='serif', size=24,
            horizontalalignment='right')

    ax.set_xticklabels(ax.get_xticks(), family='serif', size=20)
    ax.set_yticklabels(ax.get_yticks(), family='serif', size=20)
    ax2.set_yticklabels(ax2.get_yticks(), family='serif', size=20)

    ax.set_xlabel('at% {}'.format(ion), family='serif', size=28)
    ax.set_ylabel(r'$\mathrm{E_F\/(eV/atom)}$', size=28)

    ax2.yaxis.set_label_position('right')
    if ion == 'Li':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Li/Li^+\/(V)}$', size=28)
    elif ion == 'Mg':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Mg/Mg^{2+}\/(V)}$', size=28)
    elif ion == 'Al':
        ax2.set_ylabel(r'$\mathrm{Potential\/vs.\/Al/Al^{3+}\/(V)}$', size=28)

    plt.savefig('{}_hull.{}'.format(ion, fmt), transparent=True)
Exemple #12
0
def plot_band_alignments(directories, run_type="PBE", fmt="pdf"):
    """
    Plot CBM's and VBM's of all compounds together, relative to the band
    edges of H2O.

    Args:
        directories (list): list of the directory paths for materials
            to include in the plot.
        run_type (str): 'PBE' or 'HSE', so that the function knows which
            subdirectory to go into (pbe_bands or hse_bands).
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    if run_type == "HSE":
        subdirectory = "hse_bands"
    else:
        subdirectory = "pbe_bands"

    band_gaps = {}

    for directory in directories:
        if is_converged("{}/{}".format(directory, subdirectory)):
            os.chdir("{}/{}".format(directory, subdirectory))
            band_structure = Vasprun("vasprun.xml").get_band_structure()
            band_gap = band_structure.get_band_gap()

            # Vacuum level energy from LOCPOT.
            locpot = Locpot.from_file("LOCPOT")
            evac = max(locpot.get_average_along_axis(2))

            try:
                is_metal = False
                is_direct = band_gap["direct"]
                cbm = band_structure.get_cbm()
                vbm = band_structure.get_vbm()

            except AttributeError:
                cbm = None
                vbm = None
                is_metal = True
                is_direct = False

            band_gaps[directory] = {"CBM": cbm, "VBM": vbm, "Direct": is_direct, "Metal": is_metal, "E_vac": evac}

            os.chdir("../../")

    ax = plt.figure(figsize=(16, 10)).gca()

    x_max = len(band_gaps) * 1.315
    ax.set_xlim(0, x_max)

    # Rectangle representing band edges of water.
    ax.add_patch(plt.Rectangle((0, -5.67), height=1.23, width=len(band_gaps), facecolor="#00cc99", linewidth=0))
    ax.text(len(band_gaps) * 1.01, -4.44, r"$\mathrm{H+/H_2}$", size=20, verticalalignment="center")
    ax.text(len(band_gaps) * 1.01, -5.67, r"$\mathrm{O_2/H_2O}$", size=20, verticalalignment="center")

    x_ticklabels = []

    y_min = -8

    i = 0

    # Nothing but lies.
    are_directs, are_indirects, are_metals = False, False, False

    for compound in [cpd for cpd in directories if cpd in band_gaps]:
        x_ticklabels.append(compound)

        # Plot all energies relative to their vacuum level.
        evac = band_gaps[compound]["E_vac"]
        if band_gaps[compound]["Metal"]:
            cbm = -8
            vbm = -2
        else:
            cbm = band_gaps[compound]["CBM"]["energy"] - evac
            vbm = band_gaps[compound]["VBM"]["energy"] - evac

        # Add a box around direct gap compounds to distinguish them.
        if band_gaps[compound]["Direct"]:
            are_directs = True
            linewidth = 5
        elif not band_gaps[compound]["Metal"]:
            are_indirects = True
            linewidth = 0

        # Metals are grey.
        if band_gaps[compound]["Metal"]:
            are_metals = True
            linewidth = 0
            color_code = "#404040"
        else:
            color_code = "#002b80"

        # CBM
        ax.add_patch(
            plt.Rectangle(
                (i, cbm), height=-cbm, width=0.8, facecolor=color_code, linewidth=linewidth, edgecolor="#e68a00"
            )
        )
        # VBM
        ax.add_patch(
            plt.Rectangle(
                (i, y_min),
                height=(vbm - y_min),
                width=0.8,
                facecolor=color_code,
                linewidth=linewidth,
                edgecolor="#e68a00",
            )
        )

        i += 1

    ax.set_ylim(y_min, -2)

    # Set tick labels
    ax.set_xticks([n + 0.4 for n in range(i)])
    ax.set_xticklabels(x_ticklabels, family="serif", size=20, rotation=60)
    ax.set_yticklabels(ax.get_yticks(), family="serif", size=20)

    # Add a legend
    height = y_min
    if are_directs:
        ax.add_patch(
            plt.Rectangle(
                (i * 1.165, height),
                width=i * 0.15,
                height=(-y_min * 0.1),
                facecolor="#002b80",
                edgecolor="#e68a00",
                linewidth=5,
            )
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Direct",
            family="serif",
            color="w",
            size=20,
            horizontalalignment="center",
            verticalalignment="center",
        )
        height -= y_min * 0.15

    if are_indirects:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height), width=i * 0.15, height=(-y_min * 0.1), facecolor="#002b80", linewidth=0)
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Indirect",
            family="serif",
            size=20,
            color="w",
            horizontalalignment="center",
            verticalalignment="center",
        )
        height -= y_min * 0.15

    if are_metals:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height), width=i * 0.15, height=(-y_min * 0.1), facecolor="#404040", linewidth=0)
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Metal",
            family="serif",
            size=20,
            color="w",
            horizontalalignment="center",
            verticalalignment="center",
        )

    # Who needs axes?
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.spines["bottom"].set_visible(False)
    ax.spines["left"].set_visible(False)
    ax.yaxis.set_ticks_position("left")
    ax.xaxis.set_ticks_position("bottom")

    ax.set_ylabel("eV", family="serif", size=24)

    plt.savefig("band_alignments.{}".format(fmt), transparent=True)
    plt.close()
    competing_species = get_competing_species(directories)
    relax_competing_species(competing_species)

    for directory in directories:
        os.chdir(directory)
        relax()
        os.chdir('../')

    loop = True
    while loop:
        print('>> Checking convergence')
        finished_2d, finished_3d = [], []

        for directory in directories:
            if is_converged(directory):
                finished_2d.append(directory)
        for directory in competing_species:
            if is_converged('all_competitors/{}'.format(directory[0])):
                finished_3d.append(directory[0])

        if len(finished_2d + finished_3d) == len(
                directories + competing_species):
            print('>> Plotting hull distances')
            plot_hull_distances(get_hull_distances(finished_2d))
            loop = False
        else:
            print('>> Not all directories converged ({}/{})'.format(
                len(finished_2d + finished_3d), len(
                    directories + competing_species)))
        converged[directory] = False
        run_friction_calculations()
        os.chdir('../')

    loop = True

    while loop:
        time.sleep(INTERVAL)

        loop = False

        for directory in directories:
            os.chdir(directory)
            converged[directory] = True
            for subdirectory in [
                    dir for dir in os.listdir(os.getcwd())
                    if os.path.isdir(dir)
            ]:
                if not is_converged(subdirectory):
                    converged[directory] = False
                    break

            if not converged[directory]:
                print('>> Not all directories converged')
                loop = True

            print('>> Plotting gamma surface for {}'.format(directory))
            plot_gamma_surface()

            os.chdir('../')