bandu 1.3.6__py3-none-any.whl → 1.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bandu/plotter.py CHANGED
@@ -1,600 +1,600 @@
1
- import numpy as np
2
- import pyvista as pv
3
- from pyvistaqt import BackgroundPlotter
4
- from copy import copy
5
- import pickle as pkl
6
- from matplotlib.colors import ListedColormap
7
- from . import brillouin_zone as brlzn
8
- from . import isosurface_class as ic
9
- from . import wfk_class as wc
10
- from . import translate as trslt
11
-
12
- class Plotter():
13
- '''
14
- Class for creating 3D plot of Fermi surface and projection of BandU functions on Fermi surface.
15
-
16
- Parameters
17
- ----------
18
- isosurface : Isosurface
19
- An Isosurface object that contains the contours to be plotted
20
- save : bool
21
- Create save file of isosurface plot that can be loaded with the Load method
22
- Default will save the plot (True)
23
- save_file : str
24
- Name of save file
25
- Default is "Fermi_surface.pkl"
26
- empty_mesh : bool
27
- Allow PyVista plotter to plot meshes even if no surface is present
28
- Default will throw exception of an empty surface is being plotted (False)
29
- plot : bool
30
- Enable creation of PyVista Plotter
31
- Must be enabled for plotting, default enables plotter (True)
32
-
33
- Methods
34
- -------
35
- Plot
36
- Generates 3D plot of Fermi surface from contours of Isosurface object
37
- SurfaceColor
38
- Reads in a BandU XSF file and Fermi surface ABINIT WFK file to calculate the overlap between the BandU
39
- function and the states at the Fermi energy
40
- Load
41
- Loads a save file
42
- '''
43
- def __init__(
44
- self, isosurface:ic.Isosurface=ic.Isosurface(points=np.ones((1,3))), save:bool=True,
45
- empty_mesh:bool=False, _debug:bool=False, save_file:str='Fermi_surface.pkl', plot:bool=True
46
- ):
47
- self.isosurface=isosurface
48
- self.save=save
49
- self.save_file=save_file
50
- self._debug=_debug
51
- self.plot=plot
52
- if self.plot:
53
- self.p:pv.Plotter=BackgroundPlotter(window_size=(600,400))
54
- pv.global_theme.allow_empty_mesh=empty_mesh
55
- self.p.enable_depth_peeling(number_of_peels=10)
56
- #---------------------------------------------------------------------------------------------------------------------#
57
- #------------------------------------------------------ METHODS ------------------------------------------------------#
58
- #---------------------------------------------------------------------------------------------------------------------#
59
- # method for turning opacity to zero outside of BZ
60
- def _SetOpacities(
61
- self, contour:pv.PolyData
62
- )->np.ndarray:
63
- bz = brlzn.BZ(self.isosurface.rec_latt)
64
- opacities = bz.PointLocate(contour.points, cart=False)
65
- opacities[opacities >= 0] = 1
66
- opacities[opacities < 0] = 0
67
- return opacities
68
- #-----------------------------------------------------------------------------------------------------------------#
69
- # method to add nesting vector to isosurface plot
70
- def _AddArrow(
71
- self, arrow:list, rec_lattice:np.ndarray, show_endpoints:bool, color:str
72
- )->None:
73
- tail = np.array(arrow[0])
74
- shift = np.array(arrow[1])
75
- tail = np.matmul(tail, rec_lattice)
76
- shift = np.matmul(shift, rec_lattice)
77
- scale = np.linalg.norm(shift).astype(float)
78
- py_arrow = pv.Arrow(
79
- start=tail,
80
- direction=shift,
81
- tip_radius=0.05/scale,
82
- tip_length=0.15/scale,
83
- shaft_radius=0.025/scale,
84
- scale=scale
85
- )
86
- if show_endpoints:
87
- points = np.array([tail, shift+tail])
88
- points = pv.PolyData(points)
89
- self.p.add_mesh(points.points, point_size=20, color='black', render_points_as_spheres=True)
90
- self.p.add_mesh(py_arrow, color=color)
91
- #-----------------------------------------------------------------------------------------------------------------#
92
- # method to visualize cross-section of surface
93
- def _CrossSection(
94
- self, vecs:list, points:np.ndarray, width:float, rec_lattice:np.ndarray, bz_points:np.ndarray,
95
- linear:bool=False, two_dim:bool=False
96
- )->np.ndarray:
97
- from scipy.spatial import Delaunay
98
- # cross section is defined by plane of two perpendicular vectors
99
- if len(vecs) == 2:
100
- vec1 = np.matmul(vecs[0], rec_lattice)
101
- vec2 = np.matmul(vecs[1], rec_lattice)
102
- norm = np.cross(vec1, vec2)
103
- norm /= np.linalg.norm(norm)
104
- # cross section is defined by normal vector
105
- else:
106
- vec = np.matmul(vecs, rec_lattice)
107
- norm = vec/np.linalg.norm(vec)
108
- # surface points can be thought of vectors in 3D space, here we normalize the vectors
109
- norm_points = np.array(points/np.linalg.norm(points, axis=1).reshape((len(points),1)))
110
- # find dot product between vector normal to cross section and all normalized points
111
- angs = np.matmul(norm, norm_points.T)
112
- # opacity linearly fades out as points get farther from cross section
113
- if linear:
114
- angs[angs <= width] = 2
115
- angs -= 1
116
- angs = np.abs(angs)
117
- # opacity is zero beyond width of cross section
118
- else:
119
- # opacity is zero beyond width on both sides of cross section
120
- if two_dim:
121
- angs = np.abs(angs)
122
- opacities = np.zeros(len(points))
123
- opacities[angs <= width] = 1
124
- angs = opacities
125
- # set opacity to zero for points outside of the BZ
126
- beyond_bz = Delaunay(bz_points).find_simplex(points)
127
- angs[beyond_bz < 0] = 0
128
- return angs
129
- #-----------------------------------------------------------------------------------------------------------------#
130
- # method for plotting isosurface contours
131
- def Plot(
132
- self, show_points:bool=False, show_outline:bool=False, show_axes:bool=True, show_isosurf:bool=True,
133
- smooth:bool=True, lighting:bool=True, ambient:float=0.5, diffuse:float=0.5, specular:float=1.0,
134
- specular_power:float=128.0, pbr:bool=False, metallic:float=0.5, roughness:float=0.5,
135
- colormap:str|ListedColormap='plasma', color:str='white', bz_show:bool=True, bz_width:int=3, arrow:list=[],
136
- show_endpoints:bool=False, arrow_color:str='yellow', periodic_arrow:list=[], camera_position:list=[],
137
- cross_section:list=[], cross_width:float=0.1, linear:bool=False, two_dim:bool=True, show_bands:float|list=1.0,
138
- surface_vals:list|None=None, show_ird_points:bool=False
139
- ):
140
- '''
141
- Method for plotting contours made from the Isosurface object
142
-
143
- Parameters
144
- ----------
145
- colormap : str | ListedColormap
146
- Choose colormap for isosurface that colors according to assigned scalars of surface\n
147
- Can use Colors class to use default or create custom colormaps\n
148
- Default is matplotlib's plasma
149
- bz_width : int
150
- Line width of Brillouin Zone\n
151
- Default is 3
152
- bz_show : bool
153
- Show the Brillouin Zone
154
- Default is to show (True)
155
- smooth : bool
156
- Use smooth lightning techniques\n
157
- Default is to use smoothing (True)
158
- lighting : bool
159
- Apply directional lighting to surface\n
160
- Default is to enable directional lighting (True)
161
- ambient : float
162
- Intensity of light on surface\n
163
- Default is 0.5
164
- diffuse : float
165
- Amount of light scattering\n
166
- Default is 0.5
167
- specular : float
168
- Amount of reflected light\n
169
- Default is 1.0 (max)
170
- specular_power : float
171
- Determines how sharply light is reflected\n
172
- Default is 128.0 (max)
173
- pbr : bool
174
- Apply physics based rendering\n
175
- Default is no physics based rendering (False)
176
- metallic : float
177
- Determine how metallic-looking the surface is, only considered with pbr\n
178
- Default is 0.5
179
- roughness : float
180
- Determine how smooth/rough surface appear, only considered with pbr\n
181
- Default is 0.5
182
- color : str
183
- Sets color of surface (colormap overwrites this)\n
184
- Sets color of reflected light (colormap does not overwrite this)\n
185
- Default is white
186
- arrow : list
187
- Parameters for plotting nesting vector on top of Fermi surface\n
188
- Element_0 of list should be starting (or tail) position of arrow\n
189
- Element_1 of list should be orientation of arrow with desired magnitude\n
190
- Both the tail and orientation should be specified in reduced reciprocal space coordinates
191
- arrow_color : str
192
- Color of nesting arrow\n
193
- Default is black
194
- show_endpoints : bool
195
- Plot points on the end of the arrow to make visualizing start and end easier\n
196
- Default is to not show endpoints (False)
197
- periodic_arrow : list
198
- Adds periodic image of arrow that is translated [X,Y,Z] cells\n
199
- Where X, Y, and Z are the cell indices
200
- show_bands : float | list
201
- Specifies the opacity of each band\n
202
- If a single float is provided, all bands will be plotted with the same opacity\n
203
- If a list is provided, each band will be plotted with the opacity of the respective list element
204
- show_axes : bool
205
- Plots reciprocal cell axes with a* as red, b* as green, and c* as blue\n
206
- Default is to show axes (True)
207
- cross_section : list
208
- Plot cross section through surface\n
209
- If one vector is provided, it is assumed to be the normal to the cross section plane\n
210
- Else, cross section is defined by plane made by two vectors\n
211
- Vectors should be specified in reduced coordinates
212
- cross_width : float
213
- Width of cross section\n
214
- Default is 0.15
215
- linear : bool
216
- Cross section linearly fades out\n
217
- Default is not fade out linearly (False)
218
- two_dim : bool
219
- Cross section is a 2D slice instead of a section\n
220
- Default is to show cross section as 2D slice (True)
221
- surface_vals : np.ndarray
222
- A list of values defining the coloration of the isosurface
223
- Default plots no coloration
224
- '''
225
- # save file
226
- if self.save:
227
- with open(self.save_file, 'wb') as f:
228
- kwargs = locals()
229
- kwargs.pop('self', None)
230
- kwargs.pop('f', None)
231
- pkl.dump(kwargs, f)
232
- pkl.dump(self.isosurface, f)
233
- if not self.plot:
234
- raise SystemExit()
235
- # plot BZ boundary
236
- vor_verts = brlzn.BZ(self.isosurface.rec_latt).vertices
237
- if bz_show:
238
- self.p.add_lines(vor_verts, color='black', width=bz_width)
239
- # set opacity of each band
240
- if type(show_bands) is float:
241
- band_ops = show_bands*np.ones(len(self.isosurface.contours), dtype=float)
242
- show_bands = band_ops.tolist()
243
- # loop through contours of isosurface object and plot each
244
- for i, contour in enumerate(self.isosurface.contours):
245
- if surface_vals is None:
246
- scalars = None
247
- surf_max = 0.0
248
- else:
249
- scalars = surface_vals[i]
250
- surf_max = np.max(surface_vals[i])
251
- # opacities can be adjusted to show a cross section of contours
252
- if cross_section != []:
253
- opacities = self._CrossSection(
254
- cross_section,
255
- contour.points,
256
- cross_width,
257
- self.isosurface.rec_latt,
258
- vor_verts,
259
- linear=linear,
260
- two_dim=two_dim
261
- )
262
- # otherwise opacities are set to just render contours in the BZ
263
- else:
264
- opacities = self._SetOpacities(contour=contour)
265
- # set opacity of bands
266
- opacities = [op*show_bands[i] for op in opacities] # type: ignore
267
- # lighting is set to make surface appear more smooth
268
- if smooth:
269
- contour = contour.smooth_taubin(n_iter=100, pass_band=0.05)
270
- # plot contours
271
- if show_isosurf:
272
- self.p.add_mesh(
273
- contour,
274
- style='surface',
275
- smooth_shading=smooth,
276
- lighting=lighting,
277
- ambient=ambient,
278
- diffuse=diffuse,
279
- specular=specular,
280
- specular_power=specular_power,
281
- pbr=pbr,
282
- metallic=metallic,
283
- roughness=roughness,
284
- scalars=scalars,
285
- clim=[0.0,surf_max],
286
- cmap=colormap,
287
- opacity=opacities,
288
- color=color,
289
- show_scalar_bar=True,
290
- )
291
- # plot irreducible kpoints
292
- if show_ird_points:
293
- pts = pv.PolyData(self.isosurface.ir_kpts)
294
- self.p.add_mesh(pts.points, color='black')
295
- # plot points that are used to construct isosurface
296
- if show_points:
297
- pts = pv.PolyData(self.isosurface.points)
298
- self.p.add_mesh(pts.points, color='black')
299
- # plot outline of grid used in interpolation
300
- if show_outline:
301
- self.p.add_mesh(self.isosurface.grid.outline())
302
- # plot reciprocal space axes
303
- if show_axes:
304
- axes_colors = ['red', 'green', 'blue']
305
- for i in range(3):
306
- axis = self.isosurface.rec_latt[i,:]
307
- color = axes_colors[i]
308
- self._AddArrow([[0,0,0], axis], np.identity(3), False, color)
309
- # plot nesting vector
310
- if arrow != []:
311
- self._AddArrow(arrow, self.isosurface.rec_latt, show_endpoints, arrow_color)
312
- # plot a periodic image of the nesting vector
313
- if periodic_arrow != []:
314
- tail = np.array(arrow[0])
315
- cell = np.array(periodic_arrow, dtype=float)
316
- tail += cell
317
- arrow[0] = tail
318
- self._AddArrow(arrow, self.isosurface.rec_latt, show_endpoints, arrow_color)
319
- # set camera position
320
- if camera_position != []:
321
- camera_pos = np.array(camera_position).reshape((3,3))
322
- camera_pos = np.matmul(camera_pos, self.isosurface.rec_latt)
323
- self.p.camera_position = camera_pos
324
- self._Render()
325
- #-----------------------------------------------------------------------------------------------------------------#
326
- # Render isosurfaces
327
- def _Render(
328
- self
329
- ):
330
- self.p.enable_parallel_projection() # type: ignore
331
- self.p.enable_custom_trackball_style(
332
- left='rotate',
333
- shift_left='spin',
334
- right='pan',
335
- ) # type: ignore
336
- self.p.set_focus([0.0,0.0,0.0]) # type: ignore
337
- self.p.show()
338
- self.p.app.exec_() # type: ignore
339
- #-----------------------------------------------------------------------------------------------------------------#
340
- # method to compute bandu fxn and one electron wfk overlaps
341
- def _OverlapsWithSym(
342
- self, ir_wfk:wc.WFK, bandu:wc.WFK
343
- )->np.ndarray:
344
- # kpoint to symmetrically generate
345
- kpt = ir_wfk.kpoints
346
- # find number of distinct symmetries
347
- sym_kpoints, _ = ir_wfk.Symmetrize(kpt, unique=False, reciprocal=True)
348
- dupes, unique_inds = ir_wfk._FindOrbit(sym_kpoints)
349
- count = sum([1 for i, _ in enumerate(unique_inds) if i not in dupes])
350
- # initialize array for overlap values
351
- num_bands = len(self.isosurface.nbands)
352
- overlap_vals = np.zeros((count,num_bands), dtype=float)
353
- # loop over bands
354
- for i, band in enumerate(self.isosurface.nbands):
355
- # generate symmetric coefficients for each band
356
- for j, wfk in enumerate(ir_wfk.SymWFKs(kpoint=kpt, band=band)):
357
- wfk = wfk.GridWFK()
358
- wfk = wfk.IFFT()
359
- wfk = wfk.Normalize()
360
- overlap = np.sum(np.conj(bandu.wfk_coeffs)*wfk.wfk_coeffs)
361
- overlap = np.square(np.abs(overlap))
362
- overlap_vals[j,i] = overlap
363
- return overlap_vals
364
- #-----------------------------------------------------------------------------------------------------------------#
365
- # method to compute bandu fxn and one electron wfk overlaps
366
- def _OverlapsNoSym(
367
- self, ir_wfk:wc.WFK, bandu:wc.WFK
368
- )->np.ndarray:
369
- # initialize array for overlap values
370
- num_bands = len(self.isosurface.nbands)
371
- overlap_vals = np.zeros((1,num_bands), dtype=float)
372
- # loop over bands
373
- for i, band in enumerate(self.isosurface.nbands):
374
- wfk = ir_wfk.GridWFK(band_index=band)
375
- wfk = wfk.IFFT()
376
- wfk = wfk.Normalize()
377
- overlap = np.sum(np.conj(bandu.wfk_coeffs)*wfk.wfk_coeffs)
378
- overlap = np.square(np.abs(overlap))
379
- overlap_vals[0,i] = overlap
380
- return overlap_vals
381
- #-----------------------------------------------------------------------------------------------------------------#
382
- # method to interpolate overlap values
383
- def _InterpolateOverlaps(
384
- self, contour:pv.PolyData, overlap_values:np.ndarray
385
- )->np.ndarray:
386
- # recreate eigenvalue grid as base for surface color grid
387
- color_grid = copy(self.isosurface.grid)
388
- # translate overlap values to 3x3x3 grid
389
- trans_color_points, trans_color_values = trslt.TranslatePoints(
390
- self.isosurface.points,
391
- overlap_values.reshape((-1,1)),
392
- self.isosurface.rec_latt
393
- )
394
- # construct PyVista object from translated overlap grid
395
- trans_color_points = pv.PolyData(trans_color_points)
396
- trans_color_points['values'] = trans_color_values
397
- # interpolate translated grid
398
- color_grid = color_grid.interpolate(
399
- trans_color_points,
400
- sharpness=2.0,
401
- radius=self.isosurface.radius,
402
- strategy='null_value'
403
- )
404
- # sample color values from interpolated color grid
405
- color_sample = contour.sample(color_grid)
406
- return color_sample.active_scalars
407
- #-----------------------------------------------------------------------------------------------------------------#
408
- # method to calculate surface color values from XSF and WFK
409
- def SurfaceColor(
410
- self, wfk_path:str, xsf_path:str, sym:bool=False
411
- )->list:
412
- '''
413
- Method for calculating BandU function overlap with states at a specified isoenergy.
414
- Requires BandU XSF file and ABINIT WFK file.
415
-
416
- Parameters
417
- ----------
418
- wfk_path : str
419
- Path to ABINIT WFK file
420
- xsf_path : str
421
- Path to BandU XSF file
422
- sym : bool
423
- Symmetrically generate full Brillouin Zone
424
- Default will not generate Brillouin Zone and instead symmetrize surface colors (False)
425
- '''
426
- from . import abinit_reader as ar
427
- from . import xsf_reader as xsfr
428
- # list of overlap values
429
- overlaps = np.zeros(1)
430
- # read fermi surface wavefunction
431
- fermi_wfk = ar.AbinitWFK(filename=wfk_path)
432
- # get number of bands
433
- nbands = len(self.isosurface.nbands)
434
- # paths to real and imaginary bandu xsf files
435
- real_path = xsf_path + '_real.xsf'
436
- imag_path = xsf_path + '_imag.xsf'
437
- # read in xsf
438
- real_fxn = xsfr.XSF(xsf_file=real_path)
439
- imag_fxn = xsfr.XSF(xsf_file=imag_path)
440
- print('XSF read')
441
- # convert xsf to wfk object
442
- bandu_fxn = wc.WFK(
443
- wfk_coeffs=real_fxn.ReadGrid() + 1j*imag_fxn.ReadGrid(),
444
- ngfftx=real_fxn.ngfftx,
445
- ngffty=real_fxn.ngffty,
446
- ngfftz=real_fxn.ngfftz
447
- )
448
- # remove xsf format
449
- bandu_fxn = bandu_fxn.RemoveXSF()
450
- # loop through fermi surface kpoints and calc overlap with bandu fxn
451
- for i, kpt in enumerate(fermi_wfk.ReadWFK()):
452
- if sym:
453
- vals = self._OverlapsWithSym(kpt, bandu_fxn)
454
- else:
455
- vals = self._OverlapsNoSym(kpt, bandu_fxn)
456
- if i == 0:
457
- overlaps = vals
458
- else:
459
- overlaps = np.concatenate((overlaps, vals), axis=0)
460
- # symmetrically permute overlap values if they were only calculated on irreducible BZ wedge when sym==False
461
- if not sym:
462
- symrel = fermi_wfk.symrel
463
- nsym = fermi_wfk.nsym
464
- kpts = fermi_wfk.kpts
465
- all_kpts = np.zeros((1,3))
466
- all_overlaps = np.zeros((1,nbands))
467
- new_wfk = wc.WFK(symrel=np.array(symrel), nsym=nsym, nbands=nbands)
468
- for i, kpt in enumerate(kpts):
469
- unique_kpts, _ = new_wfk.Symmetrize(
470
- points=kpt,
471
- reciprocal=True
472
- )
473
- all_kpts = np.concatenate((all_kpts, unique_kpts), axis=0)
474
- new_overlaps = np.repeat(overlaps[i,:].reshape((1,-1)), unique_kpts.shape[0], axis=0)
475
- all_overlaps = np.concatenate((all_overlaps, new_overlaps), axis=0)
476
- kpts = np.delete(all_kpts, 0, axis=0)
477
- overlaps = np.delete(all_overlaps, 0, axis=0)
478
- self.isosurface.points = np.matmul(kpts, self.isosurface.rec_latt)
479
- # interpolate overlap values for smooth coloration
480
- scalars = []
481
- for i in range(overlaps.shape[1]):
482
- interp_vals = self._InterpolateOverlaps(self.isosurface.contours[i], overlaps[:,i])
483
- scalars.append(interp_vals)
484
- return scalars
485
- #-----------------------------------------------------------------------------------------------------------------#
486
- # method to load save file
487
- def Load(
488
- self, save_path:str|list, **kwargs
489
- ):
490
- '''
491
- Method for loading saved contours
492
-
493
- Parameters
494
- ----------
495
- colormap : str | ListedColormap
496
- Choose colormap for isosurface that colors according to assigned scalars of surface\n
497
- Can use Colors class to use default or create custom colormaps\n
498
- Default is matplotlib's plasma
499
- bz_width : int
500
- Line width of Brillouin Zone\n
501
- Default is 3
502
- bz_show : bool
503
- Show the Brillouin Zone
504
- Default is to show (True)
505
- smooth : bool
506
- Use smooth lightning techniques\n
507
- Default is to use smoothing (True)
508
- lighting : bool
509
- Apply directional lighting to surface\n
510
- Default is to enable directional lighting (True)
511
- ambient : float
512
- Intensity of light on surface\n
513
- Default is 0.5
514
- diffuse : float
515
- Amount of light scattering\n
516
- Default is 0.5
517
- specular : float
518
- Amount of reflected light\n
519
- Default is 1.0 (max)
520
- specular_power : float
521
- Determines how sharply light is reflected\n
522
- Default is 128.0 (max)
523
- pbr : bool
524
- Apply physics based rendering\n
525
- Default is no physics based rendering (False)
526
- metallic : float
527
- Determine how metallic-looking the surface is, only considered with pbr\n
528
- Default is 0.5
529
- roughness : float
530
- Determine how smooth/rough surface appear, only considered with pbr\n
531
- Default is 0.5
532
- color : str
533
- Sets color of surface (colormap overwrites this)\n
534
- Sets color of reflected light (colormap does not overwrite this)\n
535
- Default is white
536
- arrow : list
537
- Parameters for plotting nesting vector on top of Fermi surface\n
538
- Element_0 of list should be starting (or tail) position of arrow\n
539
- Element_1 of list should be orientation of arrow with desired magnitude\n
540
- Both the tail and orientation should be specified in reduced reciprocal space coordinates
541
- arrow_color : str
542
- Color of nesting arrow\n
543
- Default is black
544
- show_endpoints : bool
545
- Plot points on the end of the arrow to make visualizing start and end easier\n
546
- Default is to not show endpoints (False)
547
- periodic_arrow : list
548
- Adds periodic image of arrow that is translated [X,Y,Z] cells\n
549
- Where X, Y, and Z are the cell indices
550
- show_bands : float | list
551
- Specifies the opacity of each band\n
552
- If a single float is provided, all bands will be plotted with the same opacity\n
553
- If a list is provided, each band will be plotted with the opacity of the respective list element
554
- show_axes : bool
555
- Plots reciprocal cell axes with a* as red, b* as green, and c* as blue\n
556
- Default is to show axes (True)
557
- cross_section : list
558
- Plot cross section through surface\n
559
- If one vector is provided, it is assumed to be the normal to the cross section plane\n
560
- Else, cross section is defined by plane made by two vectors\n
561
- Vectors should be specified in reduced coordinates
562
- cross_width : float
563
- Width of cross section\n
564
- Default is 0.15
565
- linear : bool
566
- Cross section linearly fades out\n
567
- Default is not fade out linearly (False)
568
- two_dim : bool
569
- Cross section is a 2D slice instead of a section\n
570
- Default is to show cross section as 2D slice (True)
571
- surface_vals : np.ndarray
572
- A list of values defining the coloration of the isosurface
573
- Default plots no coloration
574
- '''
575
- # functionality for adding multiple projections onto one surface
576
- if type(save_path) == list:
577
- all_scalars = []
578
- for save in save_path:
579
- with open(save, 'rb') as f:
580
- kwargs_dict:dict = pkl.load(f)
581
- self.isosurface:ic.Isosurface = pkl.load(f)
582
- all_scalars.append(kwargs_dict['surface_vals'])
583
- sizes = []
584
- for surface_vals in all_scalars[0]:
585
- sizes.append(surface_vals.shape)
586
- new_scalars = [np.zeros((size)) for size in sizes]
587
- for scalars in all_scalars:
588
- for i, surface_vals in enumerate(scalars):
589
- new_scalars[i] += surface_vals
590
- kwargs_dict['surface_vals'] = new_scalars # type: ignore
591
- # replot a single surface
592
- else:
593
- with open(save_path, 'rb') as f: # type: ignore
594
- kwargs_dict:dict = pkl.load(f)
595
- self.isosurface:ic.Isosurface = pkl.load(f)
596
- for k, val in kwargs.items(): # type: ignore
597
- kwargs_dict[k] = val # type: ignore
598
- self.save = False
599
- self.plot = True
1
+ import numpy as np
2
+ import pyvista as pv
3
+ from pyvistaqt import BackgroundPlotter
4
+ from copy import copy
5
+ import pickle as pkl
6
+ from matplotlib.colors import ListedColormap
7
+ from . import brillouin_zone as brlzn
8
+ from . import isosurface_class as ic
9
+ from . import wfk_class as wc
10
+ from . import translate as trslt
11
+
12
+ class Plotter():
13
+ '''
14
+ Class for creating 3D plot of Fermi surface and projection of BandU functions on Fermi surface.
15
+
16
+ Parameters
17
+ ----------
18
+ isosurface : Isosurface
19
+ An Isosurface object that contains the contours to be plotted
20
+ save : bool
21
+ Create save file of isosurface plot that can be loaded with the Load method
22
+ Default will save the plot (True)
23
+ save_file : str
24
+ Name of save file
25
+ Default is "Fermi_surface.pkl"
26
+ empty_mesh : bool
27
+ Allow PyVista plotter to plot meshes even if no surface is present
28
+ Default will throw exception of an empty surface is being plotted (False)
29
+ plot : bool
30
+ Enable creation of PyVista Plotter
31
+ Must be enabled for plotting, default enables plotter (True)
32
+
33
+ Methods
34
+ -------
35
+ Plot
36
+ Generates 3D plot of Fermi surface from contours of Isosurface object
37
+ SurfaceColor
38
+ Reads in a BandU XSF file and Fermi surface ABINIT WFK file to calculate the overlap between the BandU
39
+ function and the states at the Fermi energy
40
+ Load
41
+ Loads a save file
42
+ '''
43
+ def __init__(
44
+ self, isosurface:ic.Isosurface=ic.Isosurface(points=np.ones((1,3))), save:bool=True,
45
+ empty_mesh:bool=False, _debug:bool=False, save_file:str='Fermi_surface.pkl', plot:bool=True
46
+ ):
47
+ self.isosurface=isosurface
48
+ self.save=save
49
+ self.save_file=save_file
50
+ self._debug=_debug
51
+ self.plot=plot
52
+ if self.plot:
53
+ self.p:pv.Plotter=BackgroundPlotter(window_size=(600,400))
54
+ pv.global_theme.allow_empty_mesh=empty_mesh
55
+ self.p.enable_depth_peeling(number_of_peels=10)
56
+ #---------------------------------------------------------------------------------------------------------------------#
57
+ #------------------------------------------------------ METHODS ------------------------------------------------------#
58
+ #---------------------------------------------------------------------------------------------------------------------#
59
+ # method for turning opacity to zero outside of BZ
60
+ def _SetOpacities(
61
+ self, contour:pv.PolyData
62
+ )->np.ndarray:
63
+ bz = brlzn.BZ(self.isosurface.rec_latt)
64
+ opacities = bz.PointLocate(contour.points, cart=False)
65
+ opacities[opacities >= 0] = 1
66
+ opacities[opacities < 0] = 0
67
+ return opacities
68
+ #-----------------------------------------------------------------------------------------------------------------#
69
+ # method to add nesting vector to isosurface plot
70
+ def _AddArrow(
71
+ self, arrow:list, rec_lattice:np.ndarray, show_endpoints:bool, color:str
72
+ )->None:
73
+ tail = np.array(arrow[0])
74
+ shift = np.array(arrow[1])
75
+ tail = np.matmul(tail, rec_lattice)
76
+ shift = np.matmul(shift, rec_lattice)
77
+ scale = np.linalg.norm(shift).astype(float)
78
+ py_arrow = pv.Arrow(
79
+ start=tail,
80
+ direction=shift,
81
+ tip_radius=0.05/scale,
82
+ tip_length=0.15/scale,
83
+ shaft_radius=0.025/scale,
84
+ scale=scale
85
+ )
86
+ if show_endpoints:
87
+ points = np.array([tail, shift+tail])
88
+ points = pv.PolyData(points)
89
+ self.p.add_mesh(points.points, point_size=20, color='black', render_points_as_spheres=True)
90
+ self.p.add_mesh(py_arrow, color=color)
91
+ #-----------------------------------------------------------------------------------------------------------------#
92
+ # method to visualize cross-section of surface
93
+ def _CrossSection(
94
+ self, vecs:list, points:np.ndarray, width:float, rec_lattice:np.ndarray, bz_points:np.ndarray,
95
+ linear:bool=False, two_dim:bool=False
96
+ )->np.ndarray:
97
+ from scipy.spatial import Delaunay
98
+ # cross section is defined by plane of two perpendicular vectors
99
+ if len(vecs) == 2:
100
+ vec1 = np.matmul(vecs[0], rec_lattice)
101
+ vec2 = np.matmul(vecs[1], rec_lattice)
102
+ norm = np.cross(vec1, vec2)
103
+ norm /= np.linalg.norm(norm)
104
+ # cross section is defined by normal vector
105
+ else:
106
+ vec = np.matmul(vecs, rec_lattice)
107
+ norm = vec/np.linalg.norm(vec)
108
+ # surface points can be thought of vectors in 3D space, here we normalize the vectors
109
+ norm_points = np.array(points/np.linalg.norm(points, axis=1).reshape((len(points),1)))
110
+ # find dot product between vector normal to cross section and all normalized points
111
+ angs = np.matmul(norm, norm_points.T)
112
+ # opacity linearly fades out as points get farther from cross section
113
+ if linear:
114
+ angs[angs <= width] = 2
115
+ angs -= 1
116
+ angs = np.abs(angs)
117
+ # opacity is zero beyond width of cross section
118
+ else:
119
+ # opacity is zero beyond width on both sides of cross section
120
+ if two_dim:
121
+ angs = np.abs(angs)
122
+ opacities = np.zeros(len(points))
123
+ opacities[angs <= width] = 1
124
+ angs = opacities
125
+ # set opacity to zero for points outside of the BZ
126
+ beyond_bz = Delaunay(bz_points).find_simplex(points)
127
+ angs[beyond_bz < 0] = 0
128
+ return angs
129
+ #-----------------------------------------------------------------------------------------------------------------#
130
+ # method for plotting isosurface contours
131
+ def Plot(
132
+ self, show_points:bool=False, show_outline:bool=False, show_axes:bool=True, show_isosurf:bool=True,
133
+ smooth:bool=True, lighting:bool=True, ambient:float=0.5, diffuse:float=0.5, specular:float=1.0,
134
+ specular_power:float=128.0, pbr:bool=False, metallic:float=0.5, roughness:float=0.5,
135
+ colormap:str|ListedColormap='plasma', color:str='white', bz_show:bool=True, bz_width:int=3, arrow:list=[],
136
+ show_endpoints:bool=False, arrow_color:str='yellow', periodic_arrow:list=[], camera_position:list=[],
137
+ cross_section:list=[], cross_width:float=0.1, linear:bool=False, two_dim:bool=True, show_bands:float|list=1.0,
138
+ surface_vals:list|None=None, show_ird_points:bool=False
139
+ ):
140
+ '''
141
+ Method for plotting contours made from the Isosurface object
142
+
143
+ Parameters
144
+ ----------
145
+ colormap : str | ListedColormap
146
+ Choose colormap for isosurface that colors according to assigned scalars of surface\n
147
+ Can use Colors class to use default or create custom colormaps\n
148
+ Default is matplotlib's plasma
149
+ bz_width : int
150
+ Line width of Brillouin Zone\n
151
+ Default is 3
152
+ bz_show : bool
153
+ Show the Brillouin Zone
154
+ Default is to show (True)
155
+ smooth : bool
156
+ Use smooth lightning techniques\n
157
+ Default is to use smoothing (True)
158
+ lighting : bool
159
+ Apply directional lighting to surface\n
160
+ Default is to enable directional lighting (True)
161
+ ambient : float
162
+ Intensity of light on surface\n
163
+ Default is 0.5
164
+ diffuse : float
165
+ Amount of light scattering\n
166
+ Default is 0.5
167
+ specular : float
168
+ Amount of reflected light\n
169
+ Default is 1.0 (max)
170
+ specular_power : float
171
+ Determines how sharply light is reflected\n
172
+ Default is 128.0 (max)
173
+ pbr : bool
174
+ Apply physics based rendering\n
175
+ Default is no physics based rendering (False)
176
+ metallic : float
177
+ Determine how metallic-looking the surface is, only considered with pbr\n
178
+ Default is 0.5
179
+ roughness : float
180
+ Determine how smooth/rough surface appear, only considered with pbr\n
181
+ Default is 0.5
182
+ color : str
183
+ Sets color of surface (colormap overwrites this)\n
184
+ Sets color of reflected light (colormap does not overwrite this)\n
185
+ Default is white
186
+ arrow : list
187
+ Parameters for plotting nesting vector on top of Fermi surface\n
188
+ Element_0 of list should be starting (or tail) position of arrow\n
189
+ Element_1 of list should be orientation of arrow with desired magnitude\n
190
+ Both the tail and orientation should be specified in reduced reciprocal space coordinates
191
+ arrow_color : str
192
+ Color of nesting arrow\n
193
+ Default is black
194
+ show_endpoints : bool
195
+ Plot points on the end of the arrow to make visualizing start and end easier\n
196
+ Default is to not show endpoints (False)
197
+ periodic_arrow : list
198
+ Adds periodic image of arrow that is translated [X,Y,Z] cells\n
199
+ Where X, Y, and Z are the cell indices
200
+ show_bands : float | list
201
+ Specifies the opacity of each band\n
202
+ If a single float is provided, all bands will be plotted with the same opacity\n
203
+ If a list is provided, each band will be plotted with the opacity of the respective list element
204
+ show_axes : bool
205
+ Plots reciprocal cell axes with a* as red, b* as green, and c* as blue\n
206
+ Default is to show axes (True)
207
+ cross_section : list
208
+ Plot cross section through surface\n
209
+ If one vector is provided, it is assumed to be the normal to the cross section plane\n
210
+ Else, cross section is defined by plane made by two vectors\n
211
+ Vectors should be specified in reduced coordinates
212
+ cross_width : float
213
+ Width of cross section\n
214
+ Default is 0.15
215
+ linear : bool
216
+ Cross section linearly fades out\n
217
+ Default is not fade out linearly (False)
218
+ two_dim : bool
219
+ Cross section is a 2D slice instead of a section\n
220
+ Default is to show cross section as 2D slice (True)
221
+ surface_vals : np.ndarray
222
+ A list of values defining the coloration of the isosurface
223
+ Default plots no coloration
224
+ '''
225
+ # save file
226
+ if self.save:
227
+ with open(self.save_file, 'wb') as f:
228
+ kwargs = locals()
229
+ kwargs.pop('self', None)
230
+ kwargs.pop('f', None)
231
+ pkl.dump(kwargs, f)
232
+ pkl.dump(self.isosurface, f)
233
+ if not self.plot:
234
+ raise SystemExit()
235
+ # plot BZ boundary
236
+ vor_verts = brlzn.BZ(self.isosurface.rec_latt).vertices
237
+ if bz_show:
238
+ self.p.add_lines(vor_verts, color='black', width=bz_width)
239
+ # set opacity of each band
240
+ if type(show_bands) is float:
241
+ band_ops = show_bands*np.ones(len(self.isosurface.contours), dtype=float)
242
+ show_bands = band_ops.tolist()
243
+ # loop through contours of isosurface object and plot each
244
+ for i, contour in enumerate(self.isosurface.contours):
245
+ if surface_vals is None:
246
+ scalars = None
247
+ surf_max = 0.0
248
+ else:
249
+ scalars = surface_vals[i]
250
+ surf_max = np.max(surface_vals[i])
251
+ # opacities can be adjusted to show a cross section of contours
252
+ if cross_section != []:
253
+ opacities = self._CrossSection(
254
+ cross_section,
255
+ contour.points,
256
+ cross_width,
257
+ self.isosurface.rec_latt,
258
+ vor_verts,
259
+ linear=linear,
260
+ two_dim=two_dim
261
+ )
262
+ # otherwise opacities are set to just render contours in the BZ
263
+ else:
264
+ opacities = self._SetOpacities(contour=contour)
265
+ # set opacity of bands
266
+ opacities = [op*show_bands[i] for op in opacities] # type: ignore
267
+ # lighting is set to make surface appear more smooth
268
+ if smooth:
269
+ contour = contour.smooth_taubin(n_iter=100, pass_band=0.05)
270
+ # plot contours
271
+ if show_isosurf:
272
+ self.p.add_mesh(
273
+ contour, # type: ignore
274
+ style='surface',
275
+ smooth_shading=smooth,
276
+ lighting=lighting,
277
+ ambient=ambient,
278
+ diffuse=diffuse,
279
+ specular=specular,
280
+ specular_power=specular_power,
281
+ pbr=pbr,
282
+ metallic=metallic,
283
+ roughness=roughness,
284
+ scalars=scalars,
285
+ clim=[0.0,surf_max],
286
+ cmap=colormap,
287
+ opacity=opacities,
288
+ color=color,
289
+ show_scalar_bar=True,
290
+ )
291
+ # plot irreducible kpoints
292
+ if show_ird_points:
293
+ pts = pv.PolyData(self.isosurface.ir_kpts)
294
+ self.p.add_mesh(pts.points, color='black')
295
+ # plot points that are used to construct isosurface
296
+ if show_points:
297
+ pts = pv.PolyData(self.isosurface.points)
298
+ self.p.add_mesh(pts.points, color='black')
299
+ # plot outline of grid used in interpolation
300
+ if show_outline:
301
+ self.p.add_mesh(self.isosurface.grid.outline())
302
+ # plot reciprocal space axes
303
+ if show_axes:
304
+ axes_colors = ['red', 'green', 'blue']
305
+ for i in range(3):
306
+ axis = self.isosurface.rec_latt[i,:]
307
+ color = axes_colors[i]
308
+ self._AddArrow([[0,0,0], axis], np.identity(3), False, color)
309
+ # plot nesting vector
310
+ if arrow != []:
311
+ self._AddArrow(arrow, self.isosurface.rec_latt, show_endpoints, arrow_color)
312
+ # plot a periodic image of the nesting vector
313
+ if periodic_arrow != []:
314
+ tail = np.array(arrow[0])
315
+ cell = np.array(periodic_arrow, dtype=float)
316
+ tail += cell
317
+ arrow[0] = tail
318
+ self._AddArrow(arrow, self.isosurface.rec_latt, show_endpoints, arrow_color)
319
+ # set camera position
320
+ if camera_position != []:
321
+ camera_pos = np.array(camera_position).reshape((3,3))
322
+ camera_pos = np.matmul(camera_pos, self.isosurface.rec_latt)
323
+ self.p.camera_position = camera_pos
324
+ self._Render()
325
+ #-----------------------------------------------------------------------------------------------------------------#
326
+ # Render isosurfaces
327
+ def _Render(
328
+ self
329
+ ):
330
+ self.p.enable_parallel_projection() # type: ignore
331
+ self.p.enable_custom_trackball_style(
332
+ left='rotate',
333
+ shift_left='spin',
334
+ right='pan',
335
+ ) # type: ignore
336
+ self.p.set_focus([0.0,0.0,0.0]) # type: ignore
337
+ self.p.show()
338
+ self.p.app.exec_() # type: ignore
339
+ #-----------------------------------------------------------------------------------------------------------------#
340
+ # method to compute bandu fxn and one electron wfk overlaps
341
+ def _OverlapsWithSym(
342
+ self, ir_wfk:wc.WFK, bandu:wc.WFK
343
+ )->np.ndarray:
344
+ # kpoint to symmetrically generate
345
+ kpt = ir_wfk.kpoints
346
+ # find number of distinct symmetries
347
+ sym_kpoints, _ = ir_wfk.Symmetrize(kpt, unique=False, reciprocal=True)
348
+ dupes, unique_inds = ir_wfk._FindOrbit(sym_kpoints)
349
+ count = sum([1 for i, _ in enumerate(unique_inds) if i not in dupes])
350
+ # initialize array for overlap values
351
+ num_bands = len(self.isosurface.nbands)
352
+ overlap_vals = np.zeros((count,num_bands), dtype=float)
353
+ # loop over bands
354
+ for i, band in enumerate(self.isosurface.nbands):
355
+ # generate symmetric coefficients for each band
356
+ for j, wfk in enumerate(ir_wfk.SymWFKs(kpoint=kpt, band=band)):
357
+ wfk = wfk.GridWFK()
358
+ wfk = wfk.IFFT()
359
+ wfk = wfk.Normalize()
360
+ overlap = np.sum(np.conj(bandu.wfk_coeffs)*wfk.wfk_coeffs)
361
+ overlap = np.square(np.abs(overlap))
362
+ overlap_vals[j,i] = overlap
363
+ return overlap_vals
364
+ #-----------------------------------------------------------------------------------------------------------------#
365
+ # method to compute bandu fxn and one electron wfk overlaps
366
+ def _OverlapsNoSym(
367
+ self, ir_wfk:wc.WFK, bandu:wc.WFK
368
+ )->np.ndarray:
369
+ # initialize array for overlap values
370
+ num_bands = len(self.isosurface.nbands)
371
+ overlap_vals = np.zeros((1,num_bands), dtype=float)
372
+ # loop over bands
373
+ for i, band in enumerate(self.isosurface.nbands):
374
+ wfk = ir_wfk.GridWFK(band_index=band)
375
+ wfk = wfk.IFFT()
376
+ wfk = wfk.Normalize()
377
+ overlap = np.sum(np.conj(bandu.wfk_coeffs)*wfk.wfk_coeffs)
378
+ overlap = np.square(np.abs(overlap))
379
+ overlap_vals[0,i] = overlap
380
+ return overlap_vals
381
+ #-----------------------------------------------------------------------------------------------------------------#
382
+ # method to interpolate overlap values
383
+ def _InterpolateOverlaps(
384
+ self, contour:pv.PolyData, overlap_values:np.ndarray
385
+ )->np.ndarray:
386
+ # recreate eigenvalue grid as base for surface color grid
387
+ color_grid = copy(self.isosurface.grid)
388
+ # translate overlap values to 3x3x3 grid
389
+ trans_color_points, trans_color_values = trslt.TranslatePoints(
390
+ self.isosurface.points,
391
+ overlap_values.reshape((-1,1)),
392
+ self.isosurface.rec_latt
393
+ )
394
+ # construct PyVista object from translated overlap grid
395
+ trans_color_points = pv.PolyData(trans_color_points)
396
+ trans_color_points['values'] = trans_color_values
397
+ # interpolate translated grid
398
+ color_grid = color_grid.interpolate(
399
+ trans_color_points,
400
+ sharpness=2.0,
401
+ radius=self.isosurface.radius,
402
+ strategy='null_value'
403
+ )
404
+ # sample color values from interpolated color grid
405
+ color_sample = contour.sample(color_grid) # type: ignore
406
+ return color_sample.active_scalars
407
+ #-----------------------------------------------------------------------------------------------------------------#
408
+ # method to calculate surface color values from XSF and WFK
409
+ def SurfaceColor(
410
+ self, wfk_path:str, xsf_path:str, sym:bool=False
411
+ )->list:
412
+ '''
413
+ Method for calculating BandU function overlap with states at a specified isoenergy.
414
+ Requires BandU XSF file and ABINIT WFK file.
415
+
416
+ Parameters
417
+ ----------
418
+ wfk_path : str
419
+ Path to ABINIT WFK file
420
+ xsf_path : str
421
+ Path to BandU XSF file
422
+ sym : bool
423
+ Symmetrically generate full Brillouin Zone
424
+ Default will not generate Brillouin Zone and instead symmetrize surface colors (False)
425
+ '''
426
+ from . import abinit_reader as ar
427
+ from . import xsf_reader as xsfr
428
+ # list of overlap values
429
+ overlaps = np.zeros(1)
430
+ # read fermi surface wavefunction
431
+ fermi_wfk = ar.AbinitWFK(filename=wfk_path)
432
+ # get number of bands
433
+ nbands = len(self.isosurface.nbands)
434
+ # paths to real and imaginary bandu xsf files
435
+ real_path = xsf_path + '_real.xsf'
436
+ imag_path = xsf_path + '_imag.xsf'
437
+ # read in xsf
438
+ real_fxn = xsfr.XSF(xsf_file=real_path)
439
+ imag_fxn = xsfr.XSF(xsf_file=imag_path)
440
+ print('XSF read')
441
+ # convert xsf to wfk object
442
+ bandu_fxn = wc.WFK(
443
+ wfk_coeffs=real_fxn.ReadGrid() + 1j*imag_fxn.ReadGrid(),
444
+ ngfftx=real_fxn.ngfftx,
445
+ ngffty=real_fxn.ngffty,
446
+ ngfftz=real_fxn.ngfftz
447
+ )
448
+ # remove xsf format
449
+ bandu_fxn = bandu_fxn.RemoveXSF()
450
+ # loop through fermi surface kpoints and calc overlap with bandu fxn
451
+ for i, kpt in enumerate(fermi_wfk.ReadWFK()):
452
+ if sym:
453
+ vals = self._OverlapsWithSym(kpt, bandu_fxn)
454
+ else:
455
+ vals = self._OverlapsNoSym(kpt, bandu_fxn)
456
+ if i == 0:
457
+ overlaps = vals
458
+ else:
459
+ overlaps = np.concatenate((overlaps, vals), axis=0)
460
+ # symmetrically permute overlap values if they were only calculated on irreducible BZ wedge when sym==False
461
+ if not sym:
462
+ symrel = fermi_wfk.symrel
463
+ nsym = fermi_wfk.nsym
464
+ kpts = fermi_wfk.kpts
465
+ all_kpts = np.zeros((1,3))
466
+ all_overlaps = np.zeros((1,nbands))
467
+ new_wfk = wc.WFK(symrel=np.array(symrel), nsym=nsym, nbands=nbands)
468
+ for i, kpt in enumerate(kpts):
469
+ unique_kpts, _ = new_wfk.Symmetrize(
470
+ points=kpt,
471
+ reciprocal=True
472
+ )
473
+ all_kpts = np.concatenate((all_kpts, unique_kpts), axis=0)
474
+ new_overlaps = np.repeat(overlaps[i,:].reshape((1,-1)), unique_kpts.shape[0], axis=0)
475
+ all_overlaps = np.concatenate((all_overlaps, new_overlaps), axis=0)
476
+ kpts = np.delete(all_kpts, 0, axis=0)
477
+ overlaps = np.delete(all_overlaps, 0, axis=0)
478
+ self.isosurface.points = np.matmul(kpts, self.isosurface.rec_latt)
479
+ # interpolate overlap values for smooth coloration
480
+ scalars = []
481
+ for i in range(overlaps.shape[1]):
482
+ interp_vals = self._InterpolateOverlaps(self.isosurface.contours[i], overlaps[:,i])
483
+ scalars.append(interp_vals)
484
+ return scalars
485
+ #-----------------------------------------------------------------------------------------------------------------#
486
+ # method to load save file
487
+ def Load(
488
+ self, save_path:str|list, **kwargs
489
+ ):
490
+ '''
491
+ Method for loading saved contours
492
+
493
+ Parameters
494
+ ----------
495
+ colormap : str | ListedColormap
496
+ Choose colormap for isosurface that colors according to assigned scalars of surface\n
497
+ Can use Colors class to use default or create custom colormaps\n
498
+ Default is matplotlib's plasma
499
+ bz_width : int
500
+ Line width of Brillouin Zone\n
501
+ Default is 3
502
+ bz_show : bool
503
+ Show the Brillouin Zone
504
+ Default is to show (True)
505
+ smooth : bool
506
+ Use smooth lightning techniques\n
507
+ Default is to use smoothing (True)
508
+ lighting : bool
509
+ Apply directional lighting to surface\n
510
+ Default is to enable directional lighting (True)
511
+ ambient : float
512
+ Intensity of light on surface\n
513
+ Default is 0.5
514
+ diffuse : float
515
+ Amount of light scattering\n
516
+ Default is 0.5
517
+ specular : float
518
+ Amount of reflected light\n
519
+ Default is 1.0 (max)
520
+ specular_power : float
521
+ Determines how sharply light is reflected\n
522
+ Default is 128.0 (max)
523
+ pbr : bool
524
+ Apply physics based rendering\n
525
+ Default is no physics based rendering (False)
526
+ metallic : float
527
+ Determine how metallic-looking the surface is, only considered with pbr\n
528
+ Default is 0.5
529
+ roughness : float
530
+ Determine how smooth/rough surface appear, only considered with pbr\n
531
+ Default is 0.5
532
+ color : str
533
+ Sets color of surface (colormap overwrites this)\n
534
+ Sets color of reflected light (colormap does not overwrite this)\n
535
+ Default is white
536
+ arrow : list
537
+ Parameters for plotting nesting vector on top of Fermi surface\n
538
+ Element_0 of list should be starting (or tail) position of arrow\n
539
+ Element_1 of list should be orientation of arrow with desired magnitude\n
540
+ Both the tail and orientation should be specified in reduced reciprocal space coordinates
541
+ arrow_color : str
542
+ Color of nesting arrow\n
543
+ Default is black
544
+ show_endpoints : bool
545
+ Plot points on the end of the arrow to make visualizing start and end easier\n
546
+ Default is to not show endpoints (False)
547
+ periodic_arrow : list
548
+ Adds periodic image of arrow that is translated [X,Y,Z] cells\n
549
+ Where X, Y, and Z are the cell indices
550
+ show_bands : float | list
551
+ Specifies the opacity of each band\n
552
+ If a single float is provided, all bands will be plotted with the same opacity\n
553
+ If a list is provided, each band will be plotted with the opacity of the respective list element
554
+ show_axes : bool
555
+ Plots reciprocal cell axes with a* as red, b* as green, and c* as blue\n
556
+ Default is to show axes (True)
557
+ cross_section : list
558
+ Plot cross section through surface\n
559
+ If one vector is provided, it is assumed to be the normal to the cross section plane\n
560
+ Else, cross section is defined by plane made by two vectors\n
561
+ Vectors should be specified in reduced coordinates
562
+ cross_width : float
563
+ Width of cross section\n
564
+ Default is 0.15
565
+ linear : bool
566
+ Cross section linearly fades out\n
567
+ Default is not fade out linearly (False)
568
+ two_dim : bool
569
+ Cross section is a 2D slice instead of a section\n
570
+ Default is to show cross section as 2D slice (True)
571
+ surface_vals : np.ndarray
572
+ A list of values defining the coloration of the isosurface
573
+ Default plots no coloration
574
+ '''
575
+ # functionality for adding multiple projections onto one surface
576
+ if type(save_path) == list:
577
+ all_scalars = []
578
+ for save in save_path:
579
+ with open(save, 'rb') as f:
580
+ kwargs_dict:dict = pkl.load(f)
581
+ self.isosurface:ic.Isosurface = pkl.load(f)
582
+ all_scalars.append(kwargs_dict['surface_vals'])
583
+ sizes = []
584
+ for surface_vals in all_scalars[0]:
585
+ sizes.append(surface_vals.shape)
586
+ new_scalars = [np.zeros((size)) for size in sizes]
587
+ for scalars in all_scalars:
588
+ for i, surface_vals in enumerate(scalars):
589
+ new_scalars[i] += surface_vals
590
+ kwargs_dict['surface_vals'] = new_scalars # type: ignore
591
+ # replot a single surface
592
+ else:
593
+ with open(save_path, 'rb') as f: # type: ignore
594
+ kwargs_dict:dict = pkl.load(f)
595
+ self.isosurface:ic.Isosurface = pkl.load(f)
596
+ for k, val in kwargs.items(): # type: ignore
597
+ kwargs_dict[k] = val # type: ignore
598
+ self.save = False
599
+ self.plot = True
600
600
  self.Plot(**kwargs_dict) # type: ignore