Skip to content

Commit

Permalink
- Change _determine_apw logic
Browse files Browse the repository at this point in the history
- Remove type assignment from `APWLocalOrbital`
  • Loading branch information
ndaelman committed Aug 22, 2024
1 parent 14edd64 commit 5d7775d
Showing 1 changed file with 81 additions and 80 deletions.
161 changes: 81 additions & 80 deletions src/nomad_simulations/schema_packages/basis_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ def _check_non_negative(self, quantity_names: set[str]) -> bool:
return False
return True

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

Expand All @@ -287,12 +288,20 @@ def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:
self.n_terms = None

# enforce differential order constraints
if self._check_non_negative({'differential_order'}):
self.differential_order = None # ? appropriate
if logger is not None:
logger.error(
'`APWBaseOrbital.differential_order` must be completely non-negative. Resetting to `None`.'
)
for quantity in ('differential_order', 'energy_parameter_n'):
if self._check_non_negative({quantity}):
quantity = None # ? appropriate
if logger is not None:
logger.error(
f'`{self.m_def}.{quantity}` must be completely non-negative. Resetting to `None`.'
)

# use the differential order as naming convention
self.name = (
f'{sorted(self.differential_order)}'
if len(self.differential_order) > 0
else 'APW-like'
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -308,6 +317,8 @@ class APWOrbital(APWBaseOrbital):
The energy parameter $\\epsilon_l$ is always considered fixed during diagonalization, opposed to the original APW formulation.
This representation then has to match the plane-wave $k_n$ points within the muffin-tin sphere.
Its `name` is showcased as `(s)(l)apw: <differential order>`.
* D. J. Singh and L. Nordström, \"INTRODUCTION TO THE LAPW METHOD,\" in Planewaves, pseudopotentials, and the LAPW method, 2nd ed. New York, NY: Springer, 2006, pp. 43-52.
"""

Expand All @@ -326,25 +337,29 @@ class APWOrbital(APWBaseOrbital):
""",
)

def n_terms_to_type(self, n_terms: Optional[int]) -> Optional[str]:
def do_to_type(self, do: Optional[list[int]]) -> Optional[str]:
"""
Set the type of the APW orbital based on the differential order.
"""
if n_terms is None or n_terms == 0:
if do is None or len(do) == 0:
return None
if n_terms == 1:

do = sorted(do)
if do == [0]:
return 'apw'
elif n_terms == 2:
elif do == [0, 1]:
return 'lapw'
else:
elif max(do) > 1: # exciting definition
return 'slapw'
else:
return None

@check_normalized
def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:
super().normalize(archive, logger)
# assign a APW orbital type
# this snippet works of the previous normalization
new_type = self.n_terms_to_type(self.n_terms)
new_type = self.do_to_type(self.differential_order)
if self.type is None:
self.type = new_type
elif self.type != new_type:
Expand All @@ -353,6 +368,12 @@ def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:
)
self.type = None

self.name = (
f'{self.type.upper()}: {self.name}'
if self.type and self.differential_order
else self.name
)


class APWLocalOrbital(APWBaseOrbital):
"""
Expand All @@ -363,62 +384,16 @@ class APWLocalOrbital(APWBaseOrbital):
* D. J. Singh and L. Nordström, \"Role of the Linearization Energies,\" in Planewaves, pseudopotentials, and the LAPW method, 2nd ed. New York, NY: Springer, 2006, pp. 49-52.
"""

type = Quantity(
type=MEnum('lo', 'LO', 'custom'),
description=r"""
Type of augmentation contribution. Abbreviations stand for:
| name | description | radial product |
|------|-------------|----------------|
| lo | 2-parameter local orbital | $A_l u_l (r, E_l) + B_l \dot{u}_l (r, E_l^')$ |
| LO | 3-parameter local orbital | $A_l u_l (r, E_l) + B_l \dot{u}_l (r, E_l^') + C_l \dot{u}_l (r, E_l^{''})$ |
| custom | local orbital of a different formula |
* http://susi.theochem.tuwien.ac.at/lapw/
""",
)

