Skip to content

Commit 171bd33

Browse files
Merge pull request mala-project#646 from mala-project/dos_splitting
Splitting the (L)DOS along the energy axis
2 parents 7346e97 + 66565de commit 171bd33

File tree

5 files changed

+373
-129
lines changed

5 files changed

+373
-129
lines changed

mala/common/parameters.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -604,14 +604,31 @@ class ParametersTargets(ParametersBase):
604604
Number of points in the energy grid that is used to calculate the
605605
(L)DOS.
606606
607-
ldos_gridsize : int
608-
Gridsize of the LDOS.
609-
610-
ldos_gridspacing_ev: float
607+
ldos_gridsize : int or list
608+
Gridsize of the LDOS. Can either be an int or a list of ints,
609+
in which case splitting of the (L)DOS along the energy axis is assumed.
610+
Note that this splitting feature is currently experimental and the
611+
interface may change in the future. Further, if this type of splitting
612+
is used, please make sure that ldos_gridsize, ldos_gridspacing_ev
613+
and ldos_gridoffset_ev are lists of the same length.
614+
615+
ldos_gridspacing_ev: float or list
611616
Gridspacing of the energy grid the (L)DOS is evaluated on [eV].
612-
613-
ldos_gridoffset_ev: float
617+
Can either be a float or a list of floats, in which case splitting of
618+
the (L)DOS along the energy axis is assumed.
619+
Note that this splitting feature is currently experimental and the
620+
interface may change in the future. Further, if this type of splitting
621+
is used, please make sure that ldos_gridsize, ldos_gridspacing_ev
622+
and ldos_gridoffset_ev are lists of the same length.
623+
624+
ldos_gridoffset_ev: float or list
614625
Lowest energy value on the (L)DOS energy grid [eV].
626+
Can either be a float or a list of floats, in which case splitting of
627+
the (L)DOS along the energy axis is assumed.
628+
Note that this splitting feature is currently experimental and the
629+
interface may change in the future. Further, if this type of splitting
630+
is used, please make sure that ldos_gridsize, ldos_gridspacing_ev
631+
and ldos_gridoffset_ev are lists of the same length.
615632
616633
pseudopotential_path : string
617634
Path at which pseudopotentials are located (for TEM).

mala/targets/dos.py

Lines changed: 91 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,18 @@ def from_qe_out(cls, params, path):
203203
@property
204204
def feature_size(self):
205205
"""Get dimension of this target if used as feature in ML."""
206-
return self.parameters.ldos_gridsize
206+
if isinstance(self.parameters.ldos_gridsize, int):
207+
return self.parameters.ldos_gridsize
208+
elif isinstance(self.parameters.ldos_gridsize, list):
209+
# For splits, we sum up the individual grid sizes, BUT we
210+
# have to subtract one for each split, as the last energy
211+
# of each section gets discarded. So for three sections,
212+
# we have to subtract 2.
213+
return (
214+
np.sum(self.parameters.ldos_gridsize)
215+
- len(self.parameters.ldos_gridsize)
216+
+ 1
217+
)
207218

208219
@property
209220
def data_name(self):
@@ -269,7 +280,7 @@ def invalidate_target(self):
269280
@cached_property
270281
def energy_grid(self):
271282
"""Energy grid on which the DOS is expressed."""
272-
return self.get_energy_grid()
283+
return self._get_energy_grid()
273284

274285
@cached_property
275286
def band_energy(self):
@@ -416,7 +427,7 @@ def read_from_qe_dos_txt(self, path):
416427
417428
Parameters
418429
----------
419-
path : string
430+
path : string or List
420431
Path of the file containing the DOS.
421432
422433
Returns
@@ -428,26 +439,52 @@ def read_from_qe_dos_txt(self, path):
428439
# check whether we have a correct file.
429440

