Skip to content

Commit

Permalink
Cleanup package structure (#66)
Browse files Browse the repository at this point in the history
* Add isort as development tool
* Add import order to CI and makefile
* Separate public/private for control package
* Sort import in util and third_party
* Separate public/private in data package
* Rename exceptions to exception
* Separate public/private in raw package
* Separate calculation in private and public part
  • Loading branch information
martin-schlipf authored Nov 8, 2022
1 parent 7562d0d commit 62d30dc
Show file tree
Hide file tree
Showing 120 changed files with 863 additions and 3,333 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: tests

on:
on:
push:
branches: [master]
pull_request:
Expand Down Expand Up @@ -32,6 +32,9 @@ jobs:
poetry run pytest --cov=py4vasp --cov-report term
- name: Check code style
run: |
poetry run isort --version
poetry run isort --check src
poetry run isort --check tests
poetry run black --version
poetry run black --check src
poetry run black --check tests
Expand Down Expand Up @@ -62,6 +65,9 @@ jobs:
poetry run pytest --cov=py4vasp --cov-report term
- name: Check code style
run: |
poetry run isort --version
poetry run isort --check src
poetry run isort --check tests
poetry run black --version
poetry run black --check src
poetry run black --check tests
11 changes: 8 additions & 3 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
.PHONY: test format
.PHONY: test format style import

TEST ?= tests

test:
poetry run pytest $(TEST)


format:
poetry run black .
format: import style

style:
poetry run black .

import:
poetry run isort .
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pylint = "^2.14.3"
Sphinx = "^5.0.2"
black = "^22.6.0"
hypothesis = "^6.48.1"
isort = "^5.10.1"

[tool.poetry.scripts]
error-analysis = "py4vasp.scripts.error_analysis:main"
Expand All @@ -57,6 +58,8 @@ pytest = {channel = "anaconda"}
sphinx = {channel = "anaconda"}
black = {channel = "conda-forge"}

[tool.isort]
profile = "black"

[tool.dephell.main]
from = {format = "poetry", path = "pyproject.toml"}
Expand Down
4 changes: 2 additions & 2 deletions src/py4vasp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
from .calculation import Calculation
from py4vasp._third_party.interactive import set_error_handling
from py4vasp._calculation import Calculation
from py4vasp._third_party.graph import plot
from py4vasp._third_party.interactive import set_error_handling

__version__ = "0.5.1"
set_error_handling("Minimal")
23 changes: 11 additions & 12 deletions src/py4vasp/calculation.py → src/py4vasp/_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
>>> calc.structure.print() # to print the structure in a POSCAR format
"""
import inspect
import py4vasp.data
import py4vasp.exceptions as exception
import py4vasp.control
import py4vasp._util.convert as _convert
import pathlib

from py4vasp import _data, control, data, exception
from py4vasp._util import convert


class Calculation:
"""Manage access to input and output of VASP calculations.
Expand Down Expand Up @@ -114,15 +113,15 @@ def POSCAR(self, poscar):


def _add_all_refinement_classes(calc, add_single_class):
for name, class_ in inspect.getmembers(py4vasp.data, inspect.isclass):
if issubclass(class_, py4vasp.data._base.Refinery):
for name, class_ in inspect.getmembers(data, inspect.isclass):
if issubclass(class_, _data.base.Refinery):
calc = add_single_class(calc, name, class_)
return calc


def _add_attribute_from_path(calc, name, class_):
instance = class_.from_path(calc.path())
setattr(calc, _convert.to_snakecase(name), instance)
setattr(calc, convert.to_snakecase(name), instance)
return calc


Expand All @@ -132,7 +131,7 @@ def __init__(self, file_name):

def __call__(self, calc, name, class_):
instance = class_.from_file(self._file_name)
setattr(calc, _convert.to_snakecase(name), instance)
setattr(calc, convert.to_snakecase(name), instance)
return calc


Expand All @@ -141,7 +140,7 @@ def _add_to_documentation(calc, name, class_):
functions = inspect.getmembers(class_, inspect.isfunction)
names = [name for name, _ in functions if not name.startswith("_")]
calc.__doc__ += f"""
{_convert.to_snakecase(name)}
{convert.to_snakecase(name)}
{first_line}
"""
Expand All @@ -154,7 +153,7 @@ def _add_to_documentation(calc, name, class_):


def _add_input_files(calc):
calc._INCAR = py4vasp.control.INCAR(calc.path())
calc._KPOINTS = py4vasp.control.KPOINTS(calc.path())
calc._POSCAR = py4vasp.control.POSCAR(calc.path())
calc._INCAR = control.INCAR(calc.path())
calc._KPOINTS = control.KPOINTS(calc.path())
calc._POSCAR = control.POSCAR(calc.path())
return calc
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path


class InputBase:
class InputFile:
def __init__(self, path):
self._path = path

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
from py4vasp.control._base import InputBase
from py4vasp._control import base


class INCAR(InputBase):
class INCAR(base.InputFile):
"""The INCAR file defining the input parameters of a VASP calculation.
Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
from py4vasp.control._base import InputBase
from py4vasp._control import base


class KPOINTS(InputBase):
class KPOINTS(base.InputFile):
"""The KPOINTS file defining the **k**-point grid for the VASP calculation.
Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
from py4vasp.control._base import InputBase
from py4vasp._control import base
from py4vasp.data import Structure


class POSCAR(InputBase):
class POSCAR(base.InputFile):
"""The POSCAR file defining the structure used in the VASP calculation.
Parameters
Expand Down
Empty file added src/py4vasp/_data/__init__.py
Empty file.
61 changes: 29 additions & 32 deletions src/py4vasp/data/band.py → src/py4vasp/_data/band.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
import numpy as np
import pandas as pd
from py4vasp import data
import py4vasp._util.sanity_check as _check
import py4vasp._util.documentation as _documentation
from IPython.lib.pretty import pretty
from .projector import Projector as _Projector, _selection_doc, _selection_examples
from .kpoint import Kpoint, _kpoints_opt_source
from py4vasp.data import _base
import py4vasp.data._export as _export

from py4vasp import data
from py4vasp._data import base, export, kpoint, projector
from py4vasp._util import check, documentation

_to_dict_doc = f"""Read the data into a dictionary.
Parameters
----------
{_selection_doc}
{_kpoints_opt_source}
{projector.selection_doc}
{kpoint.kpoints_opt_source}
Returns
-------
Expand All @@ -26,14 +23,14 @@
and a selection is passed, the projections of these bands on the
selected projectors are included.
{_selection_examples("band", "to_dict")}"""
{projector.selection_examples("band", "to_dict")}"""

_to_frame_doc = f"""Read the data into a DataFrame.
Parameters
----------
{_selection_doc}
{_kpoints_opt_source}
{projector.selection_doc}
{kpoint.kpoints_opt_source}
Returns
-------
Expand All @@ -42,16 +39,16 @@
bands. If a selection string is given, in addition the orbital projections
on these bands are returned.
{_selection_examples("band", "to_frame")}"""
{projector.selection_examples("band", "to_frame")}"""

_plot_doc = f"""Read the data and generate a graph.
Parameters
----------
{_selection_doc}
{projector.selection_doc}
width : float
Specifies the width of the flatbands if a selection of projections is specified.
{_kpoints_opt_source}
{kpoint.kpoints_opt_source}
Returns
-------
Expand All @@ -60,17 +57,17 @@
is provided the width of the bands represents the projections of the
bands onto the specified projectors.
{_selection_examples("band", "plot")}"""
{projector.selection_examples("band", "plot")}"""


_to_plotly_doc = f"""Read the data and generate a plotly figure.
Parameters
----------
{_selection_doc}
{projector.selection_doc}
width : float
Specifies the width of the flatbands if a selection of projections is specified.
{_kpoints_opt_source}
{kpoint.kpoints_opt_source}
Returns
-------
Expand All @@ -79,10 +76,10 @@
is provided the width of the bands represents the projections of the
bands onto the specified projectors.
{_selection_examples("band", "to_plotly")}"""
{projector.selection_examples("band", "to_plotly")}"""


class Band(_base.Refinery, _export.Image):
class Band(base.Refinery, export.Image):
"""The electronic band structure.
The most common use case of this class is to produce the electronic band
Expand All @@ -92,7 +89,7 @@ class Band(_base.Refinery, _export.Image):
**k**-point distances that are calculated are meaningless.
"""

@_base.data_access
@base.data_access
def __str__(self):
return f"""
{"spin polarized" if self._spin_polarized() else ""} band data:
Expand All @@ -101,8 +98,8 @@ def __str__(self):
{pretty(self._projector)}
""".strip()

@_base.data_access
@_documentation.add(_to_dict_doc)
@base.data_access
@documentation.add(_to_dict_doc)
def to_dict(self, selection=None):
dispersion = self._dispersion.read()
return {
Expand All @@ -114,22 +111,22 @@ def to_dict(self, selection=None):
"projections": self._read_projections(selection),
}

@_base.data_access
@_documentation.add(_plot_doc)
@base.data_access
@documentation.add(_plot_doc)
def plot(self, selection=None, width=0.5):
projections = self._projections(selection, width)
graph = self._dispersion.plot(projections)
graph = self._shift_series_by_fermi_energy(graph)
graph.ylabel = "Energy (eV)"
return graph

@_base.data_access
@_documentation.add(_to_plotly_doc)
@base.data_access
@documentation.add(_to_plotly_doc)
def to_plotly(self, selection=None, width=0.5):
return self.plot(selection, width).to_plotly()

@_base.data_access
@_documentation.add(_to_frame_doc)
@base.data_access
@documentation.add(_to_frame_doc)
def to_frame(self, selection=None):
index = self._setup_dataframe_index()
data = self._extract_relevant_data(selection)
Expand All @@ -143,17 +140,17 @@ def _dispersion(self):
return data.Dispersion.from_data(self._raw_data.dispersion)

def _kpoints(self):
return Kpoint.from_data(self._raw_data.dispersion.kpoints)
return data.Kpoint.from_data(self._raw_data.dispersion.kpoints)

@property
def _projector(self):
return _Projector.from_data(self._raw_data.projectors)
return data.Projector.from_data(self._raw_data.projectors)

def _projections(self, selection, width):
if selection is None:
return None
error_message = "Width of fat band structure must be a number."
_check.raise_error_if_not_number(width, error_message)
check.raise_error_if_not_number(width, error_message)
return {
name: width * projection
for name, projection in self._read_projections(selection).items()
Expand Down
Loading

0 comments on commit 62d30dc

Please sign in to comment.