Source code for pyprocar.scripts.scriptFermiHandler

__author__ = "Logan Lang"
__maintainer__ = "Logan Lang"
__email__ = "lllang@mix.wvu.edu"
__date__ = "March 31, 2020"

import os
import sys
from typing import List, Tuple
import yaml
import numpy as np

from pyprocar.plotter import FermiDataHandler, FermiVisualizer
from pyprocar.cfg import ConfigFactory, ConfigManager, PlotType
from pyprocar.utils import ROOT, LOGGER

from .. import io

np.set_printoptions(threshold=sys.maxsize)

def find_nearest(array, value):
    array = np.asarray(array)
    idx = (np.abs(array - value)).argmin()
    return idx

[docs]class FermiHandler:
[docs] def __init__(self, code:str, dirname:str="", fermi:float=None, repair:bool=False, apply_symmetry:bool=True, ): """ This class handles the plotting of the fermi surface. Initialize by specifying the code and directory name where the data is stored. Then call one of the plotting methods provided. Parameters ---------- code : str The code name dirname : str, optional the directory name where the calculation is, by default "" fermi : float, optional The fermi energy. This will overide the default fermi value used found in the given directory, by default None repair : bool, optional Boolean to repair the PROCAR file, by default False apply_symmetry : bool, optional Boolean to apply symmetry to the fermi sruface. This is used when only symmetry reduced kpoints used in the calculation, by default True """ self.default_config = ConfigFactory.create_config(PlotType.FERMI_SURFACE_3D) self.code = code self.dirname=dirname self.repair = repair self.apply_symmetry = apply_symmetry parser = io.Parser(code = code, dir = dirname) self.ebs = parser.ebs if fermi is None: self.e_fermi=self.ebs.efermi print(""" WARNING : Fermi Energy not set! Set `fermi={value}`. By default, using fermi energy found in given directory. --------------------------------------------------------------------------------------------------------------- """ ) else: self.e_fermi=fermi self.structure = parser.structure if self.structure.rotations is not None: self.ebs.ibz2fbz(self.structure.rotations) modes=["plain","parametric","spin_texture", "overlay" ] props=["fermi_speed","fermi_velocity","harmonic_effective_mass"] modes_txt=' , '.join(modes) props_txt=' , '.join(props) self.notification_message=f""" -------------------------------------------------------- There are additional plot options that are defined in a configuration file. You can change these configurations by passing the keyword argument to the function To print a list of plot options set print_plot_opts=True Here is a list modes : {modes_txt} Here is a list of properties: {props_txt} -------------------------------------------------------- """
[docs] def plot_fermi_surface(self, mode, bands=None, atoms=None, orbitals=None, spins=None, spin_texture=False, show=True, save_2d=None, save_gif=None, save_mp4=None, save_3d=None, print_plot_opts:bool=False, **kwargs): """A method to plot the 3d fermi surface Parameters ---------- mode : str The mode to calculate bands : List[int], optional A list of band indexes to plot, by default None atoms : List[int], optional A list of atoms, by default None orbitals : List[int], optional A list of orbitals, by default None spins : List[int], optional A list of spins, by default None spin_texture : bool, optional Boolean to plot spin texture, by default False print_plot_opts: bool, optional Boolean to print the plotting options """ config=ConfigManager.merge_configs(self.default_config, kwargs) config=ConfigManager.merge_config(config, 'mode', mode) print(self.notification_message) if print_plot_opts: self.print_default_settings() # Process the data self.data_handler = FermiDataHandler(self.ebs, config) self.data_handler.process_data( bands=bands, atoms=atoms, orbitals=orbitals, spins=spins, spin_texture=spin_texture) fermi_surface=self.data_handler.get_surface_data(fermi=self.e_fermi, property_name=config.property_name) visualizer = FermiVisualizer(self.data_handler, config) visualizer.add_brillouin_zone(fermi_surface) visualizer.add_texture( fermi_surface, scalars_name=visualizer.data_handler.scalars_name, vector_name=visualizer.data_handler.vector_name) visualizer.add_surface(fermi_surface) if mode != "plain" or spin_texture: visualizer.add_scalar_bar(name=visualizer.data_handler.scalars_name) visualizer.add_axes() visualizer.set_background_color() # save and showing setting if show and save_gif is None and save_mp4 is None and save_3d is None: visualizer.show(filename=save_2d) if save_gif is not None: visualizer.save_gif(filename=save_gif) if save_mp4: visualizer.save_gif(filename=save_mp4) if save_3d: visualizer.save_mesh(filename=save_3d,surface=fermi_surface)
[docs] def plot_fermi_isoslider(self, mode, iso_range: float=None, iso_surfaces: int=None, iso_values: List[float]=None, bands=None, atoms=None, orbitals=None, spins=None, spin_texture=False, show=True, save_2d=None, print_plot_opts:bool=False, **kwargs): """A method to plot the 3d fermi surface Parameters ---------- iso_range : float A range of energies the slide will go through iso_surfaces : int Ther number of fermi sruface to calculate on the range iso_range : List[int], optional A list of of energies the slider will go through mode : str The mode to calculate bands : List[int], optional A list of band indexes to plot, by default None atoms : List[int], optional A list of atoms, by default None orbitals : List[int], optional A list of orbitals, by default None spins : List[int], optional A list of spins, by default None spin_texture : bool, optional Boolean to plot spin texture, by default False print_plot_opts: bool, optional Boolean to print the plotting options """ config=ConfigManager.merge_configs(self.default_config, kwargs) config=ConfigManager.merge_config(config, 'mode', mode) print(self.notification_message) if print_plot_opts: self.print_default_settings() # Process the data self.data_handler = FermiDataHandler(self.ebs,config) self.data_handler.process_data(bands=bands, atoms=atoms, orbitals=orbitals, spins=spins, spin_texture=spin_texture) if iso_surfaces is not None: energy_values = np.linspace(self.e_fermi-iso_range/2,self.e_fermi+iso_range/2,iso_surfaces) if iso_values: energy_values=iso_values e_surfaces = [] for e_value in energy_values: surface=self.data_handler.get_surface_data(property_name=config.property_name,fermi=e_value) e_surfaces.append(surface) LOGGER.debug(f'___Getting surface for {e_value}__') LOGGER.debug(f'Surface shape: {surface.points.shape}') LOGGER.debug(f'Surface shape: {surface.point_data}') LOGGER.debug(f'Surface shape: {surface.point_data}') visualizer = FermiVisualizer(self.data_handler,config) visualizer.add_isoslider(e_surfaces,energy_values) # save and showing setting if show: visualizer.show(filename=save_2d)
[docs] def create_isovalue_gif(self, mode, iso_range: float=None, iso_surfaces: int=None, iso_values: List[float]=None, bands=None, atoms=None, orbitals=None, spins=None, spin_texture=False, save_gif=None, print_plot_opts:bool=False, **kwargs): """A method to plot the 3d fermi surface Parameters ---------- iso_range : float A range of energies the slide will go through iso_surfaces : int Ther number of fermi sruface to calculate on the range iso_range : List[int], optional A list of of energies the slider will go through mode : str The mode to calculate bands : List[int], optional A list of band indexes to plot, by default None atoms : List[int], optional A list of atoms, by default None orbitals : List[int], optional A list of orbitals, by default None spins : List[int], optional A list of spins, by default None spin_texture : bool, optional Boolean to plot spin texture, by default False print_plot_opts: bool, optional Boolean to print the plotting options """ config=ConfigManager.merge_configs(self.default_config, kwargs) config=ConfigManager.merge_config(config, 'mode', mode) print(self.notification_message) if print_plot_opts: self.print_default_settings() # Process the data self.data_handler = FermiDataHandler(self.ebs,config) self.data_handler.process_data(bands=bands, atoms=atoms, orbitals=orbitals, spins=spins, spin_texture=spin_texture) if iso_surfaces is not None: energy_values = np.linspace(self.e_fermi-iso_range/2,self.e_fermi+iso_range/2,iso_surfaces) if iso_values: energy_values=iso_values e_surfaces = [] for e_value in energy_values: surface=self.data_handler.get_surface_data(fermi=e_value,property_name=config.property_name) LOGGER.debug(f'___Getting surface for {e_value}__') LOGGER.debug(f'Surface shape: {surface.points.shape}') LOGGER.debug(f'Surface shape: {surface.point_data}') LOGGER.debug(f'Surface shape: {surface.point_data}') e_surfaces.append(surface) visualizer = FermiVisualizer(self.data_handler,config) visualizer.add_isovalue_gif(e_surfaces,energy_values,save_gif)
[docs] def plot_fermi_cross_section(self, mode, slice_normal: Tuple[float,float,float]=(1,0,0), slice_origin: Tuple[float,float,float]=(0,0,0), bands=None, atoms=None, orbitals=None, spins=None, spin_texture=False, show=True, save_2d=None, save_2d_slice=None, print_plot_opts:bool=False, **kwargs): """A method to plot the 3d fermi surface Parameters ---------- mode : str The mode to calculate bands : List[int], optional A list of band indexes to plot, by default None atoms : List[int], optional A list of atoms, by default None orbitals : List[int], optional A list of orbitals, by default None spins : List[int], optional A list of spins, by default None spin_texture : bool, optional Boolean to plot spin texture, by default False print_plot_opts: bool, optional Boolean to print the plotting options """ config=ConfigManager.merge_configs(self.default_config, kwargs) config=ConfigManager.merge_config(config, 'mode', mode) print(self.notification_message) if print_plot_opts: self.print_default_settings() # Process the data self.data_handler = FermiDataHandler(self.ebs,config) self.data_handler.process_data(bands=bands, atoms=atoms, orbitals=orbitals, spins=spins, spin_texture=spin_texture) surface=self.data_handler.get_surface_data(fermi=self.e_fermi,property_name=config.property_name) visualizer = FermiVisualizer(self.data_handler,config) visualizer.add_slicer(surface,show,save_2d,save_2d_slice,slice_normal,slice_origin)
[docs] def plot_fermi_cross_section_box_widget(self, mode, slice_normal: Tuple[float,float,float]=(1,0,0), slice_origin: Tuple[float,float,float]=(0,0,0), bands=None, atoms=None, orbitals=None, spins=None, spin_texture=False, show=True, save_2d=None, save_2d_slice=None, print_plot_opts:bool=False, **kwargs): """A method to plot the 3d fermi surface Parameters ---------- mode : str The mode to calculate bands : List[int], optional A list of band indexes to plot, by default None atoms : List[int], optional A list of atoms, by default None orbitals : List[int], optional A list of orbitals, by default None spins : List[int], optional A list of spins, by default None spin_texture : bool, optional Boolean to plot spin texture, by default False print_plot_opts: bool, optional Boolean to print the plotting options """ config=ConfigManager.merge_configs(self.default_config, kwargs) config=ConfigManager.merge_config(config, 'mode', mode) print(self.notification_message) if print_plot_opts: self.print_default_settings() # Process the data self.data_handler = FermiDataHandler(self.ebs,config) self.data_handler.process_data(bands=bands, atoms=atoms, orbitals=orbitals, spins=spins, spin_texture=spin_texture) surface=self.data_handler.get_surface_data(fermi=self.e_fermi,property_name=config.property_name) print("Bands being used if bands=None: ", surface.band_isosurface_index_map) visualizer = FermiVisualizer(self.data_handler,config) visualizer.add_box_slicer(surface,show,save_2d,save_2d_slice, slice_normal, slice_origin)
[docs] def print_default_settings(self): """ Prints all the configuration settings with their current values. """ for key, value in self.default_config.as_dict().items(): print(f'{key}: {value}')