Skip to content

Commit

Permalink
Feat: rename data package to calculation (vasp-dev#139)
Browse files Browse the repository at this point in the history
* Rename the data package to calculation
* Implement calculation package that acts like a Calculation object in the current directory
* Move Calculation class and all Refinery classes into calculation package
* Rename data to calculation in tests

* Prepare for Viewer refactoring by moving Viewer3d to third_party

* Update documentation configuration for next release
* Create automatic summary of documentation
* Elaborate on the documentation of each Refinery class
* Introduce custom Sphinx role for INCAR tags

* Improve error message on incorrect import
  • Loading branch information
martin-schlipf committed Feb 2, 2024
1 parent b283854 commit b9471c6
Show file tree
Hide file tree
Showing 96 changed files with 1,131 additions and 841 deletions.
4 changes: 3 additions & 1 deletion docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
api
api
_packages
_classes
5 changes: 0 additions & 5 deletions docs/Calculation.rst

This file was deleted.

4 changes: 4 additions & 0 deletions docs/_templates/class.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{ name | escape | underline }}

.. autoclass:: {{ fullname }}
:members:
19 changes: 19 additions & 0 deletions docs/_templates/member.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{ name | escape | underline }}

.. container:: quantity

.. currentmodule:: py4vasp.calculation
.. data:: {{ name }}

.. currentmodule:: py4vasp.calculation._{{name}}
.. autoclass:: {%
if name == "CONTCAR" -%}
CONTCAR
{%- else -%}
{%- for part in name.split("_") -%}
{{ part.capitalize() }}
{%- endfor -%}
{%- endif %}
:members:
:inherited-members:
:exclude-members: from_data, from_file, from_path, path
13 changes: 13 additions & 0 deletions docs/_templates/module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{ name | escape | underline }}

.. automodule:: {{ fullname }}

.. autosummary::

{% for function in functions %}
{{ function }}
{% endfor %}

{% for function in functions %}
.. autofunction:: {{ function }}
{% endfor %}
13 changes: 13 additions & 0 deletions docs/_templates/package.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{ name | escape | underline }}

.. automodule:: {{ fullname }}

.. rubric:: Attributes

.. autosummary::
:toctree:
:template: member.rst

{% for member in members %}
{{ member }}
{% endfor %}
21 changes: 19 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------

project = "py4vasp"
copyright = "2023, VASP Software GmbH"
copyright = "2024, VASP Software GmbH"
author = "VASP Software GmbH"

# The full version, including alpha/beta/rc tags
release = "0.7.1"
release = "0.9.0"


# -- General configuration ---------------------------------------------------
Expand All @@ -32,6 +32,7 @@
# ones.
extensions = ["sphinx.ext.napoleon", "sphinx_automodapi.automodapi"]
automodapi_inheritance_diagram = False
autosummary_ignore_module_all = False

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
Expand Down Expand Up @@ -64,3 +65,19 @@

# remove common py4vasp prefix from index
modindex_common_prefix = ["py4vasp."]


# -- Custom extension of Sphinx ----------------------------------------------
from docutils import nodes


# defines an INCAR tag
def tag_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
url = f"https://www.vasp.at/wiki/index.php/{text}"
node = nodes.reference(rawtext, text, refuri=url, **options)
return [node], []


def setup(app):
app.add_role("tag", tag_role)
return
9 changes: 4 additions & 5 deletions docs/convert.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
latest:
- dirhtml
- Calculation:
- dirhtml/Calculation
- calculation:
- dirhtml/_packages/py4vasp.calculation
- dirhtml/_classes/py4vasp.Calculation
- dirhtml/_packages/py4vasp.calculation.*
- raw:
- dirhtml/raw
- dirhtml/api/py4vasp.raw.*
- data:
- dirhtml/data
- dirhtml/api/py4vasp.data.*
- exception:
- dirhtml/exception
- dirhtml/api/py4vasp.exception.*
6 changes: 0 additions & 6 deletions docs/data.rst

This file was deleted.

105 changes: 64 additions & 41 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ associated with the XML or OUTCAR files.

For these two groups of users, we provide a different level of access. The
simple routines used in the tutorials will read the data from the file directly
and then generate the requested plot. For script developers, we provide an
expert interface where the data is lazily loaded as needed with some greater
flexibility when the data file is opened and closed.
and then generate the requested plot. For script developers, we provide interfaces
to convert the data to Python dictionaries for further processing. If I/O access
limits the performance, you can lazily load the data only when needed.

Installation
------------
Expand All @@ -28,15 +28,27 @@ You can then install *py4vasp* from PyPI_ using the pip package installer
pip install py4vasp
This will automatically download *py4vasp* as well as all the required dependencies.
However, we noticed that this approach is not fail-safe, because the installation
of the *mdtraj* dependency does not work on all operating systems. So in case
the simple installation above fails, you may need to use *conda* to install *mdtraj*
This will automatically download *py4vasp* as well as most of the required dependencies.
However, we do not install the *mdtraj* dependency by default because it does not
reliably work with pip. We recommend to install *mdtraj* using conda

.. code-block:: bash
conda install -c conda-forge mdtraj
pip install py4vasp
if you need the features that depend on it (plotting a trajectory of structures).

For a minimalistic setup where you use py4vasp as a library, you can install the
core package

.. code-block:: bash
pip install py4vasp-core
The core package contains the same source code as the main package and does not
impact the usage. However, it does not install any of the dependencies of *py4vasp*
except for *numpy* and *h5py*. Hence, this core package is most suitable for
script developers that do not need all the visualization features of *py4vasp*.

Alternatively, you can obtain the code from GitHub and install it. This will give you
the most recent version with all bugfixes. However, some features may only work once
Expand All @@ -47,6 +59,7 @@ the next VASP version is released.
git clone https://github.com/vasp-dev/py4vasp.git
cd py4vasp
pip install .
conda install -c conda-forge mdtraj
If these commands succeed, you should be able to use *py4vasp*. You can make a quick
test of your installation running the following command
Expand Down Expand Up @@ -75,52 +88,53 @@ The user interface of *py4vasp* is optimized for usage inside a Jupyter_ environ
(Jupyter notebook or Jupyter lab), though it can be used in regular Python scripts
as well. To give you an illustrative example of what *py4vasp* can do, we assume
that you created a Jupyter notebook inside the directory of your VASP calculation.
Then you access all the results of this calculation with

>>> from py4vasp import Calculation
>>> calc = Calculation.from_path(".")

Naturally, if you created the notebook outside of the calculation directory, you
would replace the path ``.`` with the directory of the calculation.

The attributes of the calculation correspond to different physical quantities that
you could have calculated with VASP. If you have an interactive session you can type
``calc.`` and then hit :kbd:`Tab` to get a list of all possible quantities. However
only the ones that you computed with VASP will give you any meaningful
result.
In the VASP calculation, you computed the density of states (DOS) with orbital
projections (:tag:`LORBIT` = 11). You may now want to read the data from your
VASP calculation to post-process it further with a script. This can be achieved with

.. _LORBIT: https://www.vasp.at/wiki/index.php/LORBIT

In the following, we will assume that you computed the density of states (DOS) with
orbital projections (LORBIT_ = 11). You may now want to read the data from your
VASP calculation to post-process it further with a script. This can be achieved in
a single line of code

>>> dos = calc.dos.read()
>>> import py4vasp
>>> dos = py4vasp.calculation.dos.read()

Under the hood, this will access the *vaspout.h5* file, because *py4vasp* knows where
the output is stored after you ran VASP. It will read the relevant tags from
the file and store them all in a Python dictionary. If you want to access particular
orbital projections, let's say the *p* orbitals, you can pass a ``selection = "p"`` as
an argument to the routine. More generally, you can check how to use a function with

>>> help(calc.dos.read)
>>> help(py4vasp.calculation.dos.read)

The most common use case for the DOS data may be to prepare a plot to get some
insight into the system of interest. Because of this, we provide an easy wrapper
for this particular functionality

>>> calc.dos.plot()
>>> py4vasp.calculation.dos.plot()

This will return an interactive figure that you can use to investigate the DOS.
Note that this requires a browser to work, which means it will open one if you
execute this inside a script instead of a Jupyter notebook. The *plot* command
takes the same arguments as the read command.
The *plot* command takes the same arguments as the read command. Note that this
requires a browser to work; if you execute this from within a interactive
environment, it may open a browser for you or you can enforce it by appending
`.show()`

>>> py4vasp.calculation.dos.plot().show()

The interface for the other quantities is very similar. Every quantity provides
a *read* function to get the raw data into Python and where it makes sense a
*plot* function visualizes the data. However, note that in particular, all data
visualized inside the structure require a Jupyter notebook to work.
All plots can be converted to csv files `to_csv` of pandas dataframes `to_frame`
for further refinement.

