forked from rjacobs914/VASP-Tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVASP_PostProcessing.py
1842 lines (1626 loc) · 98.4 KB
/
VASP_PostProcessing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
__author__ = 'Ryan Jacobs'
__version__ = '2.0'
__date__ = 'Last updated March 27, 2017'
"""
VASP_PostProcessing is a module designed to handle an assortment of useful post-processing tasks and calculations
based on completed DFT data, including: DOS analysis, thermodynamic stability, Wulff construction, calculation of
diffusion coefficients, and more.
"""
import matplotlib
matplotlib.use('Agg')
from matplotlib import pyplot
from pymatgen.analysis import wulff
from pymatgen.core import lattice
from pymatgen.analysis.diffusion_analyzer import DiffusionAnalyzer
from pymatgen.io.vasp.outputs import Xdatcar
from pymatgen.matproj.rest import MPRester
from pymatgen.phasediagram.entries import PDEntry
from pymatgen.core.composition import Composition
from pymatgen.phasediagram.maker import PhaseDiagram
from pymatgen.phasediagram.analyzer import PDAnalyzer
from VASP_Analyzer import PoscarAnalyzer, OutcarAnalyzer, OszicarAnalyzer
import warnings
import math
import numpy as np
import re
from scipy import integrate
import xlsxwriter
class WulffAnalyzer(object):
"""Class used to conduct Wulff construction analysis. Mainly relies on Wulff tools from pymatgen
args:
poscar: (str) The name of a POSCAR file
miller_indices: (list of tuples) A list of miller indices, where each miller index is represented as a tuple.
For example, miller_indices = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
surface_energies: (list of floats) A list of calculated surface energies to pair with the list of miller indices.
instance methods:
get_wulff_construction : (wulff object) Creates a wulff construction and saves an image of it to a file
get_wulff_area_fraction : (dict) Returns the area fraction of each miller index in the Wulff construction
get_wulff_surface_energies : (dict) Returns the surface energy of each miller index in the Wulff construction
"""
def __init__(self, miller_indices, surface_energies, poscar="POSCAR"):
self.miller_indices = miller_indices
self.surface_energies = surface_energies
self.poscar = poscar
def get_wulff_construction(self):
#Need to get lattice object from POSCAR file
lattice_parameters = PoscarAnalyzer(poscar=self.poscar).get_lattice_parameters
lattice_object = lattice.Lattice(lattice_parameters)
#Make Wulff construction
wulff_construction = wulff.WulffShape(lattice=lattice_object, miller_list=self.miller_indices,
e_surf_list=self.surface_energies)
wulff_plot = wulff_construction.get_plot()
wulff_plot.savefig('wulff_construction.pdf')
return wulff_construction
def get_wulff_area_fraction(self):
wulff_construction = self.get_wulff_construction
wulff_area = wulff_construction.area_fraction_dict
return wulff_area
def get_wulff_surface_energies(self):
wulff_construction = self.get_wulff_construction
wulff_energies = wulff_construction.miller_energy_dict
return wulff_energies
class LocpotAnalyzer(object):
"""This class is designed plot the LOCPOT file, which contains data of the electrostatic potential as a function of
position in the simulated supercell.
args:
poscar: (str) The name of a POSCAR file
outcar: (str) The name of an OUTCAR file
locpot: (str) the name of a LOCPOT file
instance methods:
get_electrostatic_potential : (list) Returns a list of the planar averaged electrostatic potential from LOCPOT
file, and also saves the data to an excel file and saves a plot of the data.
get_workfunction : (tuple of floats) Returns a tuple containing the top surface and bottom surface work functions
get_vacuum_energy : (float) Returns the vacuum energy of either the top or bottom surface.
args:
surface : (str) Set as "Top" to calculate vacuum energy of top surface, analogously for "Bottom"
get_empirical_delta_workfunction : (float) Returns the difference in top and bottom surface work functions as
calculated using the VASP calculated dipole and the Helmholtz equation from electrostatics.
"""
def __init__(self, poscar="POSCAR", outcar="OUTCAR", locpot="LOCPOT"):
self.poscar = poscar
self.outcar = outcar
self.locpot = locpot
def get_electrostatic_potential(self):
#Plot the electrostatic potential and save to a file
pyplot.figure(figsize=(8,6), dpi=80)
Nx, Ny, Nz, z_coord, LOCPOTdata_column = self._parse_locpot()
planaravg, avg_planaravg = self._calc_planaravg()
pyplot.plot(z_coord, planaravg)
pyplot.xlabel('Z-coordinate (arbitrary units)')
pyplot.ylabel('Planar averaged electrostatic potential (eV)')
pyplot.title('Electrostatic potential')
pyplot.savefig('ElectrostaticPotential.pdf')
#Output the raw ESP data to an excel file
excel_file = xlsxwriter.Workbook('Electrostatic_potential_data.xlsx')
excel_sheet = excel_file.add_worksheet('ESP data')
bold = excel_file.add_format({'bold': True})
column_name_dict = {"0" : "Supercell z-coordinate (arb units)", "1" : "Electrostatic potential (eV)"}
row = 0
for key, value in column_name_dict.iteritems():
excel_sheet.write(int(row), int(key), value, bold)
row = 1
#values_dict = {"0" : z_coord, "1" : planaravg}
for index in range(len(z_coord)):
excel_sheet.write(int(row), 0, z_coord[index])
excel_sheet.write(int(row), 1, planaravg[index])
row += 1
return planaravg
def get_workfunction(self):
fermi_energy = OutcarAnalyzer(self.outcar).get_fermi_energy()
vacuum_energy_top = self.get_vacuum_energy(surface="Top")
vacuum_energy_bottom = self.get_vacuum_energy(surface="Bottom")
workfunction_top = vacuum_energy_top-fermi_energy
workfunction_bottom = vacuum_energy_bottom-fermi_energy
return (workfunction_top, workfunction_bottom, vacuum_energy_top, vacuum_energy_bottom)
def get_vacuum_energy(self, surface):
Nx, Ny, Nz, z_coord, LOCPOTdata_column = self._parse_locpot()
planaravg, avg_planaravg = self._calc_planaravg()
# These parameters for finding the values of the vacuum level from each surface seem to work well
# This will determine how flat the region needs to be
electrostatic_difference_tolerance = 0.05
# This determines how many data points for the vacuum level will be averaged together
count_tol = 5
# This determines how far from the supercell edges to start looking at values and will determine how large of a flat region needs to be present
Nz_tol = 10
vacuum_energy_list = []
count = 0
# Loop over range of z-coordinate electrostatic potential values
if surface == "Top":
for index in range(Nz):
# Only consider first count_tol values to average electrostatic potential in vacuum region
if count < count_tol:
# Only consider Nz values that are Nz-Nz_tol away from top supercell edge and Nz_tol away from bottom supercell edge.
if index < Nz-Nz_tol and index > Nz_tol:
# Only consider electrostatic planaravg values that differ by less than electrostatic_difference_tolerance and are positive.
if abs(planaravg[index+Nz_tol]-planaravg[index-Nz_tol]) < electrostatic_difference_tolerance and planaravg[index] > 0:
vacuum_energy_list.append(planaravg[index])
count += 1
elif surface == "Bottom":
Nz_reverse = []
for index in range(Nz):
Nz_reverse.append(Nz-index)
for entry in Nz_reverse:
if count < count_tol:
if entry < Nz-Nz_tol and entry > Nz_tol:
if abs(planaravg[entry+Nz_tol]-planaravg[entry-Nz_tol]) < electrostatic_difference_tolerance and planaravg[entry] > 0:
vacuum_energy_list.append(planaravg[entry])
count += 1
else:
raise ValueError('"surface" must be set to either "Top" or "Bottom"')
sum_vacuum_energy = 0
for entry in vacuum_energy_list:
sum_vacuum_energy += entry
# Remove outliers to try to get a more reasonable vacuum level
vacuum_energy_top = sum_vacuum_energy/len(vacuum_energy_list)
stdev_vac = np.std(vacuum_energy_list)
for entry in vacuum_energy_list:
if abs(vacuum_energy_top - entry) > stdev_vac:
vacuum_energy_list.remove(entry)
sum_vacuum_energy = 0
for entry in vacuum_energy_list:
sum_vacuum_energy += entry
vacuum_energy = sum_vacuum_energy/len(vacuum_energy_list)
return vacuum_energy
def get_empirical_delta_workfunction(self):
#Obtain the work function difference between the two surfaces using the Helmholtz equation
#This code assumes the surface is oriented in the c-direction, so that a x b is the surface area
dipole = OutcarAnalyzer(self.outcar).get_dipole()
lattice_parameters = PoscarAnalyzer(self.poscar).get_lattice_parameters()
area = lattice_parameters[0][0]*lattice_parameters[1][1]
delta_workfunction = ((-181)*dipole)/area #units of eV, dipole is in eV-Ang, area, in Ang^2
return delta_workfunction
def _parse_locpot(self):
LOCPOT = open(self.locpot, "r")
LOCPOTdata = LOCPOT.readlines()
#Eliminate the header lines of the LOCPOT file
total_atoms = int(PoscarAnalyzer(self.poscar).get_total_atoms())
dataline = LOCPOTdata[total_atoms+9]
Nx = int(dataline.split()[0])
Ny = int(dataline.split()[1])
Nz = int(dataline.split()[2])
z_coord = []
for index in range(Nz):
z_coord.append(index)
count = 0
while count < total_atoms+10:
LOCPOTdata.pop(0)
count += 1
LOCPOTdata_column = []
for entry in LOCPOTdata:
split_entry = entry.split()
for entry in split_entry:
entry = float(entry)
LOCPOTdata_column.append(entry)
LOCPOT.close()
return Nx, Ny, Nz, z_coord, LOCPOTdata_column
def _calc_planaravg(self):
Nx, Ny, Nz, z_coord, LOCPOTdata_column = self._parse_locpot()
planaravg = []
for z_index in range(Nz):
planardata = [] #Need to reset planardata values for each z value
sum_planardata = 0
for y_index in range(Ny):
for x_index in range(Nx):
datapoint = LOCPOTdata_column[Ny*Nx*(z_index)+Nx*(y_index)+x_index]
planardata.append(datapoint)
for index in range(len(planardata)):
sum_planardata = sum_planardata + planardata[index]
planaravg.append(sum_planardata/len(planardata))
avg_planaravg = 0
sum_planaravg = 0
for entry in planaravg:
sum_planaravg = sum_planaravg + entry
avg_planaravg = sum_planaravg/Nz
return planaravg, avg_planaravg
class DiffusionAnalyzerAIMD(object):
"""This class wraps to the pymatgen tools to analyze the output of Ab Initio Molecular Dynamics (AIMD) runs in order
to calculate and plot the mean-squared displacement (MSD) and calculate the diffusivity components and total
diffusivity of a given element.
args:
diffusing_species: (str) The name of the element to calculate the diffusion coefficient of
temperature: (int) The temperature (in K) of the AIMD simulation
timestep: (int) The timestep (in femtoseconds) of the AIMD simulation
xdatcar: (str) The name of an XDATCAR file
plot_msd: (bool) Whether or not to plot the MSD.
**It is advised that the following variables be left as their default values, but if you change them be sure to
do your own testing**
steps_to_ignore: (int) The number of initial time steps to leave out of the calculation, generally the ones
prior to when equilibrium is reached. 100 is typically a good value.
smoothing: (str) Determines whether or not to smooth the MSD. Choose from three modes: "max", "constant", or "None".
Recommended to be set to "max".
min_obs: (int) This is only used if smoothing = "max". In this mode, it specifies the minimum number of
observations to have before data are included in the MSD calculation. The suggested default is 30.
avg_nsteps: (int) This is only used if smoothing = "constant". This determines how many timesteps are averaged over
when iterating over each timestep. The suggested default is 1000.
instance methods:
get_diffusion_analysis_from_xdatcars : (dict) Computes the diffusivity of the species of interest and provides
data of diffusivity and conductivity of the species. Also, plots the MSD and saves to file.
"""
def __init__(self, diffusing_species, temperature, timestep, xdatcar="XDATCAR", steps_to_ignore=100,
smoothing="max", min_obs=30, avg_nsteps=1000, plot_msd=True, loop_directories=False):
self.diffusing_species = diffusing_species
self.temperature = temperature
self.timestep = timestep
self.steps_to_ignore = steps_to_ignore
self.smoothing = smoothing
self.min_obs = min_obs
self.avg_nsteps = avg_nsteps
self.plot_msd = plot_msd
self.loop_directories = loop_directories
self.xdatcar = xdatcar
def get_diffusion_analysis_from_xdatcars(self):
structure_list = self._prepare_xdatcar_from_dirs()
diffusion_analysis = DiffusionAnalyzer.from_structures(structures = structure_list, specie = str(self.diffusing_species),
temperature = float(self.temperature), time_step = int(self.timestep), step_skip = 1, #Don't change this
smoothed = str(self.smoothing), min_obs = int(self.min_obs), avg_nsteps = int(self.avg_nsteps))
outputdict = diffusion_analysis.get_summary_dict(include_msd_t=True)
if self.plot_msd == bool(True):
diffusion_analysis.export_msdt("MSD.csv")
dt = outputdict["dt"]
msd = outputdict["msd"]
pyplot.figure(figsize=(8,6), dpi=80)
pyplot.plot(dt, msd)
pyplot.xlabel('time (fs)')
pyplot.ylabel('MSD (Ang^2)')
pyplot.title('MSD vs time')
pyplot.savefig('MSD.pdf')
#print "D of %s (cm^2/sec): %3.3e +/- %3.3e" % (self.diffusing_species, outputdict["D"], outputdict["D_sigma"])
#print "S of %s (mS/cm): %3.3e +/- %3.3e" % (self.diffusing_species, outputdict["S"], outputdict["S_sigma"])
#print "D components of %s (cm^2/sec):" % (self.diffusing_species), outputdict["D_components"]
#print "D sigma components of %s (cm^2/sec):" % (self.diffusing_species), outputdict["D_components_sigma"]
#print "Specie %s, temp %3.3fK, timestep %i fs, skipping %i initial structures, smoothing %s with min_obs of %i and avg_nsteps of %i" % (self.diffusing_species, self.temperature, self.timestep, self.steps_to_ignore, self.smoothing, self.min_obs, self.avg_nsteps)
return outputdict
def _prepare_xdatcar_from_dirs(self):
structure_list = []
myxdatcar = Xdatcar(self.xdatcar)
structure_list.extend(myxdatcar.structures)
count = 0
print "Removing %i initial structures from structure list." % self.steps_to_ignore
while count < self.steps_to_ignore:
structure_list.pop(0)
count += 1
print "%i structures left to analyze." % len(structure_list)
print "Running diffusion analyzer."
structures = open("structurelist.txt", "w")
structures.write(str(structure_list))
structures.close()
return structure_list
class DoscarAnalyzer(object):
"""This class is used to obtain plots of projected and total densities of states, and also to obtain useful
electronic structure quantities from the DOS data, such as bandgap and band centers (centroid of projected DOS for
certain elements).
args:
poscar: (str) the name of a POSCAR file
incar: (str) the name of an INCAR file
outcar: (str) the name of an OUTCAR file
doscar: (str) the name of a DOSCAR file
energy_cutoff: (float) A user-specified cutoff energy to select a special energy for band center calculations.
Typically you won't use this input flag.
instance methods:
plot_total_dos : (None) saves a plot of the total DOS to file
plot_projected_dos : (None) saves a plot of the projected DOS to file
get_bandgap_from_dos : (float) gives the bandgap of the system calculated from the DOS
get_O_chargetransfergap : (float) gives the charge transfer gap between occupied and unoccupied O states. Only
relevant for systems containing O.
get_bandcenters : (list of dict) returns a list of dicts containing the band centers of each orbital type (s, p, etc.)
of each element in the system.
args:
write_dicts_to_file : (bool) whether to write the bandcenter dicts to a text file.
get_effective_dos : (tuple of floats) returns the effective density of states for the valence and conduction bands,
as well as the integral of the valence and conduction bands.
args:
temperature : (int) The effective DOS is T-dependent. Specify a T value (in K) to do the analysis.
"""
def __init__(self, poscar="POSCAR", incar="INCAR", outcar="OUTCAR", doscar="DOSCAR", energy_cutoff=0):
self.poscar = poscar
self.incar = incar
self.outcar = outcar
self.doscar = doscar
self.energy_cutoff = energy_cutoff
def plot_total_dos(self):
total_dos_up_list, total_dos_down_list, dos_s_list, dos_p_list, dos_d_list = self._parse_dos_atom_type()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
dosdata, total_dos = self._cleanup_projected_dos()
number_of_atoms = PoscarAnalyzer(poscar=self.poscar).get_total_atoms()
total_dos_down_list_neg = []
for index in range(len(total_dos_down_list)):
total_dos_down_list_neg.append(-1*total_dos_down_list[index])
total_dos_normalized = []
for index in range(len(total_dos)):
total_dos_normalized.append(total_dos[index]/number_of_atoms)
pyplot.figure(figsize=(8,6), dpi=80)
pyplot.plot(energy, total_dos_up_list, 'b', label='Total DOS spin up')
pyplot.plot(energy, total_dos_down_list_neg, 'g', label='Total DOS spin down')
#pyplot.plot(energy, total_dos_normalized, 'k', label='Total DOS')
pyplot.plot([0,0], [min(total_dos_up_list), max(total_dos_up_list)], 'k-', label='Fermi energy')
pyplot.xlabel('Energy (E-EFermi, eV)')
pyplot.ylabel('Total Densities of States (states/eV-atom)')
pyplot.title('Total Density of States')
pyplot.legend()
#pyplot.ylim([-1*max(total_dos_down_list), max(total_dos_up_list)])
pyplot.ylim([-5, 5])
pyplot.xlim([-10, 10])
pyplot.savefig('TotalDOS.pdf')
return None
def plot_projected_dos(self):
sumtotaldosup, sumtotaldosdown, dos_s_list, dos_p_list, dos_d_list = self._parse_dos_atom_type()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
nedos = self._calc_nedos()
atom_amounts = PoscarAnalyzer(self.poscar).get_atom_amounts()
element_names = PoscarAnalyzer(self.poscar).get_element_names()
pyplot.figure(figsize=(8,6), dpi=80)
#Need to normalize and plot PDOS by element type
colors = "bgrkybgrkybgrkybgrkybgrky"
for element_number in range(len(atom_amounts)):
dos_to_plot_up = []
dos_to_plot_down = []
for index in range(nedos):
dos_to_plot_up.append((dos_s_list[index + nedos*element_number][0] + dos_p_list[index + nedos*element_number][0] + dos_d_list[index + nedos*element_number][0])/atom_amounts[element_number])
dos_to_plot_down.append(-1*(dos_s_list[index + nedos*element_number][1] + dos_p_list[index + nedos*element_number][1] + dos_d_list[index + nedos*element_number][1])/atom_amounts[element_number])
pyplot.plot(energy, dos_to_plot_up, color=colors[element_number], label='PDOS for %s' % (element_names[element_number]))
pyplot.plot(energy, dos_to_plot_down, color=colors[element_number])
pyplot.xlabel('Energy (E-EFermi, eV)')
pyplot.ylabel('Projected Densities of States (states/eV-atom)')
pyplot.title('Projected Density of States')
pyplot.legend()
pyplot.xlim([-10, 10])
pyplot.ylim([-5, 5])
pyplot.savefig('ProjectedDOS.pdf')
return None
def get_bandgap_from_dos(self):
E_VBM, E_CBM = self._calc_VBM_and_CBM()
Egap = E_CBM - E_VBM
if Egap < 0.1:
Egap = 0
#print "The bandgap of this material from DOS is %s eV" % (Egap)
bandgapfile = open("bandgap_fromdos.txt", "w")
Egap = str(Egap)
bandgapfile.write(Egap)
bandgapfile.close()
return Egap
def get_O_chargetransfergap(self):
# Same method as regular bandgap, but now just doing it between occupied and unoccupied O states
total_dos_up_list, total_dos_down_list, dos_s_list, dos_p_list, dos_d_list = self._parse_dos_atom_type()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
nedos = self._calc_nedos()
atom_amounts = PoscarAnalyzer(self.poscar).get_atom_amounts()
element_names = PoscarAnalyzer(self.poscar).get_element_names()
Etolerance = 0.05
DOStolerance = 0.001
# Find which element, if any, is O. This analysis only pertains to O, so throw error if no O found
if "O" not in element_names:
raise TypeError("No oxygen was detected in your element list. The charge transfer gap calculation only pertains to O states. Exiting calculation...")
# Find out which entry is O and perform charge transfer gap calculation
for element_number in range(len(element_names)):
if element_names[element_number] == "O":
# Add the respective p up and p down states based on the element entry
# print "O is element number", element_number
dos_O_p_total = []
for dos_row in range(nedos):
dos_O_p_total.append((dos_p_list[dos_row + nedos * element_number][0] +
dos_p_list[dos_row + nedos * element_number][1]) / atom_amounts[
element_number])
# Find the position of the VBM
for index in range(nedos):
if energy[index] >= 0 - Etolerance:
if energy[index] <= 0 + Etolerance:
E_VBM = energy[index]
VBM_index = index
elif energy[index] < 0 - Etolerance or energy[index] > 0 + Etolerance:
pass
E_above_VBM_range = []
for index in range(nedos - VBM_index):
E_above_VBM_range.append(VBM_index + index)
# Find the position of the last O state after Efermi (where O states go to zero)
for entry in E_above_VBM_range:
if abs(dos_O_p_total[entry] - dos_O_p_total[entry - 1]) > 0 + DOStolerance:
pass
elif abs(dos_O_p_total[entry] - dos_O_p_total[entry - 1]) <= 0 + DOStolerance:
new_E_VBM = energy[entry]
new_VBM_index = entry
break
new_E_above_VBM_range = []
for index in range(nedos - new_VBM_index):
new_E_above_VBM_range.append(new_VBM_index + index)
for entry in new_E_above_VBM_range:
if abs(dos_O_p_total[entry] - dos_O_p_total[entry - 1]) > 0 + DOStolerance:
new_CBM_index = entry
new_E_CBM = energy[entry]
break
elif abs(dos_O_p_total[entry] - dos_O_p_total[entry - 1]) < 0 + DOStolerance:
pass
Echgtransgap = new_E_CBM - new_E_VBM
if Echgtransgap < 0.1:
Echgtransgap = 0
#print "The charge transfer gap for O in this material is %s eV" % (Echgtransgap)
bandgapfile = open("chgtransgap.txt", "w")
Echgtransgap = str(Echgtransgap)
bandgapfile.write(Echgtransgap)
bandgapfile.close()
return Echgtransgap
def get_bandcenters(self, write_dicts_to_file=True):
nedos = self._calc_nedos()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
sumtotaldosup, sumtotaldosdown, dos_s_list, dos_p_list, dos_d_list = self._parse_dos_atom_type()
pa = PoscarAnalyzer(self.poscar)
element_names = pa.get_element_names()
atom_amounts = pa.get_atom_amounts()
band_center_list = []
band_center_occ_list = []
for element_number in range(len(atom_amounts)):
dos_s_list_E = []
dos_s_list_total_occ = []
dos_p_list_E = []
dos_p_list_total_occ = []
dos_d_list_E = []
dos_d_list_total_occ = []
dos_s_list_total = []
dos_s_list_E_occ = []
dos_p_list_total = []
dos_p_list_E_occ = []
dos_d_list_total = []
dos_d_list_E_occ = []
for index in range(nedos):
dos_s_list_total.append(
dos_s_list[index + nedos * element_number][0] + dos_s_list[index + nedos * element_number][1])
dos_p_list_total.append(
dos_p_list[index + nedos * element_number][0] + dos_p_list[index + nedos * element_number][1])
dos_d_list_total.append(
dos_d_list[index + nedos * element_number][0] + dos_d_list[index + nedos * element_number][1])
dos_s_list_E.append(dos_s_list_total[index] * energy[index])
dos_p_list_E.append(dos_p_list_total[index] * energy[index])
dos_d_list_E.append(dos_d_list_total[index] * energy[index])
if index < index_Fermi:
dos_s_list_total_occ.append(
dos_s_list[index + nedos * element_number][0] + dos_s_list[index + nedos * element_number][1])
dos_p_list_total_occ.append(
dos_p_list[index + nedos * element_number][0] + dos_p_list[index + nedos * element_number][1])
dos_d_list_total_occ.append(
dos_d_list[index + nedos * element_number][0] + dos_d_list[index + nedos * element_number][1])
dos_s_list_E_occ.append(dos_s_list_total[index] * energy[index])
dos_p_list_E_occ.append(dos_p_list_total[index] * energy[index])
dos_d_list_E_occ.append(dos_d_list_total[index] * energy[index])
with warnings.catch_warnings():
warnings.simplefilter("ignore")
s_bandcenter = integrate.cumtrapz(dos_s_list_E, energy)[-1] / \
integrate.cumtrapz(dos_s_list_total, energy)[-1]
p_bandcenter = integrate.cumtrapz(dos_p_list_E, energy)[-1] / \
integrate.cumtrapz(dos_p_list_total, energy)[-1]
d_bandcenter = integrate.cumtrapz(dos_d_list_E, energy)[-1] / \
integrate.cumtrapz(dos_d_list_total, energy)[-1]
s_bandcenter_occ = integrate.cumtrapz(dos_s_list_E_occ, energy_occ)[-1] / \
integrate.cumtrapz(dos_s_list_total_occ, energy_occ)[-1]
p_bandcenter_occ = integrate.cumtrapz(dos_p_list_E_occ, energy_occ)[-1] / \
integrate.cumtrapz(dos_p_list_total_occ, energy_occ)[-1]
d_bandcenter_occ = integrate.cumtrapz(dos_d_list_E_occ, energy_occ)[-1] / \
integrate.cumtrapz(dos_d_list_total_occ, energy_occ)[-1]
band_center_dict = {}
band_center_dict_occ = {}
element = element_names[element_number]
band_center_dict[str(element) + "_s"] = "%3.3f" % s_bandcenter
band_center_dict[str(element) + "_p"] = "%3.3f" % p_bandcenter
band_center_dict[str(element) + "_d"] = "%3.3f" % d_bandcenter
band_center_dict_occ[str(element) + "_s_occ"] = "%3.3f" % s_bandcenter_occ
band_center_dict_occ[str(element) + "_p_occ"] = "%3.3f" % p_bandcenter_occ
band_center_dict_occ[str(element) + "_d_occ"] = "%3.3f" % d_bandcenter_occ
if write_dicts_to_file == bool(True):
# Write bandcenter dicts to text files
filename = "%s_bandcenters.txt" % element
file = open(filename, "w")
for key, value in band_center_dict.items():
file.write(str(key)+"="+str(value)+"\n")
if key == "O_p":
Opband = open("O_pband_values.txt", "w")
Opband.write(str(value))
Opband.close()
file.close()
filename = "%s_bandcenters_occ.txt" % element
file = open(filename, "w")
for key, value in band_center_dict_occ.items():
file.write(str(key)+"="+str(value)+"\n")
file.close()
band_center_list.append(band_center_dict)
band_center_occ_list.append(band_center_dict_occ)
return band_center_list, band_center_occ_list
def get_effective_dos(self, temperature=1000):
boltz = 8.619 * 10 ** -5 #units of eV/K
E_VBM, E_CBM = self._calc_VBM_and_CBM()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
dosdata, total_dos = self._cleanup_projected_dos()
# Parse total DOS to VBM and CBM components
total_dos_VBM = []
energy_to_integrate_VBM = []
total_dos_CBM = []
energy_to_integrate_CBM = []
for index in range(len(energy)):
# if energy[index] <= E_VBM and energy[index] > E_VBM-10:
if energy[index] <= E_VBM:
total_dos_VBM.append(total_dos[index])
energy_to_integrate_VBM.append(energy[index])
# elif energy[index] > E_CBM and energy[index] < E_CBM+10:
elif energy[index] > E_CBM:
total_dos_CBM.append(total_dos[index])
energy_to_integrate_CBM.append(energy[index])
else:
continue
# Calculate the effective DOS of the valence band
VBM_effective_dos_integrand = []
for index in range(len(total_dos_VBM)):
VBM_effective_dos_integrand.append(
total_dos_VBM[index] * np.exp(-1 * (E_VBM - energy_to_integrate_VBM[index]) / (boltz * temperature)))
VBM_effective_dos = integrate.cumtrapz(VBM_effective_dos_integrand, energy_to_integrate_VBM)[-1]
VBM_int = integrate.cumtrapz(total_dos_VBM, energy_to_integrate_VBM)[-1]
# Calculate the effective DOS of the conduction band
CBM_effective_dos_integrand = []
for index in range(len(total_dos_CBM)):
CBM_effective_dos_integrand.append(
total_dos_CBM[index] * np.exp(-1 * (energy_to_integrate_CBM[index] - E_CBM) / (boltz * temperature)))
CBM_effective_dos = integrate.cumtrapz(CBM_effective_dos_integrand, energy_to_integrate_CBM)[-1]
CBM_int = integrate.cumtrapz(total_dos_CBM, energy_to_integrate_CBM)[-1]
#print "The effective DOS of the VB is:", VBM_effective_dos
#print "The effective DOS of the CB is:", CBM_effective_dos
#print "The integral of the VB DOS is:", VBM_int
#print "The integral of the CB DOS is:", CBM_int
return (VBM_effective_dos, CBM_effective_dos, VBM_int, CBM_int)
def _calc_nedos(self):
# Obtain the value of NEDOS from INCAR file
incar = open(self.incar, "r")
nedos_found = False
for line in incar:
if 'NEDOS' in line:
nedos_found = True
NEDOS_info = line
NEDOS_info = re.findall('\d+',NEDOS_info)
for entry in NEDOS_info:
nedos = int(entry)
#If no NEDOS is specified in INCAR, use default value of 301
if nedos_found == bool(False):
nedos = int(301)
incar.close()
return nedos
def _make_energy_lists(self):
Energy = []
dosdata, total_dos = self._cleanup_projected_dos()
nedos = self._calc_nedos()
fermi = OutcarAnalyzer(self.outcar).get_fermi_energy()
# Create the list of energies
count = 0
for entry in dosdata:
if count < nedos:
Energy.append(float(entry[0:12])-fermi)
count += 1
count = 0
for entry in Energy:
tol = 0.05
count += 1
if entry > 0-tol and entry < 0+tol: #So the Energy should be between -0.2 and 0.2 eV
index_Fermi = count
count = 0
if self.energy_cutoff is not None:
for entry in Energy:
tol = 0.05
count += 1
if entry > self.energy_cutoff-tol and entry < self.energy_cutoff+tol:
index_cutoff = count
Energy_occ = []
Energy_unocc = []
for index in range(nedos):
if index < index_Fermi:
Energy_occ.append(Energy[index])
if index > index_Fermi:
Energy_unocc.append(Energy[index])
# This is used to create a custom energy cutoff that is not at the Fermi level
Energy_range_below = []
Energy_range_above = []
if self.energy_cutoff is not None:
for index in range(nedos):
if index < index_cutoff:
Energy_range_below.append(Energy[index])
if index > index_cutoff:
Energy_range_above.append(Energy[index])
return Energy, Energy_unocc, Energy_occ, index_Fermi, index_cutoff
def _cleanup_projected_dos(self):
# Remove the header lines of the DOSCAR file
doscar = open(self.doscar, "r")
dosdata = doscar.readlines()
nedos = self._calc_nedos()
number_of_atoms = int(PoscarAnalyzer(self.poscar).get_total_atoms())
# Isolate just the total DOS from the DOS data
total_dos_rows = []
total_dos = []
count = 0
for line in dosdata:
if count > 5 and count < nedos+6:
total_dos_rows.append(line)
count += 1
for index in range(len(total_dos_rows)):
#print float(total_dos_rows[index].split()[1])
total_dos.append(float(total_dos_rows[index].split()[1]) + float(total_dos_rows[index].split()[2]))
count = 0
while count < nedos + 7:
dosdata.pop(0)
count += 1
# Eliminate the interspersed junk lines between each atom in DOS data
for index in range(len(dosdata)-number_of_atoms):
if index % nedos == 0 and index != 0:
dosdata.pop(index)
doscar.close()
return dosdata, total_dos
def _parse_projected_dos_string_to_float(self):
dosdata_fromfile, total_dos_fromfile = self._cleanup_projected_dos()
dosdata = []
#Split the lines of the doscar file and make all entries floats rather than strings
for line in dosdata_fromfile:
line = line.split()
for index in range(len(line)):
line[index] = float(line[index])
dosdata.append(line)
return dosdata
def _parse_dos_orbital_type(self):
dosdata = self._parse_projected_dos_string_to_float()
doscolumns = len(dosdata[0])
dosdata_orbital_summed = []
dosdata_orbital_summed_float = []
for dosrow in range(len(dosdata)):
sum_s_up = 0
sum_s_down = 0
sum_p_up = 0
sum_p_down = 0
sum_d_up = 0
sum_d_down = 0
for doscolumn in range(doscolumns):
if doscolumn > 0: #Don't include values of energy in sum, only want to sum DOS values
if doscolumn == 1: #s up
sum_s_up += dosdata[dosrow][doscolumn]
if doscolumn == 2:
sum_s_down += dosdata[dosrow][doscolumn]
if doscolumn == 3:
sum_p_up += dosdata[dosrow][doscolumn] + dosdata[dosrow][doscolumn+2] + dosdata[dosrow][doscolumn+4] #Adding columns 3, 5, and 7, which are px, py and pz up
if doscolumn == 4:
sum_p_down += (dosdata[dosrow][doscolumn] + dosdata[dosrow][doscolumn+2] + dosdata[dosrow][doscolumn+4]) #Adding columns 4, 6, and 8, which are px, py and pz down
if doscolumn > 4 and doscolumn < 9:
pass
if doscolumn == 9:
sum_d_up += dosdata[dosrow][doscolumn] + dosdata[dosrow][doscolumn+2] + dosdata[dosrow][doscolumn+4] + dosdata[dosrow][doscolumn+6] + dosdata[dosrow][doscolumn+8] #Adding columns 9, 11, 13, 15 and 17 which are dxy, dyz, dz2, dxz, dx2 up
if doscolumn == 10:
sum_d_down += (dosdata[dosrow][doscolumn] + dosdata[dosrow][doscolumn+2] + dosdata[dosrow][doscolumn+4] + dosdata[dosrow][doscolumn+6] + dosdata[dosrow][doscolumn+8]) #Adding columns 10, 12, 14, 16 and 18 which are dxy, dyz, dz2, dxz, dx2 down
if doscolumn > 10:
pass
dosdata_orbital_summed.append(str(sum_s_up)+" "+str(sum_s_down)+" "+str(sum_p_up)+" "+str(sum_p_down)+" "+str(sum_d_up)+" "+str(sum_d_down)+"\n")
for line in dosdata_orbital_summed:
line = line.split()
for index in range(len(line)):
line[index] = float(line[index])
dosdata_orbital_summed_float.append(line)
return dosdata_orbital_summed_float
def _parse_dos_atom_type(self):
dosdata = self._parse_dos_orbital_type()
nedos = self._calc_nedos()
atom_amounts = PoscarAnalyzer(self.poscar).get_atom_amounts()
element_names = PoscarAnalyzer(self.poscar).get_element_names()
element_iteration_count = 0
dos_sum_s_up_list = []
dos_sum_s_down_list = []
dos_sum_p_up_list = []
dos_sum_p_down_list = []
dos_sum_d_up_list = []
dos_sum_d_down_list = []
for index in range(len(atom_amounts)): #Loop over each atom type
#print "analyzing atom type", element_names[index]
#print "element", element_names[index], "is element number", element_iteration_count
for dos_row in range(nedos):
dos_sum_s_up = 0
dos_sum_s_down = 0
dos_sum_p_up = 0
dos_sum_p_down = 0
dos_sum_d_up = 0
dos_sum_d_down = 0
for atom_number in range(atom_amounts[index]):
if index == 0:
true_atom_number = atom_number
if index == 1:
true_atom_number = atom_number + atom_amounts[index-1]
if index == 2:
true_atom_number = atom_number + atom_amounts[index-1] + atom_amounts[index-2]
if index == 3:
true_atom_number = atom_number + atom_amounts[index-1] + atom_amounts[index-2] + atom_amounts[index-3]
if index == 4:
true_atom_number = atom_number + atom_amounts[index-1] + atom_amounts[index-2] + atom_amounts[index-3] + atom_amounts[index-4]
if index == 5:
true_atom_number = atom_number + atom_amounts[index-1] + atom_amounts[index-2] + atom_amounts[index-3] + atom_amounts[index-4] + atom_amounts[index-5]
dos_sum_s_up += dosdata[dos_row + nedos*true_atom_number][0]
dos_sum_s_down += dosdata[dos_row + nedos*true_atom_number][1]
dos_sum_p_up += dosdata[dos_row + nedos*true_atom_number][2]
dos_sum_p_down += dosdata[dos_row + nedos*true_atom_number][3]
dos_sum_d_up += dosdata[dos_row + nedos*true_atom_number][4]
dos_sum_d_down += dosdata[dos_row + nedos*true_atom_number][5]
dos_sum_s_up_list.append(dos_sum_s_up)
dos_sum_s_down_list.append(dos_sum_s_down)
dos_sum_p_up_list.append(dos_sum_p_up)
dos_sum_p_down_list.append(dos_sum_p_down)
dos_sum_d_up_list.append(dos_sum_d_up)
dos_sum_d_down_list.append(dos_sum_d_down)
element_iteration_count += 1
# The dos_sum lists will contain nedos*number_of_elements lines. So, 4 elements with 2000 nedos will be 8000 lines.
dos_s_list = []
dos_p_list = []
dos_d_list = []
dos_sum_up_list = []
dos_sum_down_list = []
for index in range(len(dos_sum_s_up_list)):
dos_s_list.append(str(dos_sum_s_up_list[index])+" "+str(dos_sum_s_down_list[index])+"\n")
dos_p_list.append(str(dos_sum_p_up_list[index])+" "+str(dos_sum_p_down_list[index])+"\n")
dos_d_list.append(str(dos_sum_d_up_list[index])+" "+str(dos_sum_d_down_list[index])+"\n")
dos_sum_up_list.append(dos_sum_s_up_list[index] + dos_sum_p_up_list[index] + dos_sum_d_up_list[index])
dos_sum_down_list.append(dos_sum_s_down_list[index] + dos_sum_p_down_list[index] + dos_sum_d_down_list[index])
dos_s_list_float = []
dos_p_list_float = []
dos_d_list_float = []
for line in dos_s_list:
line = line.split()
for index in range(len(line)):
line[index] = float(line[index])
dos_s_list_float.append(line)
for line in dos_p_list:
line = line.split()
for index in range(len(line)):
line[index] = float(line[index])
dos_p_list_float.append(line)
for line in dos_d_list:
line = line.split()
for index in range(len(line)):
line[index] = float(line[index])
dos_d_list_float.append(line)
#Now get total DOS by summing all projected dos
total_dos_up_list = []
total_dos_down_list = []
for dos_row in range(nedos):
sum_dos_up = 0
sum_dos_down = 0
for index in range(len(atom_amounts)):
# These are normalized by number of atoms of each element type
sum_dos_up += (dos_sum_up_list[dos_row + nedos*index])/atom_amounts[index]
sum_dos_down += (dos_sum_down_list[dos_row + nedos*index])/atom_amounts[index]
# These are not normalized by atom number
#sum_dos_up += (dos_sum_up_list[dos_row + nedos*index])
#sum_dos_down += (dos_sum_down_list[dos_row + nedos*index])
total_dos_up_list.append(sum_dos_up) #This is normalized by atom amounts of each element present
total_dos_down_list.append(sum_dos_down)
#print "length total dos up", len(total_dos_up_list)
#print "length sum dos up list", len(dos_sum_up_list)
return total_dos_up_list, total_dos_down_list, dos_s_list_float, dos_p_list_float, dos_d_list_float
def _calc_VBM_and_CBM(self):
total_dos_up_list, total_dos_down_list, dos_s_list, dos_p_list, dos_d_list = self._parse_dos_atom_type()
energy, energy_unocc, energy_occ, index_Fermi, index_cutoff = self._make_energy_lists()
nedos = self._calc_nedos()
Etolerance = 0.1
DOStolerance = 0.01
#Take the total dos as separate up and down spin and combine to be summed total DOS
total_dos = []
for index in range(len(total_dos_up_list)):
total_dos.append(total_dos_up_list[index]+total_dos_down_list[index])
#Find the position of the VBM
for index in range(nedos):
if energy[index] >= 0 - Etolerance:
if energy[index] <= 0 + Etolerance:
E_VBM = energy[index]
VBM_index = index
elif energy[index] < 0 - Etolerance or energy[index] > 0 + Etolerance:
pass
E_above_VBM_range = []
for index in range(nedos - VBM_index):
E_above_VBM_range.append(VBM_index + index)
#Now calculate the band gap
for entry in E_above_VBM_range:
if abs(total_dos[entry] - total_dos[entry-1]) > 0 + DOStolerance:
CBM_index = entry
E_CBM = energy[entry]
break
elif abs(total_dos[entry] - total_dos[entry-1]) < 0 + DOStolerance:
pass
return E_VBM, E_CBM
class StabilityAnalyzer(object):
"""Class that conducts a thermodynamic phase stability analysis using the values in the Materials Project database and
includes as input the user's calculated material. By default, DFT energies are used for all elemental endmembers.
However, the thermodynamic conditions represented by DFT are often not physically realistic. Because of this, the user
can supply a custom set of calculated chemical potential values for gaseous endmembers like O and H. These chemical
potentials can be calculated using the ChemicalPotentialAnalyzer class.
* A couple notes about usage *
Since this entire analysis package uses the Materials Project database, it is important that the same XC functional,
pseudopotentials and GGA+U values are used so that consistent energies are obtained. If you use the IncarFileSetup
class in the VASP_Setup module your GGA+U values will match that of the Materials Project. Just be sure to use GGA-PBE
type pseudopotentials in your DFT calculations.
args:
mapi_key : (str) Your Materials API key from Materials Project. You can obtain yours here: https://materialsproject.org/open
poscar : (str) the name of a POSCAR file
oszicar : (str) the name of an OSZICAR file
get_data_from_VASP_files : (bool) whether to obtain composition and energy data from VASP runs. This is used to
calculate the stability of newly calculated DFT structures. If this is set to False, need to specify composition
and energy of material you want to examine manually with the composition_dict and composition_energy arguments
additional_elements_to_include : (list) A list of elements to include in phase stability analysis that aren't present
in your DFT calculation or that weren't manually included in the composition_dict argument
material_ids_to_remove : (list) A list of MP material id's (e.g. "mp-123") to remove from the phase space. Useful
to analyze metastable phases by removing certain stable phases
composition_dict : (list of dict) A list containing dicts of material compositions to analyze the stability of.
This is a way to manually set the composition of interest for an arbitrary composition
composition_energy : (list) A list of energies corresponding to each composition dict in composition_dict
instance methods:
get_phase_diagram: (phase diagram object) This function generates a phase diagram and calculates the stability
of the simulated compound
args:
use_custom_chem_pots : (bool) If False, the standard DFT endmember energies as tabulated in Materials Project
are used. If True, the user must specify their own chemical potentials using the custom_chem_pot_dict input
custom_chem_pot_dict: (dict) A dictionary of {"element": chem_pot} pairs, where chem_pot is a float
include_organic_molecules: (bool) If True, a small library of DFT-calculated organic molecules is included
in the phase diagram calculation. Note that they can only be included if C is an element in your POSCAR.
include_organic_molecule_shift: (bool) If True, adds an energy shift to the organic molecules consistent with
T = 298 K and P = 10^-6 atm. Note this only applies if include_organic_molecules is set to True and you
are interested in phase stability at 298 K. Use with Caution!
"""
def __init__(self, mapi_key, poscar="POSCAR", oszicar="OSZICAR", get_data_from_VASP_files=False ,
additional_elements_to_include=None, material_ids_to_remove=None, composition_dict=None,
composition_energy=None):
self.mapi_key = mapi_key
self.poscar = poscar
self.oszicar = oszicar
self.get_data_from_VASP_files = get_data_from_VASP_files
self.additional_elements_to_include = additional_elements_to_include
self.material_ids_to_remove = material_ids_to_remove
self.composition_dict = composition_dict
self.composition_energy = composition_energy
def get_phase_diagram(self, use_custom_chem_pots=False, custom_chem_pot_dict=None, include_organic_molecules=False,
include_organic_molecule_shift=False):
pd_entry_list_chempots = []
if use_custom_chem_pots == bool(False):
entries_in_system = self._create_chemical_system_from_MP()
pd_entry_list = self._create_pdentry()
for entry in pd_entry_list:
print "The PDEntry given for this system is:", entry, " eV/cell"
elif use_custom_chem_pots == bool(True):
entries_in_system = self._create_chemical_system_from_MP_and_remove_endmembers(species_to_remove=[key for key in custom_chem_pot_dict.keys()])
pd_entry_list = self._create_pdentry()
# Also make PDEntries for species you specify chem pots of
for key, value in custom_chem_pot_dict.items():
chempot_pdentry = PDEntry(composition=key, energy=value)
entries_in_system.append(chempot_pdentry)
pd_entry_list_chempots.append(chempot_pdentry)
for entry in pd_entry_list: