Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve to_converged #154

Merged
merged 40 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7497775
Convert a Series to a frame through the to_frame method in Series
sudarshanv01 Aug 1, 2023
02d54b6
Implement to_frame for sequence of series
sudarshanv01 Aug 1, 2023
c557f72
Alter to_frame to allow it to take in multiple y-inputs
sudarshanv01 Aug 1, 2023
efca677
Refactor to_frame
sudarshanv01 Aug 1, 2023
9d180c7
Add width to series
sudarshanv01 Aug 1, 2023
18b268d
assert in to_frame that the dimension of weight and y is the same
sudarshanv01 Aug 2, 2023
0bd3f51
Allow different length series to to_frame
sudarshanv01 Aug 2, 2023
cda4814
Create the to_csv method
sudarshanv01 Aug 2, 2023
be04713
Implement to_csv method in the Mixin class
sudarshanv01 Aug 2, 2023
0b35fe1
Add to_csv and to_frame to the Mixin class
sudarshanv01 Aug 3, 2023
30b0b13
Apply black and isort
sudarshanv01 Aug 3, 2023
f5a4881
Added not_core to tests needing pandas
sudarshanv01 Aug 3, 2023
bafcb11
black and isort code formatting
sudarshanv01 Aug 3, 2023
b6db94c
Added more not_core to graph tests
sudarshanv01 Aug 3, 2023
ad66601
Change resolve to absolute when getting absolute path for mixin tests
sudarshanv01 Aug 3, 2023
92b51a4
Added documentation for the and methods
sudarshanv01 Aug 3, 2023
623124a
Update developer depedencies with ipykernel
sudarshanv01 Aug 3, 2023
bcb6f89
Merge branch 'vasp-dev:master' into master
sudarshanv01 Aug 3, 2023
a907827
Allow install github actions to run on pull requests
sudarshanv01 Aug 3, 2023
0d5838f
Merge branch 'master' of github.com:sudarshanv01/py4vasp
sudarshanv01 Aug 3, 2023
7f8739f
Added pip installation of ipykernel into install.yaml
sudarshanv01 Aug 3, 2023
f4f1515
Merge branch 'vasp-dev:master' into master
sudarshanv01 Aug 3, 2023
5c1240d
add pre-commit-config.yaml and pre-commit as a dependency
sudarshanv01 Aug 3, 2023
9a32998
Merge branch 'master' of github.com:sudarshanv01/py4vasp
sudarshanv01 Aug 3, 2023
0671d9e
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Sep 10, 2023
e5e244e
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Sep 25, 2023
f578627
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Oct 4, 2023
9f8a0ae
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Feb 2, 2024
455275b
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Feb 4, 2024
26f3480
Merge branch 'master' of github.com:vasp-dev/py4vasp
sudarshanv01 Feb 23, 2024
1b3aa1d
Change the oszicar format using labels
sudarshanv01 Feb 23, 2024
19cce10
change b to utf-8
sudarshanv01 Mar 17, 2024
d824b4e
Implement __str__ method for OSZICAR
sudarshanv01 Mar 22, 2024
6aa1797
add is_converged for the electronic steps
sudarshanv01 Mar 24, 2024
403c852
pass is_elmin_converged instead of inferring convergence
sudarshanv01 Apr 19, 2024
abbd3b3
modify tests for OSZICAR
sudarshanv01 Apr 19, 2024
020031e
move test to testing for integer
sudarshanv01 Apr 22, 2024
78a38fb
test for selection exception
sudarshanv01 Apr 23, 2024
7ada26e
flatten converged list
sudarshanv01 Apr 23, 2024
4200a65
Test slice
sudarshanv01 Apr 23, 2024
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
4 changes: 4 additions & 0 deletions src/py4vasp/_raw/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ class OSZICAR:

convergence_data: VaspData
"All columns of the OSZICAR file stored for all ionic steps."
label: VaspData
"Label of all the data from the OSZICAR file."
is_elmin_converged: VaspData
"Is the electronic minimization step converged?"


