From ba97b0859608d1e0a67f1dfb716be1b94bcd1afa Mon Sep 17 00:00:00 2001 From: Nathan Daelman Date: Wed, 18 Dec 2024 13:40:11 +0100 Subject: [PATCH] Extend to band structures --- .../schema_packages/properties/band_gap.py | 6 +- .../properties/band_structure.py | 118 ++++++++++-------- .../schema_packages/variables.py | 14 +-- 3 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/nomad_simulations/schema_packages/properties/band_gap.py b/src/nomad_simulations/schema_packages/properties/band_gap.py index 33d22de4..e744e1e7 100644 --- a/src/nomad_simulations/schema_packages/properties/band_gap.py +++ b/src/nomad_simulations/schema_packages/properties/band_gap.py @@ -4,7 +4,7 @@ from nomad.units import ureg from nomad.datamodel.data import ArchiveSection from nomad.metainfo import MEnum, Quantity -from nomad.metainfo.physical_properties import DatasetTemplate, Energy +from nomad.metainfo.datasets import DatasetTemplate, Energy from ..variables import ( SpinChannel, MomentumTransfer, @@ -29,8 +29,8 @@ class HomoLumoGap(ArchiveSection): # ? class description def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: self.m_all_validate() # ensure constraints - if np.any(self.values.fields < 0): - logger.warning(f'Negative band gap detected: {self.values.fields} J') + if np.any((energies := self.get_variable(Energy).get_values()) < 0): # ? failure handling + logger.warning(f'Negative band gap detected: {energies.to('J').magnitude} J') class ElectronicBandGap(HomoLumoGap): # ! TODO: add optical band gap diff --git a/src/nomad_simulations/schema_packages/properties/band_structure.py b/src/nomad_simulations/schema_packages/properties/band_structure.py index 6ec28c6a..8080edef 100644 --- a/src/nomad_simulations/schema_packages/properties/band_structure.py +++ b/src/nomad_simulations/schema_packages/properties/band_structure.py @@ -2,9 +2,11 @@ import numpy as np import pint +import plotly.express as px + from nomad.datamodel.data import ArchiveSection from nomad.metainfo import MEnum, Quantity -from nomad.metainfo.physical_properties import MaterialProperty, Count +from nomad.metainfo.physical_properties import DatasetTemplate, Count, Energy from ..variables import SpinChannel, KMesh if TYPE_CHECKING: @@ -33,24 +35,14 @@ class ElectronicEigenstates(ArchiveSection): - values = MaterialProperty( + values = DatasetTemplate( name='ElectronicEigenstates', - fields=[Energy, Occupancy], # shape defined by variables - variables=[SpinChannel, KMesh], # ? enforce spanned dimension at metainfo level + mandatory_fields=[Energy, Occupancy], + mandatory_variables=[SpinChannel], iri='http://fairmat-nfdi.eu/taxonomy/ElectronicEigenvalues', - description="""A base section used to define basic quantities for the `ElectronicEigenvalues` and `ElectronicEigenstates` properties.""", - ) - - 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`. + A base section used to define basic quantities for + the `ElectronicEigenvalues` and `ElectronicEigenstates` properties. """, ) @@ -58,13 +50,6 @@ class ElectronicEigenstates(ArchiveSection): # ? 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`. - """, - ) # ! - atoms_state_ref = Quantity( type=AtomsState, description=""" @@ -80,30 +65,11 @@ class ElectronicEigenstates(ArchiveSection): ) # ! TODO: unify with `atoms_state_ref` # derived properties - n_bands = Quantity( - type=np.int32, - description=""" - Number of bands / eigenvalues. - """, - ) # ? remove + n_eigenvalues = Count # ? remove - highest_occupied = Quantity( - type=np.float64, # ! energy, k-point - unit='joule', - description=""" - Highest occupied electronic eigenvalue. Together with `lowest_unoccupied`, it defines the - electronic band gap. - """, - ) + highest_occupied = Energy # ? property - lowest_unoccupied = Quantity( - type=np.float64, # ! energy, k-point - unit='joule', - description=""" - Lowest unoccupied electronic eigenvalue. Together with `highest_occupied`, it defines the - electronic band gap. - """, - ) + lowest_unoccupied = Energy # ? property def order_eigenvalues(self) -> Union[bool, tuple['pint.Quantity', np.ndarray]]: """ @@ -214,9 +180,61 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: self.reciprocal_cell = self.resolve_reciprocal_cell() -# TODO: consider matching different k-channels for indirect bandgaps -def bandstructure_to_dos(bs: ElectronicEigenstates) -> ElectronicDOS: - pass +class DensityOfStates(ElectronicEigenstates): + # fermi_level + + # band_gap + + partial_dos = DatasetTemplate( + mandatory_fields=[Energy, Count], # ! relax + mandatory_variables=[SpinChannel, AtomsState], # to be interpreted as the symbol, i.e. p_x + ) # ? instead of subsection + + def plot(self): + self.m_all_validate() + energy_axes = self.values.get_values(Energy).by(SpinChannel) + figure_main = px.line( + x=np.sort(np.array(set(*energy_axes))), # overlay along spin dim + y=self.values.get_values(Count).by(SpinChannel), + color=self.values.get_variable(SpinChannel), + ) + + +class BandStructure(ArchiveSection): + values = DatasetTemplate( + name='BandStructure', + fields=[ElectronicEigenstates.values], + variables=[KMesh], # ? at what level will `SpinChannel` exist + ) + + 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`. + """, + ) # ? move to `ElectronicEigenstates` + + reciprocal_cell = Quantity( + type=KSpace.reciprocal_lattice_vectors, + description=""" + Reference to the reciprocal lattice vectors stored under `KSpace`. + """, + ) # ! + + highest_occupied = DatasetTemplate( + name='HighestOccupied', + mandatory_fields=[Energy, KMesh], + ) # ? property + + lowest_unoccupied = ighest_occupied = DatasetTemplate( + name='LowestUnoccupied', + mandatory_fields=[Energy, KMesh], + ) # ? property -def dos_to_bandgap(dos: ElectronicDOS) -> ElectronicBandGap: - pass + # ! plot diff --git a/src/nomad_simulations/schema_packages/variables.py b/src/nomad_simulations/schema_packages/variables.py index 1f77ce15..985a44e3 100644 --- a/src/nomad_simulations/schema_packages/variables.py +++ b/src/nomad_simulations/schema_packages/variables.py @@ -4,9 +4,9 @@ import numpy as np from nomad.datamodel.data import ArchiveSection from nomad.metainfo import MEnum, Quantity -from nomadmetainfo.physical_properties import ( - PhysicalProperty, - MaterialProperty, +from nomad.metainfo.datasets import ( + ValuesTemplate, + DatasetTemplate, ) if TYPE_CHECKING: @@ -22,14 +22,14 @@ ) -SpinChannel = PhysicalProperty( +SpinChannel = ValuesTemplate( name='SpinChannel', type=MEnum('alpha', 'beta', 'both'), # ! iri ) -KMesh = PhysicalProperty( +KMesh = ValuesTemplate( type=np.float64, # ? KMeshSettings.points, shape=[3], unit='1/m', @@ -41,7 +41,7 @@ ) -MomentumTransfer = PhysicalProperty( +MomentumTransfer = ValuesTemplate( type=np.float64, shape=[2, 3], unit='1/meter', @@ -57,7 +57,7 @@ ) -class Variables(ArchiveSection): +class Variables(ArchiveSection): # ! TODO: deprecate """ Variables over which the physical property varies, and they are defined as grid points, i.e., discretized values by `n_points` and `points`. These are used to calculate the `shape` of the physical property.