From 749777584a2e9706b1d8c72f3f12c36df914fc69 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Tue, 1 Aug 2023 14:25:54 +0200 Subject: [PATCH 01/26] Convert a Series to a frame through the to_frame method in Series --- src/py4vasp/_third_party/graph/graph.py | 8 ++++++++ tests/third_party/graph/test_graph.py | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 5c3f7bc8..95f0d82d 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, fields, replace import numpy as np +import pandas as pd from py4vasp import exception from py4vasp._config import VASP_COLORS @@ -155,6 +156,13 @@ def _set_yaxis_options(self, figure): if self.y2label: figure.layout.yaxis2.title.text = self.y2label + def to_frame(self): + x = self.series.x + y = self.series.y + name = self.series.name + df = pd.DataFrame({f"{name}.x": x, f"{name}.y": y}) + return df + @property def _subplot_on(self): return any(series.subplot for series in self) diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 1663d3ce..c2ee8116 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -286,6 +286,13 @@ def test_add_label_to_multiple_lines(parabola, sine, Assert): assert graph.series[1].name == "new label sine" +def test_convert_parabola_to_frame(parabola, Assert): + graph = Graph(parabola) + df = graph.to_frame() + Assert.allclose(df["parabola.x"], parabola.x) + Assert.allclose(df["parabola.y"], parabola.y) + + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From 02d54b67ad885ea71c6bd1648979ed4c53883388 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Tue, 1 Aug 2023 14:51:55 +0200 Subject: [PATCH 02/26] Implement to_frame for sequence of series --- src/py4vasp/_third_party/graph/graph.py | 8 ++++---- tests/third_party/graph/test_graph.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 95f0d82d..e1c77c74 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -157,10 +157,10 @@ def _set_yaxis_options(self, figure): figure.layout.yaxis2.title.text = self.y2label def to_frame(self): - x = self.series.x - y = self.series.y - name = self.series.name - df = pd.DataFrame({f"{name}.x": x, f"{name}.y": y}) + df = pd.DataFrame() + for series in np.atleast_1d(self.series): + df[f"{series.name}.x"] = series.x + df[f"{series.name}.y"] = series.y return df @property diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index c2ee8116..93f36a05 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -293,6 +293,16 @@ def test_convert_parabola_to_frame(parabola, Assert): Assert.allclose(df["parabola.y"], parabola.y) +def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): + sequence = [parabola, sine] + graph = Graph(sequence) + df = graph.to_frame() + Assert.allclose(df["parabola.x"], parabola.x) + Assert.allclose(df["parabola.y"], parabola.y) + Assert.allclose(df["sine.x"], sine.x) + Assert.allclose(df["sine.y"], sine.y) + + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From c557f72d7fa6e0e71385d7eff491e9de6be20aa7 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Tue, 1 Aug 2023 15:15:58 +0200 Subject: [PATCH 03/26] Alter to_frame to allow it to take in multiple y-inputs --- src/py4vasp/_third_party/graph/graph.py | 9 +++++++-- tests/third_party/graph/test_graph.py | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index e1c77c74..4aaba1a1 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -159,8 +159,13 @@ def _set_yaxis_options(self, figure): def to_frame(self): df = pd.DataFrame() for series in np.atleast_1d(self.series): - df[f"{series.name}.x"] = series.x - df[f"{series.name}.y"] = series.y + replace_space = lambda text: text.replace(" ", "_") + df[replace_space(f"{series.name}.x")] = series.x + if series.y.ndim == 1: + df[replace_space(f"{series.name}.y")] = series.y + else: + for idx, series_y in enumerate(np.atleast_2d(series.y)): + df[replace_space(f"{series.name}.y{idx}")] = series_y return df @property diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 93f36a05..57cdf481 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -292,9 +292,8 @@ def test_convert_parabola_to_frame(parabola, Assert): Assert.allclose(df["parabola.x"], parabola.x) Assert.allclose(df["parabola.y"], parabola.y) - def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): - sequence = [parabola, sine] + sequence = [parabola, sine] graph = Graph(sequence) df = graph.to_frame() Assert.allclose(df["parabola.x"], parabola.x) @@ -302,6 +301,12 @@ def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): Assert.allclose(df["sine.x"], sine.x) Assert.allclose(df["sine.y"], sine.y) +def test_convert_multiple_lines(two_lines, Assert): + graph = Graph(two_lines) + df = graph.to_frame() + Assert.allclose(df["two_lines.x"], two_lines.x) + Assert.allclose(df["two_lines.y0"], two_lines.y[0]) + Assert.allclose(df["two_lines.y1"], two_lines.y[1]) @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): From efca6776fd324ef3891a3b0aa5dd53b1caba1edd Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Tue, 1 Aug 2023 15:39:54 +0200 Subject: [PATCH 04/26] Refactor to_frame --- src/py4vasp/_third_party/graph/graph.py | 17 ++++++++++------- tests/third_party/graph/test_graph.py | 5 ++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 4aaba1a1..de976276 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -159,15 +159,18 @@ def _set_yaxis_options(self, figure): def to_frame(self): df = pd.DataFrame() for series in np.atleast_1d(self.series): - replace_space = lambda text: text.replace(" ", "_") - df[replace_space(f"{series.name}.x")] = series.x - if series.y.ndim == 1: - df[replace_space(f"{series.name}.y")] = series.y - else: - for idx, series_y in enumerate(np.atleast_2d(series.y)): - df[replace_space(f"{series.name}.y{idx}")] = series_y + df[self._name_column(series, "x", None)] = series.x + for idx, series_y in enumerate(np.atleast_2d(series.y)): + df[self._name_column(series, "y", idx)] = series_y return df + def _name_column(self, series, suffix, idx=None): + text_suffix = series.name.replace(" ", "_") + f".{suffix}" + if series.y.ndim == 1 or idx is None: + return text_suffix + else: + return f"{text_suffix}{idx}" + @property def _subplot_on(self): return any(series.subplot for series in self) diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 57cdf481..9fc00100 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -292,8 +292,9 @@ def test_convert_parabola_to_frame(parabola, Assert): Assert.allclose(df["parabola.x"], parabola.x) Assert.allclose(df["parabola.y"], parabola.y) + def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): - sequence = [parabola, sine] + sequence = [parabola, sine] graph = Graph(sequence) df = graph.to_frame() Assert.allclose(df["parabola.x"], parabola.x) @@ -301,6 +302,7 @@ def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): Assert.allclose(df["sine.x"], sine.x) Assert.allclose(df["sine.y"], sine.y) + def test_convert_multiple_lines(two_lines, Assert): graph = Graph(two_lines) df = graph.to_frame() @@ -308,6 +310,7 @@ def test_convert_multiple_lines(two_lines, Assert): Assert.allclose(df["two_lines.y0"], two_lines.y[0]) Assert.allclose(df["two_lines.y1"], two_lines.y[1]) + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From 9d180c7f26f4e93653c9e9f30a7c3e5e3c7d1a1b Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Tue, 1 Aug 2023 16:18:56 +0200 Subject: [PATCH 05/26] Add width to series --- src/py4vasp/_third_party/graph/graph.py | 3 +++ tests/third_party/graph/test_graph.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index de976276..07b4b0e1 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -162,6 +162,9 @@ def to_frame(self): df[self._name_column(series, "x", None)] = series.x for idx, series_y in enumerate(np.atleast_2d(series.y)): df[self._name_column(series, "y", idx)] = series_y + if series.width is not None: + for idx, series_width in enumerate(np.atleast_2d(series.width)): + df[self._name_column(series, "width", idx)] = series_width return df def _name_column(self, series, suffix, idx=None): diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 9fc00100..e05b16a4 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -306,11 +306,22 @@ def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): def test_convert_multiple_lines(two_lines, Assert): graph = Graph(two_lines) df = graph.to_frame() + assert len(df.columns) == 3 Assert.allclose(df["two_lines.x"], two_lines.x) Assert.allclose(df["two_lines.y0"], two_lines.y[0]) Assert.allclose(df["two_lines.y1"], two_lines.y[1]) - +def test_convert_two_fatbands_to_frame(two_fatbands, Assert): + + print(two_fatbands._get_width(0)) + graph = Graph(two_fatbands) + df = graph.to_frame() + Assert.allclose(df["two_fatbands.x"], two_fatbands.x) + Assert.allclose(df["two_fatbands.y0"], two_fatbands.y[0]) + Assert.allclose(df["two_fatbands.y1"], two_fatbands.y[1]) + Assert.allclose(df["two_fatbands.width0"], two_fatbands.width[0]) + Assert.allclose(df["two_fatbands.width1"], two_fatbands.width[1]) + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From 18b268d6cd188bc965e5c5c14eaaa0b08a15e883 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 2 Aug 2023 11:16:50 +0200 Subject: [PATCH 06/26] assert in to_frame that the dimension of weight and y is the same --- src/py4vasp/_third_party/graph/graph.py | 1 + tests/third_party/graph/test_graph.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 07b4b0e1..784218d3 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -163,6 +163,7 @@ def to_frame(self): for idx, series_y in enumerate(np.atleast_2d(series.y)): df[self._name_column(series, "y", idx)] = series_y if series.width is not None: + assert series.width.ndim == series.y.ndim for idx, series_width in enumerate(np.atleast_2d(series.width)): df[self._name_column(series, "width", idx)] = series_width return df diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index e05b16a4..c9baaacf 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -313,7 +313,6 @@ def test_convert_multiple_lines(two_lines, Assert): def test_convert_two_fatbands_to_frame(two_fatbands, Assert): - print(two_fatbands._get_width(0)) graph = Graph(two_fatbands) df = graph.to_frame() Assert.allclose(df["two_fatbands.x"], two_fatbands.x) @@ -321,7 +320,7 @@ def test_convert_two_fatbands_to_frame(two_fatbands, Assert): Assert.allclose(df["two_fatbands.y1"], two_fatbands.y[1]) Assert.allclose(df["two_fatbands.width0"], two_fatbands.width[0]) Assert.allclose(df["two_fatbands.width1"], two_fatbands.width[1]) - + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From 0bd3f51e2c4759b242f8434f4bf5482a25e7478d Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 2 Aug 2023 11:43:51 +0200 Subject: [PATCH 07/26] Allow different length series to to_frame --- src/py4vasp/_third_party/graph/graph.py | 20 +++++++++++++------- tests/third_party/graph/test_graph.py | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 784218d3..60da5996 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -159,13 +159,19 @@ def _set_yaxis_options(self, figure): def to_frame(self): df = pd.DataFrame() for series in np.atleast_1d(self.series): - df[self._name_column(series, "x", None)] = series.x - for idx, series_y in enumerate(np.atleast_2d(series.y)): - df[self._name_column(series, "y", idx)] = series_y - if series.width is not None: - assert series.width.ndim == series.y.ndim - for idx, series_width in enumerate(np.atleast_2d(series.width)): - df[self._name_column(series, "width", idx)] = series_width + _df = self._create_and_populate_df(series) + df = df.join(_df, how="outer") + return df + + def _create_and_populate_df(self, series): + df = pd.DataFrame() + df[self._name_column(series, "x", None)] = series.x + for idx, series_y in enumerate(np.atleast_2d(series.y)): + df[self._name_column(series, "y", idx)] = series_y + if series.width is not None: + assert series.width.ndim == series.y.ndim + for idx, series_width in enumerate(np.atleast_2d(series.width)): + df[self._name_column(series, "width", idx)] = series_width return df def _name_column(self, series, suffix, idx=None): diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index c9baaacf..9c15e086 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -311,8 +311,8 @@ def test_convert_multiple_lines(two_lines, Assert): Assert.allclose(df["two_lines.y0"], two_lines.y[0]) Assert.allclose(df["two_lines.y1"], two_lines.y[1]) + def test_convert_two_fatbands_to_frame(two_fatbands, Assert): - graph = Graph(two_fatbands) df = graph.to_frame() Assert.allclose(df["two_fatbands.x"], two_fatbands.x) @@ -321,6 +321,23 @@ def test_convert_two_fatbands_to_frame(two_fatbands, Assert): Assert.allclose(df["two_fatbands.width0"], two_fatbands.width[0]) Assert.allclose(df["two_fatbands.width1"], two_fatbands.width[1]) + +def test_convert_different_length_series_to_frame(parabola, two_lines, Assert): + sequence = [two_lines, parabola] + graph = Graph(sequence) + df = graph.to_frame() + assert len(df) == max(len(parabola.x), len(two_lines.x)) + Assert.allclose(df["parabola.x"], parabola.x) + Assert.allclose(df["parabola.y"], parabola.y) + pad_width = len(parabola.x) - len(two_lines.x) + pad_nan = np.repeat(np.nan, pad_width) + padded_two_lines_x = np.hstack((two_lines.x, pad_nan)) + padded_two_lines_y = np.hstack((two_lines.y, np.vstack((pad_nan, pad_nan)))) + Assert.allclose(df["two_lines.x"], padded_two_lines_x) + Assert.allclose(df["two_lines.y0"], padded_two_lines_y[0]) + Assert.allclose(df["two_lines.y1"], padded_two_lines_y[1]) + + @patch("plotly.graph_objs.Figure._ipython_display_") def test_ipython_display(mock_display, parabola, not_core): graph = Graph(parabola) From cda48147dbc0a055cd63e320b6016e10d281100c Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 2 Aug 2023 12:31:07 +0200 Subject: [PATCH 08/26] Create the to_csv method --- src/py4vasp/_third_party/graph/graph.py | 10 +++++++++- src/py4vasp/_third_party/graph/series.py | 4 +++- tests/third_party/graph/test_graph.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 60da5996..7d1b8291 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -1,6 +1,7 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import itertools +import uuid from collections.abc import Sequence from dataclasses import dataclass, fields, replace @@ -163,6 +164,10 @@ def to_frame(self): df = df.join(_df, how="outer") return df + def to_csv(self, filename): + df = self.to_frame() + df.to_csv(filename, index=False) + def _create_and_populate_df(self, series): df = pd.DataFrame() df[self._name_column(series, "x", None)] = series.x @@ -175,7 +180,10 @@ def _create_and_populate_df(self, series): return df def _name_column(self, series, suffix, idx=None): - text_suffix = series.name.replace(" ", "_") + f".{suffix}" + if series.name: + text_suffix = series.name.replace(" ", "_") + f".{suffix}" + else: + text_suffix = "series_" + str(uuid.uuid1()) if series.y.ndim == 1 or idx is None: return text_suffix else: diff --git a/src/py4vasp/_third_party/graph/series.py b/src/py4vasp/_third_party/graph/series.py index 946051f3..802af191 100644 --- a/src/py4vasp/_third_party/graph/series.py +++ b/src/py4vasp/_third_party/graph/series.py @@ -39,7 +39,9 @@ class Series: _frozen = False def __post_init__(self): - if len(self.x) != np.array(self.y).shape[-1]: + self.x = np.asarray(self.x) + self.y = np.asarray(self.y) + if len(self.x) != self.y.shape[-1]: message = "The length of the two plotted components is inconsistent." raise exception.IncorrectUsage(message) if self.width is not None and len(self.x) != self.width.shape[-1]: diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 9c15e086..c264931d 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -4,6 +4,7 @@ from unittest.mock import patch import numpy as np +import pandas as pd import pytest from py4vasp import exception @@ -322,6 +323,17 @@ def test_convert_two_fatbands_to_frame(two_fatbands, Assert): Assert.allclose(df["two_fatbands.width1"], two_fatbands.width[1]) +def test_write_csv(tmp_path, two_fatbands, non_numpy, Assert): + sequence = [two_fatbands, *non_numpy] + graph = Graph(sequence) + graph.to_csv(tmp_path / "filename.csv") + ref = graph.to_frame() + actual = pd.read_csv(tmp_path / "filename.csv") + ref_rounded = np.round(ref.values, 12) + actual_rounded = np.round(actual.values, 12) + Assert.allclose(ref_rounded, actual_rounded) + + def test_convert_different_length_series_to_frame(parabola, two_lines, Assert): sequence = [two_lines, parabola] graph = Graph(sequence) From be04713af8334bacfc7a35ac8132a5c291d0d29b Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 2 Aug 2023 14:46:10 +0200 Subject: [PATCH 09/26] Implement to_csv method in the Mixin class --- src/py4vasp/_third_party/graph/mixin.py | 18 +++++++++++++++++- tests/third_party/graph/test_mixin.py | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/py4vasp/_third_party/graph/mixin.py b/src/py4vasp/_third_party/graph/mixin.py index 8bf93fa4..a11e1557 100644 --- a/src/py4vasp/_third_party/graph/mixin.py +++ b/src/py4vasp/_third_party/graph/mixin.py @@ -1,7 +1,9 @@ -# Copyright © VASP Software GmbH, + # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import abc +import os + from py4vasp._third_party.graph.graph import Graph from py4vasp._util import convert @@ -14,6 +16,20 @@ class Mixin(abc.ABC): @abc.abstractmethod def to_graph(self, *args, **kwargs): pass + + def to_frame(self, *args, **kwargs): + graph = self.to_graph(*args, **kwargs) + return graph.to_frame() + + def to_csv(self, *args, filename=None, **kwargs): + graph = self.to_graph(*args, **kwargs) + classname = convert.to_snakecase(self.__class__.__name__).strip("_") + filename = filename if filename is not None else f"{classname}.csv" + if os.path.isabs(filename): + writeout_path = filename + else: + writeout_path = self._path / filename + graph.to_csv(writeout_path) def plot(self, *args, **kwargs): """Wrapper around the :py:meth:`to_graph` function. diff --git a/tests/third_party/graph/test_mixin.py b/tests/third_party/graph/test_mixin.py index bf61d21e..1a37ac12 100644 --- a/tests/third_party/graph/test_mixin.py +++ b/tests/third_party/graph/test_mixin.py @@ -34,6 +34,16 @@ def test_converting_graph_to_plotly(): GRAPH.to_plotly.assert_called_once_with() assert fig == GRAPH.to_plotly.return_value +def test_convert_graph_to_frame(): + example = ExampleGraph() + df = example.to_frame() + GRAPH.to_frame.assert_called_once_with() + assert df == GRAPH.to_frame.return_value + +def test_convert_graph_to_csv(): + example = ExampleGraph() + example.to_csv() + GRAPH.to_csv.assert_called_once_with(example._path / "example_graph.csv") def test_converting_graph_to_image(): example = ExampleGraph() @@ -41,6 +51,17 @@ def test_converting_graph_to_image(): fig = GRAPH.to_plotly.return_value fig.write_image.assert_called_once_with(example._path / "example_graph.png") +def test_converting_graph_to_csv_with_relative_filename(): + example = ExampleGraph() + example.to_csv(filename="example.csv") + GRAPH.to_csv.assert_called_once_with(example._path / "example.csv") + +def test_converting_graph_to_csv_with_absolute_filename(): + example = ExampleGraph() + basedir_path = example._path.resolve() + full_path = basedir_path / "example.csv" + example.to_csv(filename=full_path) + GRAPH.to_csv.assert_called_once_with(full_path) def test_converting_graph_to_image_with_filename(): example = ExampleGraph() From 0b35fe1610a49ed2501cbeb4f45324ab8c4d140c Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 09:22:38 +0200 Subject: [PATCH 10/26] Add to_csv and to_frame to the Mixin class --- src/py4vasp/_third_party/graph/mixin.py | 10 +++++++--- tests/data/conftest.py | 2 ++ tests/third_party/graph/test_mixin.py | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/py4vasp/_third_party/graph/mixin.py b/src/py4vasp/_third_party/graph/mixin.py index a11e1557..6bf0024d 100644 --- a/src/py4vasp/_third_party/graph/mixin.py +++ b/src/py4vasp/_third_party/graph/mixin.py @@ -22,14 +22,14 @@ def to_frame(self, *args, **kwargs): return graph.to_frame() def to_csv(self, *args, filename=None, **kwargs): - graph = self.to_graph(*args, **kwargs) classname = convert.to_snakecase(self.__class__.__name__).strip("_") filename = filename if filename is not None else f"{classname}.csv" if os.path.isabs(filename): writeout_path = filename else: writeout_path = self._path / filename - graph.to_csv(writeout_path) + df = self.to_frame(*args, **kwargs) + df.to_csv(writeout_path, index=False) def plot(self, *args, **kwargs): """Wrapper around the :py:meth:`to_graph` function. @@ -65,7 +65,11 @@ def to_image(self, *args, filename=None, **kwargs): fig = self.to_plotly(*args, **kwargs) classname = convert.to_snakecase(self.__class__.__name__).strip("_") filename = filename if filename is not None else f"{classname}.png" - fig.write_image(self._path / filename) + if os.path.isabs(filename): + writeout_path = filename + else: + writeout_path = self._path / filename + fig.write_image(writeout_path) def _merge_graphs(graphs): diff --git a/tests/data/conftest.py b/tests/data/conftest.py index 0265ed0c..c310e2e0 100644 --- a/tests/data/conftest.py +++ b/tests/data/conftest.py @@ -57,6 +57,8 @@ def should_test_method(name): return False if name == "to_image": # would have side effects return False + if name == "to_csv": + return False return True diff --git a/tests/third_party/graph/test_mixin.py b/tests/third_party/graph/test_mixin.py index 1a37ac12..f2dfd3c6 100644 --- a/tests/third_party/graph/test_mixin.py +++ b/tests/third_party/graph/test_mixin.py @@ -43,7 +43,10 @@ def test_convert_graph_to_frame(): def test_convert_graph_to_csv(): example = ExampleGraph() example.to_csv() - GRAPH.to_csv.assert_called_once_with(example._path / "example_graph.csv") + GRAPH.to_frame.assert_called_once_with() + full_path = example._path / "example_graph.csv" + df = GRAPH.to_frame.return_value + df.to_csv.assert_called_once_with(full_path, index=False) def test_converting_graph_to_image(): example = ExampleGraph() @@ -54,14 +57,19 @@ def test_converting_graph_to_image(): def test_converting_graph_to_csv_with_relative_filename(): example = ExampleGraph() example.to_csv(filename="example.csv") - GRAPH.to_csv.assert_called_once_with(example._path / "example.csv") + full_path = example._path / "example.csv" + GRAPH.to_frame.assert_called_once_with() + df = GRAPH.to_frame.return_value + df.to_csv.assert_called_once_with(full_path, index=False) def test_converting_graph_to_csv_with_absolute_filename(): example = ExampleGraph() basedir_path = example._path.resolve() full_path = basedir_path / "example.csv" example.to_csv(filename=full_path) - GRAPH.to_csv.assert_called_once_with(full_path) + GRAPH.to_frame.assert_called_once_with() + df = GRAPH.to_frame.return_value + df.to_csv.assert_called_once_with(full_path, index=False) def test_converting_graph_to_image_with_filename(): example = ExampleGraph() @@ -69,6 +77,13 @@ def test_converting_graph_to_image_with_filename(): fig = GRAPH.to_plotly.return_value fig.write_image.assert_called_once_with(example._path / "example.jpg") +def test_converting_graph_to_image_with_absolute_filename(): + example = ExampleGraph() + basedir_path = example._path.resolve() + full_path = basedir_path / "example.jpg" + example.to_image(filename=full_path) + fig = GRAPH.to_plotly.return_value + fig.write_image.assert_called_once_with(full_path) def test_filename_is_keyword_only_argument(): example = ExampleGraph() From 30b0b13284a9219e9ebe843537b9a9d2f996a3a1 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 09:23:08 +0200 Subject: [PATCH 11/26] Apply black and isort --- src/py4vasp/_third_party/graph/mixin.py | 11 +++++------ tests/third_party/graph/test_mixin.py | 8 ++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/py4vasp/_third_party/graph/mixin.py b/src/py4vasp/_third_party/graph/mixin.py index 6bf0024d..12c9316a 100644 --- a/src/py4vasp/_third_party/graph/mixin.py +++ b/src/py4vasp/_third_party/graph/mixin.py @@ -1,7 +1,6 @@ - # Copyright © VASP Software GmbH, +# Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import abc - import os from py4vasp._third_party.graph.graph import Graph @@ -16,15 +15,15 @@ class Mixin(abc.ABC): @abc.abstractmethod def to_graph(self, *args, **kwargs): pass - + def to_frame(self, *args, **kwargs): graph = self.to_graph(*args, **kwargs) return graph.to_frame() - + def to_csv(self, *args, filename=None, **kwargs): classname = convert.to_snakecase(self.__class__.__name__).strip("_") filename = filename if filename is not None else f"{classname}.csv" - if os.path.isabs(filename): + if os.path.isabs(filename): writeout_path = filename else: writeout_path = self._path / filename @@ -65,7 +64,7 @@ def to_image(self, *args, filename=None, **kwargs): fig = self.to_plotly(*args, **kwargs) classname = convert.to_snakecase(self.__class__.__name__).strip("_") filename = filename if filename is not None else f"{classname}.png" - if os.path.isabs(filename): + if os.path.isabs(filename): writeout_path = filename else: writeout_path = self._path / filename diff --git a/tests/third_party/graph/test_mixin.py b/tests/third_party/graph/test_mixin.py index f2dfd3c6..92e733ef 100644 --- a/tests/third_party/graph/test_mixin.py +++ b/tests/third_party/graph/test_mixin.py @@ -34,12 +34,14 @@ def test_converting_graph_to_plotly(): GRAPH.to_plotly.assert_called_once_with() assert fig == GRAPH.to_plotly.return_value + def test_convert_graph_to_frame(): example = ExampleGraph() df = example.to_frame() GRAPH.to_frame.assert_called_once_with() assert df == GRAPH.to_frame.return_value + def test_convert_graph_to_csv(): example = ExampleGraph() example.to_csv() @@ -48,12 +50,14 @@ def test_convert_graph_to_csv(): df = GRAPH.to_frame.return_value df.to_csv.assert_called_once_with(full_path, index=False) + def test_converting_graph_to_image(): example = ExampleGraph() example.to_image() fig = GRAPH.to_plotly.return_value fig.write_image.assert_called_once_with(example._path / "example_graph.png") + def test_converting_graph_to_csv_with_relative_filename(): example = ExampleGraph() example.to_csv(filename="example.csv") @@ -62,6 +66,7 @@ def test_converting_graph_to_csv_with_relative_filename(): df = GRAPH.to_frame.return_value df.to_csv.assert_called_once_with(full_path, index=False) + def test_converting_graph_to_csv_with_absolute_filename(): example = ExampleGraph() basedir_path = example._path.resolve() @@ -71,12 +76,14 @@ def test_converting_graph_to_csv_with_absolute_filename(): df = GRAPH.to_frame.return_value df.to_csv.assert_called_once_with(full_path, index=False) + def test_converting_graph_to_image_with_filename(): example = ExampleGraph() example.to_image(filename="example.jpg") fig = GRAPH.to_plotly.return_value fig.write_image.assert_called_once_with(example._path / "example.jpg") + def test_converting_graph_to_image_with_absolute_filename(): example = ExampleGraph() basedir_path = example._path.resolve() @@ -85,6 +92,7 @@ def test_converting_graph_to_image_with_absolute_filename(): fig = GRAPH.to_plotly.return_value fig.write_image.assert_called_once_with(full_path) + def test_filename_is_keyword_only_argument(): example = ExampleGraph() with pytest.raises(TypeError): From f5a488117b9755829e0cd8d005986c7c44f789b5 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 09:48:09 +0200 Subject: [PATCH 12/26] Added not_core to tests needing pandas --- src/py4vasp/_third_party/graph/graph.py | 2 +- tests/third_party/graph/test_graph.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 7d1b8291..066b5a43 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -6,7 +6,6 @@ from dataclasses import dataclass, fields, replace import numpy as np -import pandas as pd from py4vasp import exception from py4vasp._config import VASP_COLORS @@ -15,6 +14,7 @@ go = import_.optional("plotly.graph_objects") subplots = import_.optional("plotly.subplots") +pd = import_.optional("pandas") @dataclass diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index c264931d..7cbc8f37 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -4,7 +4,6 @@ from unittest.mock import patch import numpy as np -import pandas as pd import pytest from py4vasp import exception @@ -323,7 +322,8 @@ def test_convert_two_fatbands_to_frame(two_fatbands, Assert): Assert.allclose(df["two_fatbands.width1"], two_fatbands.width[1]) -def test_write_csv(tmp_path, two_fatbands, non_numpy, Assert): +def test_write_csv(tmp_path, two_fatbands, non_numpy, Assert, not_core): + import pandas as pd sequence = [two_fatbands, *non_numpy] graph = Graph(sequence) graph.to_csv(tmp_path / "filename.csv") From bafcb1155344648600110de522ecc17ba1e73924 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 09:48:37 +0200 Subject: [PATCH 13/26] black and isort code formatting --- tests/third_party/graph/test_graph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index 7cbc8f37..b470541b 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -324,6 +324,7 @@ def test_convert_two_fatbands_to_frame(two_fatbands, Assert): def test_write_csv(tmp_path, two_fatbands, non_numpy, Assert, not_core): import pandas as pd + sequence = [two_fatbands, *non_numpy] graph = Graph(sequence) graph.to_csv(tmp_path / "filename.csv") From b6db94caa46fc7db39ab7e21380bc4e45f69a2cb Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 10:02:23 +0200 Subject: [PATCH 14/26] Added more not_core to graph tests --- tests/third_party/graph/test_graph.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/third_party/graph/test_graph.py b/tests/third_party/graph/test_graph.py index b470541b..26d05829 100644 --- a/tests/third_party/graph/test_graph.py +++ b/tests/third_party/graph/test_graph.py @@ -286,14 +286,14 @@ def test_add_label_to_multiple_lines(parabola, sine, Assert): assert graph.series[1].name == "new label sine" -def test_convert_parabola_to_frame(parabola, Assert): +def test_convert_parabola_to_frame(parabola, Assert, not_core): graph = Graph(parabola) df = graph.to_frame() Assert.allclose(df["parabola.x"], parabola.x) Assert.allclose(df["parabola.y"], parabola.y) -def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): +def test_convert_sequence_parabola_to_frame(parabola, sine, Assert, not_core): sequence = [parabola, sine] graph = Graph(sequence) df = graph.to_frame() @@ -303,7 +303,7 @@ def test_convert_sequence_parabola_to_frame(parabola, sine, Assert): Assert.allclose(df["sine.y"], sine.y) -def test_convert_multiple_lines(two_lines, Assert): +def test_convert_multiple_lines(two_lines, Assert, not_core): graph = Graph(two_lines) df = graph.to_frame() assert len(df.columns) == 3 @@ -312,7 +312,7 @@ def test_convert_multiple_lines(two_lines, Assert): Assert.allclose(df["two_lines.y1"], two_lines.y[1]) -def test_convert_two_fatbands_to_frame(two_fatbands, Assert): +def test_convert_two_fatbands_to_frame(two_fatbands, Assert, not_core): graph = Graph(two_fatbands) df = graph.to_frame() Assert.allclose(df["two_fatbands.x"], two_fatbands.x) @@ -335,7 +335,9 @@ def test_write_csv(tmp_path, two_fatbands, non_numpy, Assert, not_core): Assert.allclose(ref_rounded, actual_rounded) -def test_convert_different_length_series_to_frame(parabola, two_lines, Assert): +def test_convert_different_length_series_to_frame( + parabola, two_lines, Assert, not_core +): sequence = [two_lines, parabola] graph = Graph(sequence) df = graph.to_frame() From ad6660110170a6a3688a17a85bc15e0fe2f128e3 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 10:12:02 +0200 Subject: [PATCH 15/26] Change resolve to absolute when getting absolute path for mixin tests --- tests/third_party/graph/test_mixin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/third_party/graph/test_mixin.py b/tests/third_party/graph/test_mixin.py index 92e733ef..331fefe8 100644 --- a/tests/third_party/graph/test_mixin.py +++ b/tests/third_party/graph/test_mixin.py @@ -69,7 +69,7 @@ def test_converting_graph_to_csv_with_relative_filename(): def test_converting_graph_to_csv_with_absolute_filename(): example = ExampleGraph() - basedir_path = example._path.resolve() + basedir_path = example._path.absolute() full_path = basedir_path / "example.csv" example.to_csv(filename=full_path) GRAPH.to_frame.assert_called_once_with() @@ -86,7 +86,7 @@ def test_converting_graph_to_image_with_filename(): def test_converting_graph_to_image_with_absolute_filename(): example = ExampleGraph() - basedir_path = example._path.resolve() + basedir_path = example._path.absolute() full_path = basedir_path / "example.jpg" example.to_image(filename=full_path) fig = GRAPH.to_plotly.return_value From 92b51a476ae3d39af5265d0d6dd8821ddec8de66 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 11:18:15 +0200 Subject: [PATCH 16/26] Added documentation for the and methods --- src/py4vasp/_third_party/graph/graph.py | 22 +++++++++++++++++++ src/py4vasp/_third_party/graph/mixin.py | 29 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/py4vasp/_third_party/graph/graph.py b/src/py4vasp/_third_party/graph/graph.py index 066b5a43..6824658e 100644 --- a/src/py4vasp/_third_party/graph/graph.py +++ b/src/py4vasp/_third_party/graph/graph.py @@ -158,6 +158,18 @@ def _set_yaxis_options(self, figure): figure.layout.yaxis2.title.text = self.y2label def to_frame(self): + """Convert graph to a pandas dataframe. + + Every series will have at least two columns, named after the series name + with the suffix x and y. Additionally, if weights are provided, they will + also be written out as another column. If a series does not have a name, a + name will be generated based on a uuid. + + Returns + ------- + Dataframe + A pandas dataframe with columns for each series in the graph + """ df = pd.DataFrame() for series in np.atleast_1d(self.series): _df = self._create_and_populate_df(series) @@ -165,6 +177,16 @@ def to_frame(self): return df def to_csv(self, filename): + """Export graph to a csv file. + + Starting from the dataframe generated from `to_frame`, use the `to_csv` method + implemented in pandas to write out a csv file with a given filename + + Parameters + ---------- + filename: str | Path + Name of the exported csv file + """ df = self.to_frame() df.to_csv(filename, index=False) diff --git a/src/py4vasp/_third_party/graph/mixin.py b/src/py4vasp/_third_party/graph/mixin.py index 12c9316a..59c018f6 100644 --- a/src/py4vasp/_third_party/graph/mixin.py +++ b/src/py4vasp/_third_party/graph/mixin.py @@ -17,10 +17,39 @@ def to_graph(self, *args, **kwargs): pass def to_frame(self, *args, **kwargs): + """Wrapper around the :py:meth:`to_frame` function. + + Generates dataframes from the graph object. For information about + parameters that can be passed to this method, look at :py:meth:`to_graph`. + + Returns + ------- + Dataframe + Pandas dataframe corresponding to data in the graph + """ graph = self.to_graph(*args, **kwargs) return graph.to_frame() def to_csv(self, *args, filename=None, **kwargs): + """Converts data to a csv file. + + Writes out a csv file for data stored in a dataframe generated with + the :py:meth:`to_frame` method. Useful for creating external plots + for further analysis. + + If no filename is provided a default filename is deduced from the + name of the class. + + Note that the filename must be a keyword argument, i.e., you explicitly + need to write *filename="name_of_file"* because the arguments are passed + on to the :py:meth:`to_graph` function. Please check the documentation of that function + to learn which arguments are allowed. + + Parameters + ---------- + filename: str | Path + Name of the csv file which the data is exported to. + """ classname = convert.to_snakecase(self.__class__.__name__).strip("_") filename = filename if filename is not None else f"{classname}.csv" if os.path.isabs(filename): From 623124ab4654bde9569d128472b2125bd90cec5d Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 12:11:21 +0200 Subject: [PATCH 17/26] Update developer depedencies with ipykernel --- poetry.lock | 8 ++++---- pyproject.toml | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index d9856b67..3b02fff8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -818,13 +818,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.23.2" +version = "6.25.0" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.23.2-py3-none-any.whl", hash = "sha256:7ccb6e2d32fd958c21453db494c914f3474908a2fdefd99ab548a5375b548d1f"}, - {file = "ipykernel-6.23.2.tar.gz", hash = "sha256:fcfb67c5b504aa1bfcda1c5b3716636239e0f7b9290958f1c558c79b4c0e7ed5"}, + {file = "ipykernel-6.25.0-py3-none-any.whl", hash = "sha256:f0042e867ac3f6bca1679e6a88cbd6a58ed93a44f9d0866aecde6efe8de76659"}, + {file = "ipykernel-6.25.0.tar.gz", hash = "sha256:e342ce84712861be4b248c4a73472be4702c1b0dd77448bfd6bcfb3af9d5ddf9"}, ] [package.dependencies] @@ -2447,4 +2447,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "c75558cdad3c4e7949b51fdc36248a8ae05ec46fc3f6941377c5e3dac773c0e5" +content-hash = "385e83e3acb859db9c81e41e51bef73483a76bf5277f4ba0935d2575130dc02e" diff --git a/pyproject.toml b/pyproject.toml index 70c4a3da..e729020e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ ase = ">=3.22.1" mrcfile = ">=1.3.0" plotly = ">=5.9.0" kaleido = ">=0.2.1,<0.2.1.post1 || >0.2.1.post1" -ipython = "^8.12" +ipython = ">=8.12" [tool.poetry.group.dev.dependencies] pytest = ">=7.1.2" @@ -38,6 +38,7 @@ pylint = ">=2.15" hypothesis = ">=6.48.1" black = ">=22.6.0" isort = ">=5.10.1" +ipykernel = ">=6.25.0" [tool.poetry.group.doc.dependencies] sphinx = ">=5.0.2" From a907827cece99239fe255c33f3ced1ca6993b274 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 12:18:06 +0200 Subject: [PATCH 18/26] Allow install github actions to run on pull requests --- .github/workflows/install.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 0b834cb5..be10edb0 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -5,6 +5,8 @@ on: - cron: "0 2 * * 6" push: branches: [master] + pull_request: + branches: [master] jobs: tests: From 7f8739f969ee3c525fd419ce3243d39123833198 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 13:44:46 +0200 Subject: [PATCH 19/26] Added pip installation of ipykernel into install.yaml --- .github/workflows/install.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index be10edb0..e42d3635 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -29,6 +29,7 @@ jobs: python -m pip install --progress-bar=off --upgrade pip pip install --progress-bar=off . pip install --progress-bar=off pytest hypothesis + pip install --progress-bar=off ipykernel - name: Install mdtraj with conda shell: bash -el {0} run: | From 5c1240d5455067682ee4c0ca7018fc752463088f Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 3 Aug 2023 14:36:56 +0200 Subject: [PATCH 20/26] add pre-commit-config.yaml and pre-commit as a dependency --- .pre-commit-config.yaml | 10 +++ README.md | 6 +- poetry.lock | 170 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 4 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..1d952953 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: https://github.com/ambv/black + rev: 22.6.0 + hooks: + - id: black + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) \ No newline at end of file diff --git a/README.md b/README.md index cf279b90..d912b912 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,11 @@ black src tests isort src tests ~~~ before committing the code. This will autoformat your code and sort the import -statements in a consistent order. +statements in a consistent order. If you would like this code formatting to be done +along with each commit, you can run +~~~shell +pre-commit install +~~~ ## Contributing to py4vasp diff --git a/poetry.lock b/poetry.lock index 3b02fff8..a934746f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -257,6 +257,17 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + [[package]] name = "charset-normalizer" version = "3.2.0" @@ -581,6 +592,17 @@ files = [ [package.extras] graph = ["objgraph (>=1.7.2)"] +[[package]] +name = "distlib" +version = "0.3.7" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, +] + [[package]] name = "docutils" version = "0.20.1" @@ -620,6 +642,21 @@ files = [ [package.extras] tests = ["asttokens", "littleutils", "pytest", "rich"] +[[package]] +name = "filelock" +version = "3.12.2" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, +] + +[package.extras] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + [[package]] name = "fonttools" version = "4.40.0" @@ -746,6 +783,20 @@ pytz = ["pytz (>=2014.1)"] redis = ["redis (>=3.0.0)"] zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2023.3)"] +[[package]] +name = "identify" +version = "2.5.26" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.26-py2.py3-none-any.whl", hash = "sha256:c22a8ead0d4ca11f1edd6c9418c3220669b3b7533ada0a0ffa6cc0ef85cf9b54"}, + {file = "identify-2.5.26.tar.gz", hash = "sha256:7243800bce2f58404ed41b7c002e53d4d22bcf3ae1b7900c2d7aefd95394bf7f"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" version = "3.4" @@ -1361,6 +1412,20 @@ qcelemental = ["qcelemental"] rdkit = ["rdkit"] simpletraj = ["simpletraj"] +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "numpy" version = "1.24.3" @@ -1651,6 +1716,24 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pre-commit" +version = "3.3.3" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, + {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "prompt-toolkit" version = "3.0.38" @@ -1872,6 +1955,55 @@ files = [ {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "pyzmq" version = "25.1.0" @@ -2020,6 +2152,22 @@ dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -2323,6 +2471,26 @@ secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17. socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "virtualenv" +version = "20.24.1" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.24.1-py3-none-any.whl", hash = "sha256:01aacf8decd346cf9a865ae85c0cdc7f64c8caa07ff0d8b1dfc1733d10677442"}, + {file = "virtualenv-20.24.1.tar.gz", hash = "sha256:2ef6a237c31629da6442b0bcaa3999748108c7166318d1f55cc9f8d7294e97bd"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.12,<4" +platformdirs = ">=3.5.1,<4" + +[package.extras] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] + [[package]] name = "wcwidth" version = "0.2.6" @@ -2447,4 +2615,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "385e83e3acb859db9c81e41e51bef73483a76bf5277f4ba0935d2575130dc02e" +content-hash = "cfc46d75bd912b8fe2afc0579fd004a0efa2b1f1feb55d59047fdaf02dd46fe4" diff --git a/pyproject.toml b/pyproject.toml index e729020e..296aed3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ hypothesis = ">=6.48.1" black = ">=22.6.0" isort = ">=5.10.1" ipykernel = ">=6.25.0" +pre-commit = ">=3.3.3" [tool.poetry.group.doc.dependencies] sphinx = ">=5.0.2" From edd75dd2f184d9c94e0ca104fd9788a6d269820d Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 4 Oct 2023 15:51:51 +0200 Subject: [PATCH 21/26] allow scale to be a numpy float --- src/py4vasp/_data/structure.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/py4vasp/_data/structure.py b/src/py4vasp/_data/structure.py index a90fe7c7..245489eb 100644 --- a/src/py4vasp/_data/structure.py +++ b/src/py4vasp/_data/structure.py @@ -291,6 +291,8 @@ def _lattice_vectors(self): return self._scale() * lattice_vectors[self._get_steps()] def _scale(self): + if isinstance(self._raw_data.cell.scale, np.float_): + return self._raw_data.cell.scale if not self._raw_data.cell.scale.is_none(): return self._raw_data.cell.scale[()] else: From 3b3fed5dc01c81a7e63df946787eb73dc670ee78 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Wed, 4 Oct 2023 15:58:56 +0200 Subject: [PATCH 22/26] Expose MLFFErrorAnalysis --- src/py4vasp/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/py4vasp/__init__.py b/src/py4vasp/__init__.py index 812a9621..af009893 100644 --- a/src/py4vasp/__init__.py +++ b/src/py4vasp/__init__.py @@ -2,6 +2,7 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) from py4vasp._calculation import Calculation from py4vasp._calculations import Calculations +from py4vasp._analysis.mlff import MLFFErrorAnalysis from py4vasp._third_party.graph import plot from py4vasp._third_party.interactive import set_error_handling From 17fdd32b664919c53bdc92368d29eae58aeab3c8 Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Thu, 5 Oct 2023 12:04:49 +0200 Subject: [PATCH 23/26] Fix calling plot with keyword arguments --- src/py4vasp/_third_party/graph/plot.py | 15 +++++++++++---- tests/third_party/graph/test_plot.py | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/py4vasp/_third_party/graph/plot.py b/src/py4vasp/_third_party/graph/plot.py index 94ef0522..fa89a10e 100644 --- a/src/py4vasp/_third_party/graph/plot.py +++ b/src/py4vasp/_third_party/graph/plot.py @@ -37,12 +37,19 @@ def plot(*args, **kwargs): def _parse_series(*args, **kwargs): + if series := _parse_multiple_series(*args, **kwargs): + return series + else: + return _parse_single_series(*args, **kwargs) + + +def _parse_multiple_series(*args, **kwargs): try: return [Series(*arg) for arg in args] except TypeError: - # A TypeError is raised, if plot(x, y) is called instead of plot((x, y)). - # Because we creating the Series may raise another error, we leave the - # exception handling first to avoid reraising the TypeError. - pass + return [] + + +def _parse_single_series(*args, **kwargs): for_series = {key: val for key, val in kwargs.items() if key in Series._fields} return Series(*args, **for_series) diff --git a/tests/third_party/graph/test_plot.py b/tests/third_party/graph/test_plot.py index 11d289ab..3a424d3d 100644 --- a/tests/third_party/graph/test_plot.py +++ b/tests/third_party/graph/test_plot.py @@ -19,6 +19,8 @@ def test_plot(): assert plot((x1, y1)) == Graph([series0]) assert plot((x1, y1), (x2, y2, "label2")) == Graph([series0, series2]) assert plot((x1, y1), xlabel="xaxis") == Graph([series0], xlabel="xaxis") + assert plot(x1, y=y1) == Graph(series0) + assert plot(x=x1, y=y1) == Graph(series0) def test_plot_small_dataset(): From 92e93cdf42e53a794efeae934038c21ac7712af2 Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Thu, 5 Oct 2023 15:33:38 +0200 Subject: [PATCH 24/26] fix string fix-length issue for MD energy plotting --- src/py4vasp/_data/energy.py | 24 ++++++++++++------------ tests/conftest.py | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/py4vasp/_data/energy.py b/src/py4vasp/_data/energy.py index f84dd8d6..30aa691b 100644 --- a/src/py4vasp/_data/energy.py +++ b/src/py4vasp/_data/energy.py @@ -18,16 +18,16 @@ def _selection_string(default): _SELECTIONS = { - "ion-electron TOTEN ": ["ion_electron", "TOTEN"], - "kinetic energy EKIN ": ["kinetic_energy", "EKIN"], + "ion-electron TOTEN": ["ion_electron", "TOTEN"], + "kinetic energy EKIN": ["kinetic_energy", "EKIN"], "kin. lattice EKIN_LAT": ["kinetic_lattice", "EKIN_LAT"], - "temperature TEIN ": ["temperature", "TEIN"], - "nose potential ES ": ["nose_potential", "ES"], - "nose kinetic EPS ": ["nose_kinetic", "EPS"], - "total energy ETOTAL ": ["total_energy", "ETOTAL"], - "free energy TOTEN ": ["free_energy", "TOTEN"], - "energy without entropy ": ["without_entropy", "ENOENT"], - "energy(sigma->0) ": ["sigma_0", "ESIG0"], + "temperature TEIN": ["temperature", "TEIN"], + "nose potential ES": ["nose_potential", "ES"], + "nose kinetic EPS": ["nose_kinetic", "EPS"], + "total energy ETOTAL": ["total_energy", "ETOTAL"], + "free energy TOTEN": ["free_energy", "TOTEN"], + "energy without entropy": ["without_entropy", "ENOENT"], + "energy(sigma->0)": ["sigma_0", "ESIG0"], } @@ -168,7 +168,7 @@ def _init_selection_dict(self): return { selection: index for index, label in enumerate(self._raw_data.labels) - for selection in _SELECTIONS.get(convert.text_to_string(label), ()) + for selection in _SELECTIONS.get(convert.text_to_string(label).strip(), ()) } def _make_series(self, yaxes, tree): @@ -188,9 +188,9 @@ def __init__(self, tree): self.y2label = "Temperature (K)" if self.use_both else None def _is_temperature(self, selection): - choices = _SELECTIONS["temperature TEIN "] + choices = _SELECTIONS["temperature TEIN"] return any(select.contains(selection, choice) for choice in choices) def use_y2(self, label): - choices = _SELECTIONS["temperature TEIN "] + choices = _SELECTIONS["temperature TEIN"] return self.use_both and label in choices diff --git a/tests/conftest.py b/tests/conftest.py index 30991aff..f3acc139 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -438,13 +438,13 @@ def _polarization(): def _MD_energy(randomize: bool = False): labels = ( - "ion-electron TOTEN ", - "kinetic energy EKIN ", + "ion-electron TOTEN", + "kinetic energy EKIN", "kin. lattice EKIN_LAT", - "temperature TEIN ", - "nose potential ES ", - "nose kinetic EPS ", - "total energy ETOTAL ", + "temperature TEIN", + "nose potential ES", + "nose kinetic EPS", + "total energy ETOTAL", ) return _create_energy(labels, randomize=randomize) From 83296f3d6ac07ffcfd981a53a48f3ca4e8705fbf Mon Sep 17 00:00:00 2001 From: Sudarshan Vijay Date: Fri, 6 Oct 2023 10:46:39 +0200 Subject: [PATCH 25/26] Replace calculations import with py4vasp import --- src/py4vasp/__init__.py | 2 +- src/py4vasp/_analysis/mlff.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/py4vasp/__init__.py b/src/py4vasp/__init__.py index af009893..60643461 100644 --- a/src/py4vasp/__init__.py +++ b/src/py4vasp/__init__.py @@ -1,8 +1,8 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +from py4vasp._analysis.mlff import MLFFErrorAnalysis from py4vasp._calculation import Calculation from py4vasp._calculations import Calculations -from py4vasp._analysis.mlff import MLFFErrorAnalysis from py4vasp._third_party.graph import plot from py4vasp._third_party.interactive import set_error_handling diff --git a/src/py4vasp/_analysis/mlff.py b/src/py4vasp/_analysis/mlff.py index 4b78b221..a6bdfd29 100644 --- a/src/py4vasp/_analysis/mlff.py +++ b/src/py4vasp/_analysis/mlff.py @@ -5,7 +5,8 @@ import numpy as np -from py4vasp import Calculations, exception +import py4vasp +from py4vasp import exception class MLFFErrorAnalysis: @@ -71,7 +72,9 @@ def from_paths(cls, dft_data, mlff_data): Path to the MLFF data. Accepts wildcards. """ mlff_error_analysis = cls(_internal=True) - calculations = Calculations.from_paths(dft_data=dft_data, mlff_data=mlff_data) + calculations = py4vasp.Calculations.from_paths( + dft_data=dft_data, mlff_data=mlff_data + ) mlff_error_analysis._calculations = calculations set_appropriate_attrs(mlff_error_analysis) return mlff_error_analysis @@ -92,7 +95,9 @@ def from_files(cls, dft_data, mlff_data): Path to the MLFF data. Accepts wildcards. """ mlff_error_analysis = cls(_internal=True) - calculations = Calculations.from_files(dft_data=dft_data, mlff_data=mlff_data) + calculations = py4vasp.Calculations.from_files( + dft_data=dft_data, mlff_data=mlff_data + ) mlff_error_analysis._calculations = calculations set_appropriate_attrs(mlff_error_analysis) return mlff_error_analysis From ededac88e409b4eb530cea3da0fd36e1989b6800 Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Fri, 6 Oct 2023 10:46:40 +0200 Subject: [PATCH 26/26] Fix access to hdf5 files for trajectories --- src/py4vasp/_data/structure.py | 1 + tests/data/conftest.py | 6 ++++-- tests/data/test_structure.py | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/py4vasp/_data/structure.py b/src/py4vasp/_data/structure.py index 245489eb..04d5fad6 100644 --- a/src/py4vasp/_data/structure.py +++ b/src/py4vasp/_data/structure.py @@ -326,6 +326,7 @@ def _step_string(self): else: return f" (step {self._steps + 1})" + @base.data_access def __getitem__(self, steps): if not self._is_trajectory: message = "The structure is not a Trajectory so accessing individual elements is not allowed." diff --git a/tests/data/conftest.py b/tests/data/conftest.py index e968a3f2..6e9e27c3 100644 --- a/tests/data/conftest.py +++ b/tests/data/conftest.py @@ -36,7 +36,7 @@ def inner(cls, data, parameters={}): def check_instance_accesses_data(instance, data, parameters, file=None): failed = [] for name, method in inspect.getmembers(instance, inspect.ismethod): - if should_test_method(name): + if should_test_method(name, parameters): kwargs = parameters.get(name, {}) try: check_method_accesses_data(data, method, file, **kwargs) @@ -50,7 +50,9 @@ def check_instance_accesses_data(instance, data, parameters, file=None): raise AssertionError(message) -def should_test_method(name): +def should_test_method(name, parameters): + if name in parameters: + return True if name in ("__str__", "_repr_html_"): return True if name.startswith("from") or name.startswith("_"): diff --git a/tests/data/test_structure.py b/tests/data/test_structure.py index aac6c07d..c16598cc 100644 --- a/tests/data/test_structure.py +++ b/tests/data/test_structure.py @@ -360,4 +360,5 @@ def test_print_Ca3AsBr3(Ca3AsBr3, format_): def test_factory_methods(raw_data, check_factory_methods): data = raw_data.structure("Sr2TiO4") - check_factory_methods(Structure, data) + parameters = {"__getitem__": {"steps": slice(None)}} + check_factory_methods(Structure, data, parameters)