Esempio n. 1
0
def get_symmetrized_strain_mapping(
    bulk_structure,
    strain_mapping,
    symprec=defaults["symprec"],
    symprec_deformation=defaults["symprec"] / 100,
):
    # get symmetry operations of the bulk structure
    frac_ops = get_symmops(bulk_structure, symprec=symprec)

    for strain, calc in strain_mapping.items():
        # expand band structure to cover full brillouin zone, otherwise rotation won't
        # include all necessary points
        # default symprec is lower otherwise the strain will not be noticed
        calc["bandstructure"] = expand_bandstructure(
            calc["bandstructure"], symprec=symprec_deformation)

    for strain, calc in strain_mapping.items():
        k_old = get_kpoints_from_bandstructure(calc["bandstructure"],
                                               sort=True)
        k_old = kpoints_to_first_bz(k_old)

        for frac_op in frac_ops:
            # apply cartesian transformation matrix from the right side
            # hence the transpose
            r_cart = similarity_transformation(bulk_structure.lattice.matrix.T,
                                               frac_op.rotation_matrix.T)
            tstrain = strain.rotate(r_cart)

            independent = tstrain.get_deformation_matrix().is_independent(
                _mapping_tol)
            if independent and tstrain not in strain_mapping:
                rband = rotate_bandstructure(calc["bandstructure"], frac_op)

                k_new = get_kpoints_from_bandstructure(rband, sort=True)
                k_new = kpoints_to_first_bz(k_new)

                # check whether k-points match; if no match found this indicates
                # that the real and reciprocal lattice have different symmetries
                kpoints_match = np.max(np.linalg.norm(k_old - k_new,
                                                      axis=1)) < 0.001

                if kpoints_match:
                    tcalc = {
                        "reference": calc["reference"],
                        "bandstructure": rband
                    }
                    strain_mapping[tstrain] = tcalc

    return strain_mapping
Esempio n. 2
0
def reciprocal_lattice_match(band_structure: BandStructure,
                             symprec=defaults["symprec"],
                             tol=ktol):
    """Test whether reciprocal lattice and k-point mesh are consistent.

    I.e., do all the reciprocal space group operations result preserve the k-point mesh.
    """
    round_dp = int(np.log10(1 / tol))

    kpoints = get_kpoints_from_bandstructure(band_structure)
    kpoints = expand_kpoints(band_structure.structure,
                             kpoints,
                             symprec=symprec,
                             verbose=False)
    rotations, _, _ = get_reciprocal_point_group_operations(
        band_structure.structure, symprec=symprec)
    kpoints = kpoints_to_first_bz(kpoints.round(round_dp))
    kpoints = sort_kpoints(kpoints)

    for rotation in rotations:
        rot_kpoints = np.dot(rotation, kpoints.T).T
        rot_kpoints = kpoints_to_first_bz(rot_kpoints).round(round_dp)
        rot_kpoints = sort_kpoints(rot_kpoints)
        kpoints_match = np.max(np.linalg.norm(rot_kpoints - kpoints,
                                              axis=1)) < 0.01

        if not kpoints_match:
            return False

    return True
Esempio n. 3
0
def get_strain_deformation_potential(
    strain,
    bulk_bandstructure,
    deformation_bandstructure,
    bulk_reference,
    deformation_reference,
):
    strain = strain.round(5)
    flat_strain = strain.ravel()
    strain_amount = flat_strain[np.abs(flat_strain).argmax()]
    ref_diff = bulk_reference - deformation_reference

    kpoints = get_kpoints_from_bandstructure(bulk_bandstructure)
    mesh, is_shifted = get_mesh_from_band_structure(bulk_bandstructure)
    indices_to_keep = get_kpoint_indices(kpoints, mesh, is_shifted=is_shifted)

    deform_kpoints = get_kpoints_from_bandstructure(deformation_bandstructure)
    deform_indices = get_kpoint_indices(deform_kpoints,
                                        mesh,
                                        is_shifted=is_shifted)

    if not set(indices_to_keep).issubset(set(deform_indices)):
        raise RuntimeError(
            "Deformation band structure doesn't contain the same k-points "
            "as the bulk band structure. Try changing symprec.")

    deform_map = np.full(np.max(deform_indices) + 1, -1)
    deform_map[deform_indices] = np.arange(len(deform_indices))
    select_indices = deform_map[indices_to_keep]

    energy_diff = {}
    for spin, spin_origin in bulk_bandstructure.bands.items():
        diff = spin_origin - deformation_bandstructure.bands[
            spin][:, select_indices]
        diff -= ref_diff
        energy_diff[spin] = np.abs(diff / strain_amount)

    return energy_diff
