Skip to content

Commit

Permalink
Add ElectronicEigenvalues and ElectronicBandStructure properties (#63)
Browse files Browse the repository at this point in the history
* Deleted .vscode/settings.json from repo

* Changed _check methods for validate_

* Added ElectronicEigenvalues, ElectronicBandStructure and FermiSurface to list of Outputs

Added modules properties/band_structure.py and properties/fermi_surface.py

* Added BaseElectronicEigenvalues to handle contributions

Added methods for extracting band_gap and fermi_surface from eigenvalues

Added methods to extract homo and lumo eigenvalues

* Fixed highest_occupied and lowest_unoccupied in spectral_profile.py

* Added TODO in fermi_surface.py; to implement later

* Added decorator validate_quantity_wrt_value in physical_property.py

Added check if the quantities for dimensions starting with n_ exists during init of a PhysicalProperty section

Changed rank from init of PhysicalProperties using kwargs instead of the self assigned value

* Added extensive testing and fixed previous one
  • Loading branch information
JosePizarro3 authored Jun 3, 2024
1 parent 4968164 commit 51e9401
Show file tree
Hide file tree
Showing 21 changed files with 1,139 additions and 98 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ cython_debug/

# VSCode settings
.vscode/launch.json
.vscode/settings.json

# comments scripts
comments.py
Expand Down
24 changes: 0 additions & 24 deletions .vscode/settings.json

This file was deleted.

8 changes: 4 additions & 4 deletions src/nomad_simulations/atoms_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,15 @@ def __init__(self, m_def: Section = None, m_context: Context = None, **kwargs):
'ms_numbers': dict((zip(('down', 'up'), (-0.5, 0.5)))),
}

def _check_quantum_numbers(self, logger: BoundLogger) -> bool:
def validate_quantum_numbers(self, logger: BoundLogger) -> bool:
"""
Checks the physicality of the quantum numbers.
Validate the quantum numbers (`n`, `l`, `ml`, `ms`) by checking if they are physically sensible.
Args:
logger (BoundLogger): The logger to log messages.
Returns:
(bool): True if the quantum numbers are physical, False otherwise.
(bool): True if the quantum numbers are physically sensible, False otherwise.
"""
if self.n_quantum_number is not None and self.n_quantum_number < 1:
logger.error('The `n_quantum_number` must be greater than 0.')
Expand Down Expand Up @@ -293,7 +293,7 @@ def normalize(self, archive, logger) -> None:
super().normalize(archive, logger)

# General checks for physical quantum numbers and symbols
if not self._check_quantum_numbers(logger):
if not self.validate_quantum_numbers(logger):
logger.error('The quantum numbers are not physical.')
return

Expand Down
25 changes: 13 additions & 12 deletions src/nomad_simulations/numerical_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pint
from itertools import accumulate, tee, chain
from structlog.stdlib import BoundLogger
from typing import Optional, List, Tuple, Union, Dict
from typing import Optional, List, Tuple, Union
from ase.dft.kpoints import monkhorst_pack, get_monkhorst_pack_size_and_offset

