def parse_interpolate(args): """Parse command-line arguments for the interpolate subcommand.""" # Check if the output file is writable at this point. Note that this # does not guarantee that it remains so. It is just a "courtesy" check # to avoid a lengthy calculation ending in failure to save. args.directory = os.path.abspath(args.directory) args.output = os.path.abspath(args.output) if not os.path.isdir(args.directory): lexit("the specified directory does not exist") if not is_writable(args.output): lexit("the output file is not writable") # Try and read the input. data = BoltzTraP2.dft.DFTData(args.directory, args.derivatives) # Perform some sanity checks on the energy window specification if args.emin >= args.emax: lexit("zero-width energy window") emin = args.emin + (0. if args.absolute else data.fermi) emax = args.emax + (0. if args.absolute else data.fermi) if emin >= data.fermi or emax <= data.fermi: lexit("the energy window must bracked the Fermi level") info("Nvalence (before BANDANA) =", data.nelect) # Drop bands outside the chosen energy range nemin = data.bandana(emin, emax)[0] info("Nvalence (after BANDANA) =", data.nelect) # Refuse to interpolate to fewer k points than there are in the input nkinput = data.kpoints.shape[0] logging.info( "there are {} irreducible k points in the input".format(nkinput)) if args.multiplier is not None: nktarget = args.multiplier * nkinput else: nktarget = args.kpoints logging.info( "{} irreducible k points have been requested".format(nktarget)) equivalences = BoltzTraP2.sphere.get_equivalences(data.atoms, nktarget) nkoutput = len(equivalences) logging.info( "{} irreducible k points have been generated".format(nkoutput)) if nkoutput < nkinput: lexit("refusing to interpolate to a sparser grid") # Perform the interpolation with TimerContext() as timer: coeffs = BoltzTraP2.fite.fitde3D(data, equivalences, args.nworkers) deltat = timer.get_deltat() info("the interpolation took {:.3g} s".format(deltat)) # Gather the metadata metadata = BoltzTraP2.serialization.gen_bt2_metadata(data, args.derivatives) # Save the result info("about to save the results to", args.output) with TimerContext() as timer: BoltzTraP2.serialization.save_calculation( args.output, data, equivalences, coeffs, metadata) deltat = timer.get_deltat() info("saving the results took {:.3g} s".format(deltat))
def compute_radius(atoms, nkpt, symprec=1e-4): """Estimate the right radius to be passed to Equivalence_builder to obtain the desired number of points. """ lattvec = atoms.get_cell().T vol = abs(np.linalg.det(lattvec)) spg = spglib.get_symmetry_dataset(atoms, symprec) rotations = spg["rotations"].astype(int) nrot = len(rotations) info("{} rotations".format(nrot)) newrotations = set() # Include time-reversal symmetry in the estimate if total inversion of # the axes is not among the symmetries inv = 2 for i in range(nrot): tmp = tuple(tuple(j) for j in rotations[i].tolist()) newrotations.add(tmp) if tmp == ((-1, 0, 0), (0, -1, 0), (0, 0, -1)): inv = 1 nrot = len(newrotations) info(nrot, "unique rotations", "including the inversion" if inv == 1 else "") npoints = nkpt * nrot * inv info(npoints, "total k points in the sphere") radius = (3. / (4. * np.pi) * npoints * vol)**(1. / 3.) return radius
def parse_fermisurface(args): """Parse command-line arguments for the fermisurface subcommand.""" global terminal_colors # Try and load the data from the interpolation step data, equivalences, coeffs, metadata = ( BoltzTraP2.serialization.load_calculation(args.bt2_file)) info("sucessfully loaded " + args.bt2_file) lattvec = data.get_lattvec() # Rebuild the bands with TimerContext() as timer: ebands = BoltzTraP2.fite.getBTPbands(equivalences, coeffs, lattvec, True, args.nworkers)[0] deltat = timer.get_deltat() info("rebuilding the bands took {:.3g} s".format(deltat)) # Print out some useful information if terminal_colors: colors = dict(green=colorama.Fore.GREEN, red=colorama.Fore.RED, bold=colorama.Style.BRIGHT, normal=colorama.Style.RESET_ALL) else: colors = dict(green="", red="", bold="", normal="") print(colors["bold"] + "─" * 72 + colors["normal"]) print("""{bold}Keyboard and mouse interface:{normal} {green}r{normal}: reset camera {green}w{normal}: switch between surface and wireframe mode {green}e{normal}: exit {red}left mouse button{normal}: rotate around in-screen axes {green}CTRL{normal} + {red}left mouse button{normal}: rotate around through-screen axis {red}right mouse button{normal}: zoom {red}middle mouse button{normal}: move / pick a point""".format(**colors)) print(colors["bold"] + "─" * 72 + colors["normal"]) # Call the function in charge of setting up the representation. # The function will only return after the user closes the interface. BoltzTraP2.fermisurface.plot_fermisurface(data, equivalences, ebands, args.mu, edge_thickness=args.thickness)
def parse_plot(args): """Parse command-line arguments for the plotbands subcommand.""" # Try and load the data from the integration step (data, fermi, Tr, mu0, mur, N, sdos, cv, cond, seebeck, kappa, hall, metadata) = BoltzTraP2.serialization.load_results(args.btj_file) info("sucessfully loaded " + args.btj_file) nformulas = data.get_formula_count() # Perform some sanity checks tensors = ("sigma", "S", "kappae", "L", "PF", "RH") components = args.components if args.quantity in tensors: if args.components is None: lexit("{} is a tensor but no components have been specified" .format(args.quantity)) if args.quantity not in tensors: if components: warning(("{} is not a tensor. " "The --components options will have no effect" ).format(args.quantity)) components = [()] elif args.quantity == "RH": if not all(i is None or len(i) == 3 for i in args.components): lexit("component specifications for the Hall tensor" " need three indices") else: if not all(i is None or len(i) == 2 for i in args.components): lexit(("component specifications for {}" " need two indices").format(args.quantity)) # Prepare the abscissas, the third variable and the axis labels if args.abscissa == "T": x = Tr xlabel = r"$T\;\left[\mathrm{K}\right]$" z = mur[::args.subsample] zlabel0 = r"$\mu - \varepsilon_F = {:5g}\;\mathrm{{Ry}}$" else: x = mur - fermi xlabel = r"$\mu - \varepsilon_F\;\left[\mathrm{Ha}\right]$" z = Tr[::args.subsample] zlabel0 = r"$T = {:5g}\;\mathrm{{K}}$" ylabel = dict( cv=r"$c_v\;\left[\mathrm{J\,mol^{-1}\,K^{-1}}\right]$", n=r"$n\;\left[\mathrm{\left|e\right|\,uc^{-1}}\right]$", DOS=r"$\mathrm{DOS}\;\left[\mathrm{uc^{-1}}\right]$", sigma=r"$\sigma^{{\left({}\right)}}/\tau_0\;" r"\left[\mathrm{{ohm^{{-1}}\,m^{{-1}}\," r"s^{{-1}}}}\right]$", S=r"$S^{{\left({}\right)}}\;" + r"\left[\mathrm{{V\,K^{{-1}}}}\right]$", kappae=r"$\kappa_e^{{\left({}\right)}}/\tau_0\;" r"\left[\mathrm{{W\,m^{{-1}}\,K^{{-1}}\,s^{{-1}}}}\right]$", L=r"$L^{{\left({}\right)}}\;" r"\left[\mathrm{{W\,\Omega\,K^{{-2}}}}\right]$", PF=r"$\left(S^2\sigma\right)^{{\left({}\right)}}/ \tau_0" r"\;\left[\mathrm{{W\,m^{{-1}}\,K^{{-2}}\,s^{{-1}}}}\right]$", RH=r"$R_H^{{\left({}\right)}}\;" + r"\left[\mathrm{{m^3\,C^{{-1}}}}\right]$") ylabel0 = ylabel[args.quantity] # Prepare the ordinate if args.quantity == "cv": y = cv * AVOGADRO / nformulas elif args.quantity == "n": y = N + data.nelect elif args.quantity == "DOS": y = sdos elif args.quantity == "sigma": y = cond elif args.quantity == "S": y = seebeck elif args.quantity == "kappae": y = kappa elif args.quantity == "L": L0 = 2.44e-8 y = np.empty_like(cond) for iT in range(y.shape[0]): for imu in range(y.shape[1]): y[iT, imu] = la.solve(cond[iT, imu].T, kappa[iT, imu].T).T / Tr[iT] elif args.quantity == "PF": y = np.empty_like(cond) for iT in range(y.shape[0]): for imu in range(y.shape[1]): y[iT, imu] = (cond[iT, imu] @ seebeck[iT, imu] @ seebeck[iT, imu]) elif args.quantity == "RH": y = hall if args.abscissa == "T": y = y[:, ::args.subsample] else: y = y[::args.subsample, :] # Create the plots for c in components: if args.quantity in tensors: desc = "scalar" if c is None else "".join("xyz" [i] for i in c) ylabel = ylabel0.format(desc) else: ylabel = ylabel0 plt.figure() for iz, zv in enumerate(z): zlabel = zlabel0.format(zv) if args.abscissa == "T": indices = [slice(None, None, None), iz] else: indices = [iz, slice(None, None, None)] if c is None: thisy = np.zeros_like(x) if args.quantity == "RH": complements = ((0, 1, 2), (1, 2, 0), (2, 0, 1)) else: complements = [[i, i] for i in range(3)] for i in complements: tindices = tuple(indices + list(i)) thisy += y[tindices] thisy /= float(len(complements)) else: indices += c indices = tuple(indices) thisy = y[indices] plt.plot(x, thisy, label=zlabel) if args.abscissa == "mu": plt.axvline(x=0., color=PSEUDO_BLACK, lw=2) if args.quantity == "L": plt.axhline(y=L0, color=PSEUDO_BLACK, lw=2) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.legend(loc="best").draggable(True) plt.tight_layout() plt.show()
def parse_plotbands(args): """Parse command-line arguments for the plotbands subcommand.""" # Try and load the data from the interpolation step data, equivalences, coeffs, metadata = ( BoltzTraP2.serialization.load_calculation(args.bt2_file)) lattvec = data.get_lattvec() info("sucessfully loaded " + args.bt2_file) # The second position alargument is first interpreted as a Python literal, # and after parsing it is cast to a NumPy array, which must have the right # dimensions. The special value None directs the parser to split the path # in several parts. try: kpaths = ast.literal_eval(args.kpath) except ValueError: lexit("'{}' cannot be parsed as a Python literal".format(kpaths)) if not isinstance(kpaths, list): lexit("'{}' cannot be parsed as a Python list".format(kpaths)) kpaths = [ list(group) for k, group in itertools.groupby( kpaths, key=lambda x: x is not None) if k ] try: kpaths = [np.array(i, dtype=np.float64) for i in kpaths] for i in kpaths: if i.shape[0] < 2 or i.shape[1] != 3: raise ValueError except ValueError: lexit("the path cannot be interpreted as a set of N x 3" " arrays (with N >= 2") plt.figure() ax = plt.gca() ticks = [] dividers = [] offset = 0. for ikpath, kpath in enumerate(kpaths): ax.set_prop_cycle( color=matplotlib.rcParams["axes.prop_cycle"].by_key()["color"]) info("k path #{}".format(i + 1)) # Generate the explicit point list. kp, dkp, dcl = asekp.bandpath(kpath, data.atoms.cell, args.nkpoints) dkp += offset dcl += offset # Compute the band energies. with TimerContext() as timer: egrid = BoltzTraP2.fite.getBands(kp, equivalences, data.get_lattvec(), coeffs)[0] deltat = timer.get_deltat() info("rebuilding the bands took {:.3g} s".format(deltat)) egrid -= data.fermi # Create the plot nbands = egrid.shape[0] for i in range(nbands): plt.plot(dkp, egrid[i, :], lw=2.) ticks += dcl.tolist() dividers += [dcl[0], dcl[-1]] offset = dkp[-1] ax.set_xticks(ticks) ax.set_xticklabels([]) for d in ticks: plt.axvline(x=d, color=PSEUDO_BLACK, ls="--", lw=.5) for d in dividers: plt.axvline(x=d, color=PSEUDO_BLACK, ls="-", lw=2.) plt.axhline(y=0., color=PSEUDO_BLACK, lw=1.) plt.ylabel(r"$\varepsilon - \varepsilon_F\;\left[\mathrm{Ha}\right]$") plt.tight_layout() plt.show()
def parse_integrate(args): """Parse command-line arguments for the integrate subcommand.""" # If the number of bins is not set by hand, let the code handle it # automatically. try: args.bins except AttributeError: args.bins = None # Try and load the data from the interpolation step data, equivalences, coeffs, metadata = ( BoltzTraP2.serialization.load_calculation(args.bt2_file)) lattvec = data.get_lattvec() info("sucessfully loaded " + args.bt2_file) # Rebuild the bands with TimerContext() as timer: eband, vvband, cband = BoltzTraP2.fite.getBTPbands( equivalences, coeffs, lattvec, True, args.nworkers) deltat = timer.get_deltat() info("rebuilding the bands took {:.3g} s".format(deltat)) # Compute the DOS histogram with TimerContext() as timer: epsilon, dos, vvdos, cdos = BoltzTraP2.bandlib.BTPDOS( eband, vvband, cband, npts=args.bins, scattering_model=args.scattering_model) deltat = timer.get_deltat() info("computing the DOS took {:.3g} s".format(deltat)) info("Number of DOS bins:", epsilon.size) # Refine the estimate of the intrinsic chemical potential at # each temperature. Tr = args.temperature mu0 = np.empty_like(Tr) for iT, T in enumerate(Tr): mu0[iT] = BoltzTraP2.bandlib.refine_mu0(epsilon, dos, data.nelect, T, data.dosweight) # And at 0 K fermi = BoltzTraP2.bandlib.refine_mu0(epsilon, dos, data.nelect, 0., data.dosweight) # Compute the moments of the FD distribution at each point. # Determine the chemical potentials to explore by taking the values of the # energy bins and discarding those that are too close to the edge to give # meaningful results. # 9 * kB * T gives a reduction in fFD of about 1e-4, a relatively # safe margin (although still far from the hard cutoff in bandlib). margin = 9. * BOLTZMANN * Tr.max() mur_indices = np.logical_and(epsilon > epsilon.min() + margin, epsilon < epsilon.max() - margin) mur = epsilon[mur_indices] if mur.size == 0: lexit("the energy window is too narrow") # Get the smooth DOS at each temperature with TimerContext() as timer: sdos = np.empty((Tr.size, epsilon.size)) for iT, T in enumerate(Tr): sdos[iT, :] = BoltzTraP2.bandlib.smoothen_DOS(epsilon, dos, T) sdos = sdos[:, mur_indices] deltat = timer.get_deltat() info("smoothing the DOS took {:.3g} s".format(deltat)) info("Temperatures:", Tr) info("Fermi level from DFT:", data.fermi) info("Refined Fermi level:", fermi) info("Intrinsic chemical potential for each T:", mu0) info("Chemical potentials:", mur) with TimerContext() as timer: cv = BoltzTraP2.bandlib.calc_cv( epsilon, dos, mur, Tr, dosweight=data.dosweight) N, L0, L1, L2, Lm11 = BoltzTraP2.bandlib.fermiintegrals( epsilon, dos, vvdos, mur=mur, Tr=Tr, dosweight=data.dosweight, cdos=cdos) deltat = timer.get_deltat() info("computing the FD moments took {:.3g} s".format(deltat)) # Rescale and combine the moments to get the Onsager transport coefficients vuc = data.atoms.get_volume() * Angstrom**3 L11, seebeck, kappa, hall = BoltzTraP2.bandlib.calc_Onsager_coefficients( L0, L1, L2, mur, Tr, vuc, Lm11) # Save the results to BoltzTraP-style files basefn = os.path.splitext(args.bt2_file)[0] tracefn = basefn + ".trace" info("trace output file:", tracefn) BoltzTraP2.io.save_trace(tracefn, data, Tr, mur, N, sdos, cv, L11, seebeck, kappa, hall) condtensfn = basefn + ".condtens" info("conductivity/seebeck output file:", condtensfn) BoltzTraP2.io.save_condtens(condtensfn, Tr, mur, N, L11, seebeck, kappa) halltensfn = basefn + ".halltens" info("Hall coefficient output file:", halltensfn) BoltzTraP2.io.save_halltens(halltensfn, Tr, mur, N, hall) # Save the results to a single compressed JSON file metadata2 = BoltzTraP2.serialization.gen_btj_metadata( metadata, args.scattering_model) jsonfn = basefn + ".btj" info("JSON output file:", jsonfn) BoltzTraP2.serialization.save_results(jsonfn, data, fermi, Tr, mu0, mur, N, sdos, cv, L11, seebeck, kappa, hall, metadata2)
def plot_fermisurface(data, equivalences, ebands, mu, nworkers=1): """Launch an interactive VTK representation of the Fermi surface. Make sure to check the module-level variable "available" before calling this function. Args: data: a DFTData object equivalences: list of k-point equivalence classes ebands: eband: (nbands, nkpoints) array with the band energies mu: initial value of the energy at which the surface will be plotted, with respect to data.fermi. This can later be changed by the user using an interactive slider. nworkers: number of worker processes to use for the band reconstruction Returns: None. """ lattvec = data.get_lattvec() rlattvec = 2. * np.pi * la.inv(lattvec).T # Obtain the first Brillouin zone as the Voronoi polyhedron of Gamma points = [] for ijk0 in itertools.product(range(5), repeat=3): ijk = [i if i <= 2 else i - 5 for i in ijk0] points.append(rlattvec @ np.array(ijk)) voronoi = scipy.spatial.Voronoi(points) region_index = voronoi.point_region[0] vertex_indices = voronoi.regions[region_index] vertices = voronoi.vertices[vertex_indices, :] # Compute a center and an outward-pointing normal for each of the facets # of the BZ facets = [] for ridge in voronoi.ridge_vertices: if all(i in vertex_indices for i in ridge): facets.append(ridge) centers = [] normals = [] for f in facets: corners = np.array([voronoi.vertices[i, :] for i in f]) center = corners.mean(axis=0) v1 = corners[0, :] for i in range(1, corners.shape[0]): v2 = corners[i, :] prod = np.cross(v1 - center, v2 - center) if not np.allclose(prod, 0.): break if np.dot(center, prod) < 0.: prod = -prod centers.append(center) normals.append(prod) # Get the extent of the regular grid in reciprocal space hdims = np.max(np.abs(np.vstack(equivalences)), axis=0) dims = 2 * hdims + 1 class PointPickerInteractorStyle(vtk.vtkInteractorStyleTrackballCamera): """Custom interaction style enabling the user to pick points on the screen. """ def __init__(self, parent=None): """Simple constructor that adds an observer to the middle mouse button press. """ self.AddObserver("MiddleButtonPressEvent", self.pick_point) def pick_point(self, obj, event): """Get the coordinates of the point selected with the middle mouse button, find the nearest data point, and print its direct coordinates. """ interactor = self.GetInteractor() picker = interactor.GetPicker() pos = interactor.GetEventPosition() picker.Pick( pos[0], pos[1], 0, interactor.GetRenderWindow().GetRenderers().GetFirstRenderer()) picked = np.array(picker.GetPickPosition()) # Move the sphere to the new coordinates and make it visible sphere.SetCenter(*picked.tolist()) sphere_actor.VisibilityOn() picked = la.solve(rlattvec, picked) print("Point picked:", picked) self.OnMiddleButtonDown() # Create the VTK representation of the grid sgrid = vtk.vtkStructuredGrid() sgrid.SetDimensions(*dims) spoints = vtk.vtkPoints() for ijk0 in itertools.product(*(range(0, d) for d in dims)): ijk = [ ijk0[i] if ijk0[i] <= hdims[i] else ijk0[i] - dims[i] for i in range(len(dims)) ] abc = np.array(ijk, dtype=np.float64) / np.array(dims) xyz = rlattvec @ abc spoints.InsertNextPoint(*xyz.tolist()) sgrid.SetPoints(spoints) # Find the shortest distance between points to compute a good # radius for the selector sphere later. dmin = np.infty for i in range(3): abc = np.zeros(3) abc[i] = 1. / dims[i] xyz = rlattvec @ abc dmin = min(dmin, la.norm(xyz)) ebands -= data.fermi emax = ebands.max(axis=1) emin = ebands.min(axis=1) # Remove all points outside the BZ for i, ijk0 in enumerate(itertools.product(*(range(0, d) for d in dims))): ijk = [ ijk0[j] if ijk0[j] <= hdims[j] else ijk0[j] - dims[j] for j in range(len(dims)) ] abc = np.array(ijk, dtype=np.float64) / np.array(dims) xyz = rlattvec @ abc for c, n in zip(centers, normals): if np.dot(xyz - c, n) > 0.: ebands[:, i] = np.nan break # Create a 2D chemical potential slider slider = vtk.vtkSliderRepresentation2D() slider.SetMinimumValue(emin.min()) slider.SetMaximumValue(emax.max()) slider.SetValue(mu) slider.SetTitleText("Chemical potential") slider.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay() slider.GetPoint1Coordinate().SetValue(0.1, 0.9) slider.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay() slider.GetPoint2Coordinate().SetValue(0.9, 0.9) slider.GetTubeProperty().SetColor(*colors.hex2color("#2e3436")) slider.GetSliderProperty().SetColor(*colors.hex2color("#a40000")) slider.GetCapProperty().SetColor(*colors.hex2color("#babdb6")) slider.GetSelectedProperty().SetColor(*colors.hex2color("#a40000")) slider.GetTitleProperty().SetColor(*colors.hex2color("#2e3436")) # Find all the isosurfaces with energy equal to the threshold allcontours = [] with TimerContext() as timer: fermiactors = [] for band in ebands: sgridp = vtk.vtkStructuredGrid() sgridp.DeepCopy(sgrid) # Feed the energies to VTK scalar = vtk.vtkFloatArray() for i in band: scalar.InsertNextValue(i) sgridp.GetPointData().SetScalars(scalar) # Estimate the isosurfaces contours = vtk.vtkMarchingContourFilter() contours.SetInputData(sgridp) contours.UseScalarTreeOn() contours.SetValue(0, mu) contours.ComputeNormalsOff() contours.ComputeGradientsOff() allcontours.append(contours) # Use vtkStrippers to plot the surfaces faster stripper = vtk.vtkStripper() stripper.SetInputConnection(contours.GetOutputPort()) # Compute the normals to the surfaces to obtain better lighting normals = vtk.vtkPolyDataNormals() normals.SetInputConnection(stripper.GetOutputPort()) normals.ComputeCellNormalsOn() normals.ComputePointNormalsOn() # Create a mapper and an actor for the surfaces mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(normals.GetOutputPort()) mapper.ScalarVisibilityOff() fermiactors.append(vtk.vtkActor()) fermiactors[-1].SetMapper(mapper) fermiactors[-1].GetProperty().SetColor(*colors.hex2color( "#a40000")) deltat = timer.get_deltat() info("building the surfaces took {:.3g} s".format(deltat)) # Represent the BZ as a polyhedron in VTK points = vtk.vtkPoints() for v in voronoi.vertices: points.InsertNextPoint(*v) fids = vtk.vtkIdList() fids.InsertNextId(len(facets)) for f in facets: fids.InsertNextId(len(f)) for i in f: fids.InsertNextId(i) fgrid = vtk.vtkUnstructuredGrid() fgrid.SetPoints(points) fgrid.InsertNextCell(vtk.VTK_POLYHEDRON, fids) # Create an actor and a mapper for the BZ mapper = vtk.vtkDataSetMapper() mapper.SetInputData(fgrid) bzactor = vtk.vtkActor() bzactor.SetMapper(mapper) bzactor.GetProperty().SetColor(*colors.hex2color("#204a87")) bzactor.GetProperty().SetOpacity(0.2) # Create a visual representation of the selected point, and hide # it for the time being. sphere = vtk.vtkSphereSource() sphere.SetRadius(dmin / 2.) sphere_mapper = vtk.vtkPolyDataMapper() sphere_mapper.SetInputConnection(sphere.GetOutputPort()) sphere_mapper.ScalarVisibilityOff() sphere_actor = vtk.vtkActor() sphere_actor.SetMapper(sphere_mapper) sphere_actor.GetProperty().SetColor(*colors.hex2color("#f57900")) sphere_actor.VisibilityOff() # Create a VTK window and other elements of an interactive scene renderer = vtk.vtkRenderer() renderer.AddActor(bzactor) renderer.AddActor(sphere_actor) for f in fermiactors: renderer.AddActor(f) renderer.ResetCamera() renderer.GetActiveCamera().Zoom(5.) renderer.SetBackground(1., 1., 1.) window = vtk.vtkRenderWindow() window.AddRenderer(renderer) interactor = vtk.vtkRenderWindowInteractor() interactor.SetInteractorStyle(PointPickerInteractorStyle()) interactor.SetRenderWindow(window) # Add a set of axes axes = vtk.vtkAxesActor() assembly = vtk.vtkPropAssembly() assembly.AddPart(axes) marker = vtk.vtkOrientationMarkerWidget() marker.SetOrientationMarker(assembly) marker.SetInteractor(interactor) marker.SetEnabled(1) marker.InteractiveOff() def callback(obj, ev): """Update the isosurface with a new value""" mu = obj.GetRepresentation().GetValue() for e, E, c, a in zip(emin, emax, allcontours, fermiactors): visible = e <= mu and E >= mu a.SetVisibility(visible) if visible: c.SetValue(0, mu) # Add the slider widget widget = vtk.vtkSliderWidget() widget.SetInteractor(interactor) widget.SetRepresentation(slider) widget.SetAnimationModeToJump() widget.EnabledOn() widget.AddObserver(vtk.vtkCommand.InteractionEvent, callback) # Launch the visualization interactor.Initialize() window.Render() interactor.Start()
def parse_dope(args): """Parse command-line arguments for the dope subcommand. This is based on parse_integrate, but it selects the values of the chemical potential to explore based on the requested doping levels. """ try: args.bins except AttributeError: args.bins = None Tr = args.temperature if Tr.size == 0: lexit("empty temperature specification") elif Tr.min() <= 0.: lexit("all temperatures must be positive") data, equivalences, coeffs, metadata = ( BoltzTraP2.serialization.load_calculation(args.bt2_file)) lattvec = data.get_lattvec() info("sucessfully loaded " + args.bt2_file) with TimerContext() as timer: eband, vvband, cband = BoltzTraP2.fite.getBTPbands( equivalences, coeffs, lattvec, True, args.nworkers) deltat = timer.get_deltat() info("rebuilding the bands took {:.3g} s".format(deltat)) # Calculate the DOS. with TimerContext() as timer: epsilon, dos, vvdos, cdos = BoltzTraP2.bandlib.BTPDOS( eband, vvband, cband, npts=args.bins, scattering_model=args.scattering_model, Tmin=Tr.min()) deltat = timer.get_deltat() info("computing the DOS took {:.3g} s".format(deltat)) # Change the band gap if required. if args.scissor is not None: args.scissor *= eV info("Band gap value of {:.3g} Ha specified.".format(args.scissor) + " Trying to shift the gap to that value.") eband = BoltzTraP2.bandlib.apply_scissor(epsilon, dos, data.nelect, eband, args.scissor, data.dosweight) with TimerContext() as timer: epsilon, dos, vvdos, cdos = BoltzTraP2.bandlib.BTPDOS( eband, vvband, cband, npts=args.bins, scattering_model=args.scattering_model, Tmin=Tr.min()) deltat = timer.get_deltat() info("recomputing the DOS took {:.3g} s".format(deltat)) info("Number of DOS bins:", epsilon.size) nT = len(Tr) mu0 = np.empty_like(Tr) for iT, T in enumerate(Tr): mu0[iT] = BoltzTraP2.bandlib.solve_for_mu(epsilon, dos, data.nelect, T, data.dosweight, refine=True, try_center=True) fermi = BoltzTraP2.bandlib.solve_for_mu(epsilon, dos, data.nelect, 0., data.dosweight, refine=True, try_center=True) margin = 9. * BOLTZMANN * Tr.max() # The function diverges from parse_integrate from this point on. info("Temperatures:", Tr) info("Fermi level from DFT:", data.fermi) info("Refined Fermi level:", fermi) info("Intrinsic chemical potential for each T:", mu0) # Minimum and maximum reasonable values of mu mumin = epsilon.min() + margin mumax = epsilon.max() - margin if mumin >= mumax: lexit("the energy window is too narrow") # Convert the doping levels to values of mu at each temperature vuc = data.atoms.get_volume() * Angstrom**3 vuccm3 = data.atoms.get_volume() * 1e-24 # in cm^3 dopingr = args.doping_level ndoping = len(dopingr) mur = np.empty((nT, ndoping)) for iT, T in enumerate(Tr): dopingmin = BoltzTraP2.bandlib.calc_N(epsilon, dos, mumax, T, data.dosweight) + data.nelect dopingmin /= vuccm3 dopingmax = BoltzTraP2.bandlib.calc_N(epsilon, dos, mumin, T, data.dosweight) + data.nelect dopingmax /= vuccm3 # Check that the requested doping levels are in the acceptable range if dopingr.min() <= dopingmin or dopingr.max() >= dopingmax: lexit("minimum and maximum possible concentrations" " at T = {:.2f} K: {:.2g}, {:.2g}".format( T, dopingmin, dopingmax)) for idoping, doping in enumerate(dopingr): # Invert N(mu) to obtain an estimate of mu for each case. # Refine the estimate by not constraining it to one of the energies # in the histogram. N = data.nelect - doping * vuccm3 mur[iT, idoping] = BoltzTraP2.bandlib.solve_for_mu( epsilon, dos, N, T, data.dosweight, refine=True, try_center=False) info("Chemical potentials for T = {:.2f} K:".format(T), mur[iT, :]) # The smooth DOS is obtained using a direct KDE in energy space, instead # of a convolution with TimerContext() as timer: sdos = np.empty((nT, ndoping)) for iT, T in enumerate(Tr): sdos[iT, :] = BoltzTraP2.bandlib.smoothen_DOS_direct( epsilon, dos, T, mur[iT, :]) deltat = timer.get_deltat() info("smoothing the DOS took {:.3g} s".format(deltat)) # The flow from this point is again similar to that in parse_integrate, # with the exception that the chemical potentials are different for # each temperature. cv = np.empty((nT, ndoping)) N = np.empty((nT, ndoping)) L0 = np.empty((nT, ndoping, 3, 3)) L1 = np.empty((nT, ndoping, 3, 3)) L2 = np.empty((nT, ndoping, 3, 3)) Lm11 = np.empty((nT, ndoping, 3, 3, 3)) with TimerContext() as timer: for iT, T in enumerate(Tr): cv[iT] = BoltzTraP2.bandlib.calc_cv(epsilon, dos, mur[iT, :], np.array([T]), dosweight=data.dosweight) (N[iT], L0[iT], L1[iT], L2[iT], Lm11[iT]) = BoltzTraP2.bandlib.fermiintegrals( epsilon, dos, vvdos, mur=mur[iT], Tr=np.array([T]), dosweight=data.dosweight, cdos=cdos) deltat = timer.get_deltat() info("computing the FD moments took {:.3g} s".format(deltat)) L11 = np.empty((nT, ndoping, 3, 3)) seebeck = np.empty((nT, ndoping, 3, 3)) kappa = np.empty((nT, ndoping, 3, 3)) hall = np.empty((nT, ndoping, 3, 3, 3)) for iT, T in enumerate(Tr): (L11[iT], seebeck[iT], kappa[iT], hall[iT]) = BoltzTraP2.bandlib.calc_Onsager_coefficients( L0[[iT]], L1[[iT]], L2[[iT]], mur[iT], np.array([T]), vuc, Lm11[[iT]]) basefn = os.path.splitext(args.bt2_file)[0] tracefn = basefn + ".dope.trace" info("trace output file:", tracefn) BoltzTraP2.io.save_trace(tracefn, data, Tr, mur, N, sdos, cv, L11, seebeck, kappa, hall, scattering_model=args.scattering_model) condtensfn = basefn + ".dope.condtens" info("conductivity/seebeck output file:", condtensfn) BoltzTraP2.io.save_condtens(condtensfn, data, Tr, mur, N, L11, seebeck, kappa, scattering_model=args.scattering_model) halltensfn = basefn + ".dope.halltens" info("Hall coefficient output file:", halltensfn) BoltzTraP2.io.save_halltens(halltensfn, data, Tr, mur, N, hall)