If your calculation is not in the root directory, you can create your own
instance

>>> from py4vasp import Calculation
>>> calc = Calculation.from_path("/path/to/your/VASP/calcualtion")

The attributes of the calculation correspond to different physical quantities that
you could have calculated with VASP. If you have an interactive session you can type
``calc.`` and then hit :kbd:`Tab` to get a list of all possible quantities. However
only the ones that you computed with VASP will give you any meaningful
result.

.. _tutorials: https://www.vasp.at/tutorials/latest

Expand All @@ -129,15 +143,24 @@ a look at the tutorials_ for VASP. Many of them use *py4vasp* to plot or analyze
the data produced by VASP, so this may give you an excellent starting point to learn
how you can apply *py4vasp* in your research.

.. toctree::
:maxdepth: 1
:caption: Contents:
.. currentmodule:: py4vasp

.. rubric:: Packages

.. autosummary::
:toctree: _packages
:template: package.rst
:recursive:

calculation

.. rubric:: Classes

.. autosummary::
:toctree: _classes
:template: class.rst

Calculation
control
raw
data
exception

----------------------------------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion src/py4vasp/__init__.py
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._analysis.mlff import MLFFErrorAnalysis
from py4vasp._calculation import Calculation
from py4vasp._calculations import Calculations
from py4vasp._third_party.graph import plot
from py4vasp._third_party.interactive import set_error_handling
from py4vasp.calculation._class import Calculation

__version__ = "0.8.0"
set_error_handling("Minimal")
24 changes: 12 additions & 12 deletions src/py4vasp/_combine/base.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
import importlib
import inspect
import pathlib
from typing import Dict, List

from py4vasp import data, exception
from py4vasp import calculation, exception


def _match_combine_with_refinement(combine_name: str):
combine_to_refinement_name = {
"Energies": "Energy",
"Forces": "Force",
"Stresses": "Stress",
"Energies": "energy",
"Forces": "force",
"Stresses": "stress",
}
for _, class_ in inspect.getmembers(data, inspect.isclass):
if class_.__name__ == combine_to_refinement_name[combine_name]:
return class_
else:
raise exception.IncorrectUsage(
f"Could not find refinement class for {combine_name}."
)
return getattr(calculation, combine_to_refinement_name[combine_name])
# for _, class_ in inspect.getmembers(data_depr, inspect.isclass):
# if class_.__name__ == combine_to_refinement_name[combine_name]:
# return class_
# else:
# raise exception.IncorrectUsage(
# f"Could not find refinement class for {combine_name}."
# )


class BaseCombine:
Expand Down
4 changes: 2 additions & 2 deletions src/py4vasp/_control/poscar.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright © VASP Software GmbH,
# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
from py4vasp import calculation
from py4vasp._control import base
from py4vasp.data import Structure


class POSCAR(base.InputFile):
Expand All @@ -28,4 +28,4 @@ def plot(self, *args, **kwargs):
Viewer3d
Visualize the structure as a 3d figure.
"""
return Structure.from_POSCAR(self).plot(*args, **kwargs)
return calculation.structure.from_POSCAR(self).plot(*args, **kwargs)
File renamed without changes.
29 changes: 29 additions & 0 deletions src/py4vasp/_util/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,35 @@ def _to_snakecase(word: str) -> str:
return word.lower()


# NOTE: to_camelcase is the function camelize from the inflection package
# (Copyright (C) 2012-2020 Janne Vanhala)
def to_camelcase(string: str, uppercase_first_letter: bool = True) -> str:
"""
Convert strings to CamelCase.
Examples::
>>> camelize("device_type")
'DeviceType'
>>> camelize("device_type", False)
'deviceType'
:func:`camelize` can be thought of as a inverse of :func:`underscore`,
although there are some cases where that does not hold::
>>> camelize(underscore("IOError"))
'IoError'
:param uppercase_first_letter: if set to `True` :func:`camelize` converts
strings to UpperCamelCase. If set to `False` :func:`camelize` produces
lowerCamelCase. Defaults to `True`.
"""
if uppercase_first_letter:
return re.sub(r"(?:^|_)(.)", lambda m: m.group(1).upper(), string)
else:
return string[0].lower() + camelize(string)[1:]


def to_rgb(hex):
"Convert a HEX color code to fractional RGB."
hex = hex.lstrip("#")
Expand Down
Loading

0 comments on commit b9471c6

Please sign in to comment.