Skip to content

Commit

Permalink
- Apply schema defined on 10.12.2024
Browse files Browse the repository at this point in the history
- Apply ruff format
  • Loading branch information
ndaelman committed Dec 11, 2024
1 parent 03f8906 commit de7b75d
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 95 deletions.
44 changes: 27 additions & 17 deletions src/nomad_simulations/schema_packages/properties/band_gap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@

import numpy as np
from nomad.units import ureg
from nomad.metainfo import MEnum, Quantity
from nomad.metainfo.dataset import MDataset
from nomad.datamodel.metainfo.physical_properties import PhysicalProperty
from nomad.datamodel.data import ArchiveSection
from ..variables import SpinChannel, MomentumTransfer
from nomad.metainfo import MEnum, Quantity
from nomad.metainfo.physical_properties import MaterialProperty, Energy
from ..variables import (
MaterialPropertyAttributes,
fetch_instance,
SpinChannel,
MomentumTransfer,
)

if TYPE_CHECKING:
from nomad.datamodel.datamodel import EntryArchive
from structlog.stdlib import BoundLogger


class ElectronicBandGap(MDataset, ArchiveSection): # ? add optical band gap
m_def = PhysicalProperty(
type=np.float64,
unit='joule',
class ElectronicBandGap(ArchiveSection): # ! TODO: add optical band gap
values = MaterialProperty(
name='BandGap',
fields=[Energy],
variables=[SpinChannel, MomentumTransfer], # target index
iri='http://fairmat-nfdi.eu/taxonomy/ElectronicBandGap',
description="""Energy difference between the highest occupied electronic state and the lowest unoccupied electronic state.""",
default_variables=['SpinChannel', 'MomentumTransfer'],
description="""Energy difference between the highest occupied electronic state and the lowest unoccupied electronic state.""", # ? necessity
)

type = Quantity(
Expand All @@ -34,17 +38,23 @@ class ElectronicBandGap(MDataset, ArchiveSection): # ? add optical band gap
)

def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
# super().normalize(archive, logger)

if np.any(self.data < 0):
logger.warning(f'Negative band gap detected: {self.data} J')
if np.any(self.values.fields < 0): # ? check for Energy
logger.warning(f'Negative band gap detected: {self.values.fields} J')

if [True for var in self.variables if isinstance(var, SpinChannel)]:
if np.any(
fetch_instance(
self, SpinChannel, attribute=MaterialPropertyAttributes.variables
)
):
logger.warning(
f'Band gap without specifying any spin channel: {self.variables}'
f'Band gap without specifying any spin channel: {self.values.variables}'
)

if not [True for var in self.variables if isinstance(var, MomentumTransfer)]:
if not np.any(
fetch_instance(
self, MomentumTransfer, attribute=MaterialPropertyAttributes.variables
)
):
if self.type == 'direct':
self.variables.append(
MomentumTransfer(data=[2 * [3 * [0.0]]] * ureg.angstrom**-1)
Expand Down
99 changes: 50 additions & 49 deletions src/nomad_simulations/schema_packages/properties/band_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import numpy as np
import pint
from nomad.metainfo import MEnum, Quantity, SubSection
from nomad.metainfo.dataset import MDataset, Dataset
from nomad.datamodel.metainfo.physical_properties import PhysicalProperty
from nomad.datamodel.data import ArchiveSection
from nomad.metainfo import MEnum, Quantity
from nomad.metainfo.physical_properties import MaterialProperty, Count
from ..variables import SpinChannel, KMesh

if TYPE_CHECKING:
Expand All @@ -16,83 +16,76 @@
from nomad_simulations.schema_packages.properties.band_gap import ElectronicBandGap

from nomad.config import config

configuration = config.get_plugin_entry_point(
'nomad_simulations.schema_packages:nomad_simulations_plugin'
)

class ElectronicEigenvalues(MDataset):
m_def = PhysicalProperty(
type=np.float64,
unit='joule',
iri = 'http://fairmat-nfdi.eu/taxonomy/ElectronicEigenvalues',

Occupancy = Count.m_copy() # ? establish semantic connection # values between 0 - 1 or 0 - 2
Occupancy.iri='http://fairmat-nfdi.eu/taxonomy/Occupancy',
Occupancy.description="""
Electrons occupancy of an atom per orbital and spin. This is a number defined between 0 and 1 for
spin-polarized systems, and between 0 and 2 for non-spin-polarized systems. This property is
important when studying if an orbital or spin channel are fully occupied, at half-filling, or
fully emptied, which have an effect on the electron-electron interaction effects.
"""


class ElectronicEigenstates(ArchiveSection):
values = MaterialProperty(
name='ElectronicEigenstates',
fields=[Energy, Occupancy], # shape defined by variables
variables=[SpinChannel, KMesh], # ? enforce spanned dimension at metainfo level
iri='http://fairmat-nfdi.eu/taxonomy/ElectronicEigenvalues',
description="""A base section used to define basic quantities for the `ElectronicEigenvalues` and `ElectronicEigenstates` properties.""",
default_variables=[SpinChannel, KMesh],
)

# ? Should we add functionalities to handle min/max of the `value` in some specific cases, .e.g, bands around the Fermi level,
kind = Quantity(
type=MEnum('KS', 'KSxc', 'SigX', 'SigC', 'Zk'),
description="""
Contributions to the electronic eigenvalues. Example, in the case of a DFT+GW calculation, the GW eigenvalues
are stored under `value`, and each contribution is identified by `label`:
- `'KS'`: Kohn-Sham contribution. This is also stored in the DFT entry under `ElectronicEigenvalues.value`.
- `'KSxc'`: Diagonal matrix elements of the expectation value of the Kohn-Sahm exchange-correlation potential.
- `'SigX'`: Diagonal matrix elements of the exchange self-energy. This is also stored in the GW entry under `ElectronicSelfEnergy.value`.
- `'SigC'`: Diagonal matrix elements of the correlation self-energy. This is also stored in the GW entry under `ElectronicSelfEnergy.value`.
- `'Zk'`: Quasiparticle renormalization factors contribution. This is also stored in the GW entry under `QuasiparticleWeights.value`.
""",
)

# ? Should we add functionalities to handle min/max of the `value` in some specific cases, e.g. bands around the Fermi level,
# ? core bands separated by gaps, and equivalently, higher-energy valence bands separated by gaps?

# references
reciprocal_cell = Quantity(
type=KSpace.reciprocal_lattice_vectors,
description="""
Reference to the reciprocal lattice vectors stored under `KSpace`.
""",
) # !


class Occupancy(MDataset):
m_def = PhysicalProperty(
type=np.float64, # actually values between 0 - 2
iri = 'http://fairmat-nfdi.eu/taxonomy/Occupancy',
description="""
Electrons occupancy of an atom per orbital and spin. This is a number defined between 0 and 1 for
spin-polarized systems, and between 0 and 2 for non-spin-polarized systems. This property is
important when studying if an orbital or spin channel are fully occupied, at half-filling, or
fully emptied, which have an effect on the electron-electron interaction effects.
""",
default_variables=[SpinChannel, KMesh],
)

atoms_state_ref = Quantity(
type=AtomsState,
description="""
Reference to the `AtomsState` section in which the occupancy is calculated.
Reference to the matching `AtomsState` section.
""",
)

orbitals_state_ref = Quantity(
type=OrbitalsState,
description="""
Reference to the `OrbitalsState` section in which the occupancy is calculated.
Reference to the matching `OrbitalsState` section.
""",
)


class ElectronicEigenstates(MDataset): # ? rename
m_def = Dataset(
type=bool,
default_variables=[ElectronicEigenvalues, Occupancy],
)
) # ! TODO: unify with `atoms_state_ref`

# derived properties
n_bands = Quantity(
type=np.int32,
description="""
Number of bands / eigenvalues.
""",
)

kind = Quantity(
type=MEnum('KS', 'KSxc', 'SigX', 'SigC', 'Zk'),
description="""
Contributions to the electronic eigenvalues. Example, in the case of a DFT+GW calculation, the GW eigenvalues
are stored under `value`, and each contribution is identified by `label`:
- `'KS'`: Kohn-Sham contribution. This is also stored in the DFT entry under `ElectronicEigenvalues.value`.
- `'KSxc'`: Diagonal matrix elements of the expectation value of the Kohn-Sahm exchange-correlation potential.
- `'SigX'`: Diagonal matrix elements of the exchange self-energy. This is also stored in the GW entry under `ElectronicSelfEnergy.value`.
- `'SigC'`: Diagonal matrix elements of the correlation self-energy. This is also stored in the GW entry under `ElectronicSelfEnergy.value`.
- `'Zk'`: Quasiparticle renormalization factors contribution. This is also stored in the GW entry under `QuasiparticleWeights.value`.
""",
)
) # ? remove

highest_occupied = Quantity(
type=np.float64,
Expand Down Expand Up @@ -218,4 +211,12 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
self.m_parent.electronic_band_gaps.append(band_gap)

# Resolve `reciprocal_cell` from the `KSpace` numerical settings section
self.reciprocal_cell = self.resolve_reciprocal_cell()
self.reciprocal_cell = self.resolve_reciprocal_cell()


# TODO: consider matching different k-channels for indirect bandgaps
def bandstructure_to_dos(bs: ElectronicEigenstates) -> ElectronicDOS:
pass

def dos_to_bandgap(dos: ElectronicDOS) -> ElectronicBandGap:
pass
5 changes: 4 additions & 1 deletion src/nomad_simulations/schema_packages/properties/energies.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from nomad.datamodel.datamodel import EntryArchive
from structlog.stdlib import BoundLogger


class Energy(MDataset):
m_def = Dataset(
type=np.float64,
Expand All @@ -33,7 +34,9 @@ class Energy(MDataset):
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)

energy_sums = np.sum([var.data for var in self.variables if isinstance(var, Energy)], axis=0)
energy_sums = np.sum(
[var.data for var in self.variables if isinstance(var, Energy)], axis=0
)
if self.data is None or self.data == []:
self.data = energy_sums
elif not np.allclose(self.data, energy_sums):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@


from nomad.config import config

configuration = config.get_plugin_entry_point(
'nomad_simulations.schema_packages:nomad_simulations_plugin'
)
Expand Down Expand Up @@ -65,6 +66,7 @@ def extract_fermi_surface(self, logger: 'BoundLogger') -> Optional['FermiSurface
fermi_surface.value = fermi_values
return fermi_surface


# TODO This class is not implemented yet. @JosePizarro3 will work in another PR to implement it.
class FermiSurface(PhysicalProperty):
"""
Expand Down
91 changes: 63 additions & 28 deletions src/nomad_simulations/schema_packages/variables.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import TYPE_CHECKING, Optional

from enum import Enum
import numpy as np
from nomad.datamodel.data import ArchiveSection
from nomad.metainfo import MEnum, Quantity
from nomad.metainfo.dataset import MDataset, Dataset
from nomad.datamodel.metainfo.physical_properties import PhysicalProperty
from nomadmetainfo.physical_properties import (
PhysicalProperty,
MaterialProperty,
)

if TYPE_CHECKING:
from nomad.datamodel.datamodel import EntryArchive
Expand All @@ -19,38 +22,70 @@
)


class SpinChannel(MDataset):
m_def = Dataset(
type=MEnum('up', 'down', 'all'), # ? alpha, beta
)
class MaterialPropertyAttributes(Enum):
fields = 'fields'
variables = 'variables'


class KMesh(MDataset):
m_def = Dataset(
type=np.float64, # ? KMeshSettings.points,
unit='1/meter',
shape=[3],
description="""
K-point mesh over which the physical property is calculated. This is used to define `ElectronicEigenvalues(PhysicalProperty)` and
other k-space properties. The `points` are obtained from a reference to the `NumericalSettings` section, `KMesh(NumericalSettings)`.
""",
)
def fetch_instance(
prop: MaterialProperty,
target: PhysicalProperty,
attribute: MaterialPropertyAttributes = MaterialPropertyAttributes.fields,
) -> np.ndarray[PhysicalProperty]:
"""
Fetches instances of a specified physical property from a material property based on a given attribute.
Args:
prop (MaterialProperty): The material property from which to fetch instances.
target (PhysicalProperty): The type of physical property to fetch.
attribute (MaterialPropertyAttributes): The attribute of the material property to check.
Returns:
np.ndarray[PhysicalProperty]: An array of instances of the specified physical property.
Raises:
ValueError: If the provided attribute is not a valid MaterialProperty attribute.
"""
if not isinstance(attribute, MaterialPropertyAttributes):
raise ValueError(
f'Attribute {attribute} is not a valid MaterialProperty attribute.'
)
return np.where(lambda x: isinstance(x, target), getattr(prop, attribute), [])

class MomentumTransfer(MDataset):
m_def = Dataset(
type=np.float64,
shape=[2, 3],
unit='1/meter',
description="""
The change in momentum for any (quasi-)particle, e.g. electron, hole,
traversing the band gap.

For example, the momentum transfer in bulk Si happens
between the Γ and X points in the Brillouin zone; thus:
`momentum_transfer = [[0, 0, 0], [0.5, 0.5, 0]]`.
SpinChannel = PhysicalProperty(
name='SpinChannel',
type=MEnum('alpha', 'beta', 'both'),
# ! iri
)


KMesh = PhysicalProperty(
type=np.float64, # ? KMeshSettings.points,
shape=[3],
unit='1/m',
description="""
K-point mesh over which the physical property is calculated. This is used to define `ElectronicEigenvalues(PhysicalProperty)` and
other k-space properties. The `points` are obtained from a reference to the `NumericalSettings` section, `KMesh(NumericalSettings)`.
""",
)
# ! iri
)


MomentumTransfer = PhysicalProperty(
type=np.float64,
shape=[2, 3],
unit='1/meter',
description="""
The change in momentum for any (quasi-)particle, e.g. electron, hole,
traversing the band gap.
For example, the momentum transfer in bulk Si happens
between the Γ and X points in the Brillouin zone; thus:
`momentum_transfer = [[0, 0, 0], [0.5, 0.5, 0]]`.
""",
# ! iri
)


class Variables(ArchiveSection):
Expand Down

0 comments on commit de7b75d

Please sign in to comment.