# -*- coding: utf-8 -*-"""Created on 2021-06-04@author: Vincent Michaud-Rioux"""fromnptypingimportNDArrayfromtypingimportListimportattrimportmatplotlib.pyplotaspltimportnumpyasnpfromnanotools.baseimportBasefromnanotools.totalenergyimportTotalEnergyfromnanotools.utilsimportdict_converter,to_quantity,Quantity
[docs]@attr.sclassValenceBandMaximum(Base):"""``ValenceBandMaximum`` class. Examples:: from nanotools.totalenergy import TotalEnergy from nanotools.vbm import ValenceBandMaximum as VBM ecalc = TotalEnergy.read("nano_scf_init.json") calc = VBM.from_totalenergy(ecalc) calc.charge_deltas = [0.001] calc.solve() fig = calc.plot_vbm() calc.print_vbm() Attributes: calculators: Stores the calculators for each element of ``charge_deltas``. charge_deltas: Charge variations in electrons. reference_calculator: Total energy calculator. vbm: Valence band maximum. """# input is dictionary with default constructorreference_calculator:TotalEnergy=attr.ib(converter=lambdad:dict_converter(d,TotalEnergy),validator=attr.validators.instance_of(TotalEnergy),)calculators:List[TotalEnergy]=attr.ib(default=[])charge_deltas:NDArray=attr.ib(default=np.array([0.001]),converter=lambdav:np.array(v),)vbm:Quantity=attr.ib(default=None,converter=attr.converters.optional(lambdax:to_quantity(x,"eV")),validator=attr.validators.optional(attr.validators.instance_of(Quantity),),)def__attrs_post_init__(self):ifself.calculatorsisnotNone:calc=[]forsinself.calculators:ifisinstance(s,dict):calc.append(TotalEnergy(**s))elifisinstance(s,TotalEnergy):calc.append(s)else:raiseException("Invalid TotalEnergy calculators.")self.calculators=calc@classmethoddeffrom_totalenergy(cls,totalenergy,**kwargs):ifisinstance(totalenergy,TotalEnergy):passelse:totalenergy=TotalEnergy.read(totalenergy)calc=cls(reference_calculator=totalenergy,**kwargs)returncalcdefget_charge_deltas(self):delta=[self.reference_calculator.system.atoms.valence_charge]delta+=[(delta[0]-d)fordinself.charge_deltas]returndelta[0]-np.array(delta)defget_total_energies(self):etots=[self.reference_calculator.energy.etot]etots+=[c.energy.etotforcinself.calculators]returnQuantity.from_list(etots)defget_vbm(self):ifself.vbmisNone:deltas=self.get_charge_deltas()etots=self.get_total_energies()self.vbm=(etots[0]-etots[1:])/deltas[1:]returnself.vbmdefprint_vbm(self):vbm=self.get_vbm()forv,dinzip(vbm,self.charge_deltas):print("The VBM is = %f%s (delta = %f)"%(v.m,v.u,d))
[docs]defplot_vbm(self,filename=None,show=True):"""Generates a semilogx plot of the VBMs (one for each charge_delta). Args: filename (str, optional): If not None, then the figure is saved to filename. show (bool, optional): If True block and show figure. If False, do not show figure. Returns: fig (:obj:`matplotlib.figure.Figure`): A figure handle. """vbm=self.get_vbm()fig=plt.figure()x=self.charge_deltasy=vbm.mplt.semilogx(x,y,"o-k")plt.xlabel("Delta (e)")plt.ylabel(f"VBM ({vbm.u})")ifshow:plt.show()iffilenameisnotNone:fig.savefig(filename)returnfig
defsolve(self):# make sure units are consistentself.reference_calculator.solve()n=0fordeltainself.charge_deltas:ifdelta<0.0:raiseException("delta must be positive.")calc=self.reference_calculator.copy()calc.system.atoms.valence_charge-=deltacalc.solve(output=f"nano_scf_out_{n}.json")self.calculators.append(calc)self.write("nano_vbm_out.json",units="si")n+=1