Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions docs/Adding_Calculators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,25 @@ reference backend):
the corresponding ``PLANAR_POTENTIALS_FILE`` / ``SITE_POTENTIALS_FILE``
file-name constants): lazy loading of charge-correction data, when not
parsed up-front into ``CalculationOutputs``.
- ``check_run_compatibility(defect_outputs, bulk_outputs, warn)`` (and
``MISMATCH_WARNING_SPECS``): defect/bulk calculation settings
compatibility checks, populating ``"run_metadata"`` (and any
``"mismatching_..."`` entries) in ``DefectEntry.calculation_metadata``.
- ``check_entry_compatibility(entries, template_candidates)``: the same
check for competing phase entries, comparing each against a reference
(template) entry and warning about mismatched calculation settings, for
:class:`~doped.chemical_potentials.CompetingPhasesAnalyzer` (with
``check_compatibility=True``, the default). Entries lacking the relevant
settings data are skipped.
- ``compare_calculation_settings(settings, reference_settings, competing_phases, warn)``
(and ``MISMATCH_WARNING_SPECS``): calculation settings compatibility checks,
comparing the energy-affecting settings of a calculation to a reference and returning
``{"mismatching_...": mismatches or False}``. ``settings`` are
``CalculationOutputs.run_metadata`` for defect/bulk supercells and ``entry.data``
for competing phases (with ``competing_phases=True``, for settings which can
legitimately differ between phases, such as k-point meshes).
``MISMATCH_WARNING_SPECS`` gives, for each ``"mismatching_..."`` key, the settings
name (``"object_name"``), the settings ``"data_key"`` (used to choose the reference
competing phase and skip entries lacking the settings data), the ``"format"`` and
``"closing"`` warning lines, the prefix of any per-defect warnings
(``"per_defect_warning_prefix"``) and optional ``"transform"``/``"body"``
functions, from which ``doped`` generates grouped mismatch warnings for
defects and competing phases (the latter with |CompetingPhasesAnalyzer| with
``check_compatibility=True``, the default).
- ``check_run_compatibility(defect_outputs, bulk_outputs, warn)``: defect/bulk
calculation settings compatibility checks (using ``compare_calculation_settings``),
populating ``"run_metadata"`` (and the ``"mismatching_..."`` entries) in
``DefectEntry.calculation_metadata``.
- ``load_eigenvalue_outputs(path, outputs, projections, label, run_metadata)``:
loading of outputs `with orbital projections` for eigenvalue / band-edge
analysis (``DefectEntry.get_eigenvalue_analysis()``), when not parsed
Expand All @@ -143,6 +152,11 @@ reference backend):
``"folder"`` (the parsed calculation folder). Without this,
``CompetingPhasesAnalyzer`` can still be initialised directly from a list
of ``ComputedEntry``/``ComputedStructureEntry`` objects.
- ``find_calc_output(directory) -> str``: selection of the calculation output
file to parse in a competing phase directory (e.g. with the filename
priorities used for defect parsing), for ``CompetingPhasesAnalyzer``. If not
implemented, or raising ``FileNotFoundError``/``ValueError`` (no or ambiguous
files), the generic ``CALC_OUTPUT_MASK[0]`` filename matching is used.

The generic calculation-file discovery helpers in ``doped.io.utils``
(e.g. ``_find_calc_outputs``, ``_determine_subfolder``) are parameterised by
Expand Down
35 changes: 18 additions & 17 deletions docs/GPAW_Support.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,32 +165,33 @@ been applied):
Chemical potentials and formation energies
------------------------------------------

Competing-phase calculations must use energies that are consistent with the
GPAW settings used for the bulk and defects. Individual outputs can be converted
to ``pymatgen`` computed entries and used to determine the chemical-potential
limits:
GPAW competing-phase outputs can be parsed directly with
``CompetingPhasesAnalyzer(..., calculator="gpaw")``:

.. code-block:: python

from doped.chemical_potentials import get_doped_chempots_from_entries
from doped.io import get_calculation_outputs
from doped.chemical_potentials import CompetingPhasesAnalyzer
from doped.thermodynamics import DefectThermodynamics