@dataclasses.dataclass
Expand Down
2 changes: 2 additions & 0 deletions src/py4vasp/_raw/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,9 @@ def selections(quantity):
schema.add(
raw.OSZICAR,
required=raw.Version(6, 5),
label="intermediate/ion_dynamics/oszicar_label",
convergence_data="intermediate/ion_dynamics/oszicar",
is_elmin_converged="/intermediate/ion_dynamics/electronic_step_converged",
)
#
group = "intermediate/pair_correlation"
Expand Down
79 changes: 53 additions & 26 deletions src/py4vasp/calculation/_OSZICAR.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,7 @@

from py4vasp import exception, raw
from py4vasp._third_party import graph
from py4vasp._util import convert
from py4vasp.calculation import _base, _slice, _structure

INDEXING_OSZICAR = {
"iteration_number": 0,
"free_energy": 1,
"free_energy_change": 2,
"bandstructure_energy_change": 3,
"number_hamiltonian_evaluations": 4,
"norm_residual": 5,
"difference_charge_density": 6,
}
from py4vasp.calculation import _base, _slice


class OSZICAR(_slice.Mixin, _base.Refinery, graph.Mixin):
Expand All @@ -26,6 +15,35 @@ class OSZICAR(_slice.Mixin, _base.Refinery, graph.Mixin):
Please check the vasp-wiki (https://www.vasp.at/wiki/index.php/OSZICAR) for more
details about the exact outputs generated for each combination of INCAR tags."""

def _more_than_one_ionic_step(self, data):
return any(isinstance(_data, list) for _data in data) == True

@_base.data_access
def __str__(self):
format_rep = "{0:g}\t{1:0.12E}\t{2:0.6E}\t{3:0.6E}\t{4:g}\t{5:0.3E}\t{6:0.3E}\n"
label_rep = "{}\t\t{}\t\t{}\t\t{}\t\t{}\t{}\t\t{}\n"
string = ""
labels = [label.decode("utf-8") for label in getattr(self._raw_data, "label")]
data = self.to_dict()
electronic_iterations = data["N"]
if not self._more_than_one_ionic_step(electronic_iterations):
electronic_iterations = [electronic_iterations]
ionic_steps = len(electronic_iterations)
for ionic_step in range(ionic_steps):
string += label_rep.format(*labels)
electronic_steps = len(electronic_iterations[ionic_step])
for electronic_step in range(electronic_steps):
_data = []
for label in self._raw_data.label:
_values_electronic = data[label.decode("utf-8")]
if not self._more_than_one_ionic_step(_values_electronic):
_values_electronic = [_values_electronic]
_value = _values_electronic[ionic_step][electronic_step]
_data.append(_value)
_data = [float(_value) for _value in _data]
string += format_rep.format(*_data)
return string

@_base.data_access
def to_dict(self, selection=None):
"""Extract convergence data from the HDF5 file and make it available in a dict
Expand All @@ -45,20 +63,23 @@ def to_dict(self, selection=None):
"""
return_data = {}
if selection is None:
keys_to_include = INDEXING_OSZICAR
keys_to_include = self._from_bytes_to_utf(self._raw_data.label)
else:
if keys_to_include not in INDEXING_OSZICAR:
labels_as_str = self._from_bytes_to_utf(self._raw_data.label)
if selection not in labels_as_str:
message = """\
Please choose a selection including at least one of the following keywords:
iteration_number, free_energy, free_energy_change, bandstructure_energy_change,
number_hamiltonian_evaluations, norm_residual, difference_charge_density. Else do not
select anything and all OSZICAR outputs will be provided."""
N, E, dE, deps, ncg, rms, rms(c)"""
raise exception.RefinementError(message)
keys_to_include = selection
for key in INDEXING_OSZICAR:
keys_to_include = [selection]
for key in keys_to_include:
return_data[key] = self._read(key)
return return_data

def _from_bytes_to_utf(self, quantity: list):
return [_quantity.decode("utf-8") for _quantity in quantity]

@_base.data_access
def _read(self, key):
# data represents all of the electronic steps for all ionic steps
data = getattr(self._raw_data, "convergence_data")
Expand All @@ -69,23 +90,21 @@ def _read(self, key):
data = [raw.VaspData(_data) for _data in data]
else:
data = [raw.VaspData(data)]
data_index = INDEXING_OSZICAR[key]
labels = [label.decode("utf-8") for label in self._raw_data.label]
data_index = labels.index(key)
return_data = [list(_data[:, data_index]) for _data in data]
is_none = [_data.is_none() for _data in data]
if len(return_data) == 1:
return_data = return_data[0]
return return_data if not np.all(is_none) else {}

def to_graph(self, selection="free_energy"):
def to_graph(self, selection="E"):
"""Graph the change in parameter with iteration number.

Parameters
----------
selection: str
Choose from either iteration_number, free_energy, free_energy_change,
bandstructure_energy_change, number_hamiltonian_evaluations, norm_residual,
difference_charge_density to get specific columns of the OSZICAR file. In
case no selection is provided, the free energy is plotted.
Choose strings consistent with the OSZICAR format

Returns
-------
Expand All @@ -94,10 +113,18 @@ def to_graph(self, selection="free_energy"):
the x-axis.
"""
data = self.to_dict()
series = graph.Series(data["iteration_number"], data[selection], selection)
series = graph.Series(data["N"], data[selection], selection)
ylabel = " ".join(select.capitalize() for select in selection.split("_"))
return graph.Graph(
series=[series],
xlabel="Iteration number",
ylabel=ylabel,
)

@_base.data_access
def is_converged(self):
is_elmin_converged = self._raw_data.is_elmin_converged[self._steps]
converged = is_elmin_converged == 0
if isinstance(converged, bool):
converged = np.array([converged])
return converged.flatten()
87 changes: 62 additions & 25 deletions tests/calculation/test_oszicar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

import types

import numpy as np
import pytest

from py4vasp import calculation
from py4vasp import calculation, exception


@pytest.fixture
Expand All @@ -14,39 +15,75 @@ def OSZICAR(raw_data):
oszicar = calculation.OSZICAR.from_data(raw_oszicar)
oszicar.ref = types.SimpleNamespace()
convergence_data = raw_oszicar.convergence_data
oszicar.ref.iteration_number = convergence_data[:, 0]
oszicar.ref.free_energy = convergence_data[:, 1]
oszicar.ref.free_energy_change = convergence_data[:, 2]
oszicar.ref.bandstructure_energy_change = convergence_data[:, 3]
oszicar.ref.number_hamiltonian_evaluations = convergence_data[:, 4]
oszicar.ref.norm_residual = convergence_data[:, 5]
oszicar.ref.difference_charge_density = convergence_data[:, 6]
oszicar.ref.N = np.int64(convergence_data[:, 0])
oszicar.ref.E = convergence_data[:, 1]
oszicar.ref.dE = convergence_data[:, 2]
oszicar.ref.deps = convergence_data[:, 3]
oszicar.ref.ncg = convergence_data[:, 4]
oszicar.ref.rms = convergence_data[:, 5]
oszicar.ref.rmsc = convergence_data[:, 6]
oszicar.ref.is_elmin_converged = [raw_oszicar.is_elmin_converged == [0.0]]
string_rep = "N\t\tE\t\tdE\t\tdeps\t\tncg\trms\t\trms(c)\n"
format_rep = "{0:g}\t{1:0.12E}\t{2:0.6E}\t{3:0.6E}\t{4:g}\t{5:0.3E}\t{6:0.3E}\n"
for idx in range(len(convergence_data)):
string_rep += format_rep.format(*convergence_data[idx])
oszicar.ref.string_rep = str(string_rep)
return oszicar


def test_read(OSZICAR, Assert):
actual = OSZICAR.read()
expected = OSZICAR.ref
Assert.allclose(actual["iteration_number"], expected.iteration_number)
Assert.allclose(actual["free_energy"], expected.free_energy)
Assert.allclose(actual["free_energy_change"], expected.free_energy_change)
Assert.allclose(
actual["bandstructure_energy_change"], expected.bandstructure_energy_change
)
Assert.allclose(
actual["number_hamiltonian_evaluations"],
expected.number_hamiltonian_evaluations,
)
Assert.allclose(actual["norm_residual"], expected.norm_residual)
Assert.allclose(
actual["difference_charge_density"], expected.difference_charge_density
)
Assert.allclose(actual["N"], expected.N)
Assert.allclose(actual["E"], expected.E)
Assert.allclose(actual["dE"], expected.dE)
Assert.allclose(actual["deps"], expected.deps)
Assert.allclose(actual["ncg"], expected.ncg)
Assert.allclose(actual["rms"], expected.rms)
Assert.allclose(actual["rms(c)"], expected.rmsc)


@pytest.mark.parametrize(
"quantity_name", ["N", "E", "dE", "deps", "ncg", "rms", "rms(c)"]
)
def test_read_selection(quantity_name, OSZICAR, Assert):
actual = OSZICAR.read(quantity_name)
expected = getattr(OSZICAR.ref, quantity_name.replace("(", "").replace(")", ""))
Assert.allclose(actual[quantity_name], expected)


def test_read_incorrect_selection(OSZICAR):
with pytest.raises(exception.RefinementError):
OSZICAR.read("forces")


def test_slice(OSZICAR, Assert):
actual = OSZICAR[0:1].read()
expected = OSZICAR.ref
Assert.allclose(actual["N"], expected.N)
Assert.allclose(actual["E"], expected.E)
Assert.allclose(actual["dE"], expected.dE)
Assert.allclose(actual["deps"], expected.deps)
Assert.allclose(actual["ncg"], expected.ncg)
Assert.allclose(actual["rms"], expected.rms)
Assert.allclose(actual["rms(c)"], expected.rmsc)


def test_plot(OSZICAR, Assert):
graph = OSZICAR.plot()
assert graph.xlabel == "Iteration number"
assert graph.ylabel == "Free Energy"
assert graph.ylabel == "E"
assert len(graph.series) == 1
Assert.allclose(graph.series[0].x, OSZICAR.ref.iteration_number)
Assert.allclose(graph.series[0].y, OSZICAR.ref.free_energy)
Assert.allclose(graph.series[0].x, OSZICAR.ref.N)
Assert.allclose(graph.series[0].y, OSZICAR.ref.E)


def test_print(OSZICAR, format_):
actual, _ = format_(OSZICAR)
assert actual["text/plain"] == OSZICAR.ref.string_rep


def test_is_converged(OSZICAR):
actual = OSZICAR.is_converged()
expected = OSZICAR.ref.is_elmin_converged
assert actual == expected
16 changes: 13 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,11 +654,21 @@ def _Sr2TiO4_cell():


def _example_OSZICAR():
random_convergence_data = np.random.rand(9, 6)
random_convergence_data = np.random.rand(9, 3)
iteration_number = np.arange(1, 10)[:, np.newaxis]
convergence_data = np.hstack([iteration_number, random_convergence_data])
ncg = np.random.randint(4, 10, (9, 1))
random_rms = np.random.rand(9, 2)
convergence_data = np.hstack(
[iteration_number, random_convergence_data, ncg, random_rms]
)
convergence_data = raw.VaspData(convergence_data)
return raw.OSZICAR(convergence_data=convergence_data)
label = raw.VaspData([b"N", b"E", b"dE", b"deps", b"ncg", b"rms", b"rms(c)"])
is_elmin_converged = [0]
return raw.OSZICAR(
convergence_data=convergence_data,
label=label,
is_elmin_converged=is_elmin_converged,
)


def _Sr2TiO4_CONTCAR():
Expand Down
Loading