|
| 1 | +# Copyright © VASP Software GmbH, |
| 2 | +# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) |
| 3 | + |
| 4 | +import numpy as np |
| 5 | + |
| 6 | +from py4vasp import exception, raw |
| 7 | +from py4vasp._third_party import graph |
| 8 | +from py4vasp._util import convert |
| 9 | +from py4vasp.calculation import _base, _slice, _structure |
| 10 | + |
| 11 | +INDEXING_OSZICAR = { |
| 12 | + "iteration_number": 0, |
| 13 | + "free_energy": 1, |
| 14 | + "free_energy_change": 2, |
| 15 | + "bandstructure_energy_change": 3, |
| 16 | + "number_hamiltonian_evaluations": 4, |
| 17 | + "norm_residual": 5, |
| 18 | + "difference_charge_density": 6, |
| 19 | +} |
| 20 | + |
| 21 | + |
| 22 | +class OSZICAR(_slice.Mixin, _base.Refinery, graph.Mixin): |
| 23 | + """Access the convergence data for each electronic step. |
| 24 | +
|
| 25 | + The OSZICAR file written out by VASP stores information related to convergence. |
| 26 | + Please check the vasp-wiki (https://www.vasp.at/wiki/index.php/OSZICAR) for more |
| 27 | + details about the exact outputs generated for each combination of INCAR tags.""" |
| 28 | + |
| 29 | + @_base.data_access |
| 30 | + def to_dict(self, selection=None): |
| 31 | + """Extract convergence data from the HDF5 file and make it available in a dict |
| 32 | +
|
| 33 | + Parameters |
| 34 | + ---------- |
| 35 | + selection: str |
| 36 | + Choose from either iteration_number, free_energy, free_energy_change, |
| 37 | + bandstructure_energy_change, number_hamiltonian_evaluations, norm_residual, |
| 38 | + difference_charge_density to get specific columns of the OSZICAR file. In |
| 39 | + case no selection is provided, supply all columns. |
| 40 | +
|
| 41 | + Returns |
| 42 | + ------- |
| 43 | + dict |
| 44 | + Contains a dict from the HDF5 related to OSZICAR convergence data |
| 45 | + """ |
| 46 | + return_data = {} |
| 47 | + if selection is None: |
| 48 | + keys_to_include = INDEXING_OSZICAR |
| 49 | + else: |
| 50 | + if keys_to_include not in INDEXING_OSZICAR: |
| 51 | + message = """\ |
| 52 | +Please choose a selection including at least one of the following keywords: |
| 53 | +iteration_number, free_energy, free_energy_change, bandstructure_energy_change, |
| 54 | +number_hamiltonian_evaluations, norm_residual, difference_charge_density. Else do not |
| 55 | +select anything and all OSZICAR outputs will be provided.""" |
| 56 | + raise exception.RefinementError(message) |
| 57 | + keys_to_include = selection |
| 58 | + for key in INDEXING_OSZICAR: |
| 59 | + return_data[key] = self._read(key) |
| 60 | + return return_data |
| 61 | + |
| 62 | + def _read(self, key): |
| 63 | + # data represents all of the electronic steps for all ionic steps |
| 64 | + data = getattr(self._raw_data, "convergence_data") |
| 65 | + iteration_number = data[:, 0] |
| 66 | + split_index = np.where(iteration_number == 1)[0] |
| 67 | + data = np.vsplit(data, split_index)[1:][self._steps] |
| 68 | + if isinstance(self._steps, slice): |
| 69 | + data = [raw.VaspData(_data) for _data in data] |
| 70 | + else: |
| 71 | + data = [raw.VaspData(data)] |
| 72 | + data_index = INDEXING_OSZICAR[key] |
| 73 | + return_data = [list(_data[:, data_index]) for _data in data] |
| 74 | + is_none = [_data.is_none() for _data in data] |
| 75 | + if len(return_data) == 1: |
| 76 | + return_data = return_data[0] |
| 77 | + return return_data if not np.all(is_none) else {} |
| 78 | + |
| 79 | + def to_graph(self, selection="free_energy"): |
| 80 | + """Graph the change in parameter with iteration number. |
| 81 | +
|
| 82 | + Parameters |
| 83 | + ---------- |
| 84 | + selection: str |
| 85 | + Choose from either iteration_number, free_energy, free_energy_change, |
| 86 | + bandstructure_energy_change, number_hamiltonian_evaluations, norm_residual, |
| 87 | + difference_charge_density to get specific columns of the OSZICAR file. In |
| 88 | + case no selection is provided, the free energy is plotted. |
| 89 | +
|
| 90 | + Returns |
| 91 | + ------- |
| 92 | + Graph |
| 93 | + The Graph with the quantity plotted on y-axis and the iteration number of |
| 94 | + the x-axis. |
| 95 | + """ |
| 96 | + data = self.to_dict() |
| 97 | + series = graph.Series(data["iteration_number"], data[selection], selection) |
| 98 | + ylabel = " ".join(select.capitalize() for select in selection.split("_")) |
| 99 | + return graph.Graph( |
| 100 | + series=[series], |
| 101 | + xlabel="Iteration number", |
| 102 | + ylabel=ylabel, |
| 103 | + ) |
0 commit comments