bulk_entry = get_calculation_outputs("MgO_bulk", calculator="gpaw").get_computed_entry()

# phase_entries should contain consistently calculated elemental and
# competing-phase entries.
chempots = get_doped_chempots_from_entries(
[bulk_entry, *phase_entries],
bulk_entry,
phase_analyzer = CompetingPhasesAnalyzer(
"MgO",
entries="CompetingPhases_GPAW",
calculator="gpaw",
)
defect_thermodynamics = DefectThermodynamics(defect_dict, chempots=chempots)
defect_thermodynamics = DefectThermodynamics(defect_dict, chempots=phase_analyzer.chempots)
defect_thermodynamics.plot(limit="Mg-rich")
defect_thermodynamics.plot(limit="O-rich")

The complete MgO example prepares Mg structures obtained from the
|CompetingPhases| workflow and an isolated triplet O2 reference, then generates
Mg-rich and O-rich formation-energy diagrams.
The parser reads the stored structures, total energies and GPAW parameters, and
warns if the functional, PAW datasets, basis set or mode (e.g. plane-wave
cutoff) differ between competing phases (while k-point and real-space meshes
can differ). Ensure energies use consistent functionals, PAW datasets, basis
sets and cutoffs with the bulk and defects. Competing-phase input generation
with GPAW is not yet implemented (TODO).

The complete MgO example prepares a primitive-cell MgO host calculation, Mg
structures obtained from the |CompetingPhases| workflow and an isolated triplet
O2 reference, then generates Mg-rich and O-rich formation-energy diagrams.

Anisotropic and two-dimensional systems
---------------------------------------
Expand Down
80 changes: 63 additions & 17 deletions doped/chemical_potentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -2883,8 +2883,9 @@ def __init__(
multiprocessing.
check_compatibility (bool):
Whether to check the compatibility of the parsed entries,
by comparing their ``INCAR`` and ``POTCAR`` settings (if
available). Default is ``True``.
by comparing their calculation settings (e.g. ``INCAR`` tags
and ``POTCAR`` symbols with VASP), if available. Default is
``True``.
calculator (str):
Name of the calculator used for the competing phase
calculations (matching a ``doped.io.<calculator>`` parsing
Expand Down Expand Up @@ -2995,8 +2996,8 @@ def _from_entries(
potential limits.
check_compatibility (bool):
Whether to check the compatibility of the parsed entries,
by comparing their ``INCAR`` and ``POTCAR`` settings.
Default is ``True``.
by comparing their calculation settings (e.g. ``INCAR`` tags
and ``POTCAR`` symbols with VASP). Default is ``True``.
"""
self.entries = entries
intrinsic_entries: list[ComputedEntry | ComputedStructureEntry] = []
Expand Down Expand Up @@ -3079,10 +3080,9 @@ def _entry_contains_missing_elemental_ref(
# check entry compatibilities (calculation settings), with the calculator backend (if it
# implements this check); taking the first of bulk entry, bulk comp entries, intrinsic, all
# entries `with` the relevant settings data as the reference (template) entry:
if check_compatibility and (
check_entry_compatibility := getattr(self._backend, "check_entry_compatibility", None)
):
check_entry_compatibility(
if check_compatibility:
_check_entry_compatibility(
self._backend,
entries,
template_candidates=[self.bulk_entry, *bulk_comp_entries, *intrinsic_entries, *entries],
)
Expand Down Expand Up @@ -3195,8 +3195,8 @@ def _from_calc_outputs(
discovered ``vasprun.xml(.gz)`` files are parsed.

If ``path`` is a list, each element may be a direct path to a
``vasprun.xml(.gz)`` file **or** to a directory containing one,
which will be located via a recursive search.
``vasprun.xml(.gz)`` file **or** to the directory directly containing
one (i.e. not searched recursively).

Args:
path (PathLike or list):
Expand All @@ -3221,8 +3221,8 @@ def _from_calc_outputs(
multiprocessing.
check_compatibility (bool):
Whether to check the compatibility of the parsed entries,
by comparing their ``INCAR`` and ``POTCAR`` settings.
Default is ``True``.
by comparing their calculation settings (e.g. ``INCAR`` tags
and ``POTCAR`` symbols with VASP). Default is ``True``.
"""
output_file = self._backend.CALC_OUTPUT_MASK[0] # e.g. "vasprun.xml" with VASP
if isinstance(path, list):
Expand Down Expand Up @@ -3405,6 +3405,10 @@ def _find_calc_output_in_directory(self, directory: PathLike) -> str | None:
calc_output_path, multiple = _io_utils._get_output_files_and_check_if_multiple(
output_file, str(directory)
)
# optional backend file selection (e.g. GPAW filename priority, matching defect parsing):
if (find_calc_output := getattr(self._backend, "find_calc_output", None)) is not None:
with contextlib.suppress(FileNotFoundError, ValueError): # none/ambiguous: keep generic pick
calc_output_path = find_calc_output(directory)
if calc_output_path and os.path.exists(calc_output_path):
if multiple:
self._multiple_files_warning(output_file, directory, calc_output_path)
Expand All @@ -3414,12 +3418,9 @@ def _find_calc_output_in_directory(self, directory: PathLike) -> str | None:
def _multiple_files_warning(self, output_file, directory, chosen_filepath) -> None:
"""
Warn that multiple calculation output files were found in
``directory``, using the backend's ``FILE_PARSING_ACTIONS`` for the
(informative) description of what the file is parsed for.
``directory``, describing the competing-phase parsing task.
"""
action = getattr(self._backend, "FILE_PARSING_ACTIONS", {}).get(
output_file, "parse the calculation energy and metadata."
)
action = "parse the competing-phase energy and metadata."
_io_utils._multiple_files_warning(
output_file, directory, chosen_filepath, action, dir_type="competing phase"
)
Expand Down Expand Up @@ -4603,6 +4604,51 @@ def _nudge_labels_inside_axes(ax: plt.Axes, padding: float | None, exclude: set
text.set_position(new_position)


def _check_entry_compatibility(
backend: ModuleType, entries: list[ComputedEntry], template_candidates: list[ComputedEntry]
) -> None:
r"""
Check the calculation settings of the competing phase ``entries`` against
those of a reference (template) entry, using the
``compare_calculation_settings()`` function and ``MISMATCH_WARNING_SPECS``
of the ``doped.io`` calculator ``backend`` (if implemented); recording any
mismatches in ``entry.data`` (under the ``MISMATCH_WARNING_SPECS`` keys)
and warning about these collectively.

For each type of setting, the first entry in ``template_candidates`` with
the relevant settings data (``"data_key"`` in ``MISMATCH_WARNING_SPECS``)
is used as the reference, and entries without this data (e.g.
``ComputedEntry``\s supplied directly by the user, rather than parsed
from calculation outputs) are skipped.

Args:
backend (ModuleType):
The ``doped.io`` calculator parsing backend module.
entries (list[|ComputedEntry|]):
The competing phase entries to check.
template_candidates (list[|ComputedEntry|]):
Priority-ordered candidate entries from which to take the reference
(template) calculation settings.
"""
if (compare := getattr(backend, "compare_calculation_settings", None)) is None:
return
for mismatch_key, spec in getattr(backend, "MISMATCH_WARNING_SPECS", {}).items():
template = next((e for e in template_candidates if e.data.get(spec["data_key"])), None)
if template is None:
continue
for entry in entries:
if entry.data.get(spec["data_key"]) and mismatch_key in (
mismatches := compare(entry.data, template.data, competing_phases=True)
):
entry.data[mismatch_key] = mismatches[mismatch_key]
_io_utils._warn_calculation_mismatches(
[(entry.name, entry.data) for entry in entries],
{mismatch_key: spec},
competing_phases=True,
reference_name=template.name,
)


def _parse_entry_and_catch_exception(
calc_output_path: PathLike, calculator: str = "vasp"
) -> tuple[str | ComputedStructureEntry, PathLike, bool, bool]:
Expand Down
Loading
Loading