From d436e95deffe59272913fb561525f37ca1c750dc Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 24 Sep 2024 14:26:59 +0200 Subject: [PATCH 01/30] add main and auxiliary basis set quantities to AtomCenteredBasisSet --- .../schema_packages/basis_set.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index bbd763e0..cea559ec 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -200,13 +200,41 @@ class AtomCenteredBasisSet(BasisSetComponent): Defines an atom-centered basis set. """ + main_basis_set = Quantity( + type=str, + description=""" + Name of the main basis set. + """, + ) + + aux_c_basis_set = Quantity( + type=str, + description=""" + AuxC type of basis set. + """ + ) + + aux_j_basis_set = Quantity( + type=str, + description=""" + AuxJ type of basis set. + """ + ) + + aux_jk_basis_set = Quantity( + type=str, + description=""" + AuxJK type of basis set. + """ + ) + functional_composition = SubSection( sub_section=AtomCenteredFunction.m_def, repeats=True ) # TODO change name def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - # self.name = self.m_def.name + self.name = self.m_def.name # TODO: set name based on basis functions # ? use basis set names from Basis Set Exchange From 022c1fadba9188bdba0dccc0621a8fabb170dd5e Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 24 Sep 2024 14:32:22 +0200 Subject: [PATCH 02/30] formatting --- src/nomad_simulations/schema_packages/basis_set.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index cea559ec..90a97408 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -211,21 +211,21 @@ class AtomCenteredBasisSet(BasisSetComponent): type=str, description=""" AuxC type of basis set. - """ + """, ) aux_j_basis_set = Quantity( type=str, description=""" AuxJ type of basis set. - """ + """, ) aux_jk_basis_set = Quantity( type=str, description=""" AuxJK type of basis set. - """ + """, ) functional_composition = SubSection( From af88624e32f93fc46dba294a4b4284e73e5f9519 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 25 Sep 2024 14:04:34 +0200 Subject: [PATCH 03/30] add a draft for AtomCenteredFunction --- .../schema_packages/basis_set.py | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 90a97408..984326ef 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -190,9 +190,40 @@ class AtomCenteredFunction(ArchiveSection): Specifies a single function (term) in an atom-centered basis set. """ - pass + function_type = Quantity( + type=str, + description=""" + Type of the function (e.g. GTO for Gaussian, STO for Slater) + """, + ) + + exponents = Quantity( + type=np.float32, + shape=['*'], + description=""" + List of exponents for the basis function. + """, + ) - # TODO: design system for writing basis functions like gaussian or slater orbitals + contraction_coefficients = Quantity( + type=np.float32, + shape=['*'], + description=""" + List of contraction coefficients corresponding to the exponents. + """, + ) + + atom_state = SubSection(sub_section=AtomsState.m_def, repeats=False) + + def __init__(self, atom_state: AtomsState, function_type: str, exponents: list, contraction_coefficients: list): + self.atom_state = atom_state + self.function_type = function_type + self.exponents = exponents + self.contraction_coefficients = contraction_coefficients + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + self.name = self.m_def.name class AtomCenteredBasisSet(BasisSetComponent): From 0a9d98b19e2a154d7922ce4530bce10ef594a746 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 25 Sep 2024 15:09:10 +0200 Subject: [PATCH 04/30] add a quantity for number of primitives --- .../schema_packages/basis_set.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 984326ef..ea165072 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -191,9 +191,16 @@ class AtomCenteredFunction(ArchiveSection): """ function_type = Quantity( - type=str, + type=MEnum('S', 'P', 'D', 'F', 'G', 'H', 'I', 'J'), + description=""" + the angular momentum of the shell to be added. + """, + ) + + n_primitive = Quantity( + type=int, description=""" - Type of the function (e.g. GTO for Gaussian, STO for Slater) + Number of primitives. """, ) @@ -215,15 +222,23 @@ class AtomCenteredFunction(ArchiveSection): atom_state = SubSection(sub_section=AtomsState.m_def, repeats=False) - def __init__(self, atom_state: AtomsState, function_type: str, exponents: list, contraction_coefficients: list): + def __init__( + self, + atom_state: AtomsState, + function_type: str, + n_primitive: int, + exponents: list, + contraction_coefficients: list, + ): self.atom_state = atom_state self.function_type = function_type + self.n_primitive = n_primitive self.exponents = exponents self.contraction_coefficients = contraction_coefficients def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - self.name = self.m_def.name + # self.name = self.m_def.name class AtomCenteredBasisSet(BasisSetComponent): @@ -265,7 +280,7 @@ class AtomCenteredBasisSet(BasisSetComponent): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - self.name = self.m_def.name + # self.name = self.m_def.name # TODO: set name based on basis functions # ? use basis set names from Basis Set Exchange From a204ec481e232b20c7ee334e7669bf0804d8d3f1 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 26 Sep 2024 13:10:18 +0200 Subject: [PATCH 05/30] add atoms_state reference to AtomCenteredBasisSet --- .../schema_packages/basis_set.py | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index ea165072..0941a628 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -206,7 +206,7 @@ class AtomCenteredFunction(ArchiveSection): exponents = Quantity( type=np.float32, - shape=['*'], + shape=['n_primitive'], description=""" List of exponents for the basis function. """, @@ -214,28 +214,12 @@ class AtomCenteredFunction(ArchiveSection): contraction_coefficients = Quantity( type=np.float32, - shape=['*'], + shape=['n_primitive'], description=""" List of contraction coefficients corresponding to the exponents. """, ) - atom_state = SubSection(sub_section=AtomsState.m_def, repeats=False) - - def __init__( - self, - atom_state: AtomsState, - function_type: str, - n_primitive: int, - exponents: list, - contraction_coefficients: list, - ): - self.atom_state = atom_state - self.function_type = function_type - self.n_primitive = n_primitive - self.exponents = exponents - self.contraction_coefficients = contraction_coefficients - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) # self.name = self.m_def.name @@ -274,6 +258,14 @@ class AtomCenteredBasisSet(BasisSetComponent): """, ) + atoms_ref = Quantity( + type=AtomsState, + shape=['*'], + description=""" + References to the `AtomsState` sections that define the atoms this basis set applies to. + """, + ) + functional_composition = SubSection( sub_section=AtomCenteredFunction.m_def, repeats=True ) # TODO change name From 8866ab7785a103cf11c50698f4fccd9637adf6d1 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 2 Oct 2024 11:03:52 +0200 Subject: [PATCH 06/30] assign JSON format to basis set --- .../schema_packages/basis_set.py | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 0941a628..97b01c4a 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -13,7 +13,7 @@ from nomad import utils from nomad.datamodel.data import ArchiveSection from nomad.datamodel.metainfo.annotations import ELNAnnotation -from nomad.metainfo import MEnum, Quantity, SubSection +from nomad.metainfo import MEnum, Quantity, SubSection, JSON from nomad.units import ureg from nomad_simulations.schema_packages.atoms_state import AtomsState @@ -191,7 +191,7 @@ class AtomCenteredFunction(ArchiveSection): """ function_type = Quantity( - type=MEnum('S', 'P', 'D', 'F', 'G', 'H', 'I', 'J'), + type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j'), description=""" the angular momentum of the shell to be added. """, @@ -224,7 +224,47 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) # self.name = self.m_def.name +class AtomCenteredBasisSet(BasisSetComponent): + """ + Defines an atom-centered basis set, using a single JSON quantity for all basis set types, + while allowing different AtomState references for each basis set type. + """ + + basis_set_data = Quantity( + type=JSON, # Use JSON to store basis set information, including atom references + description=""" + JSON object containing all the basis set information along with atom references. Example: + { + "main_basis_set": { + "name": "cc-pVTZ", + "atoms_ref": [ref_to_atoms_1, ref_to_atoms_2] + }, + "aux_c_basis_set": { + "name": "cc-pVTZ/C", + "atoms_ref": [ref_to_atoms_3] + }, + "aux_j_basis_set": { + "name": "RIJ", + "atoms_ref": [ref_to_atoms_4] + }, + "aux_jk_basis_set": { + "name": "aug-cc-pVTZ/JK", + "atoms_ref": [ref_to_atoms_1, ref_to_atoms_5] + } + } + """, + ) + + functional_composition = SubSection( + sub_section=AtomCenteredFunction.m_def, repeats=True + ) + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + + +''' class AtomCenteredBasisSet(BasisSetComponent): """ Defines an atom-centered basis set. @@ -275,7 +315,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: # self.name = self.m_def.name # TODO: set name based on basis functions # ? use basis set names from Basis Set Exchange - +''' class APWBaseOrbital(ArchiveSection): """ From c7b078a7e589a4ea1db8c779114ac21dadf4f42f Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 8 Oct 2024 11:38:24 +0200 Subject: [PATCH 07/30] add type and auxiliary_type quantities to AtomCenteredBasisSet --- .../schema_packages/basis_set.py | 92 +++++-------------- 1 file changed, 21 insertions(+), 71 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 97b01c4a..c3b8c517 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -13,7 +13,7 @@ from nomad import utils from nomad.datamodel.data import ArchiveSection from nomad.datamodel.metainfo.annotations import ELNAnnotation -from nomad.metainfo import MEnum, Quantity, SubSection, JSON +from nomad.metainfo import JSON, MEnum, Quantity, SubSection from nomad.units import ureg from nomad_simulations.schema_packages.atoms_state import AtomsState @@ -187,13 +187,16 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: class AtomCenteredFunction(ArchiveSection): """ - Specifies a single function (term) in an atom-centered basis set. + Specifies a single function (term) in a Gaussian-type basis set. + Cartesian Gaussian-type orbitals (GTOs) """ function_type = Quantity( type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j'), description=""" - the angular momentum of the shell to be added. + the l value: + l = i + j + k + the angular momentum of GTO to be added. """, ) @@ -224,98 +227,45 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) # self.name = self.m_def.name -class AtomCenteredBasisSet(BasisSetComponent): - """ - Defines an atom-centered basis set, using a single JSON quantity for all basis set types, - while allowing different AtomState references for each basis set type. - """ - - basis_set_data = Quantity( - type=JSON, # Use JSON to store basis set information, including atom references - description=""" - JSON object containing all the basis set information along with atom references. Example: - { - "main_basis_set": { - "name": "cc-pVTZ", - "atoms_ref": [ref_to_atoms_1, ref_to_atoms_2] - }, - "aux_c_basis_set": { - "name": "cc-pVTZ/C", - "atoms_ref": [ref_to_atoms_3] - }, - "aux_j_basis_set": { - "name": "RIJ", - "atoms_ref": [ref_to_atoms_4] - }, - "aux_jk_basis_set": { - "name": "aug-cc-pVTZ/JK", - "atoms_ref": [ref_to_atoms_1, ref_to_atoms_5] - } - } - """, - ) - - functional_composition = SubSection( - sub_section=AtomCenteredFunction.m_def, repeats=True - ) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) - - - -''' class AtomCenteredBasisSet(BasisSetComponent): """ Defines an atom-centered basis set. """ - main_basis_set = Quantity( + basis_set = Quantity( type=str, description=""" - Name of the main basis set. + name of the basis set """, ) - aux_c_basis_set = Quantity( - type=str, + type = Quantity( + type=MEnum('STO', 'GTO'), description=""" - AuxC type of basis set. + Type of the basis set, e.g. STO or GTO. """, ) - aux_j_basis_set = Quantity( - type=str, - description=""" - AuxJ type of basis set. - """, - ) + # TODO: connect RI approximation - aux_jk_basis_set = Quantity( - type=str, + auxiliary_type = Quantity( + type=MEnum('AuxC', 'AuxJ', 'AuxJK'), description=""" - AuxJK type of basis set. - """, - ) - - atoms_ref = Quantity( - type=AtomsState, - shape=['*'], - description=""" - References to the `AtomsState` sections that define the atoms this basis set applies to. + the type of RI approximation. + AuxJ and AuxJK: Fock matrix construction. + AuxC: all other integral generation steps, e.g. post-HF methods. + Since a JK-type basis set can be assined to AuxJ, this quantity is needed. """, ) functional_composition = SubSection( sub_section=AtomCenteredFunction.m_def, repeats=True - ) # TODO change name + ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - # self.name = self.m_def.name - # TODO: set name based on basis functions - # ? use basis set names from Basis Set Exchange -''' + #self.name = self.m_def.name + class APWBaseOrbital(ArchiveSection): """ From 1208e88c4088d57fe02d8d7ac0fcbf33e2adadaa Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 8 Oct 2024 11:42:54 +0200 Subject: [PATCH 08/30] reformatted basis_set.py --- src/nomad_simulations/schema_packages/basis_set.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index c3b8c517..3deaafb4 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -227,6 +227,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) # self.name = self.m_def.name + class AtomCenteredBasisSet(BasisSetComponent): """ Defines an atom-centered basis set. @@ -248,7 +249,7 @@ class AtomCenteredBasisSet(BasisSetComponent): # TODO: connect RI approximation - auxiliary_type = Quantity( + auxiliary_type = Quantity( type=MEnum('AuxC', 'AuxJ', 'AuxJK'), description=""" the type of RI approximation. @@ -264,7 +265,7 @@ class AtomCenteredBasisSet(BasisSetComponent): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - #self.name = self.m_def.name + # self.name = self.m_def.name class APWBaseOrbital(ArchiveSection): From fbc13f9ca069c4e19079102ac92d19d05271b717 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 8 Oct 2024 13:57:01 +0200 Subject: [PATCH 09/30] add NAO and point charges to basis set types --- .../schema_packages/basis_set.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 3deaafb4..58c1c40e 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -191,6 +191,19 @@ class AtomCenteredFunction(ArchiveSection): Cartesian Gaussian-type orbitals (GTOs) """ + # TODO: add a quantity for spherical-harmonic or Cartesian angular functions + # Most of the codes use only spherical harmonic. + + basis_type = Quantity( + type=MEnum( + 'spherical', + 'cartesian', + ), + description=""" + spherical-harmonic or cartesian functions. + """, + ) + function_type = Quantity( type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j'), description=""" @@ -236,26 +249,31 @@ class AtomCenteredBasisSet(BasisSetComponent): basis_set = Quantity( type=str, description=""" - name of the basis set + name of the basis set. """, ) type = Quantity( - type=MEnum('STO', 'GTO'), + type=MEnum( + 'STO', # Slater-type orbitals + 'GTO', # Gaussian-type orbitals + 'NAO', # Numerical atomic orbitals + 'PC', # Point charges + ), description=""" Type of the basis set, e.g. STO or GTO. """, ) - # TODO: connect RI approximation - - auxiliary_type = Quantity( - type=MEnum('AuxC', 'AuxJ', 'AuxJK'), + role = Quantity( + type=MEnum( + 'orbital', + 'auxiliary_scf', + 'auxiliary_post_hf', + 'cabs', + ), description=""" - the type of RI approximation. - AuxJ and AuxJK: Fock matrix construction. - AuxC: all other integral generation steps, e.g. post-HF methods. - Since a JK-type basis set can be assined to AuxJ, this quantity is needed. + role of the basis set """, ) From ddab789f0ce91cca39348e6fb841e85d02ed13f5 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 9 Oct 2024 15:51:53 +0200 Subject: [PATCH 10/30] add cECPs and pointcharges to AtomCenteredBasisSet --- .../schema_packages/basis_set.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 58c1c40e..f2e68a8e 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -199,8 +199,9 @@ class AtomCenteredFunction(ArchiveSection): 'spherical', 'cartesian', ), + default='spherical', description=""" - spherical-harmonic or cartesian functions. + Specifies whether the basis functions are spherical-harmonic or cartesian functions. """, ) @@ -214,7 +215,7 @@ class AtomCenteredFunction(ArchiveSection): ) n_primitive = Quantity( - type=int, + type=np.int32, description=""" Number of primitives. """, @@ -236,6 +237,13 @@ class AtomCenteredFunction(ArchiveSection): """, ) + point_charge = Quantity( + type=np.int32, + description=""" + the value of the point charge. + """, + ) + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) # self.name = self.m_def.name @@ -258,6 +266,7 @@ class AtomCenteredBasisSet(BasisSetComponent): 'STO', # Slater-type orbitals 'GTO', # Gaussian-type orbitals 'NAO', # Numerical atomic orbitals + 'cECP', # Capped effective core potentials 'PC', # Point charges ), description=""" @@ -270,10 +279,10 @@ class AtomCenteredBasisSet(BasisSetComponent): 'orbital', 'auxiliary_scf', 'auxiliary_post_hf', - 'cabs', + 'cabs', # complementary auxiliary basis set ), description=""" - role of the basis set + The role of the basis set. """, ) From 8c6ae1a4b3bac5967c1f24e6f4ffa34e24b0179b Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Fri, 11 Oct 2024 14:55:26 +0200 Subject: [PATCH 11/30] fix point charge Quantity type --- src/nomad_simulations/schema_packages/basis_set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index f2e68a8e..39079686 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -238,7 +238,7 @@ class AtomCenteredFunction(ArchiveSection): ) point_charge = Quantity( - type=np.int32, + type=np.float32, description=""" the value of the point charge. """, From c94e995fcf2a00445702d9552b3f1118e752b998 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Mon, 14 Oct 2024 11:08:39 +0200 Subject: [PATCH 12/30] a bit of a cleanup --- src/nomad_simulations/schema_packages/basis_set.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 39079686..84c4c261 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -187,13 +187,9 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: class AtomCenteredFunction(ArchiveSection): """ - Specifies a single function (term) in a Gaussian-type basis set. - Cartesian Gaussian-type orbitals (GTOs) + Specifies a single function (term) in an atom-centered basis set. """ - # TODO: add a quantity for spherical-harmonic or Cartesian angular functions - # Most of the codes use only spherical harmonic. - basis_type = Quantity( type=MEnum( 'spherical', @@ -206,11 +202,10 @@ class AtomCenteredFunction(ArchiveSection): ) function_type = Quantity( - type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j'), + type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), description=""" - the l value: - l = i + j + k - the angular momentum of GTO to be added. + L=a+b+c + The angular momentum of GTO to be added. """, ) @@ -218,6 +213,7 @@ class AtomCenteredFunction(ArchiveSection): type=np.int32, description=""" Number of primitives. + Linear combinations of the primitive Gaussians are formed to approximate the radial extent of an STO. """, ) From 62de2433e45df887df9ac6308c11623b704ba918 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 24 Oct 2024 17:02:55 +0200 Subject: [PATCH 13/30] move GTOIntegralDecomposition to NumericalSettings --- .../schema_packages/numerical_settings.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 515deea7..d8d2f781 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -887,3 +887,25 @@ def __init__(self, m_def: 'Section' = None, m_context: 'Context' = None, **kwarg def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) + + +class GTOIntegralDecomposition(NumericalSettings): + """ + A general class for integral decomposition techniques for Coulomb and exchange integrals. + Examples: + Resolution of identity (RI-approximation): + RI + RIJK + .... + Chain-of-spheres (COSX) algorithm for exchange: doi:10.1016/j.chemphys.2008.10.036 + """ + + approximation_type = Quantity( + type=str, + description=""" + RIJ, RIK, RIJK, + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) \ No newline at end of file From 52d6b9ad6da931edc72d597b7f192bc5b540cedd Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 19 Nov 2024 09:24:53 +0100 Subject: [PATCH 14/30] merge develop --- src/nomad_simulations/schema_packages/numerical_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index d8d2f781..fb005ff1 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -908,4 +908,4 @@ class GTOIntegralDecomposition(NumericalSettings): ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) \ No newline at end of file + super().normalize(archive, logger) From 646430cf67257fd980047b1715232da571720622 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 19 Nov 2024 17:11:30 +0100 Subject: [PATCH 15/30] add Mesh, NumericalIntegration and MolecularHamiltonianSubTerms --- .../schema_packages/model_method.py | 13 ++ .../schema_packages/numerical_settings.py | 127 +++++++++++------- 2 files changed, 91 insertions(+), 49 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index c7b143ff..cfdc487c 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1221,3 +1221,16 @@ class DMFT(ModelMethodElectronic): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) + + +class MolecularHamiltonianSubTerms(BaseModelMethod): + type=Quantity( + type=MEnum('coulomb', 'exchange'), + description=""" + Typical sub-terms of the molecular hamiltonian. + Relativistic effects, SOC, ..... + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) \ No newline at end of file diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index fb005ff1..6a5b63a2 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -52,66 +52,45 @@ class Smearing(NumericalSettings): class Mesh(ArchiveSection): """ - A base section used to specify the settings of a sampling mesh. - It supports uniformly-spaced meshes and symmetry-reduced representations. + A base section used to define the mesh or space partitioning over which a discrete numerical integration is performed. """ - spacing = Quantity( - type=MEnum('Equidistant', 'Logarithmic', 'Tan'), - shape=['dimensionality'], + dimensionality = Quantity( + type=np.int32, + default=3, description=""" - Identifier for the spacing of the Mesh. Defaults to 'Equidistant' if not defined. It can take the values: - - | Name | Description | - | --------- | -------------------------------- | - | `'Equidistant'` | Equidistant grid (also known as 'Newton-Cotes') | - | `'Logarithmic'` | log distance grid | - | `'Tan'` | Non-uniform tan mesh for grids. More dense at low abs values of the points, while less dense for higher values | + Dimensionality of the mesh: 1, 2, or 3. Defaults to 3. """, ) - quadrature = Quantity( - type=MEnum( - 'Gauss-Legendre', - 'Gauss-Laguerre', - 'Clenshaw-Curtis', - 'Newton-Cotes', - 'Gauss-Hermite', - ), + kind = Quantity( + type=MEnum('equidistant', 'logarithmic', 'tan'), + shape=['dimensionality'], description=""" - Quadrature rule used for integration of the Mesh. This quantity is relevant for 1D meshes: + Kind of mesh identifying the spacing in each of the dimensions specified by `dimensionality`. It can take the values: | Name | Description | | --------- | -------------------------------- | - | `'Gauss-Legendre'` | Quadrature rule for integration using Legendre polynomials | - | `'Gauss-Laguerre'` | Quadrature rule for integration using Laguerre polynomials | - | `'Clenshaw-Curtis'` | Quadrature rule for integration using Chebyshev polynomials using discrete cosine transformations | - | `'Gauss-Hermite'` | Quadrature rule for integration using Hermite polynomials | - """, - ) # ! @JosePizarro3 I think that this is separate from the spacing - - n_points = Quantity( - type=np.int32, - description=""" - Number of points in the mesh. + | `'equidistant'` | Equidistant grid (also known as 'Newton-Cotes') | + | `'logarithmic'` | log distance grid | + | `'Tan'` | Non-uniform tan mesh for grids. More dense at low abs values of the points, while less dense for higher values | """, ) - dimensionality = Quantity( + grid = Quantity( type=np.int32, - default=3, + shape=['dimensionality'], description=""" - Dimensionality of the mesh: 1, 2, or 3. Defaults to 3. + Amount of mesh point sampling along each axis. """, ) - grid = Quantity( + n_points = Quantity( type=np.int32, - shape=['dimensionality'], description=""" - Amount of mesh point sampling along each axis. See `type` for the axes definition. + Number of points in the mesh. """, - ) # ? @JosePizzaro3: should the mesh also contain its boundary information + ) points = Quantity( type=np.complex128, @@ -126,23 +105,73 @@ class Mesh(ArchiveSection): shape=['n_points'], description=""" The amount of times the same point reappears. A value larger than 1, typically indicates - a symmetry operation that was applied to the `Mesh`. This quantity is equivalent to `weights`: - - multiplicities = n_points * weights + a symmetry operation that was applied to the `Mesh`. """, ) - weights = Quantity( - type=np.float64, - shape=['n_points'], + pruning = Quantity( + type=MEnum('fixed', 'adaptive'), description=""" - Weight of each point. A value smaller than 1, typically indicates a symmetry operation that was - applied to the mesh. This quantity is equivalent to `multiplicities`: + Pruning method applied for reducing the amount of points in the Mesh. This is typically + used for numerical integration near the core levels in atoms. + In the fixed grid methods, the number of angular grid points is predetermined for + ranges of radial grid points, while in the adaptive methods, the angular grid is adjusted + on-the-fly for each radial point according to some accuracy criterion. + """ + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) - weights = multiplicities / n_points + if self.dimensionality not in [1, 2, 3]: + logger.error('`dimensionality` meshes different than 1, 2, or 3 are not supported.') + + +class NumericalIntegration(NumericalSettings): + """ + Numerical integration settings used to resolve the following type of integrals by discrete + numerical integration: + + ```math + \int_{\vec{r}_a}^{\vec{r}_b} d^3 \vec{r} F(\vec{r}) \approx \sum_{n=a}^{b} w(\vec{r}_n) F(\vec{r}_n) + ``` + + Here, $F$ can be any type of function which would define the type of rules that can be applied + to solve such integral (e.g., 1D Gaussian quadrature rule or multi-dimensional `angular` rules like the + Lebedev quadrature rule). + + These multidimensional integral has a `Mesh` defined over which the integration is performed, i.e., the + $\vec{r}_n$ points. + """ + + coordinate = Quantity( + type=MEnum('all', 'radial', 'angular'), + description=""" + Coordinate over which the integration is performed. `all` means the integration is performed in + all the space. `radial` and `angular` describe cases where the integration is performed for + functions which can be splitted into radial and angular distributions (e.g., orbital wavefunctions). """, ) + integration_rule = Quantity( + type=str, # ? extend to MEnum? + description=""" + Integration rule used. This can be any 1D Gaussian quadrature rule or multi-dimensional `angular` rules, + e.g., Lebedev quadrature rule (see e.g., Becke, Chem. Phys. 88, 2547 (1988)). + """ + ) + + weight_partitioning = Quantity( + type=str, + description=""" + Approximation applied to the weight when doing the numerical integration. + See e.g., C. W. Murray, N. C. Handy + and G. J. Laming, Mol. Phys. 78, 997 (1993). + """ + ) + + mesh = SubSection(sub_section=Mesh.m_def) + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) @@ -908,4 +937,4 @@ class GTOIntegralDecomposition(NumericalSettings): ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) + super().normalize(archive, logger) \ No newline at end of file From cca109f6ae371e0cc882b2709578ac086c68b836 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 19 Nov 2024 17:27:06 +0100 Subject: [PATCH 16/30] minor adjustments to Mesh and NumericalIntegration --- .../schema_packages/model_method.py | 4 +-- .../schema_packages/numerical_settings.py | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index cfdc487c..9c1c727f 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1224,7 +1224,7 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: class MolecularHamiltonianSubTerms(BaseModelMethod): - type=Quantity( + type = Quantity( type=MEnum('coulomb', 'exchange'), description=""" Typical sub-terms of the molecular hamiltonian. @@ -1233,4 +1233,4 @@ class MolecularHamiltonianSubTerms(BaseModelMethod): ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) \ No newline at end of file + super().normalize(archive, logger) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 6a5b63a2..2986b674 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -92,6 +92,12 @@ class Mesh(ArchiveSection): """, ) + spacing = Quantity( + type=np.float64, + shape=['dimensionality'], + description='Grid spacing for equidistant meshes. Ignored for other kinds.', + ) + points = Quantity( type=np.complex128, shape=['n_points', 'dimensionality'], @@ -117,14 +123,16 @@ class Mesh(ArchiveSection): In the fixed grid methods, the number of angular grid points is predetermined for ranges of radial grid points, while in the adaptive methods, the angular grid is adjusted on-the-fly for each radial point according to some accuracy criterion. - """ + """, ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) if self.dimensionality not in [1, 2, 3]: - logger.error('`dimensionality` meshes different than 1, 2, or 3 are not supported.') + logger.error( + '`dimensionality` meshes different than 1, 2, or 3 are not supported.' + ) class NumericalIntegration(NumericalSettings): @@ -145,10 +153,10 @@ class NumericalIntegration(NumericalSettings): """ coordinate = Quantity( - type=MEnum('all', 'radial', 'angular'), + type=MEnum('full', 'radial', 'angular'), description=""" - Coordinate over which the integration is performed. `all` means the integration is performed in - all the space. `radial` and `angular` describe cases where the integration is performed for + Coordinate over which the integration is performed. `full` means the integration is performed in + entire space. `radial` and `angular` describe cases where the integration is performed for functions which can be splitted into radial and angular distributions (e.g., orbital wavefunctions). """, ) @@ -158,16 +166,16 @@ class NumericalIntegration(NumericalSettings): description=""" Integration rule used. This can be any 1D Gaussian quadrature rule or multi-dimensional `angular` rules, e.g., Lebedev quadrature rule (see e.g., Becke, Chem. Phys. 88, 2547 (1988)). - """ + """, ) - weight_partitioning = Quantity( + weight_approximtion = Quantity( type=str, description=""" Approximation applied to the weight when doing the numerical integration. See e.g., C. W. Murray, N. C. Handy and G. J. Laming, Mol. Phys. 78, 997 (1993). - """ + """, ) mesh = SubSection(sub_section=Mesh.m_def) @@ -937,4 +945,4 @@ class GTOIntegralDecomposition(NumericalSettings): ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) \ No newline at end of file + super().normalize(archive, logger) From 42f4f38ac18c7079f6438ffc54afae94a248452b Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 20 Nov 2024 11:42:40 +0100 Subject: [PATCH 17/30] add integration_thresh and weight_cutoff to NumericalIntegration --- .../schema_packages/numerical_settings.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 2986b674..63fb2ab4 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -169,7 +169,16 @@ class NumericalIntegration(NumericalSettings): """, ) - weight_approximtion = Quantity( + integration_thresh = Quantity( + type=np.float64, + description=""" + Accuracy threshold for integration grid. + GRIDTHR in Molpro. + BFCut in ORCA. + """, + ) + + weight_approximation = Quantity( type=str, description=""" Approximation applied to the weight when doing the numerical integration. @@ -178,6 +187,17 @@ class NumericalIntegration(NumericalSettings): """, ) + weight_cutoff = Quantity( + type=np.float64, + description=""" + Grid points very close to the nucleus can have very small grid weights. + These can be discarded with the option WEIGHT_CUT=thr, i.e., grid points with weights + smaller than thr will then not be used in the numerical integration anymore. + WEIGHT_CUT in Molpro. + Wcut in ORCA. + """, + ) + mesh = SubSection(sub_section=Mesh.m_def) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: From 0f92eb9ea0468f7417f30fd0edc0a2f76abfdbdc Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 20 Nov 2024 13:32:31 +0100 Subject: [PATCH 18/30] check whether n_primitive matches the lengths of exponents and contraction coefficients --- src/nomad_simulations/schema_packages/basis_set.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 84c4c261..5fd5ac03 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -242,7 +242,19 @@ class AtomCenteredFunction(ArchiveSection): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - # self.name = self.m_def.name + + # Validation: Check that n_primitive matches the lengths of exponents and contraction coefficients + if self.n_primitive is not None: + if len(self.exponents or []) != self.n_primitive: + logger.error( + f"Mismatch in number of exponents: expected {self.n_primitive}, " + f"found {len(self.exponents or [])}." + ) + if len(self.contraction_coefficients or []) != self.n_primitive: + logger.error( + f"Mismatch in number of contraction coefficients: expected {self.n_primitive}, " + f"found {len(self.contraction_coefficients or [])}." + ) class AtomCenteredBasisSet(BasisSetComponent): From e8fb5ef559c83170c1e0facedc521104f0892fc5 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 20 Nov 2024 13:48:53 +0100 Subject: [PATCH 19/30] add tests for AtomCenteredBasisSet and AtomCenteredFunction --- .../schema_packages/basis_set.py | 14 +-- tests/test_basis_set.py | 109 ++++++++++++++++++ 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 5fd5ac03..4aaf55f6 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -245,15 +245,13 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: # Validation: Check that n_primitive matches the lengths of exponents and contraction coefficients if self.n_primitive is not None: - if len(self.exponents or []) != self.n_primitive: - logger.error( - f"Mismatch in number of exponents: expected {self.n_primitive}, " - f"found {len(self.exponents or [])}." + if self.exponents is not None and len(self.exponents) != self.n_primitive: + raise ValueError( + f"Mismatch in number of exponents: expected {self.n_primitive}, found {len(self.exponents)}." ) - if len(self.contraction_coefficients or []) != self.n_primitive: - logger.error( - f"Mismatch in number of contraction coefficients: expected {self.n_primitive}, " - f"found {len(self.contraction_coefficients or [])}." + if self.contraction_coefficients is not None and len(self.contraction_coefficients) != self.n_primitive: + raise ValueError( + f"Mismatch in number of contraction coefficients: expected {self.n_primitive}, found {len(self.contraction_coefficients)}." ) diff --git a/tests/test_basis_set.py b/tests/test_basis_set.py index b1dcb03c..e96c224b 100644 --- a/tests/test_basis_set.py +++ b/tests/test_basis_set.py @@ -15,6 +15,7 @@ APWOrbital, APWPlaneWaveBasisSet, AtomCenteredBasisSet, + AtomCenteredFunction, BasisSetContainer, MuffinTinRegion, PlaneWaveBasisSet, @@ -418,3 +419,111 @@ def test_quick_step() -> None: ], } # TODO: generate a QuickStep generator in the CP2K plugin + + +@pytest.mark.parametrize( + 'basis_set_name, basis_type, role', + [ + ('cc-pVTZ', 'GTO', 'orbital'), + ('def2-TZVP', 'GTO', 'auxiliary_scf'), + ('aug-cc-pVDZ', 'STO', 'auxiliary_post_hf'), + ('custom_basis', None, None), # Undefined type and role + ], +) +def test_atom_centered_basis_set_init(basis_set_name, basis_type, role) -> None: + """Test initialization of AtomCenteredBasisSet.""" + bs = AtomCenteredBasisSet(basis_set=basis_set_name, type=basis_type, role=role) + assert bs.basis_set == basis_set_name + assert bs.type == basis_type + assert bs.role == role + + +@pytest.mark.parametrize( + 'functions', + [ + [ + AtomCenteredFunction( + basis_type='spherical', + function_type='s', + n_primitive=3, + exponents=[1.0, 2.0, 3.0], + contraction_coefficients=[0.5, 0.3, 0.2], + ), + ], + [ + AtomCenteredFunction( + basis_type='cartesian', + function_type='p', + n_primitive=1, + exponents=[0.5], + contraction_coefficients=[1.0], + ), + AtomCenteredFunction( + basis_type='spherical', + function_type='d', + n_primitive=2, + exponents=[1.0, 2.0], + contraction_coefficients=[0.4, 0.6], + ), + ], + ], +) +def test_atom_centered_basis_set_functional_composition(functions) -> None: + """Test functional composition within AtomCenteredBasisSet.""" + bs = AtomCenteredBasisSet(functional_composition=functions) + assert len(bs.functional_composition) == len(functions) + for f, ref_f in zip(bs.functional_composition, functions): + assert f.basis_type == ref_f.basis_type + assert f.function_type == ref_f.function_type + assert f.n_primitive == ref_f.n_primitive + assert np.allclose(f.exponents, ref_f.exponents) + assert np.allclose(f.contraction_coefficients, ref_f.contraction_coefficients) + + +def test_atom_centered_basis_set_normalize() -> None: + """Test normalization of AtomCenteredBasisSet.""" + bs = AtomCenteredBasisSet( + basis_set='cc-pVTZ', + type='GTO', + role='orbital', + functional_composition=[ + AtomCenteredFunction( + basis_type='spherical', + function_type='s', + n_primitive=2, + exponents=[1.0, 2.0], + contraction_coefficients=[0.5, 0.5], + ) + ], + ) + bs.normalize(None, logger) + # Add checks for normalized behavior, if any + assert bs.basis_set == 'cc-pVTZ' + +def test_atom_centered_basis_set_invalid_data() -> None: + """Test behavior with missing or invalid data.""" + bs = AtomCenteredBasisSet( + basis_set='invalid_basis', + type=None, # Missing type + role=None, # Missing role + ) + assert bs.basis_set == 'invalid_basis' + assert bs.type is None + assert bs.role is None + + # Test functional composition with invalid data + invalid_function = AtomCenteredFunction( + basis_type='spherical', + function_type='s', + n_primitive=2, + exponents=[1.0], # Mismatched length + contraction_coefficients=[0.5, 0.5], + ) + bs.functional_composition = [invalid_function] + + # Call normalize to trigger validation + with pytest.raises(ValueError, match="Mismatch in number of exponents"): + invalid_function.normalize(None, logger) + + + From 4826f0c5d3ab84a6722c3652c8eec3a4855a6829 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 20 Nov 2024 14:09:53 +0100 Subject: [PATCH 20/30] add tests for Mesh and NumericalIntegration --- .../schema_packages/basis_set.py | 9 ++- .../schema_packages/numerical_settings.py | 6 ++ tests/test_basis_set.py | 6 +- tests/test_numerical_settings.py | 81 +++++++++++++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 4aaf55f6..0428c0c6 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -247,11 +247,14 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if self.n_primitive is not None: if self.exponents is not None and len(self.exponents) != self.n_primitive: raise ValueError( - f"Mismatch in number of exponents: expected {self.n_primitive}, found {len(self.exponents)}." + f'Mismatch in number of exponents: expected {self.n_primitive}, found {len(self.exponents)}.' ) - if self.contraction_coefficients is not None and len(self.contraction_coefficients) != self.n_primitive: + if ( + self.contraction_coefficients is not None + and len(self.contraction_coefficients) != self.n_primitive + ): raise ValueError( - f"Mismatch in number of contraction coefficients: expected {self.n_primitive}, found {len(self.contraction_coefficients)}." + f'Mismatch in number of contraction coefficients: expected {self.n_primitive}, found {len(self.contraction_coefficients)}.' ) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 63fb2ab4..37842f84 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -202,6 +202,12 @@ class NumericalIntegration(NumericalSettings): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) + valid_coordinates = ['full', 'radial', 'angular', None] + if self.coordinate not in valid_coordinates: + logger.warning( + f'Invalid coordinate value: {self.coordinate}. Resetting to None.' + ) + self.coordinate = None class KSpaceFunctionalities: diff --git a/tests/test_basis_set.py b/tests/test_basis_set.py index e96c224b..eb2e20f0 100644 --- a/tests/test_basis_set.py +++ b/tests/test_basis_set.py @@ -500,6 +500,7 @@ def test_atom_centered_basis_set_normalize() -> None: # Add checks for normalized behavior, if any assert bs.basis_set == 'cc-pVTZ' + def test_atom_centered_basis_set_invalid_data() -> None: """Test behavior with missing or invalid data.""" bs = AtomCenteredBasisSet( @@ -522,8 +523,5 @@ def test_atom_centered_basis_set_invalid_data() -> None: bs.functional_composition = [invalid_function] # Call normalize to trigger validation - with pytest.raises(ValueError, match="Mismatch in number of exponents"): + with pytest.raises(ValueError, match='Mismatch in number of exponents'): invalid_function.normalize(None, logger) - - - diff --git a/tests/test_numerical_settings.py b/tests/test_numerical_settings.py index 6426b4cd..0ead803f 100644 --- a/tests/test_numerical_settings.py +++ b/tests/test_numerical_settings.py @@ -9,6 +9,8 @@ KLinePath, KMesh, KSpaceFunctionalities, + Mesh, + NumericalIntegration, ) from . import logger @@ -377,3 +379,82 @@ def test_resolve_points(self, k_line_path: KLinePath): ] ) assert np.allclose(k_line_path.points, points) + + +@pytest.mark.parametrize( + 'dimensionality, expected_warning', + [ + (3, None), # Valid case + (2, None), # Valid case + ( + 5, + '`dimensionality` meshes different than 1, 2, or 3 are not supported.', + ), # Invalid + ( + 0, + '`dimensionality` meshes different than 1, 2, or 3 are not supported.', + ), # Invalid + ], +) +def test_mesh_dimensionality_validation(dimensionality, expected_warning, caplog): + mesh = Mesh(dimensionality=dimensionality) + mesh.normalize(None, logger) + if expected_warning: + assert expected_warning in caplog.text + else: + assert caplog.text == '' + + +@pytest.mark.parametrize( + 'dimensionality, grid, points', + [ + (3, [10, 10, 10], None), # Valid grid, no points defined yet + (3, None, None), # Missing grid and points + ( + 3, + [10, 10, 10], + [[0, 0, 0], [1, 1, 1]], + ), # Valid points (though fewer than grid suggests) + ], +) +def test_mesh_grid_and_points(dimensionality, grid, points): + mesh = Mesh(dimensionality=dimensionality, grid=grid, points=points) + assert mesh.dimensionality == dimensionality + if grid is not None: + assert np.allclose(mesh.grid, grid) + else: + assert mesh.grid == grid + if points is not None: + assert np.allclose(mesh.points, points) + else: + assert mesh.points == points + + +def test_mesh_spacing_normalization(): + mesh = Mesh(dimensionality=3, grid=[10, 10, 10], spacing=[0.1, 0.1, 0.1]) + mesh.normalize(None, logger) + assert np.allclose(mesh.spacing, [0.1, 0.1, 0.1]) + + +def test_numerical_integration_mesh(): + mesh = Mesh(dimensionality=3, grid=[10, 10, 10]) + integration = NumericalIntegration(mesh=mesh) + assert integration.mesh.dimensionality == 3 + assert np.allclose(integration.mesh.grid, [10, 10, 10]) + + +@pytest.mark.parametrize( + 'integration_thresh, weight_cutoff', + [ + (1e-6, 1e-3), # Valid thresholds + (None, 1e-3), # Missing integration threshold + (1e-6, None), # Missing weight cutoff + (None, None), # Both thresholds missing + ], +) +def test_numerical_integration_thresholds(integration_thresh, weight_cutoff): + integration = NumericalIntegration( + integration_thresh=integration_thresh, weight_cutoff=weight_cutoff + ) + assert integration.integration_thresh == integration_thresh + assert integration.weight_cutoff == weight_cutoff From c5141abd65be9ec2447ce7e70fee42fcb2aaee05 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 20 Nov 2024 16:56:10 +0100 Subject: [PATCH 21/30] modify Mesh --- .../schema_packages/numerical_settings.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 37842f84..1a06b8ad 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -63,7 +63,7 @@ class Mesh(ArchiveSection): """, ) - kind = Quantity( + mesh_type = Quantity( type=MEnum('equidistant', 'logarithmic', 'tan'), shape=['dimensionality'], description=""" @@ -81,21 +81,22 @@ class Mesh(ArchiveSection): type=np.int32, shape=['dimensionality'], description=""" - Amount of mesh point sampling along each axis. + Number of points sampled along each axis of the mesh. """, ) n_points = Quantity( type=np.int32, description=""" - Number of points in the mesh. + Total number of points in the mesh. """, ) spacing = Quantity( type=np.float64, shape=['dimensionality'], - description='Grid spacing for equidistant meshes. Ignored for other kinds.', + description="""Grid spacing for equidistant meshes. Ignored for other kinds. + """, ) points = Quantity( @@ -152,6 +153,8 @@ class NumericalIntegration(NumericalSettings): $\vec{r}_n$ points. """ + mesh = SubSection(sub_section=Mesh.m_def) + coordinate = Quantity( type=MEnum('full', 'radial', 'angular'), description=""" @@ -190,16 +193,13 @@ class NumericalIntegration(NumericalSettings): weight_cutoff = Quantity( type=np.float64, description=""" + Threshold for discarding small weights during integration. Grid points very close to the nucleus can have very small grid weights. - These can be discarded with the option WEIGHT_CUT=thr, i.e., grid points with weights - smaller than thr will then not be used in the numerical integration anymore. WEIGHT_CUT in Molpro. Wcut in ORCA. """, ) - mesh = SubSection(sub_section=Mesh.m_def) - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) valid_coordinates = ['full', 'radial', 'angular', None] From 23d7615dcc6984854d07192005f66990a979cdff Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 21 Nov 2024 11:15:08 +0100 Subject: [PATCH 22/30] MEnum for MolecularHamiltonianContributions --- .../schema_packages/model_method.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index 9c1c727f..47d78939 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1223,12 +1223,22 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) -class MolecularHamiltonianSubTerms(BaseModelMethod): +class MolecularHamiltonianContributions(BaseModelMethod): type = Quantity( - type=MEnum('coulomb', 'exchange'), + type=MEnum( + 'coulomb', + 'exchange', + 'spin_orbit_coupling', + 'scalar_relativistic', + 'ri_approximation', + 'cosx_approximation', + 'density_fitting', + 'local_correlation', + 'explicit_correlation', + ), # TODO: expand this description=""" + TODO: Name is only a placeholder. Typical sub-terms of the molecular hamiltonian. - Relativistic effects, SOC, ..... """, ) From 9ef13ea5aedcc8f70db3bb0e7e20bfc55c887a70 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 21 Nov 2024 14:57:41 +0100 Subject: [PATCH 23/30] remove contributions --- .../schema_packages/model_method.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index 47d78939..c7b143ff 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1221,26 +1221,3 @@ class DMFT(ModelMethodElectronic): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - - -class MolecularHamiltonianContributions(BaseModelMethod): - type = Quantity( - type=MEnum( - 'coulomb', - 'exchange', - 'spin_orbit_coupling', - 'scalar_relativistic', - 'ri_approximation', - 'cosx_approximation', - 'density_fitting', - 'local_correlation', - 'explicit_correlation', - ), # TODO: expand this - description=""" - TODO: Name is only a placeholder. - Typical sub-terms of the molecular hamiltonian. - """, - ) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger) From 80c8b642dc3031e0a314ce4b46df8ae50bd99fa0 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 28 Nov 2024 16:44:30 +0100 Subject: [PATCH 24/30] add a normalizer function for the AtomCenteredFunction to handle combined orbital types --- .../schema_packages/basis_set.py | 58 +++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 0428c0c6..7ee5ba87 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -190,7 +190,7 @@ class AtomCenteredFunction(ArchiveSection): Specifies a single function (term) in an atom-centered basis set. """ - basis_type = Quantity( + harmonic_type = Quantity( type=MEnum( 'spherical', 'cartesian', @@ -202,7 +202,9 @@ class AtomCenteredFunction(ArchiveSection): ) function_type = Quantity( - type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'sp', 'spd', 'spdf', + ), description=""" L=a+b+c The angular momentum of GTO to be added. @@ -227,7 +229,7 @@ class AtomCenteredFunction(ArchiveSection): contraction_coefficients = Quantity( type=np.float32, - shape=['n_primitive'], + shape=['*'], # Flexible shape to handle combined types (e.g. SP, SPD..) description=""" List of contraction coefficients corresponding to the exponents. """, @@ -241,20 +243,51 @@ class AtomCenteredFunction(ArchiveSection): ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + """ + Validates the input data + and resolves combined types like SP, SPD, SPDF, etc. + + Raises ValueError: If the data is inconsistent (e.g., mismatch in exponents and coefficients). + """ super().normalize(archive, logger) - # Validation: Check that n_primitive matches the lengths of exponents and contraction coefficients + # Validate number of primitives if self.n_primitive is not None: if self.exponents is not None and len(self.exponents) != self.n_primitive: raise ValueError( - f'Mismatch in number of exponents: expected {self.n_primitive}, found {len(self.exponents)}.' + f"Mismatch in number of exponents: expected {self.n_primitive}, " + f"found {len(self.exponents)}." ) - if ( - self.contraction_coefficients is not None - and len(self.contraction_coefficients) != self.n_primitive - ): + + # Resolve combined types + if self.function_type and len(self.function_type) > 1: + num_types = len(self.function_type) # For SP: 2, SPD: 3, etc. + if self.contraction_coefficients is not None: + expected_coeffs = num_types * self.n_primitive + if len(self.contraction_coefficients) != expected_coeffs: + raise ValueError( + f"Mismatch in contraction coefficients for {self.function_type} type: " + f"expected {expected_coeffs}, found {len(self.contraction_coefficients)}." + ) + + # Split coefficients into separate lists for each type + self.coefficient_sets = { + t: self.contraction_coefficients[i::num_types] + for i, t in enumerate(self.function_type) + } + + # Debug: Log split coefficients + for t, coeffs in self.coefficient_sets.items(): + logger.info(f"{t}-type coefficients: {coeffs}") + else: + logger.warning(f"No contraction coefficients provided for {self.function_type} type.") + + # For single types, ensure coefficients match primitives + elif self.contraction_coefficients is not None: + if len(self.contraction_coefficients) != self.n_primitive: raise ValueError( - f'Mismatch in number of contraction coefficients: expected {self.n_primitive}, found {len(self.contraction_coefficients)}.' + f"Mismatch in contraction coefficients: expected {self.n_primitive}, " + f"found {len(self.contraction_coefficients)}." ) @@ -295,6 +328,11 @@ class AtomCenteredBasisSet(BasisSetComponent): """, ) + total_number_of_basis_functions = Quantity( + type=np.int32, + description="", + ) + functional_composition = SubSection( sub_section=AtomCenteredFunction.m_def, repeats=True ) From 4ac10765abe0581df19aa3f97358d0ccecda353a Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 4 Dec 2024 08:59:59 +0100 Subject: [PATCH 25/30] fix test_basis_set.py --- tests/test_basis_set.py | 102 ++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/tests/test_basis_set.py b/tests/test_basis_set.py index eb2e20f0..b209e92a 100644 --- a/tests/test_basis_set.py +++ b/tests/test_basis_set.py @@ -422,65 +422,73 @@ def test_quick_step() -> None: @pytest.mark.parametrize( - 'basis_set_name, basis_type, role', + 'basis_set_name, basis_type, role, expected_name, expected_type, expected_role', [ - ('cc-pVTZ', 'GTO', 'orbital'), - ('def2-TZVP', 'GTO', 'auxiliary_scf'), - ('aug-cc-pVDZ', 'STO', 'auxiliary_post_hf'), - ('custom_basis', None, None), # Undefined type and role + ('cc-pVTZ', 'GTO', 'orbital', 'cc-pVTZ', 'GTO', 'orbital'), + ('def2-TZVP', 'GTO', 'auxiliary_scf', 'def2-TZVP', 'GTO', 'auxiliary_scf'), + ('aug-cc-pVDZ', 'STO', 'auxiliary_post_hf', 'aug-cc-pVDZ', 'STO', 'auxiliary_post_hf'), + ('custom_basis', None, None, 'custom_basis', None, None), # Undefined type and role ], ) -def test_atom_centered_basis_set_init(basis_set_name, basis_type, role) -> None: +def test_atom_centered_basis_set_init( + basis_set_name, basis_type, role, expected_name, expected_type, expected_role +): """Test initialization of AtomCenteredBasisSet.""" bs = AtomCenteredBasisSet(basis_set=basis_set_name, type=basis_type, role=role) - assert bs.basis_set == basis_set_name - assert bs.type == basis_type - assert bs.role == role + assert bs.basis_set == expected_name + assert bs.type == expected_type + assert bs.role == expected_role @pytest.mark.parametrize( - 'functions', + 'functions, expected_count', [ - [ - AtomCenteredFunction( - basis_type='spherical', - function_type='s', - n_primitive=3, - exponents=[1.0, 2.0, 3.0], - contraction_coefficients=[0.5, 0.3, 0.2], - ), - ], - [ - AtomCenteredFunction( - basis_type='cartesian', - function_type='p', - n_primitive=1, - exponents=[0.5], - contraction_coefficients=[1.0], - ), - AtomCenteredFunction( - basis_type='spherical', - function_type='d', - n_primitive=2, - exponents=[1.0, 2.0], - contraction_coefficients=[0.4, 0.6], - ), - ], + ( + [ + AtomCenteredFunction( + harmonic_type='spherical', + function_type='s', + n_primitive=3, + exponents=[1.0, 2.0, 3.0], + contraction_coefficients=[0.5, 0.3, 0.2], + ), + ], + 1, + ), + ( + [ + AtomCenteredFunction( + harmonic_type='cartesian', + function_type='p', + n_primitive=1, + exponents=[0.5], + contraction_coefficients=[1.0], + ), + AtomCenteredFunction( + harmonic_type='spherical', + function_type='d', + n_primitive=2, + exponents=[1.0, 2.0], + contraction_coefficients=[0.4, 0.6], + ), + ], + 2, + ), ], ) -def test_atom_centered_basis_set_functional_composition(functions) -> None: +def test_atom_centered_basis_set_functional_composition(functions, expected_count): """Test functional composition within AtomCenteredBasisSet.""" bs = AtomCenteredBasisSet(functional_composition=functions) - assert len(bs.functional_composition) == len(functions) + assert len(bs.functional_composition) == expected_count for f, ref_f in zip(bs.functional_composition, functions): - assert f.basis_type == ref_f.basis_type + assert f.harmonic_type == ref_f.harmonic_type assert f.function_type == ref_f.function_type assert f.n_primitive == ref_f.n_primitive assert np.allclose(f.exponents, ref_f.exponents) assert np.allclose(f.contraction_coefficients, ref_f.contraction_coefficients) -def test_atom_centered_basis_set_normalize() -> None: +def test_atom_centered_basis_set_normalize(): """Test normalization of AtomCenteredBasisSet.""" bs = AtomCenteredBasisSet( basis_set='cc-pVTZ', @@ -488,7 +496,7 @@ def test_atom_centered_basis_set_normalize() -> None: role='orbital', functional_composition=[ AtomCenteredFunction( - basis_type='spherical', + harmonic_type='spherical', function_type='s', n_primitive=2, exponents=[1.0, 2.0], @@ -496,12 +504,15 @@ def test_atom_centered_basis_set_normalize() -> None: ) ], ) - bs.normalize(None, logger) - # Add checks for normalized behavior, if any + bs.normalize(None, None) + # Add assertions for normalized attributes if needed assert bs.basis_set == 'cc-pVTZ' + assert bs.type == 'GTO' + assert bs.role == 'orbital' + assert len(bs.functional_composition) == 1 -def test_atom_centered_basis_set_invalid_data() -> None: +def test_atom_centered_basis_set_invalid_data(): """Test behavior with missing or invalid data.""" bs = AtomCenteredBasisSet( basis_set='invalid_basis', @@ -514,7 +525,7 @@ def test_atom_centered_basis_set_invalid_data() -> None: # Test functional composition with invalid data invalid_function = AtomCenteredFunction( - basis_type='spherical', + harmonic_type='spherical', function_type='s', n_primitive=2, exponents=[1.0], # Mismatched length @@ -524,4 +535,5 @@ def test_atom_centered_basis_set_invalid_data() -> None: # Call normalize to trigger validation with pytest.raises(ValueError, match='Mismatch in number of exponents'): - invalid_function.normalize(None, logger) + invalid_function.normalize(None, None) + From 10e782cec730a8d987c8d7d417fac2b8886c752f Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 4 Dec 2024 09:51:05 +0100 Subject: [PATCH 26/30] add OrbitalLocalization to numerical_settings.py --- .../schema_packages/basis_set.py | 40 +++++++++++++------ .../schema_packages/numerical_settings.py | 36 +++++++++++++++++ tests/test_basis_set.py | 19 +++++++-- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/nomad_simulations/schema_packages/basis_set.py b/src/nomad_simulations/schema_packages/basis_set.py index 7ee5ba87..b19f6618 100644 --- a/src/nomad_simulations/schema_packages/basis_set.py +++ b/src/nomad_simulations/schema_packages/basis_set.py @@ -202,9 +202,21 @@ class AtomCenteredFunction(ArchiveSection): ) function_type = Quantity( - type=MEnum('s', 'p', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'sp', 'spd', 'spdf', - ), + type=MEnum( + 's', + 'p', + 'd', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'sp', + 'spd', + 'spdf', + ), description=""" L=a+b+c The angular momentum of GTO to be added. @@ -229,7 +241,7 @@ class AtomCenteredFunction(ArchiveSection): contraction_coefficients = Quantity( type=np.float32, - shape=['*'], # Flexible shape to handle combined types (e.g. SP, SPD..) + shape=['*'], # Flexible shape to handle combined types (e.g. SP, SPD..) description=""" List of contraction coefficients corresponding to the exponents. """, @@ -255,8 +267,8 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: if self.n_primitive is not None: if self.exponents is not None and len(self.exponents) != self.n_primitive: raise ValueError( - f"Mismatch in number of exponents: expected {self.n_primitive}, " - f"found {len(self.exponents)}." + f'Mismatch in number of exponents: expected {self.n_primitive}, ' + f'found {len(self.exponents)}.' ) # Resolve combined types @@ -266,8 +278,8 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: expected_coeffs = num_types * self.n_primitive if len(self.contraction_coefficients) != expected_coeffs: raise ValueError( - f"Mismatch in contraction coefficients for {self.function_type} type: " - f"expected {expected_coeffs}, found {len(self.contraction_coefficients)}." + f'Mismatch in contraction coefficients for {self.function_type} type: ' + f'expected {expected_coeffs}, found {len(self.contraction_coefficients)}.' ) # Split coefficients into separate lists for each type @@ -278,16 +290,18 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: # Debug: Log split coefficients for t, coeffs in self.coefficient_sets.items(): - logger.info(f"{t}-type coefficients: {coeffs}") + logger.info(f'{t}-type coefficients: {coeffs}') else: - logger.warning(f"No contraction coefficients provided for {self.function_type} type.") + logger.warning( + f'No contraction coefficients provided for {self.function_type} type.' + ) # For single types, ensure coefficients match primitives elif self.contraction_coefficients is not None: if len(self.contraction_coefficients) != self.n_primitive: raise ValueError( - f"Mismatch in contraction coefficients: expected {self.n_primitive}, " - f"found {len(self.contraction_coefficients)}." + f'Mismatch in contraction coefficients: expected {self.n_primitive}, ' + f'found {len(self.contraction_coefficients)}.' ) @@ -330,7 +344,7 @@ class AtomCenteredBasisSet(BasisSetComponent): total_number_of_basis_functions = Quantity( type=np.int32, - description="", + description='', ) functional_composition = SubSection( diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 1a06b8ad..4abe1471 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -952,6 +952,42 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) +class OrbitalLocalization(SelfConsistency): + """ + Numerical settings that control orbital localization. + """ + + localization_method = ( + Quantity( + type=MEnum('FB', 'PM', 'IBO', 'IAOIBO', 'IAOBOYS' 'NEWBOYS' 'AHFB'), + description=""" + Name of the localization method. + """, + ), + ) + + orbital_window = ( + Quantity( + shape=['*'], + description=""" + the Molecular orbital range to be localized. + """, + ), + ) + + core_threshold = ( + Quantity( + type=np.float64, + description=""" + the energy window for the first occupied MO to be localized (in a.u.). + """, + ), + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + class GTOIntegralDecomposition(NumericalSettings): """ A general class for integral decomposition techniques for Coulomb and exchange integrals. diff --git a/tests/test_basis_set.py b/tests/test_basis_set.py index b209e92a..7e6dd427 100644 --- a/tests/test_basis_set.py +++ b/tests/test_basis_set.py @@ -426,8 +426,22 @@ def test_quick_step() -> None: [ ('cc-pVTZ', 'GTO', 'orbital', 'cc-pVTZ', 'GTO', 'orbital'), ('def2-TZVP', 'GTO', 'auxiliary_scf', 'def2-TZVP', 'GTO', 'auxiliary_scf'), - ('aug-cc-pVDZ', 'STO', 'auxiliary_post_hf', 'aug-cc-pVDZ', 'STO', 'auxiliary_post_hf'), - ('custom_basis', None, None, 'custom_basis', None, None), # Undefined type and role + ( + 'aug-cc-pVDZ', + 'STO', + 'auxiliary_post_hf', + 'aug-cc-pVDZ', + 'STO', + 'auxiliary_post_hf', + ), + ( + 'custom_basis', + None, + None, + 'custom_basis', + None, + None, + ), # Undefined type and role ], ) def test_atom_centered_basis_set_init( @@ -536,4 +550,3 @@ def test_atom_centered_basis_set_invalid_data(): # Call normalize to trigger validation with pytest.raises(ValueError, match='Mismatch in number of exponents'): invalid_function.normalize(None, None) - From 5c15e97c84ed5f16c99c20fc999df0652a805258 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 4 Dec 2024 13:36:25 +0100 Subject: [PATCH 27/30] add method to LocalCorrelation --- .../schema_packages/model_method.py | 55 +++++++++++++++++++ .../schema_packages/numerical_settings.py | 24 +++----- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index c7b143ff..f66955dc 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1221,3 +1221,58 @@ class DMFT(ModelMethodElectronic): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) + + +class PerturbationMethod(ModelMethodElectronic): + type = Quantity( + type=MEnum('MP', 'RS', 'BW'), + description=""" + Perturbation approach. The abbreviations stand for: + | Abbreviation | Description | + | ------------ | ----------- | + | `'MP'` | Moller-Plesset | + | `'RS'` | Rayleigh-Schrödigner | + | `'BW'` | Brillouin-Wigner | + """, + a_eln=ELNAnnotation(component='EnumEditQuantity'), + ) # TODO: check if the special symbols are supported + + order = Quantity( + type=np.int32, + description=""" + Order up to which the perturbation is expanded. + """, + a_eln=ELNAnnotation(component='NumberEditQuantity'), + ) + + density = Quantity( + type=MEnum('relaxed', 'unrelaxed'), + description=""" + unrelaxed density: MP2 expectation value density + relaxed density : incorporates orbital relaxation + """, + ) + + +class LocalCorrelation(ArchiveSection): + """ + A base section used to define the parameters of a local correlation for the post-HF methods. + + It has a corresponding localization method. + """ + + method = Quantity( + type=MEnum('LMP2', 'LCCD', 'LCCSD', 'LCCSD(T)', 'DLPNO-CCSD(T)', 'LocalDFT'), + description=""" + The local correlation method applied. For example: + - LMP2: Local Møller-Plesset perturbation theory + - LCCD: Local Coupled-Cluster with Doubles + - LCCSD: Local Coupled-Cluster with Singles and Doubles + - LCCSD(T): Local Coupled-Cluster with Singles, Doubles, and Perturbative Triples + - DLPNO-CCSD(T): Domain-Based Local Pair Natural Orbital CCSD(T) + - LocalDFT: Local Density Functional Theory. + + # TODO: improve list! + """, + a_eln=ELNAnnotation(component='EnumEditQuantity'), + ) diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 4abe1471..34d59402 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -957,31 +957,25 @@ class OrbitalLocalization(SelfConsistency): Numerical settings that control orbital localization. """ - localization_method = ( - Quantity( - type=MEnum('FB', 'PM', 'IBO', 'IAOIBO', 'IAOBOYS' 'NEWBOYS' 'AHFB'), - description=""" + localization_method = Quantity( + type=MEnum('FB', 'PM', 'IBO', 'IAOIBO', 'IAOBOYS' 'NEWBOYS' 'AHFB'), + description=""" Name of the localization method. """, - ), ) - orbital_window = ( - Quantity( - shape=['*'], - description=""" + orbital_window = Quantity( + shape=['*'], + description=""" the Molecular orbital range to be localized. """, - ), ) - core_threshold = ( - Quantity( - type=np.float64, - description=""" + core_threshold = Quantity( + type=np.float64, + description=""" the energy window for the first occupied MO to be localized (in a.u.). """, - ), ) def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: From 2fd6398a939aa1ce9ae03a1f0fa33f4206b0e2b6 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Thu, 5 Dec 2024 15:58:58 +0100 Subject: [PATCH 28/30] add total_charge and total_spin to ModelSystem --- .../schema_packages/model_system.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/nomad_simulations/schema_packages/model_system.py b/src/nomad_simulations/schema_packages/model_system.py index 66364d5e..33ee56e7 100644 --- a/src/nomad_simulations/schema_packages/model_system.py +++ b/src/nomad_simulations/schema_packages/model_system.py @@ -1113,6 +1113,21 @@ class ModelSystem(System): """, ) + total_charge = Quantity( + type=np.int32, + description=""" + Total charge of the system. + """, + ) + + total_spin = Quantity( + type=np.int32, + description=""" + Total spin state of the system. + Not to be confused with the spin multiplicity 2S + 1. + """, + ) + model_system = SubSection(sub_section=SectionProxy('ModelSystem'), repeats=True) def resolve_system_type_and_dimensionality( From c9d6118fca0cbab4e325293542a009d4bee706f2 Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Tue, 10 Dec 2024 14:42:49 +0100 Subject: [PATCH 29/30] add a simple HF class --- .../schema_packages/model_method.py | 30 +++++++++++++++++++ .../schema_packages/numerical_settings.py | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index f66955dc..1a2a29c5 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1223,6 +1223,36 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) +class GTOIntegralDecomposition(BaseModelMethod): + """ + A general class for integral decomposition techniques for Coulomb and exchange integrals. + Examples: + Resolution of identity (RI-approximation): + RI + RIJK + .... + Chain-of-spheres (COSX) algorithm for exchange: doi:10.1016/j.chemphys.2008.10.036 + """ + + approximation_type = Quantity( + type=str, + description=""" + RIJ, RIK, RIJK, + """, + ) + + approximated_term = Quantity( + type=str, + description=""" + such as coulomb, exchange, explicit-correlation + to be converted to an MEnum later. + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + class PerturbationMethod(ModelMethodElectronic): type = Quantity( type=MEnum('MP', 'RS', 'BW'), diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 34d59402..477eadd3 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -958,7 +958,7 @@ class OrbitalLocalization(SelfConsistency): """ localization_method = Quantity( - type=MEnum('FB', 'PM', 'IBO', 'IAOIBO', 'IAOBOYS' 'NEWBOYS' 'AHFB'), + type=MEnum('FB', 'PM', 'IBO', 'IAOIBO', 'IAOBOYS', 'NEWBOYS', 'AHFB'), description=""" Name of the localization method. """, From 816c7baf1b8512d473a033d5a6e22545fa8d12da Mon Sep 17 00:00:00 2001 From: EBB2675 Date: Wed, 11 Dec 2024 15:20:13 +0100 Subject: [PATCH 30/30] a placeholder for MO and LCAO --- .../schema_packages/model_method.py | 171 ++++++++++++++++++ .../schema_packages/numerical_settings.py | 22 --- 2 files changed, 171 insertions(+), 22 deletions(-) diff --git a/src/nomad_simulations/schema_packages/model_method.py b/src/nomad_simulations/schema_packages/model_method.py index 1a2a29c5..c6700e1f 100644 --- a/src/nomad_simulations/schema_packages/model_method.py +++ b/src/nomad_simulations/schema_packages/model_method.py @@ -1223,6 +1223,164 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) +class MolecularOrbital(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.' + ) + + reference_orbital = SubSection( + sub_section=OrbitalsState.m_def, + description='Reference to the underlying atomic orbital state.', + ) + + +class LCAO(ArchiveSection): + """ + A base class for molecular orbital schemes used in quantum chemistry calculations. + Supports unrestricted (UHF, UKS), restricted (RHF, RKS), and restricted open-shell (ROHF, ROKS) schemes. + """ + + reference_type = Quantity( + type=MEnum('RHF', 'ROHF', 'UHF', 'RKS', 'ROKS', 'UKS'), + description=""" + Specifies the type of reference wavefunction: + - RHF: Restricted Hartree-Fock + - ROHF: Restricted Open-Shell Hartree-Fock + - UHF: Unrestricted Hartree-Fock + - RKS: Restricted Kohn-Sham + - ROKS: Restricted Open-Shell Kohn-Sham + - UKS: Unrestricted Kohn-Sham + """, + 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 many other things. + """, + a_eln=ELNAnnotation(component='EnumEditQuantity'), + ) + + n_alpha_electrons = Quantity( + type=np.int32, + description=""" + Number of alpha electrons (spin up) in the molecular system. + """, + ) + + n_beta_electrons = Quantity( + type=np.int32, + description=""" + Number of beta electrons (spin down) in the molecular system. + """, + ) + + n_molecular_orbitals = Quantity( + type=np.int32, + description=""" + Total number of molecular orbitals in the system. + """, + ) + + molecular_orbitals = SubSection( + sub_section=MolecularOrbital.m_def, + repeats=True, + description=""" + Detailed information about each molecular orbital, + including energy, occupation, and symmetry label. + """, + ) + + total_spin = Quantity( + type=np.float64, + description=""" + Total spin of the system defined as S = (n_alpha_electrons - n_beta_electrons) / 2. + Connect to the model system. + """, + ) + + spin_multiplicity = Quantity( + type=np.int32, + description=""" + Spin multiplicity of the system defined as 2S + 1. + """, + ) + + def resolve_spin_properties(self, logger: 'BoundLogger') -> None: + """ + Resolves the total spin and spin multiplicity of the system based on alpha and beta electrons. + """ + if self.n_alpha_electrons is not None and self.n_beta_electrons is not None: + self.total_spin = (self.n_alpha_electrons - self.n_beta_electrons) / 2 + self.spin_multiplicity = int(2 * self.total_spin + 1) + + def validate_scheme(self, logger: 'BoundLogger') -> bool: + """ + Validates the consistency of the molecular orbital scheme. + + Returns: + (bool): True if the scheme is consistent, False otherwise. + """ + if self.reference_type in ['RHF', 'RKS']: + if self.n_alpha_electrons != self.n_beta_electrons: + logger.error( + f'For {self.reference_type}, the number of alpha and beta electrons must be equal.' + ) + return False + if self.reference_type in ['ROHF', 'ROKS']: + if abs(self.n_alpha_electrons - self.n_beta_electrons) != 1: + logger.error( + f'For {self.reference_type}, there must be exactly one unpaired electron.' + ) + return False + return True + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + # Resolve spin properties + self.resolve_spin_properties(logger) + + # Validate the molecular orbital scheme + 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): """ A general class for integral decomposition techniques for Coulomb and exchange integrals. @@ -1253,6 +1411,19 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) +class HartreeFock(ModelMethodElectronic): + """ + A base section for Hartree Fock ansatz. + """ + + reference_determinant = Quantity( + type=MEnum('UHF', 'RHF', 'ROHF', 'UKS', 'RKS', 'ROKS'), + description=""" + the type of reference determinant. + """, + ) + + class PerturbationMethod(ModelMethodElectronic): type = Quantity( type=MEnum('MP', 'RS', 'BW'), diff --git a/src/nomad_simulations/schema_packages/numerical_settings.py b/src/nomad_simulations/schema_packages/numerical_settings.py index 477eadd3..48ec6dde 100644 --- a/src/nomad_simulations/schema_packages/numerical_settings.py +++ b/src/nomad_simulations/schema_packages/numerical_settings.py @@ -980,25 +980,3 @@ class OrbitalLocalization(SelfConsistency): def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) - - -class GTOIntegralDecomposition(NumericalSettings): - """ - A general class for integral decomposition techniques for Coulomb and exchange integrals. - Examples: - Resolution of identity (RI-approximation): - RI - RIJK - .... - Chain-of-spheres (COSX) algorithm for exchange: doi:10.1016/j.chemphys.2008.10.036 - """ - - approximation_type = Quantity( - type=str, - description=""" - RIJ, RIK, RIJK, - """, - ) - - def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: - super().normalize(archive, logger)