Esempio n. 4
0
def expand_bandstructure(
    bandstructure, symprec=defaults["symprec"], time_reversal=True
):
    kpoints = get_kpoints_from_bandstructure(bandstructure)
    full_kpoints, _, _, _, _, kp_mapping = expand_kpoints(
        bandstructure.structure,
        kpoints,
        symprec=symprec,
        time_reversal=time_reversal,
        return_mapping=True,
    )
    return BandStructure(
        full_kpoints,
        {s: b[:, kp_mapping] for s, b in bandstructure.bands.items()},
        bandstructure.structure.lattice.reciprocal_lattice,
        bandstructure.efermi,
        structure=bandstructure.structure,
    )
Esempio n. 5
0
def rotate_bandstructure(bandstructure: BandStructure, frac_symop: SymmOp):
    """Won't rotate projections..."""
    kpoints = get_kpoints_from_bandstructure(bandstructure)
    recip_rot = frac_symop.rotation_matrix.T
    rot_kpoints = np.dot(recip_rot, kpoints.T).T

    # map to first BZ, use VASP zone boundary convention
    rot_kpoints = kpoints_to_first_bz(rot_kpoints, negative_zone_boundary=False)

    # rotate structure
    structure = bandstructure.structure.copy()
    structure.apply_operation(frac_symop, fractional=True)

    return BandStructure(
        rot_kpoints,
        bandstructure.bands,
        structure.lattice.reciprocal_lattice,
        bandstructure.efermi,
        structure=structure,
    )
Esempio n. 6
0
def read(bulk_folder, deformation_folders, **kwargs):
    """
    Read deformation calculations and extract deformation potentials.
    """
    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
    from pymatgen.util.string import unicodeify_spacegroup

    from amset.constants import defaults
    from amset.deformation.common import get_formatted_tensors
    from amset.deformation.io import parse_calculation, write_deformation_potentials
    from amset.deformation.potentials import (
        calculate_deformation_potentials,
        extract_bands,
        get_strain_mapping,
        get_symmetrized_strain_mapping,
        strain_coverage_ok,
    )
    from amset.electronic_structure.common import get_ibands
    from amset.electronic_structure.kpoints import get_kpoints_from_bandstructure
    from amset.electronic_structure.symmetry import expand_bandstructure

    energy_cutoff = kwargs.pop("energy_cutoff")
    if not energy_cutoff:
        energy_cutoff = defaults["energy_cutoff"]

    zwk_mode = kwargs.pop("zero_weighted_kpoints")
    if not zwk_mode:
        zwk_mode = defaults["zero_weighted_kpoints"]

    symprec = _parse_symprec(kwargs["symprec"])
    symprec_deformation = kwargs["symprec_deformation"]
    click.echo("Reading bulk (undeformed) calculation")
    bulk_calculation = parse_calculation(bulk_folder, zero_weighted_kpoints=zwk_mode)
    bulk_structure = bulk_calculation["bandstructure"].structure

    deformation_calculations = []
    for deformation_folder in deformation_folders:
        click.echo("Reading deformation calculation in {}".format(deformation_folder))
        deformation_calculation = parse_calculation(
            deformation_folder, zero_weighted_kpoints=zwk_mode
        )
        if check_calculation(bulk_calculation, deformation_calculation):
            deformation_calculations.append(deformation_calculation)

    sga = SpacegroupAnalyzer(bulk_structure, symprec=symprec)
    spg_symbol = unicodeify_spacegroup(sga.get_space_group_symbol())
    spg_number = sga.get_space_group_number()
    click.echo("\nSpacegroup: {} ({})".format(spg_symbol, spg_number))

    lattice_match = reciprocal_lattice_match(
        bulk_calculation["bandstructure"], symprec=symprec
    )
    if not lattice_match:
        click.echo(
            "\nWARNING: Reciprocal lattice and k-lattice belong to different\n"
            "         class of lattices. Often results are still useful but\n"
            "         it is recommended to regenerate deformations without\n"
            "         symmetry using: amset deform create --symprec N"
        )

    strain_mapping = get_strain_mapping(bulk_structure, deformation_calculations)
    click.echo("\nFound {} strains:".format(len(strain_mapping)))
    fmt_strain = get_formatted_tensors(strain_mapping.keys())
    click.echo("  - " + "\n  - ".join(fmt_strain))

    strain_mapping = get_symmetrized_strain_mapping(
        bulk_structure,
        strain_mapping,
        symprec=symprec,
        symprec_deformation=symprec_deformation,
    )
    click.echo("\nAfter symmetrization found {} strains:".format(len(strain_mapping)))
    fmt_strain = get_formatted_tensors(strain_mapping.keys())
    click.echo("  - " + "\n  - ".join(fmt_strain))

    if not strain_coverage_ok(list(strain_mapping.keys())):
        click.echo("\nERROR: Strains do not cover full tensor, check calculations")
        sys.exit()

    click.echo("\nCalculating deformation potentials")
    bulk_calculation["bandstructure"] = expand_bandstructure(
        bulk_calculation["bandstructure"], symprec=symprec
    )
    deformation_potentials = calculate_deformation_potentials(
        bulk_calculation, strain_mapping
    )

    print_deformation_summary(bulk_calculation["bandstructure"], deformation_potentials)

    if "bands" in kwargs and kwargs["bands"] is not None:
        ibands = parse_ibands(kwargs["bands"])
    else:
        ibands = get_ibands(energy_cutoff, bulk_calculation["bandstructure"])

    echo_ibands(ibands, bulk_calculation["bandstructure"].is_spin_polarized)
    deformation_potentials = extract_bands(deformation_potentials, ibands)

    kpoints = get_kpoints_from_bandstructure(bulk_calculation["bandstructure"])
    filename = write_deformation_potentials(
        deformation_potentials, kpoints, bulk_structure, filename=kwargs["output"]
    )
    click.echo("\nDeformation potentials written to {}".format(filename))
