Skip to content

Commit

Permalink
Update docstrings to note degeneracy handling behaviour, and refactor…
Browse files Browse the repository at this point in the history
… 'unrelaxed symmetry' to 'bulk site symmetry' to be clearer
  • Loading branch information
kavanase committed Feb 8, 2024
1 parent c4a4bfc commit 009631f
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 74 deletions.
9 changes: 5 additions & 4 deletions doped/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ def check_and_set_defect_entry_name(
If the DefectEntry.name attribute is not defined or does not end with the
charge state, then the entry will be renamed with the doped default name
for the `unrelaxed` defect.
for the `unrelaxed` defect (i.e. using the point symmetry of the defect
site in the bulk cell).
Args:
defect_entry (DefectEntry): DefectEntry object.
Expand Down Expand Up @@ -1642,19 +1643,19 @@ def _read_bulk_voronoi_node_dict(bulk_path):
relaxed_point_group, periodicity_breaking = point_symmetry_from_defect_entry(
defect_entry, relaxed=True, verbose=False, return_periodicity_breaking=True
) # relaxed so defect symm_ops
unrelaxed_point_group = point_symmetry_from_defect_entry(
bulk_site_point_group = point_symmetry_from_defect_entry(
defect_entry,
symm_ops=bulk_supercell_symm_ops, # unrelaxed so bulk symm_ops
relaxed=False,
symprec=0.01, # same symprec used w/interstitial multiplicity for consistency
)
orientational_degeneracy = get_orientational_degeneracy(
relaxed_point_group=relaxed_point_group, unrelaxed_point_group=unrelaxed_point_group
relaxed_point_group=relaxed_point_group, bulk_site_point_group=bulk_site_point_group
)
# TODO: Show these properties in tutorials:
defect_entry.degeneracy_factors["orientational degeneracy"] = orientational_degeneracy
defect_entry.calculation_metadata["relaxed point symmetry"] = relaxed_point_group
defect_entry.calculation_metadata["unrelaxed point symmetry"] = unrelaxed_point_group
defect_entry.calculation_metadata["bulk site symmetry"] = bulk_site_point_group
defect_entry.calculation_metadata["periodicity_breaking_supercell"] = periodicity_breaking

if bulk_voronoi_node_dict: # save to bulk folder for future expedited parsing:
Expand Down
4 changes: 2 additions & 2 deletions doped/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"This will not affect defect formation energies / transition levels, but is important for "
"concentrations/doping/Fermi level behaviour (see e.g. doi.org/10.1039/D2FD00043A & "
"doi.org/10.1039/D3CS00432E).\n"
"You can manually check (and edit) the computed relaxed/unrelaxed point symmetries and "
"You can manually check (and edit) the computed defect/bulk point symmetries and "
"corresponding orientational degeneracy factors by inspecting/editing the "
"calculation_metadata['relaxed point symmetry']/['unrelaxed point symmetry'] and "
"calculation_metadata['relaxed point symmetry']/['bulk site symmetry'] and "
"degeneracy_factors['orientational degeneracy'] attributes."
)

Expand Down
6 changes: 3 additions & 3 deletions doped/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,9 @@ def get_defect_name_from_entry(
octahedral distortions etc).
relaxed (bool):
If False, determines the site symmetry using the defect site `in the
unrelaxed bulk supercell`, otherwise uses the defect supercell to
determine the site symmetry (i.e. try determine the point symmetry
of a relaxed defect in the defect supercell). Default is True.
unrelaxed bulk supercell`, otherwise tries to determine the point
symmetry of the relaxed defect in the defect supercell).
Default is True.
Returns:
str: Defect name.
Expand Down
64 changes: 34 additions & 30 deletions doped/thermodynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ def group_defects_by_name(entry_list: List[DefectEntry]) -> Dict[str, List[Defec
"{defect_name}_{optional_site_info}_{charge_state}".
If the DefectEntry.name attribute is not defined or does not end with the
charge state, then the entry will be renamed with the doped default name
for the `unrelaxed` defect.
for the `unrelaxed` defect (i.e. using the point symmetry of the defect
site in the bulk cell).
For example, ``v_Cd_C3v_+1``, ``v_Cd_Td_+1`` and ``v_Cd_C3v_+2`` will be grouped
as {``v_Cd_C3v``: [``v_Cd_C3v_+1``, ``v_Cd_C3v_+2``], ``v_Cd_Td``: [``v_Cd_Td_+1``]}.
Expand Down Expand Up @@ -2285,28 +2286,29 @@ def _single_formation_energy_table(

def get_symmetries_and_degeneracies(self, skip_formatting: bool = False) -> pd.DataFrame:
r"""
Generates a table of the unrelaxed & relaxed point group symmetries,
and spin/orientational/total degeneracies for each defect in the
DefectThermodynamics object.
Generates a table of the bulk-site & relaxed defect point group
symmetries, and spin/orientational/total degeneracies for each
defect in the ``DefectThermodynamics`` object.
Table Key:
- 'Defect': Defect name (without charge)
- 'q': Defect charge state.
- 'Symm_Unrelax': Point group symmetry of the relaxed defect.
- 'Symm_Relax': Point group symmetry of the relaxed defect.
- 'Site_Symm': Point group symmetry of the defect site in the bulk cell.
- 'Defect_Symm': Point group symmetry of the relaxed defect.
- 'g_Orient': Orientational degeneracy of the defect.
- 'g_Spin': Spin degeneracy of the defect.
- 'g_Total': Total degeneracy of the defect.
For interstitials, the 'unrelaxed' point group symmetry
correspond to the point symmetry of the interstitial site
with `no relaxation of the host structure`. For vacancies
and substitutions, this is equivalent to the initial point
symmetry.
For interstitials, the bulk site symmetry corresponds to the
point symmetry of the interstitial site with `no relaxation
of the host structure`, while for vacancies/substitutions it is
simply the symmetry of their corresponding bulk site.
This corresponds to the point symmetry of ``DefectEntry.defect``,
or ``calculation_metadata["bulk_site"]/["unrelaxed_defect_structure"]``.
Point group symmetries are taken from the calculation_metadata
("relaxed point symmetry" and "unrelaxed point symmetry") if
("relaxed point symmetry" and "bulk site symmetry") if
present (should be, if parsed with doped and defect supercell
doesn't break host periodicity), otherwise are attempted to be
recalculated.
Expand All @@ -2333,12 +2335,18 @@ def get_symmetries_and_degeneracies(self, skip_formatting: bool = False) -> pd.D
- otherwise periodicity-breaking prevents this.
If periodicity-breaking prevents auto-symmetry determination, you can manually
determine the relaxed and unrelaxed point symmetries and/or orientational degeneracy
from visualising the structures (e.g. using VESTA)(can use
``get_orientational_degeneracy`` to obtain the corresponding orientational degeneracy
factor for given initial/relaxed point symmetries) and setting the corresponding
values in the calculation_metadata['relaxed point symmetry']/['unrelaxed point
symmetry'] and/or degeneracy_factors['orientational degeneracy'] attributes.
determine the relaxed defect and bulk-site point symmetries, and/or orientational
degeneracy, from visualising the structures (e.g. using VESTA)(can use
``get_orientational_degeneracy`` to obtain the corresponding orientational
degeneracy factor for given defect/bulk-site point symmetries) and setting the
corresponding values in the
``calculation_metadata['relaxed point symmetry']/['bulk site symmetry']`` and/or
``degeneracy_factors['orientational degeneracy']`` attributes.
Note that the bulk-site point symmetry corresponds to that of ``DefectEntry.defect``,
or equivalently ``calculation_metadata["bulk_site"]/["unrelaxed_defect_structure"]``,
which for vacancies/substitutions is the symmetry of the corresponding bulk site,
while for interstitials it is the point symmetry of the `final relaxed` interstitial
site when placed in the (unrelaxed) bulk structure.
The degeneracy factor is used in the calculation of defect/carrier concentrations
and Fermi level behaviour (see e.g. doi.org/10.1039/D2FD00043A &
doi.org/10.1039/D3CS00432E).
Expand Down Expand Up @@ -2374,23 +2382,23 @@ def get_symmetries_and_degeneracies(self, skip_formatting: bool = False) -> pd.D
f"Unable to determine relaxed point group symmetry for {defect_entry.name}, got "
f"error:\n{e!r}"
)
if "unrelaxed point symmetry" not in defect_entry.calculation_metadata:
if "bulk site symmetry" not in defect_entry.calculation_metadata:
try:
defect_entry.calculation_metadata[
"unrelaxed point symmetry"
"bulk site symmetry"
] = point_symmetry_from_defect_entry(
defect_entry, relaxed=False, symprec=0.01
) # unrelaxed so bulk symm_ops
except Exception as e:
warnings.warn(
f"Unable to determine unrelaxed point group symmetry for {defect_entry.name}, got "
f"error:\n{e!r}"
f"Unable to determine bulk site symmetry for {defect_entry.name}, got error:"
f"\n{e!r}"
)

if (
all(
x in defect_entry.calculation_metadata
for x in ["relaxed point symmetry", "unrelaxed point symmetry"]
for x in ["relaxed point symmetry", "bulk site symmetry"]
)
and "orientational degeneracy" not in defect_entry.degeneracy_factors
):
Expand All @@ -2399,9 +2407,7 @@ def get_symmetries_and_degeneracies(self, skip_formatting: bool = False) -> pd.D
"orientational degeneracy"
] = get_orientational_degeneracy(
relaxed_point_group=defect_entry.calculation_metadata["relaxed point symmetry"],
unrelaxed_point_group=defect_entry.calculation_metadata[
"unrelaxed point symmetry"
],
bulk_site_point_group=defect_entry.calculation_metadata["bulk site symmetry"],
)
except Exception as e:
warnings.warn(
Expand All @@ -2413,10 +2419,8 @@ def get_symmetries_and_degeneracies(self, skip_formatting: bool = False) -> pd.D
{
"Defect": defect_entry.name.rsplit("_", 1)[0], # name without charge
"q": defect_entry.charge_state,
"Symm_Unrelax": defect_entry.calculation_metadata.get(
"unrelaxed point symmetry", "N/A"
),
"Symm_Relax": defect_entry.calculation_metadata.get("relaxed point symmetry", "N/A"),
"Site_Symm": defect_entry.calculation_metadata.get("bulk site symmetry", "N/A"),
"Defect_Symm": defect_entry.calculation_metadata.get("relaxed point symmetry", "N/A"),
"g_Orient": defect_entry.degeneracy_factors.get("orientational degeneracy", "N/A"),
"g_Spin": defect_entry.degeneracy_factors.get("spin degeneracy", "N/A"),
"g_Total": total_degeneracy,
Expand Down
59 changes: 37 additions & 22 deletions doped/utils/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,22 +800,29 @@ def get_interstitial_site_and_orientational_degeneracy(
def get_orientational_degeneracy(
defect_entry: Optional[DefectEntry] = None,
relaxed_point_group: Optional[str] = None,
unrelaxed_point_group: Optional[str] = None,
bulk_site_point_group: Optional[str] = None,
bulk_symm_ops: Optional[list] = None,
defect_symm_ops: Optional[list] = None,
symprec: float = 0.2,
) -> float:
r"""
Get the orientational degeneracy factor for a given `relaxed` DefectEntry,
by supplying either the DefectEntry object or the relaxed and unrelaxed
point group symbols (e.g. "Td", "C3v" etc).
by supplying either the DefectEntry object or the bulk-site & relaxed
defect point group symbols (e.g. "Td", "C3v" etc).
If a DefectEntry is supplied (and the point group symbols are not),
this is computed by determining the `relaxed` point symmetry and the
original/`unrelaxed` defect site symmetry, and then getting the ratio of
this is computed by determining the `relaxed` defect point symmetry and the
(unrelaxed) bulk site symmetry, and then getting the ratio of
their point group orders (equivalent to the ratio of partition
functions or number of symmetry operations (i.e. degeneracy)).
For interstitials, the bulk site symmetry corresponds to the
point symmetry of the interstitial site with `no relaxation
of the host structure`, while for vacancies/substitutions it is
simply the symmetry of their corresponding bulk site.
This corresponds to the point symmetry of ``DefectEntry.defect``,
or ``calculation_metadata["bulk_site"]/["unrelaxed_defect_structure"]``.
Note: This tries to use the defect_entry.defect_supercell to determine
the `relaxed` site symmetry. However, it should be noted that this is not
guaranteed to work in all cases; namely for non-diagonal supercell
Expand All @@ -839,12 +846,18 @@ def get_orientational_degeneracy(
prevents this.
If periodicity-breaking prevents auto-symmetry determination, you can manually
determine the relaxed and unrelaxed point symmetries and/or orientational degeneracy
from visualising the structures (e.g. using VESTA)(can use
``get_orientational_degeneracy`` to obtain the corresponding orientational degeneracy
factor for given initial/relaxed point symmetries) and setting the corresponding
values in the calculation_metadata['relaxed point symmetry']/['unrelaxed point
symmetry'] and/or degeneracy_factors['orientational degeneracy'] attributes.
determine the relaxed defect and bulk-site point symmetries, and/or orientational
degeneracy, from visualising the structures (e.g. using VESTA)(can use
``get_orientational_degeneracy`` to obtain the corresponding orientational
degeneracy factor for given defect/bulk-site point symmetries) and setting the
corresponding values in the
``calculation_metadata['relaxed point symmetry']/['bulk site symmetry']`` and/or
``degeneracy_factors['orientational degeneracy']`` attributes.
Note that the bulk-site point symmetry corresponds to that of ``DefectEntry.defect``,
or equivalently ``calculation_metadata["bulk_site"]/["unrelaxed_defect_structure"]``,
which for vacancies/substitutions is the symmetry of the corresponding bulk site,
while for interstitials it is the point symmetry of the `final relaxed` interstitial
site when placed in the (unrelaxed) bulk structure.
The degeneracy factor is used in the calculation of defect/carrier concentrations
and Fermi level behaviour (see e.g. doi.org/10.1039/D2FD00043A &
doi.org/10.1039/D3CS00432E).
Expand All @@ -854,14 +867,16 @@ def get_orientational_degeneracy(
relaxed_point_group (str): Point group symmetry (e.g. "Td", "C3v" etc)
of the `relaxed` defect structure, if already calculated / manually
determined. Default is None (automatically calculated by doped).
unrelaxed_point_group (str): Point group symmetry (e.g. "Td", "C3v" etc)
of the non-relaxed (initial) defect site, if already calculated /
manually determined. This should match the site symmetry label from
``doped`` when generating the defect. Default is None (automatically
calculated by doped).
bulk_site_point_group (str): Point group symmetry (e.g. "Td", "C3v" etc)
of the defect site in the bulk, if already calculated / manually
determined. For vacancies/substitutions, this should match the site
symmetry label from ``doped`` when generating the defect, while for
interstitials it should be the point symmetry of the `final relaxed`
interstitial site, when placed in the bulk structure.
Default is None (automatically calculated by doped).
bulk_symm_ops (list):
List of symmetry operations of the defect_entry.bulk_supercell
structure (used in determining the `unrelaxed` point symmetry), to
structure (used in determining the `unrelaxed` bulk site symmetry), to
avoid re-calculating. Default is None (recalculates).
defect_symm_ops (list):
List of symmetry operations of the defect_entry.defect_supercell
Expand All @@ -879,9 +894,9 @@ def get_orientational_degeneracy(
from doped.utils.symmetry import group_order_from_schoenflies, point_symmetry_from_defect_entry

if defect_entry is None:
if relaxed_point_group is None or unrelaxed_point_group is None:
if relaxed_point_group is None or bulk_site_point_group is None:
raise ValueError(
"Either the DefectEntry or both relaxed and unrelaxed point group symbols must be "
"Either the DefectEntry or both defect and bulk site point group symbols must be "
"provided for doped to determine the orientational degeneracy! "
)

Expand All @@ -900,15 +915,15 @@ def get_orientational_degeneracy(
relaxed=True, # relaxed
)

if unrelaxed_point_group is None:
unrelaxed_point_group = point_symmetry_from_defect_entry(
if bulk_site_point_group is None:
bulk_site_point_group = point_symmetry_from_defect_entry(
defect_entry, # type: ignore
symm_ops=bulk_symm_ops, # bulk not defect symm_ops
symprec=symprec, # same symprec as relaxed_point_group for consistency
relaxed=False, # unrelaxed
)

return group_order_from_schoenflies(unrelaxed_point_group) / group_order_from_schoenflies(
return group_order_from_schoenflies(bulk_site_point_group) / group_order_from_schoenflies(
relaxed_point_group
)

Expand Down
Loading

0 comments on commit 009631f

Please sign in to comment.