boundary_order = Quantity(
type=np.int32,
shape=['n_terms'],
description="""
Differential order to which the radial wavefunction is matched at the boundary.
""",
)

def get_n_terms(
self,
representative_quantities={
'energy_parameter',
'energy_parameter_n',
'differential_order',
'boundary_order',
},
) -> Optional[int]:
"""Determine the value of `n_terms` based on the lengths of the representative quantities."""
return super().get_n_terms(representative_quantities=representative_quantities)

def bo_terms_to_type(self, bo_terms: Optional[list[int]]) -> Optional[str]:
"""
Set the type of the local orbital based on the boundary order.
""" # ? include differential_order
if bo_terms is None or len(bo_terms) == 0:
return None
if sorted(bo_terms) == [0, 1]:
return 'lo'
elif sorted(bo_terms) == [0, 0, 1]: # ! double-check
return 'LO'
else:
return 'custom'
# there's no community consensus on `type`

@check_normalized
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:
super().normalize(archive, logger)
if self._check_non_negative({'boundary_order'}):
self.boundary_order = None # ? appropriate
if logger is not None:
logger.error(
'`APWLOrbital.boundary_order` must be completely non-negative. Resetting to `None`.'
)
self.name = (
f'LO: {sorted(self.differential_order)}'
if self.differential_order
else 'LO'
)


class APWLChannel(BasisSet):
Expand Down Expand Up @@ -470,7 +445,7 @@ def __init__(self, *args, **kwargs):

@check_normalized
def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:
BasisSet.normalize(self, archive, logger)
ArchiveSection.normalize(self, archive, logger)
self.n_orbitals = len(self.orbitals)


Expand Down Expand Up @@ -512,15 +487,17 @@ def _determine_apw(self) -> dict[str, int]:
for l_channel in self.l_channels:
l_channel.normalize(None, logger)

type_count: dict[str, int]
type_count: dict[str, int] = {}
if len(self.l_channels) > 0:
l_channel = self.l_channels[0]
# dynamically determine `type_count` structure
type_count = l_channel._determine_apw(l_channel.orbitals)
for channel in self.l_channels[1:]:
type_count.update(channel._determine_apw())
for l_channel in self.l_channels:
type_count.update(l_channel._determine_apw())
return type_count

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._is_normalized = False

@check_normalized
def normalize(self, archive, logger):
super().normalize(archive, logger)
Expand Down Expand Up @@ -566,8 +543,11 @@ def _determine_apw(self) -> Optional[str]:
answer += 'lapw'.upper()
elif type_count['apw'] > 0:
answer += 'apw'.upper()
if type_count['lo'] > 0:
answer += '+lo'
elif type_count['lo'] > 0:
answer += '+lo'
else:
answer = 'APW-like'
break
elif isinstance(comp, PlaneWaveBasisSet):
has_plane_wave = True
return answer if has_plane_wave else None
Expand Down Expand Up @@ -600,7 +580,8 @@ def normalize(self, archive: EntryArchive, logger: BoundLogger) -> None:


def generate_apw(
species: dict[str, dict[str, Any]], cutoff: Optional[float] = None
species: dict[str, dict[str, Any]],
cutoff: Optional[float] = None,
) -> BasisSetContainer: # TODO: extend to cover all parsing use cases (maybe split up?)
"""
Generate a mock APW basis set with the following structure:
Expand All @@ -616,8 +597,10 @@ def generate_apw(
<species_name>: {
'r': <muffin-tin radius>,
'l_max': <maximum angular momentum>,
'orb_type': [<APWOrbital.type>],
'lo_type': [<APWLocalOrbital.type>],
'orb_do': [[int]],
'orb_param': [<APWOrbital.energy_parameter>|<APWOrbital.energy_parameter_n>],
'lo_do': [[int]],
'lo_param': [<APWOrbital.energy_parameter>|<APWOrbital.energy_parameter_n>],
}
}
"""
Expand All @@ -630,8 +613,10 @@ def generate_apw(
for sp_ref, sp in species.items():
sp['r'] = sp.get('r', None)
sp['l_max'] = sp.get('l_max', 0)
sp['orb_type'] = sp.get('orb_type', [])
sp['lo_type'] = sp.get('lo_type', [])
sp['orb_d_o'] = sp.get('orb_d_o', [])
sp['orb_param'] = sp.get('orb_param', [])
sp['lo_d_o'] = sp.get('lo_d_o', [])
sp['lo_param'] = sp.get('lo_param', [])

basis_set_components.extend(
[
Expand All @@ -644,8 +629,24 @@ def generate_apw(
name=l_channel,
orbitals=list(
itertools.chain(
(APWOrbital(type=orb) for orb in sp['orb_type']),
(APWLocalOrbital(type=lo) for lo in sp['lo_type']),
(
APWOrbital(
energy_parameter=param, # TODO: add energy_parameter_n
differential_order=d_o,
)
for param, d_o in zip(
sp['orb_param'], sp['orb_d_o']
)
),
(
APWLocalOrbital(
energy_parameter=param, # TODO: add energy_parameter_n
differential_order=d_o,
)
for param, d_o in zip(
sp['lo_param'], sp['lo_d_o']
)
),
)
),
)
Expand Down

1 comment on commit 5d7775d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/nomad_simulations
   __init__.py4250%3–4
   _version.py11282%5–6
src/nomad_simulations/schema_packages
   __init__.py14286%53–55
   atoms_state.py1902189%31–33, 219–222, 246, 301–302, 370–371, 373, 555, 567–568, 629–633, 648–652, 659
   basis_set.py2307767%33, 82–83, 110, 116–117, 179, 344–355, 362–371, 391–392, 432–439, 448–449, 487–495, 503, 534–553, 559–564, 567–579
   general.py75791%29–30, 98, 162, 272–273, 283
   model_method.py2657771%28–30, 189–192, 195–202, 294–295, 315, 336–355, 371–397, 400–417, 771, 782, 824–831, 869, 888, 968, 1025, 1100, 1214
   model_system.py2612292%43–45, 520–523, 570–577, 751–752, 973–977, 983–984, 992–993, 998, 1021
   numerical_settings.py2596276%30–32, 165, 235, 237–238, 241–244, 248–249, 256–259, 268–271, 275–278, 280–283, 288–291, 297–300, 484–511, 586, 621–624, 648, 651, 696, 698–701, 705, 709, 756, 760–781, 836–837, 904
   outputs.py1151091%27–28, 247–250, 290–293, 318, 320, 357, 376
   physical_property.py102793%38–40, 220, 349–351
   variables.py651183%26–28, 116, 139, 159–160, 163, 185, 208, 228
src/nomad_simulations/schema_packages/properties
   band_gap.py51590%26–28, 153–154
   band_structure.py1112280%27–29, 249–282, 295, 302, 338–339, 342
   energies.py36975%25–27, 54, 75, 100, 121, 131, 140
   fermi_surface.py17476%25–27, 58
   forces.py22673%26–28, 55, 75, 98
   hopping_matrix.py29583%25–27, 75, 110
   permittivity.py48883%25–27, 115–123
   spectral_profile.py25612452%27–29, 75–78, 113–116, 217–318, 374–386, 411–414, 434, 439–442, 484–520, 544, 591–594, 610–611, 616–622
   thermodynamics.py751876%25–27, 53, 74, 90, 99, 108, 119, 128, 155, 165, 175, 195, 211, 236, 252, 277
src/nomad_simulations/schema_packages/utils
   utils.py681479%26–29, 79–88, 97–98, 103, 106
TOTAL231451578% 

Tests Skipped Failures Errors Time
354 0 💤 48 ❌ 0 🔥 8.360s ⏱️

Please sign in to comment.