430441
energy_grid = self.energy_grid
431-
return_dos_values = []
432-
433-
# Open the file, then iterate through its contents.
434-
with open(path, "r") as infile:
435-
lines = infile.readlines()
436-
i = 0
437-
438-
for dos_line in lines:
439-
# The first column contains the energy value.
440-
if "#" not in dos_line and i < self.parameters.ldos_gridsize:
441-
e_val = float(dos_line.split()[0])
442-
dosval = float(dos_line.split()[1])
443-
if (
444-
np.abs(e_val - energy_grid[i])
445-
< self.parameters.ldos_gridspacing_ev * 0.98
446-
):
447-
return_dos_values.append(dosval)
448-
i += 1
449-
450-
array = np.array(return_dos_values)
442+
443+
if isinstance(path, str):
444+
readpaths = [path]
445+
else:
446+
readpaths = path
447+
448+
current_energy_index = 0
449+
450+
for path_index, readpath in enumerate(readpaths):
451+
return_dos_values = []
452+
453+
# Open the file, then iterate through its contents.
454+
with open(readpath, "r") as infile:
455+
lines = infile.readlines()
456+
457+
# Directly at the split we discard the last energy value
458+
# of the left side of the split. This requires that both
459+
# DOS have been sampled to/from the EXACT same value.
460+
# Currently, this responsibility lies with the user, and I
461+
# am not sure if we can consistently check for this, even
462+
# if we wanted to. In the DOS case, the energies get reported,
463+
# but that is NOT the case in the LDOS case.
464+
end = (
465+
self.parameters.ldos_gridsize[path_index] - 1
466+
if path_index != len(readpaths) - 1
467+
else self.parameters.ldos_gridsize[path_index]
468+
)
469+
end += current_energy_index
470+
471+
for dos_line in lines:
472+
# The first column contains the energy value.
473+
if "#" not in dos_line and current_energy_index < end:
474+
e_val = float(dos_line.split()[0])
475+
dosval = float(dos_line.split()[1])
476+
if (
477+
np.abs(e_val - energy_grid[current_energy_index])
478+
< self.parameters.ldos_gridspacing_ev[path_index]
479+
* 0.98
480+
):
481+
return_dos_values.append(dosval)
482+
current_energy_index += 1
483+
# print(path_index, i)
484+
if path_index == 0:
485+
array = np.array(return_dos_values)
486+
else:
487+
array = np.concatenate((array, return_dos_values))
451488
self.density_of_states = array
452489
return array
453490

@@ -485,17 +522,40 @@ def read_from_qe_out(self, path=None, smearing_factor=2):
485522
"Rerun calculation with verbosity set to 'high'."
486523
)
487524

525+
if isinstance(self.parameters.ldos_gridspacing_ev, list):
526+
grid_spacings = self.parameters.ldos_gridspacing_ev
527+
grid_sizes = self.parameters.ldos_gridsize
528+
else:
529+
grid_spacings = [self.parameters.ldos_gridspacing_ev]
530+
grid_sizes = [self.parameters.ldos_gridsize]
531+
488532
# Get the gaussians for all energy values and calculate the DOS per
489533
# band.
490-
dos_per_band = gaussians(
491-
self.energy_grid,
492-
atoms_object.get_calculator().band_structure().energies[0, :, :],
493-
smearing_factor * self.parameters.ldos_gridspacing_ev,
494-
)
495-
dos_per_band = kweights[:, np.newaxis, np.newaxis] * dos_per_band
534+
dos_data = None
535+
previous_beginning = 0
536+
for spacing_idx, grid_spacing in enumerate(grid_spacings):
537+
size_for_spacing = grid_sizes[spacing_idx] + previous_beginning
538+
if spacing_idx != len(grid_spacings) - 1:
539+
size_for_spacing -= 1
540+
541+
dos_per_band = gaussians(
542+
self.energy_grid[previous_beginning:size_for_spacing],
543+
atoms_object.get_calculator()
544+
.band_structure()
545+
.energies[0, :, :],
546+
smearing_factor * grid_spacing,
547+
)
548+
dos_per_band = kweights[:, np.newaxis, np.newaxis] * dos_per_band
496549

497-
# QE gives the band energies in eV, so no conversion necessary here.
498-
dos_data = np.sum(dos_per_band, axis=(0, 1))
550+
# QE gives the band energies in eV, so no conversion necessary
551+
# here.
552+
if spacing_idx == 0:
553+
dos_data = np.sum(dos_per_band, axis=(0, 1))
554+
else:
555+
dos_data = np.concatenate(
556+
(dos_data, np.sum(dos_per_band, axis=(0, 1)))
557+
)
558+
previous_beginning = size_for_spacing
499559
self.density_of_states = dos_data
500560
return dos_data
501561

@@ -548,26 +608,6 @@ def read_from_numpy_file(
548608
# Calculations
549609
##############
550610

551-
def get_energy_grid(self):
552-
"""
553-
Get energy grid.
554-
555-
Returns
556-
-------
557-
e_grid : numpy.ndarray
558-
Energy grid on which the DOS is defined.
559-
"""
560-
emin = self.parameters.ldos_gridoffset_ev
561-
562-
emax = (
563-
self.parameters.ldos_gridoffset_ev
564-
+ self.parameters.ldos_gridsize
565-
* self.parameters.ldos_gridspacing_ev
566-
)
567-
grid_size = self.parameters.ldos_gridsize
568-
linspace_array = np.linspace(emin, emax, grid_size, endpoint=False)
569-
return linspace_array
570-
571611
def get_band_energy(
572612
self,
573613
dos_data=None,

0 commit comments

Comments
 (0)