diff --git a/src/nomad_simulations/schema_packages/atoms_state.py b/src/nomad_simulations/schema_packages/atoms_state.py index 32fbdd11..8e2c7327 100644 --- a/src/nomad_simulations/schema_packages/atoms_state.py +++ b/src/nomad_simulations/schema_packages/atoms_state.py @@ -641,3 +641,72 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: self.chemical_symbol = self.resolve_chemical_symbol(logger=logger) if self.atomic_number is None: self.atomic_number = self.resolve_atomic_number(logger=logger) + + +class MolecularOrbitalsState(Entity): + """ + A base section to define molecular orbitals. + """ + + symmetry_label = Quantity( + type=str, + description=""" + Symmetry label of the molecular orbital (e.g., 'sigma', 'pi', 'delta'). + """, + ) + + energy = Quantity( + type=np.float64, + #unit='eV', + description=""" + Energy of the molecular orbital. + """, + ) + + occupation = Quantity( + type=np.float64, + description=""" + Occupation of the molecular orbital. This value is typically an integer (0 or 2) + in closed-shell systems, but can be fractional in open-shell or spin-polarized + calculations. + """, + ) + + spin = Quantity( + type=MEnum('alpha', 'beta'), + description=""" + Spin of the molecular orbital. 'alpha' corresponds to spin-up, 'beta' corresponds + to spin-down. + """, + ) + + coefficients = Quantity( + type=np.float64, + shape=['number_of_atoms', 'number_of_basis_functions'], + description=""" + Coefficients of the molecular orbital expressed as a linear combination of atomic orbitals. + The shape corresponds to the number of atoms and their associated basis functions. + """, + ) + + atom_contributions = SubSection( + sub_section=AtomsState.m_def, + repeats=True, + description=""" + Contribution of each atom to the molecular orbital, as defined by its basis functions. + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + # Validation: Ensure occupation values are consistent + if self.occupation is not None and (self.occupation < 0 or self.occupation > 2): + logger.error("The molecular orbital occupation must be between 0 and 2.") + + # Validation: Ensure coefficients are provided if atom contributions are defined + if self.atom_contributions and self.coefficients is None: + logger.error( + "Coefficients must be defined when atom contributions are provided." + ) + diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index 1c89b94c..29266538 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1223,31 +1223,6 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) -class BaseMolecularOrbital(ArchiveSection): - """ - A section representing a single molecular orbital. - """ - - energy = Quantity( - type=np.float64, - # unit='electron_volt', - description='Energy of the molecular orbital.', - ) - - occupation = Quantity( - type=np.float64, description='Occupation number of the molecular orbital.' - ) - - symmetry_label = Quantity( - type=str, description='Symmetry label of the molecular orbital.' - ) - - orbital_ref = SubSection( - sub_section=OrbitalsState.m_def, - description='Reference to the underlying atomic orbital state.', - ) - - class MolecularOrbitals(ArchiveSection): """ A section for molecular orbital schemes used in quantum chemistry calculations. @@ -1268,18 +1243,6 @@ class MolecularOrbitals(ArchiveSection): a_eln=ELNAnnotation(component='EnumEditQuantity'), ) - orbital_set = Quantity( - type=MEnum('canonical', 'natural', 'localized'), - description=""" - Specifies the type of orbitals used in the molecular orbital scheme: - - canonical: Default canonical molecular orbitals. - - natural: Natural orbitals obtained from the density matrix. - - localized: Localized orbitals such as Boys or Foster-Boys localization. - TODO: this will be later connected to MCSCF. - """, - a_eln=ELNAnnotation(component='EnumEditQuantity'), - ) - n_alpha_electrons = Quantity( type=np.int32, description=""" @@ -1301,16 +1264,16 @@ class MolecularOrbitals(ArchiveSection): """, ) - molecular_orbitals = SubSection( - sub_section=BaseMolecularOrbital.m_def, - repeats=True, - description=""" - Detailed information about each molecular orbital, - including energy, occupation, and symmetry label. - """, - ) + # molecular_orbitals = SubSection( + # sub_section=BaseMolecularOrbital.m_def, + # repeats=True, + # description=""" + # Detailed information about each molecular orbital, + # including energy, occupation, and symmetry label. + # """, + # ) - total_spin = Quantity( + sz_projection = Quantity( type=np.float64, description=""" Total spin of the system defined as S = (n_alpha_electrons - n_beta_electrons) / 2. @@ -1364,24 +1327,8 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if not self.validate_scheme(logger): logger.error('Invalid molecular orbital scheme.') - # Resolve the number of molecular orbitals - if self.n_molecular_orbitals is None and self.molecular_orbitals: - self.n_molecular_orbitals = len(self.molecular_orbitals) - - # Validate molecular orbital occupation - total_occupation = sum( - orbital.occupation - for orbital in self.molecular_orbitals - if orbital.occupation is not None - ) - expected_occupation = self.n_alpha_electrons + self.n_beta_electrons - if total_occupation != expected_occupation: - logger.warning( - f'The total occupation ({total_occupation}) does not match the expected value ({expected_occupation}).' - ) - -class GTOIntegralDecomposition(BaseModelMethod): +class IntegralDecomposition(BaseModelMethod): """ A general class for integral decomposition techniques for Coulomb and exchange integrals to speed up the calculation. Examples: @@ -1463,16 +1410,20 @@ class LocalCorrelation(ArchiveSection): """ type = Quantity( - type=MEnum('PNO', 'domain'), + type=MEnum('PNO', 'LPNO', 'DLPNO'), description=""" the type of local correlation. """, ) ansatz = Quantity( - type=MEnum('LMP2', 'LCC', 'LocalDFT'), + type=MEnum('MP2', 'CC', 'DH-DFT'), description=""" The underlying ansatz for the local correlation treatment. + LMP2 + LCC + DH-DFT: double-hybrid DFT + ... """, ) @@ -1493,6 +1444,7 @@ class CoupledCluster(ModelMethodElectronic): a_eln=ELNAnnotation(component='StringEditQuantity'), ) + # add a real reference determinant reference reference_determinant = Quantity( type=MEnum('UHF', 'RHF', 'ROHF', 'UKS', 'RKS', 'ROKS'), description="""