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
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
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
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, )
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, )
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))
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))