from nomad.units import ureg
Expand Down Expand Up @@ -165,15 +165,15 @@ class KSpaceFunctionalities:
A functionality class useful for defining methods shared between `KSpace`, `KMesh`, and `KLinePath`.
"""

def _check_reciprocal_lattice_vectors(
def validate_reciprocal_lattice_vectors(
self,
reciprocal_lattice_vectors: Optional[pint.Quantity],
logger: BoundLogger,
check_grid: Optional[bool] = False,
grid: Optional[List[int]] = [],
) -> bool:
"""
Check if the `reciprocal_lattice_vectors` exist and if they have the same dimensionality as `grid`.
Validate the `reciprocal_lattice_vectors` by checking if they exist and if they have the same dimensionality as `grid`.
Args:
reciprocal_lattice_vectors (Optional[pint.Quantity]): The reciprocal lattice vectors of the atomic cell.
Expand Down Expand Up @@ -404,7 +404,7 @@ def get_k_line_density(
(np.float64): The k-line density of the `KMesh`.
"""
# Initial check
if not KSpaceFunctionalities()._check_reciprocal_lattice_vectors(
if not KSpaceFunctionalities().validate_reciprocal_lattice_vectors(
reciprocal_lattice_vectors=reciprocal_lattice_vectors,
logger=logger,
check_grid=True,
Expand Down Expand Up @@ -438,7 +438,7 @@ def resolve_k_line_density(
(Optional[pint.Quantity]): The resolved `k_line_density` of the `KMesh`.
"""
# Initial check
if not KSpaceFunctionalities()._check_reciprocal_lattice_vectors(
if not KSpaceFunctionalities().validate_reciprocal_lattice_vectors(
reciprocal_lattice_vectors=reciprocal_lattice_vectors,
logger=logger,
check_grid=True,
Expand Down Expand Up @@ -553,7 +553,7 @@ def resolve_high_symmetry_path_values(
(Optional[List[float]]): The resolved `high_symmetry_path_values`.
"""
# Initial check on the `reciprocal_lattice_vectors`
if not KSpaceFunctionalities()._check_reciprocal_lattice_vectors(
if not KSpaceFunctionalities().validate_reciprocal_lattice_vectors(
reciprocal_lattice_vectors=reciprocal_lattice_vectors, logger=logger
):
return []
Expand All @@ -576,9 +576,9 @@ def resolve_high_symmetry_path_values(
]
return high_symmetry_path_values

def _check_high_symmetry_path(self, logger: BoundLogger) -> bool:
def validate_high_symmetry_path(self, logger: BoundLogger) -> bool:
"""
Check if the `high_symmetry_path_names` and `high_symmetry_path_values` are defined and have the same length.
Validate `high_symmetry_path_names` and `high_symmetry_path_values` by checking if they are defined and have the same length.
Args:
logger (BoundLogger): The logger to log messages.
Expand Down Expand Up @@ -624,7 +624,7 @@ def get_high_symmetry_path_norms(
`high_symmetry_path_value_norms = [0, 0.5, 0.5 + 1 / np.sqrt(2), 1 + 1 / np.sqrt(2)]`
"""
# Checking the high symmetry path quantities
if not self._check_high_symmetry_path(logger):
if not self.validate_high_symmetry_path(logger):
return None
# Checking if `reciprocal_lattice_vectors` is defined and taking its magnitude to operate
if reciprocal_lattice_vectors is None:
Expand Down Expand Up @@ -672,7 +672,7 @@ def resolve_points(
logger (BoundLogger): The logger to log messages.
"""
# General checks for quantities
if not self._check_high_symmetry_path(logger):
if not self.validate_high_symmetry_path(logger):
return None
if reciprocal_lattice_vectors is None:
logger.warning(
Expand Down Expand Up @@ -755,7 +755,7 @@ def normalize(self, archive, logger) -> None:
)

# If `high_symmetry_path` is not defined, we do not normalize the KLinePath
if not self._check_high_symmetry_path(logger):
if not self.validate_high_symmetry_path(logger):
return


Expand Down Expand Up @@ -803,8 +803,9 @@ def resolve_reciprocal_lattice_vectors(
# General checks to proceed with normalization
if is_not_representative(model_system, logger):
continue

# TODO extend this for other dimensions (@ndaelman-hu)
if model_system.type != 'bulk':
if model_system.type is not None and model_system.type != 'bulk':
logger.warning('`ModelSystem.type` is not describing a bulk system.')
continue

Expand Down
13 changes: 13 additions & 0 deletions src/nomad_simulations/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
HoppingMatrix,
ElectronicBandGap,
ElectronicDensityOfStates,
ElectronicEigenvalues,
FermiSurface,
ElectronicBandStructure,
AbsorptionSpectrum,
XASSpectrum,
Permittivity,
Expand Down Expand Up @@ -75,12 +78,22 @@ class Outputs(ArchiveSection):

hopping_matrices = SubSection(sub_section=HoppingMatrix.m_def, repeats=True)

electronic_eigenvalues = SubSection(
sub_section=ElectronicEigenvalues.m_def, repeats=True
)

electronic_band_gaps = SubSection(sub_section=ElectronicBandGap.m_def, repeats=True)

electronic_dos = SubSection(
sub_section=ElectronicDensityOfStates.m_def, repeats=True
)

fermi_surfaces = SubSection(sub_section=FermiSurface.m_def, repeats=True)

electronic_band_structures = SubSection(
sub_section=ElectronicBandStructure.m_def, repeats=True
)

permittivities = SubSection(sub_section=Permittivity.m_def, repeats=True)

absorption_spectra = SubSection(sub_section=AbsorptionSpectrum.m_def, repeats=True)
Expand Down
47 changes: 47 additions & 0 deletions src/nomad_simulations/physical_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import numpy as np
from typing import Any, Optional
from functools import wraps

from nomad import utils
from nomad.datamodel.data import ArchiveSection
Expand All @@ -42,6 +43,43 @@
logger = utils.get_logger(__name__)


def validate_quantity_wrt_value(name: str = ''):
"""
Decorator to validate the existence of a quantity and its shape with respect to the `PhysicalProperty.value`
before calling a method. An example can be found in the module `properties/band_structure.py` for the method
`ElectronicEigenvalues.order_eigenvalues()`.
Args:
name (str, optional): The name of the `quantity` to validate. Defaults to ''.
"""

def decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
# Checks if `quantity` is defined
quantity = getattr(self, name, None)
if quantity is None or len(quantity) == 0:
logger.warning(f'The quantity `{name}` is not defined.')
return False

# Checks if `value` exists and has the same shape as `quantity`
value = getattr(self, 'value', None)
if value is None:
logger.warning(f'The quantity `value` is not defined.')
return False
if value is not None and value.shape != quantity.shape:
logger.warning(
f'The shape of the quantity `{name}` does not match the shape of the `value`.'
)
return False

return func(self, *args, **kwargs)

return wrapper

return decorator


class PhysicalProperty(ArchiveSection):
"""
A base section used to define the physical properties obtained in a simulation, experiment, or in a post-processing
Expand Down Expand Up @@ -221,11 +259,20 @@ def __init__(
self, m_def: Section = None, m_context: Context = None, **kwargs
) -> None:
super().__init__(m_def, m_context, **kwargs)

# Checking if IRI is defined
if self.iri is None:
logger.warning(
'The used property is not defined in the FAIRmat taxonomy (https://fairmat-nfdi.github.io/fairmat-taxonomy/). You can contribute there if you want to extend the list of available materials properties.'
)

# Checking if the quantities `n_` are defined, as this are used to calculate `rank`
for quantity, _ in self.m_def.all_quantities.items():
if quantity.startswith('n_') and getattr(self, quantity) is None:
raise ValueError(
f'`{quantity}` is not defined during initialization of the class.'
)

def __setattr__(self, name: str, val: Any) -> None:
# For the special case of `value`, its `shape` needs to be defined from `_full_shape`
if name == 'value':
Expand Down
2 changes: 2 additions & 0 deletions src/nomad_simulations/properties/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@
)
from .hopping_matrix import HoppingMatrix, CrystalFieldSplitting
from .permittivity import Permittivity
from .fermi_surface import FermiSurface
from .band_structure import ElectronicEigenvalues, ElectronicBandStructure
6 changes: 3 additions & 3 deletions src/nomad_simulations/properties/band_gap.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ def __init__(
self.name = self.m_def.name
self.rank = []

def _check_negative_values(self, logger: BoundLogger) -> Optional[pint.Quantity]:
def validate_values(self, logger: BoundLogger) -> Optional[pint.Quantity]:
"""
Checks if the electronic band gaps is negative and sets them to None if they are.
Validate the electronic band gap `value` by checking if they are negative and sets them to None if they are.
Args:
logger (BoundLogger): The logger to log messages.
Expand Down Expand Up @@ -143,7 +143,7 @@ def normalize(self, archive, logger) -> None:
super().normalize(archive, logger)

# Checks if the `value` is negative and sets it to None if it is.
self.value = self._check_negative_values(logger)
self.value = self.validate_values(logger)
if self.value is None:
# ? What about deleting the class if `value` is None?
logger.error('The `value` of the electronic band gap is not stored.')
Expand Down
Loading

0 comments on commit 51e9401

Please sign in to comment.