Esempio n. 7
0
def read(bulk_folder, deformation_folders, **kwargs):
    """
    Read deformation calculations and extract deformation potentials.
    """
    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
    from pymatgen.util.string import unicodeify_spacegroup

    from amset.constants import defaults
    from amset.deformation.common import get_formatted_tensors
    from amset.deformation.io import parse_calculation, write_deformation_potentials
    from amset.electronic_structure.symmetry import expand_bandstructure
    from amset.deformation.potentials import (
        calculate_deformation_potentials,
        extract_bands,
        get_strain_mapping,
        get_symmetrized_strain_mapping,
        strain_coverage_ok,
    )
    from amset.electronic_structure.common import get_ibands
    from amset.electronic_structure.kpoints import get_kpoints_from_bandstructure

    energy_cutoff = kwargs.pop("energy_cutoff")
    if not energy_cutoff:
        energy_cutoff = defaults["energy_cutoff"]

    symprec = _parse_symprec(kwargs["symprec"])
    click.echo("Reading bulk (undeformed) calculation")
    bulk_calculation = parse_calculation(bulk_folder)
    bulk_structure = bulk_calculation["bandstructure"].structure

    deformation_calculations = []
    for deformation_folder in deformation_folders:
        click.echo("Reading deformation calculation in {}".format(deformation_folder))
        deformation_calculation = parse_calculation(deformation_folder)
        if check_calculation(bulk_calculation, deformation_calculation):
            deformation_calculations.append(deformation_calculation)

    sga = SpacegroupAnalyzer(bulk_structure, symprec=symprec)
    spg_symbol = unicodeify_spacegroup(sga.get_space_group_symbol())
    spg_number = sga.get_space_group_number()
    click.echo("\nSpacegroup: {} ({})".format(spg_symbol, spg_number))

    strain_mapping = get_strain_mapping(bulk_structure, deformation_calculations)
    click.echo("\nFound {} strains:".format(len(strain_mapping)))
    fmt_strain = get_formatted_tensors(strain_mapping.keys())
    click.echo("  - " + "\n  - ".join(fmt_strain))

    strain_mapping = get_symmetrized_strain_mapping(
        bulk_structure, strain_mapping, symprec=symprec
    )
    click.echo("\nAfter symmetrization found {} strains:".format(len(strain_mapping)))
    fmt_strain = get_formatted_tensors(strain_mapping.keys())
    click.echo("  - " + "\n  - ".join(fmt_strain))

    if not strain_coverage_ok(list(strain_mapping.keys())):
        click.echo("\nERROR: Strains do not cover full tensor, check calculations")
        sys.exit()

    click.echo("\nCalculating deformation potentials")
    bulk_calculation["bandstructure"] = expand_bandstructure(
        bulk_calculation["bandstructure"], symprec=symprec
    )
    deformation_potentials = calculate_deformation_potentials(
        bulk_calculation, strain_mapping
    )

    print_deformation_summary(bulk_calculation["bandstructure"], deformation_potentials)

    ibands = get_ibands(energy_cutoff, bulk_calculation["bandstructure"])
    echo_ibands(ibands, bulk_calculation["bandstructure"].is_spin_polarized)
    click.echo("")
    deformation_potentials = extract_bands(deformation_potentials, ibands)

    kpoints = get_kpoints_from_bandstructure(bulk_calculation["bandstructure"])
    filename = write_deformation_potentials(
        deformation_potentials, kpoints, bulk_structure, filename=kwargs["output"]
    )
    click.echo("\nDeformation potentials written to {}".format(filename))