From 066043c735a9143aa54d1af667259a96c1a907af Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Fri, 7 Jun 2024 15:00:54 +0200 Subject: [PATCH 1/6] WIP: hide input files --- src/py4vasp/__init__.py | 2 +- .../{calculation => _calculation}/_CONTCAR.py | 0 .../{calculation => _calculation}/_OSZICAR.py | 0 .../_class.py => _calculation/__init__.py} | 105 ++++++++++++++++-- .../{calculation => _calculation}/_density.py | 0 .../_dielectric_function.py | 0 .../_dielectric_tensor.py | 0 .../_dispersion.py | 10 +- .../{calculation => _calculation}/_dos.py | 0 .../_elastic_modulus.py | 0 .../{calculation => _calculation}/_energy.py | 0 .../{calculation => _calculation}/_fatband.py | 0 .../{calculation => _calculation}/_force.py | 0 .../_force_constant.py | 0 .../_internal_strain.py | 0 .../_magnetism.py | 0 .../_pair_correlation.py | 0 .../_partial_charge.py | 0 .../{calculation => _calculation}/_phonon.py | 0 .../_phonon_band.py | 0 .../_phonon_dos.py | 0 .../_piezoelectric_tensor.py | 0 .../_polarization.py | 0 .../_potential.py | 0 .../{calculation => _calculation}/_stress.py | 0 .../{calculation => _calculation}/_system.py | 0 .../_topology.py | 26 ++--- .../_velocity.py | 0 .../_workfunction.py | 0 .../_band.py => _calculation/band.py} | 36 +++--- .../_bandgap.py => _calculation/bandgap.py} | 32 +++--- .../_base.py => _calculation/base.py} | 0 .../born_effective_charge.py} | 8 +- .../_kpoint.py => _calculation/kpoint.py} | 22 ++-- .../projector.py} | 20 ++-- .../selection.py} | 0 .../_slice.py => _calculation/slice_.py} | 0 .../structure.py} | 50 ++++----- src/py4vasp/_combine/base.py | 5 +- src/py4vasp/_control/poscar.py | 4 +- src/py4vasp/_util/convert.py | 7 +- src/py4vasp/calculation/__init__.py | 98 ---------------- src/py4vasp/exception.py | 5 - tests/calculation/test_band.py | 4 +- tests/calculation/test_bandgap.py | 6 +- tests/calculation/test_base.py | 24 ++-- tests/calculation/test_class.py | 8 +- ..._module.py => test_default_calculation.py} | 10 +- tests/calculation/test_projector.py | 2 +- tests/calculation/test_slice_mixin.py | 8 +- tests/calculation/test_structure.py | 2 +- tests/calculation/test_topology.py | 10 +- tests/util/test_convert.py | 10 +- 53 files changed, 254 insertions(+), 260 deletions(-) rename src/py4vasp/{calculation => _calculation}/_CONTCAR.py (100%) rename src/py4vasp/{calculation => _calculation}/_OSZICAR.py (100%) rename src/py4vasp/{calculation/_class.py => _calculation/__init__.py} (59%) rename src/py4vasp/{calculation => _calculation}/_density.py (100%) rename src/py4vasp/{calculation => _calculation}/_dielectric_function.py (100%) rename src/py4vasp/{calculation => _calculation}/_dielectric_tensor.py (100%) rename src/py4vasp/{calculation => _calculation}/_dispersion.py (96%) rename src/py4vasp/{calculation => _calculation}/_dos.py (100%) rename src/py4vasp/{calculation => _calculation}/_elastic_modulus.py (100%) rename src/py4vasp/{calculation => _calculation}/_energy.py (100%) rename src/py4vasp/{calculation => _calculation}/_fatband.py (100%) rename src/py4vasp/{calculation => _calculation}/_force.py (100%) rename src/py4vasp/{calculation => _calculation}/_force_constant.py (100%) rename src/py4vasp/{calculation => _calculation}/_internal_strain.py (100%) rename src/py4vasp/{calculation => _calculation}/_magnetism.py (100%) rename src/py4vasp/{calculation => _calculation}/_pair_correlation.py (100%) rename src/py4vasp/{calculation => _calculation}/_partial_charge.py (100%) rename src/py4vasp/{calculation => _calculation}/_phonon.py (100%) rename src/py4vasp/{calculation => _calculation}/_phonon_band.py (100%) rename src/py4vasp/{calculation => _calculation}/_phonon_dos.py (100%) rename src/py4vasp/{calculation => _calculation}/_piezoelectric_tensor.py (100%) rename src/py4vasp/{calculation => _calculation}/_polarization.py (100%) rename src/py4vasp/{calculation => _calculation}/_potential.py (100%) rename src/py4vasp/{calculation => _calculation}/_stress.py (100%) rename src/py4vasp/{calculation => _calculation}/_system.py (100%) rename src/py4vasp/{calculation => _calculation}/_topology.py (94%) rename src/py4vasp/{calculation => _calculation}/_velocity.py (100%) rename src/py4vasp/{calculation => _calculation}/_workfunction.py (100%) rename src/py4vasp/{calculation/_band.py => _calculation/band.py} (87%) rename src/py4vasp/{calculation/_bandgap.py => _calculation/bandgap.py} (93%) rename src/py4vasp/{calculation/_base.py => _calculation/base.py} (100%) rename src/py4vasp/{calculation/_born_effective_charge.py => _calculation/born_effective_charge.py} (93%) rename src/py4vasp/{calculation/_kpoint.py => _calculation/kpoint.py} (97%) rename src/py4vasp/{calculation/_projector.py => _calculation/projector.py} (97%) rename src/py4vasp/{calculation/_selection.py => _calculation/selection.py} (100%) rename src/py4vasp/{calculation/_slice.py => _calculation/slice_.py} (100%) rename src/py4vasp/{calculation/_structure.py => _calculation/structure.py} (93%) delete mode 100644 src/py4vasp/calculation/__init__.py rename tests/calculation/{test_module.py => test_default_calculation.py} (68%) diff --git a/src/py4vasp/__init__.py b/src/py4vasp/__init__.py index ec74a95d..6bdbeb73 100644 --- a/src/py4vasp/__init__.py +++ b/src/py4vasp/__init__.py @@ -4,7 +4,7 @@ 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 +from py4vasp._calculation import calculation, Calculation __version__ = "0.9.0" set_error_handling("Minimal") diff --git a/src/py4vasp/calculation/_CONTCAR.py b/src/py4vasp/_calculation/_CONTCAR.py similarity index 100% rename from src/py4vasp/calculation/_CONTCAR.py rename to src/py4vasp/_calculation/_CONTCAR.py diff --git a/src/py4vasp/calculation/_OSZICAR.py b/src/py4vasp/_calculation/_OSZICAR.py similarity index 100% rename from src/py4vasp/calculation/_OSZICAR.py rename to src/py4vasp/_calculation/_OSZICAR.py diff --git a/src/py4vasp/calculation/_class.py b/src/py4vasp/_calculation/__init__.py similarity index 59% rename from src/py4vasp/calculation/_class.py rename to src/py4vasp/_calculation/__init__.py index c0e4e3dc..c05c4cc8 100644 --- a/src/py4vasp/calculation/_class.py +++ b/src/py4vasp/_calculation/__init__.py @@ -1,8 +1,84 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +"""Provide refinement functions for a the raw data of a VASP calculation run in the +current directory. + +Usually one is not directly interested in the raw data that is produced but +wants to produce either a figure for a publication or some post-processing of +the data. This package contains multiple modules that enable these kinds of +workflows by extracting the relevant data from the HDF5 file and transforming +them into an accessible format. The modules also provide plotting functionality +to get a quick insight about the data, which can then be refined either within +python or a different tool to obtain publication-quality figures. + +Generally, all modules provide a `read` function that extracts the data from the +HDF5 file and puts it into a Python dictionary. Where it makes sense in addition +a `plot` function is available that converts the data into a figure for Jupyter +notebooks. In addition, data conversion routines `to_X` may be available +transforming the data into another format or file, which may be useful to +generate plots with tools other than Python. For the specifics, please refer to +the documentation of the individual modules. + +The raw data is read from the current directory. The :class:`~py4vasp.Calculation` +class provides a more flexible interface with which you can determine the source +directory or file for the VASP calculation manually. That class exposes functions +of the modules as methods of attributes, i.e., the two following examples are +equivalent: + +.. rubric:: using :mod:`~py4vasp.calculation` module + +>>> from py4vasp import calculation +>>> calculation.dos.read() + +.. rubric:: using :class:`~py4vasp.Calculation` class + +>>> from py4vasp import Calculation +>>> calc = Calculation.from_path(".") +>>> calc.dos.read() + +In the latter example, you can change the path from which the data is extracted. +""" +import importlib import pathlib -from py4vasp import calculation, control, exception +from py4vasp import control, exception +from py4vasp._util import convert + +INPUT_FILES = ("INCAR", "KPOINTS", "POSCAR") +QUANTITIES = ( + "band", + "bandgap", + "born_effective_charge", + # "CONTCAR", + # "density", + # "dielectric_function", + # "dielectric_tensor", + # "dos", + # "elastic_modulus", + # "energy", + # "fatband", + # "force", + # "force_constant", + # "internal_strain", + "kpoint", + # "magnetism", + # "OSZICAR", + # "pair_correlation", + # "partial_charge", + # "phonon_band", + # "phonon_dos", + # "piezoelectric_tensor", + # "polarization", + # "potential", + "projector", + # "stress", + "structure", + # "system", + # "velocity", + # "workfunction", + "_dispersion", + "_topology", +) class Calculation: @@ -123,13 +199,15 @@ def POSCAR(self, poscar): def _add_all_refinement_classes(calc, add_single_class): - for name in calculation._quantities: + for name in QUANTITIES: calc = add_single_class(calc, name) return calc def _add_attribute_from_path(calc, name): - class_ = getattr(calculation, name) + class_name = convert.to_camelcase(name) + module = importlib.import_module(f"py4vasp._calculation.{name}") + class_ = getattr(module, class_name) instance = class_.from_path(calc.path()) setattr(calc, name, instance) return calc @@ -140,7 +218,9 @@ def __init__(self, file_name): self._file_name = file_name def __call__(self, calc, name): - class_ = getattr(calculation, name) + class_name = convert.to_camelcase(name) + module = importlib.import_module(f"py4vasp._calculation.{name}") + class_ = getattr(module, class_name) instance = class_.from_file(self._file_name) setattr(calc, name, instance) return calc @@ -150,12 +230,19 @@ def _add_to_documentation(calc, name): calc.__doc__ += f" ~py4vasp.calculation.{name}\n " return calc - -Calculation = _add_all_refinement_classes(Calculation, _add_to_documentation) - - def _add_input_files(calc): - for name in calculation._input_files: + for name in INPUT_FILES: file_ = getattr(control, name)(calc.path()) setattr(calc, f"_{name}", file_) return calc + +Calculation = _add_all_refinement_classes(Calculation, _add_to_documentation) + +class DefaultCalculationFactory: + def __getattr__(self, attr): + calc = Calculation.from_path(".") + return getattr(calc, attr) + def __setattr__(self, attr, value): + calc = Calculation.from_path(".") + return setattr(calc, attr, value) +calculation = DefaultCalculationFactory() diff --git a/src/py4vasp/calculation/_density.py b/src/py4vasp/_calculation/_density.py similarity index 100% rename from src/py4vasp/calculation/_density.py rename to src/py4vasp/_calculation/_density.py diff --git a/src/py4vasp/calculation/_dielectric_function.py b/src/py4vasp/_calculation/_dielectric_function.py similarity index 100% rename from src/py4vasp/calculation/_dielectric_function.py rename to src/py4vasp/_calculation/_dielectric_function.py diff --git a/src/py4vasp/calculation/_dielectric_tensor.py b/src/py4vasp/_calculation/_dielectric_tensor.py similarity index 100% rename from src/py4vasp/calculation/_dielectric_tensor.py rename to src/py4vasp/_calculation/_dielectric_tensor.py diff --git a/src/py4vasp/calculation/_dispersion.py b/src/py4vasp/_calculation/_dispersion.py similarity index 96% rename from src/py4vasp/calculation/_dispersion.py rename to src/py4vasp/_calculation/_dispersion.py index e2025f17..375ac7ab 100644 --- a/src/py4vasp/calculation/_dispersion.py +++ b/src/py4vasp/_calculation/_dispersion.py @@ -4,22 +4,22 @@ import py4vasp._third_party.graph as _graph from py4vasp import calculation -from py4vasp.calculation import _base +from py4vasp._calculation import base -class Dispersion(_base.Refinery): +class Dispersion(base.Refinery): """Generic class for all dispersions (electrons, phonons). Provides some utility functionalities common to all dispersions to avoid duplication of code.""" - @_base.data_access + @base.data_access def __str__(self): return f"""band data: {self._kpoints.number_kpoints()} k-points {self._raw_data.eigenvalues.shape[-1]} bands""" - @_base.data_access + @base.data_access def to_dict(self): """Read the dispersion into a dictionary. @@ -39,7 +39,7 @@ def to_dict(self): def _kpoints(self): return calculation.kpoint.from_data(self._raw_data.kpoints) - @_base.data_access + @base.data_access def plot(self, projections=None): """Generate a graph of the dispersion. diff --git a/src/py4vasp/calculation/_dos.py b/src/py4vasp/_calculation/_dos.py similarity index 100% rename from src/py4vasp/calculation/_dos.py rename to src/py4vasp/_calculation/_dos.py diff --git a/src/py4vasp/calculation/_elastic_modulus.py b/src/py4vasp/_calculation/_elastic_modulus.py similarity index 100% rename from src/py4vasp/calculation/_elastic_modulus.py rename to src/py4vasp/_calculation/_elastic_modulus.py diff --git a/src/py4vasp/calculation/_energy.py b/src/py4vasp/_calculation/_energy.py similarity index 100% rename from src/py4vasp/calculation/_energy.py rename to src/py4vasp/_calculation/_energy.py diff --git a/src/py4vasp/calculation/_fatband.py b/src/py4vasp/_calculation/_fatband.py similarity index 100% rename from src/py4vasp/calculation/_fatband.py rename to src/py4vasp/_calculation/_fatband.py diff --git a/src/py4vasp/calculation/_force.py b/src/py4vasp/_calculation/_force.py similarity index 100% rename from src/py4vasp/calculation/_force.py rename to src/py4vasp/_calculation/_force.py diff --git a/src/py4vasp/calculation/_force_constant.py b/src/py4vasp/_calculation/_force_constant.py similarity index 100% rename from src/py4vasp/calculation/_force_constant.py rename to src/py4vasp/_calculation/_force_constant.py diff --git a/src/py4vasp/calculation/_internal_strain.py b/src/py4vasp/_calculation/_internal_strain.py similarity index 100% rename from src/py4vasp/calculation/_internal_strain.py rename to src/py4vasp/_calculation/_internal_strain.py diff --git a/src/py4vasp/calculation/_magnetism.py b/src/py4vasp/_calculation/_magnetism.py similarity index 100% rename from src/py4vasp/calculation/_magnetism.py rename to src/py4vasp/_calculation/_magnetism.py diff --git a/src/py4vasp/calculation/_pair_correlation.py b/src/py4vasp/_calculation/_pair_correlation.py similarity index 100% rename from src/py4vasp/calculation/_pair_correlation.py rename to src/py4vasp/_calculation/_pair_correlation.py diff --git a/src/py4vasp/calculation/_partial_charge.py b/src/py4vasp/_calculation/_partial_charge.py similarity index 100% rename from src/py4vasp/calculation/_partial_charge.py rename to src/py4vasp/_calculation/_partial_charge.py diff --git a/src/py4vasp/calculation/_phonon.py b/src/py4vasp/_calculation/_phonon.py similarity index 100% rename from src/py4vasp/calculation/_phonon.py rename to src/py4vasp/_calculation/_phonon.py diff --git a/src/py4vasp/calculation/_phonon_band.py b/src/py4vasp/_calculation/_phonon_band.py similarity index 100% rename from src/py4vasp/calculation/_phonon_band.py rename to src/py4vasp/_calculation/_phonon_band.py diff --git a/src/py4vasp/calculation/_phonon_dos.py b/src/py4vasp/_calculation/_phonon_dos.py similarity index 100% rename from src/py4vasp/calculation/_phonon_dos.py rename to src/py4vasp/_calculation/_phonon_dos.py diff --git a/src/py4vasp/calculation/_piezoelectric_tensor.py b/src/py4vasp/_calculation/_piezoelectric_tensor.py similarity index 100% rename from src/py4vasp/calculation/_piezoelectric_tensor.py rename to src/py4vasp/_calculation/_piezoelectric_tensor.py diff --git a/src/py4vasp/calculation/_polarization.py b/src/py4vasp/_calculation/_polarization.py similarity index 100% rename from src/py4vasp/calculation/_polarization.py rename to src/py4vasp/_calculation/_polarization.py diff --git a/src/py4vasp/calculation/_potential.py b/src/py4vasp/_calculation/_potential.py similarity index 100% rename from src/py4vasp/calculation/_potential.py rename to src/py4vasp/_calculation/_potential.py diff --git a/src/py4vasp/calculation/_stress.py b/src/py4vasp/_calculation/_stress.py similarity index 100% rename from src/py4vasp/calculation/_stress.py rename to src/py4vasp/_calculation/_stress.py diff --git a/src/py4vasp/calculation/_system.py b/src/py4vasp/_calculation/_system.py similarity index 100% rename from src/py4vasp/calculation/_system.py rename to src/py4vasp/_calculation/_system.py diff --git a/src/py4vasp/calculation/_topology.py b/src/py4vasp/_calculation/_topology.py similarity index 94% rename from src/py4vasp/calculation/_topology.py rename to src/py4vasp/_calculation/_topology.py index 35612096..0c37193e 100644 --- a/src/py4vasp/calculation/_topology.py +++ b/src/py4vasp/_calculation/_topology.py @@ -6,8 +6,8 @@ from py4vasp import raw from py4vasp._util import check, convert, import_, select -from py4vasp.calculation import _base -from py4vasp.calculation._selection import Selection +from py4vasp._calculation import base +from py4vasp._calculation.selection import Selection mdtraj = import_.optional("mdtraj") pd = import_.optional("pandas") @@ -15,7 +15,7 @@ _subscript = "_" -class Topology(_base.Refinery): +class Topology(base.Refinery): """The topology of the crystal describes the ions of a crystal and their connectivity. At the current stage, this class only exposes the name of the atoms in the unit @@ -31,17 +31,17 @@ def from_ase(cls, structure): """Generate a Topology from the given ase Atoms object.""" return cls.from_data(raw_topology_from_ase(structure)) - @_base.data_access + @base.data_access def __str__(self): number_suffix = lambda number: str(number) if number > 1 else "" return self._create_repr(number_suffix) - @_base.data_access + @base.data_access def _repr_html_(self): number_suffix = lambda number: f"{number}" if number > 1 else "" return self._create_repr(number_suffix) - @_base.data_access + @base.data_access def to_dict(self): """Read the topology and convert it to a dictionary. @@ -57,7 +57,7 @@ def to_dict(self): """ return {**self._default_selection(), **self._specific_selection()} - @_base.data_access + @base.data_access def to_frame(self): """Convert the topology to a DataFrame @@ -68,7 +68,7 @@ def to_frame(self): """ return pd.DataFrame({"name": self.names(), "element": self.elements()}) - @_base.data_access + @base.data_access def to_mdtraj(self): """Convert the topology to a mdtraj.Topology.""" df = self.to_frame() @@ -78,7 +78,7 @@ def to_mdtraj(self): df["chainID"] = 0 return mdtraj.Topology.from_dataframe(df) - @_base.data_access + @base.data_access def to_POSCAR(self, format_newline=""): """Generate the topology lines for the POSCAR file. @@ -100,24 +100,24 @@ def to_POSCAR(self, format_newline=""): number_ion_types = " ".join(str(x) for x in self._raw_data.number_ion_types) return ion_types + format_newline + "\n" + number_ion_types - @_base.data_access + @base.data_access def names(self): """Extract the labels of all atoms.""" atom_dict = self.to_dict() return [val.label for val in atom_dict.values() if _subscript in val.label] - @_base.data_access + @base.data_access def elements(self): """Extract the element of all atoms.""" repeated_types = (itertools.repeat(*x) for x in self._type_numbers()) return list(itertools.chain.from_iterable(repeated_types)) - @_base.data_access + @base.data_access def ion_types(self): "Return the type of all ions in the system as string." return list(dict.fromkeys(self._ion_types)) - @_base.data_access + @base.data_access def number_atoms(self): "Return the number of atoms in the system." return np.sum(self._raw_data.number_ion_types) diff --git a/src/py4vasp/calculation/_velocity.py b/src/py4vasp/_calculation/_velocity.py similarity index 100% rename from src/py4vasp/calculation/_velocity.py rename to src/py4vasp/_calculation/_velocity.py diff --git a/src/py4vasp/calculation/_workfunction.py b/src/py4vasp/_calculation/_workfunction.py similarity index 100% rename from src/py4vasp/calculation/_workfunction.py rename to src/py4vasp/_calculation/_workfunction.py diff --git a/src/py4vasp/calculation/_band.py b/src/py4vasp/_calculation/band.py similarity index 87% rename from src/py4vasp/calculation/_band.py rename to src/py4vasp/_calculation/band.py index 4e18d705..a9e375ec 100644 --- a/src/py4vasp/calculation/_band.py +++ b/src/py4vasp/_calculation/band.py @@ -5,13 +5,13 @@ from py4vasp import calculation from py4vasp._third_party import graph from py4vasp._util import check, documentation, import_ -from py4vasp.calculation import _base, _projector +from py4vasp._calculation import base, projector pd = import_.optional("pandas") pretty = import_.optional("IPython.lib.pretty") -class Band(_base.Refinery, graph.Mixin): +class Band(base.Refinery, graph.Mixin): """The band structure contains the **k** point resolved eigenvalues. The most common use case of this class is to produce the electronic band @@ -21,19 +21,19 @@ class Band(_base.Refinery, graph.Mixin): **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: {self._raw_data.dispersion.eigenvalues.shape[1]} k-points {self._raw_data.dispersion.eigenvalues.shape[2]} bands -{pretty.pretty(self._projector)} +{pretty.pretty(self._projector())} """.strip() - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("band", "to_dict"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("band", "to_dict"), ) def to_dict(self, selection=None): """Read the data into a dictionary. @@ -52,7 +52,7 @@ def to_dict(self, selection=None): {examples} """ - dispersion = self._dispersion.read() + dispersion = self._dispersion().read() return { "kpoint_distances": dispersion["kpoint_distances"], "kpoint_labels": dispersion["kpoint_labels"], @@ -62,10 +62,10 @@ def to_dict(self, selection=None): "projections": self._read_projections(selection), } - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("band", "to_graph"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("band", "to_graph"), ) def to_graph(self, selection=None, width=0.5): """Read the data and generate a graph. @@ -86,15 +86,15 @@ def to_graph(self, selection=None, width=0.5): {examples} """ projections = self._projections(selection, width) - graph = self._dispersion.plot(projections) + graph = self._dispersion().plot(projections) graph = self._shift_series_by_fermi_energy(graph) graph.ylabel = "Energy (eV)" return graph - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("band", "to_frame"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("band", "to_frame"), ) def to_frame(self, selection=None): """Read the data into a DataFrame. @@ -119,11 +119,9 @@ def to_frame(self, selection=None): def _spin_polarized(self): return len(self._raw_data.dispersion.eigenvalues) == 2 - @property def _dispersion(self): - return calculation.dispersion.from_data(self._raw_data.dispersion) + return calculation._dispersion.from_data(self._raw_data.dispersion) - @property def _projector(self): return calculation.projector.from_data(self._raw_data.projectors) @@ -138,7 +136,7 @@ def _projections(self, selection, width): } def _read_projections(self, selection): - return self._projector.project(selection, self._raw_data.projections) + return self._projector().project(selection, self._raw_data.projections) def _read_occupations(self): if self._spin_polarized(): diff --git a/src/py4vasp/calculation/_bandgap.py b/src/py4vasp/_calculation/bandgap.py similarity index 93% rename from src/py4vasp/calculation/_bandgap.py rename to src/py4vasp/_calculation/bandgap.py index 52a0d524..6b1bc72d 100644 --- a/src/py4vasp/calculation/_bandgap.py +++ b/src/py4vasp/_calculation/bandgap.py @@ -8,7 +8,7 @@ from py4vasp import exception from py4vasp._third_party import graph from py4vasp._util import convert, documentation, select -from py4vasp.calculation import _base, _slice +from py4vasp._calculation import base, slice_ class Gap(typing.NamedTuple): @@ -24,8 +24,8 @@ class Gap(typing.NamedTuple): COMPONENTS = ("independent", "up", "down") -@documentation.format(examples=_slice.examples("bandgap")) -class Bandgap(_slice.Mixin, _base.Refinery, graph.Mixin): +@documentation.format(examples=slice_.examples("bandgap")) +class Bandgap(slice_.Mixin, base.Refinery, graph.Mixin): """This class describes the band extrema during the relaxation or MD simulation. The bandgap represents the energy difference between the highest energy electrons @@ -44,7 +44,7 @@ class Bandgap(_slice.Mixin, _base.Refinery, graph.Mixin): {examples} """ - @_base.data_access + @base.data_access def __str__(self): template = """\ Band structure @@ -95,8 +95,8 @@ def _output_kpoint(self, label): to_string = lambda kpoint: " ".join(map("{:8.4f}".format, kpoint)) return " " + " ".join(map(to_string, kpoints)) - @_base.data_access - @documentation.format(examples=_slice.examples("bandgap", "to_dict")) + @base.data_access + @documentation.format(examples=slice_.examples("bandgap", "to_dict")) def to_dict(self): """Read the bandgap data from a VASP relaxation or MD trajectory. @@ -131,8 +131,8 @@ def _kpoint_dict(self, label): def _suffixes(self): return ("", "_up", "_down") if self._spin_polarized() else ("",) - @_base.data_access - @documentation.format(examples=_slice.examples("bandgap", "fundamental")) + @base.data_access + @documentation.format(examples=slice_.examples("bandgap", "fundamental")) def fundamental(self): """Return the fundamental bandgap. @@ -148,8 +148,8 @@ def fundamental(self): """ return self._gap("fundamental", component=0) - @_base.data_access - @documentation.format(examples=_slice.examples("bandgap", "direct")) + @base.data_access + @documentation.format(examples=slice_.examples("bandgap", "direct")) def direct(self): """Return the direct bandgap. @@ -165,8 +165,8 @@ def direct(self): """ return self._gap("direct", component=0) - @_base.data_access - @documentation.format(examples=_slice.examples("bandgap", "valence_band_maximum")) + @base.data_access + @documentation.format(examples=slice_.examples("bandgap", "valence_band_maximum")) def valence_band_maximum(self): """Return the valence band maximum. @@ -179,9 +179,9 @@ def valence_band_maximum(self): """ return self._get(GAPS["fundamental"].bottom, component=0) - @_base.data_access + @base.data_access @documentation.format( - examples=_slice.examples("bandgap", "conduction_band_minimum") + examples=slice_.examples("bandgap", "conduction_band_minimum") ) def conduction_band_minimum(self): """Return the conduction band minimum. @@ -195,8 +195,8 @@ def conduction_band_minimum(self): """ return self._get(GAPS["fundamental"].top, component=0) - @_base.data_access - @documentation.format(examples=_slice.examples("bandgap", "to_graph")) + @base.data_access + @documentation.format(examples=slice_.examples("bandgap", "to_graph")) def to_graph(self, selection="fundamental, direct"): """Plot the direct and fundamental bandgap along the trajectory. diff --git a/src/py4vasp/calculation/_base.py b/src/py4vasp/_calculation/base.py similarity index 100% rename from src/py4vasp/calculation/_base.py rename to src/py4vasp/_calculation/base.py diff --git a/src/py4vasp/calculation/_born_effective_charge.py b/src/py4vasp/_calculation/born_effective_charge.py similarity index 93% rename from src/py4vasp/calculation/_born_effective_charge.py rename to src/py4vasp/_calculation/born_effective_charge.py index c81d4650..2100c76f 100644 --- a/src/py4vasp/calculation/_born_effective_charge.py +++ b/src/py4vasp/_calculation/born_effective_charge.py @@ -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.calculation import _base, _structure +from py4vasp._calculation import base, structure -class BornEffectiveCharge(_base.Refinery, _structure.Mixin): +class BornEffectiveCharge(base.Refinery, structure.Mixin): """The Born effective charge tensors couple electric field and atomic displacement. You can use this class to extract the Born effective charges of a linear @@ -15,7 +15,7 @@ class BornEffectiveCharge(_base.Refinery, _structure.Mixin): piezoelectric and ferroelectric behavior. """ - @_base.data_access + @base.data_access def __str__(self): data = self.to_dict() result = """ @@ -32,7 +32,7 @@ def __str__(self): 3 {vec_to_string(charge_tensor[2])}""" return result - @_base.data_access + @base.data_access def to_dict(self): """Read structure information and Born effective charges into a dictionary. diff --git a/src/py4vasp/calculation/_kpoint.py b/src/py4vasp/_calculation/kpoint.py similarity index 97% rename from src/py4vasp/calculation/_kpoint.py rename to src/py4vasp/_calculation/kpoint.py index ea301810..f8eaae5c 100644 --- a/src/py4vasp/calculation/_kpoint.py +++ b/src/py4vasp/_calculation/kpoint.py @@ -7,7 +7,7 @@ from py4vasp import exception from py4vasp._util import convert, documentation -from py4vasp.calculation import _base +from py4vasp._calculation import base _kpoints_selection = """\ selection : str, optional @@ -16,7 +16,7 @@ """ -class Kpoint(_base.Refinery): +class Kpoint(base.Refinery): """The **k**-point mesh used in the VASP calculation. In VASP calculations, **k** points play an important role in discretizing the @@ -39,7 +39,7 @@ class Kpoint(_base.Refinery): selected **k** point mesh or take subsets along high symmetry lines. """ - @_base.data_access + @base.data_access def __str__(self): text = f"""k-points {len(self._raw_data.coordinates)} @@ -48,7 +48,7 @@ def __str__(self): text += "\n" + f"{kpoint[0]} {kpoint[1]} {kpoint[2]} {weight}" return text - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def to_dict(self): """Read the **k** points data into a dictionary. @@ -75,7 +75,7 @@ def to_dict(self): "labels": self.labels(), } - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def line_length(self): """Get the number of points per line in the Brillouin zone. @@ -93,7 +93,7 @@ def line_length(self): return self._raw_data.number return self.number_kpoints() - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def number_lines(self): """Get the number of lines in the Brillouin zone. @@ -109,7 +109,7 @@ def number_lines(self): """ return self.number_kpoints() // self.line_length() - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def number_kpoints(self): """Get the number of points in the Brillouin zone. @@ -125,7 +125,7 @@ def number_kpoints(self): """ return len(self._raw_data.coordinates) - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def distances(self): """Convert the coordinates of the **k** points into a one dimensional array @@ -154,7 +154,7 @@ def distances(self): ) return functools.reduce(concatenate_distances, kpoint_norms) - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def mode(self): """Get the **k**-point generation mode specified in the Vasp input file @@ -187,7 +187,7 @@ def mode(self): f"Could not understand the mode '{mode}' when refining the raw kpoints data." ) - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def labels(self): """Get any labels given in the input file for specific **k** points. @@ -209,7 +209,7 @@ def labels(self): else: return None - @_base.data_access + @base.data_access @documentation.format(selection=_kpoints_selection) def path_indices(self, start, finish): """Find linear dependent k points between start and finish diff --git a/src/py4vasp/calculation/_projector.py b/src/py4vasp/_calculation/projector.py similarity index 97% rename from src/py4vasp/calculation/_projector.py rename to src/py4vasp/_calculation/projector.py index 48f782f3..2b5032a7 100644 --- a/src/py4vasp/calculation/_projector.py +++ b/src/py4vasp/_calculation/projector.py @@ -6,8 +6,8 @@ from py4vasp import calculation, exception from py4vasp._util import convert, documentation, index, select -from py4vasp.calculation import _base -from py4vasp.calculation._selection import Selection +from py4vasp._calculation import base +from py4vasp._calculation.selection import Selection selection_doc = """\ selection : str @@ -66,7 +66,7 @@ def selection_examples(instance_name, function_name): _select_all = select.all -class Projector(_base.Refinery): +class Projector(base.Refinery): """The projectors used for atom and orbital resolved quantities. This is a utility class that facilitates projecting quantities such as the @@ -79,7 +79,7 @@ class Projector(_base.Refinery): _missing_data_message = "No projectors found, please verify the LORBIT tag is set." - @_base.data_access + @base.data_access def __str__(self): if self._raw_data.orbital_types.is_none(): return "no projectors" @@ -87,7 +87,7 @@ def __str__(self): atoms: {", ".join(self._topology().ion_types())} orbitals: {", ".join(self._orbital_types())}""" - @_base.data_access + @base.data_access def to_dict(self, selection=None, projections=None): """Return a map from labels to indices in the arrays produced by VASP. @@ -115,7 +115,7 @@ def to_dict(self, selection=None, projections=None): warnings.warn(message, DeprecationWarning, stacklevel=2) return self.project(selection, projections) - @_base.data_access + @base.data_access @documentation.format(selection_doc=selection_doc) def project(self, selection, projections): """Select a certain subset of the given projections and return them with a @@ -146,7 +146,7 @@ def project(self, selection, projections): for selection in self._parse_selection(selection) } - @_base.data_access + @base.data_access def selections(self): """Return a dictionary describing what options are available to specify the atom, orbital, and spin.""" @@ -191,7 +191,7 @@ def _raise_error_if_orbitals_missing(self): raise exception.IncorrectUsage(message) def _topology(self): - return calculation.topology.from_data(self._raw_data.topology) + return calculation._topology.from_data(self._raw_data.topology) def _init_dicts(self): if self._raw_data.orbital_types.is_none(): @@ -261,7 +261,7 @@ class Index(NamedTuple): spin: Union[str, Selection] "Label of the spin component or a Selection object to read the corresponding data." - @_base.data_access + @base.data_access @documentation.format(separator=select.range_separator) def select( self, @@ -307,7 +307,7 @@ def select( spin=dicts["spin"][spin], ) - @_base.data_access + @base.data_access @documentation.format(selection_doc=selection_doc) def parse_selection(self, selection=_select_all): """Generate all possible indices where the projected information is stored. diff --git a/src/py4vasp/calculation/_selection.py b/src/py4vasp/_calculation/selection.py similarity index 100% rename from src/py4vasp/calculation/_selection.py rename to src/py4vasp/_calculation/selection.py diff --git a/src/py4vasp/calculation/_slice.py b/src/py4vasp/_calculation/slice_.py similarity index 100% rename from src/py4vasp/calculation/_slice.py rename to src/py4vasp/_calculation/slice_.py diff --git a/src/py4vasp/calculation/_structure.py b/src/py4vasp/_calculation/structure.py similarity index 93% rename from src/py4vasp/calculation/_structure.py rename to src/py4vasp/_calculation/structure.py index 81510c3d..6286fc7d 100644 --- a/src/py4vasp/calculation/_structure.py +++ b/src/py4vasp/_calculation/structure.py @@ -8,7 +8,7 @@ from py4vasp import calculation, exception, raw from py4vasp._third_party import view from py4vasp._util import documentation, import_, reader -from py4vasp.calculation import _base, _slice, _topology +from py4vasp._calculation import base, slice_, _topology ase = import_.optional("ase") ase_io = import_.optional("ase.io") @@ -47,8 +47,8 @@ def _element_to_string(self, element): return f"{element:21.16f}" -@documentation.format(examples=_slice.examples("structure")) -class Structure(_slice.Mixin, _base.Refinery, view.Mixin): +@documentation.format(examples=slice_.examples("structure")) +class Structure(slice_.Mixin, base.Refinery, view.Mixin): """The structure contains the unit cell and the position of all ions within. The crystal structure is the specific arrangement of ions in a three-dimensional @@ -101,12 +101,12 @@ def from_ase(cls, structure): ) return cls.from_data(structure) - @_base.data_access + @base.data_access def __str__(self): "Generate a string representing the final structure usable as a POSCAR file." return self._create_repr() - @_base.data_access + @base.data_access def _repr_html_(self): format_ = _Format( begin_table="\n
", @@ -129,8 +129,8 @@ def _create_repr(self, format_=_Format()): ) return "\n".join(lines) - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "to_dict")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "to_dict")) def to_dict(self): """Read the structural information into a dictionary. @@ -150,8 +150,8 @@ def to_dict(self): "names": self._topology().names(), } - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "to_view")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "to_view")) def to_view(self, supercell=None): """Generate a 3d representation of the structure(s). @@ -176,8 +176,8 @@ def to_view(self, supercell=None): supercell=self._parse_supercell(supercell), ) - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "to_ase")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "to_ase")) def to_ase(self, supercell=None): """Convert the structure to an ase Atoms object. @@ -220,8 +220,8 @@ def to_ase(self, supercell=None): order = sorted(range(num_atoms_super), key=lambda n: n % num_atoms_prim) return structure[order] - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "to_mdtraj")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "to_mdtraj")) def to_mdtraj(self): """Convert the trajectory to mdtraj.Trajectory @@ -243,8 +243,8 @@ def to_mdtraj(self): trajectory.unitcell_vectors = data["lattice_vectors"] * Structure.A_to_nm return trajectory - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "to_POSCAR")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "to_POSCAR")) def to_POSCAR(self): """Convert the structure(s) to a POSCAR format @@ -261,7 +261,7 @@ def to_POSCAR(self): message = "Converting multiple structures to a POSCAR is currently not implemented." raise exception.NotImplemented(message) - @_base.data_access + @base.data_access def lattice_vectors(self): """Return the lattice vectors spanning the unit cell @@ -273,7 +273,7 @@ def lattice_vectors(self): lattice_vectors = _LatticeVectors(self._raw_data.cell.lattice_vectors) return self._scale() * lattice_vectors[self._get_steps()] - @_base.data_access + @base.data_access def positions(self): """Return the direct coordinates of all ions in the unit cell. @@ -287,8 +287,8 @@ def positions(self): """ return self._raw_data.positions[self._get_steps()] - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "cartesian_positions")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "cartesian_positions")) def cartesian_positions(self): """Convert the positions from direct coordinates to cartesian ones. @@ -301,8 +301,8 @@ def cartesian_positions(self): """ return self.positions() @ self.lattice_vectors() - @_base.data_access - @documentation.format(examples=_slice.examples("structure", "volume")) + @base.data_access + @documentation.format(examples=slice_.examples("structure", "volume")) def volume(self): """Return the volume of the unit cell for the selected steps. @@ -315,7 +315,7 @@ def volume(self): """ return np.abs(np.linalg.det(self.lattice_vectors())) - @_base.data_access + @base.data_access def number_atoms(self): """Return the total number of atoms in the structure.""" if self._is_trajectory: @@ -323,7 +323,7 @@ def number_atoms(self): else: return self._raw_data.positions.shape[0] - @_base.data_access + @base.data_access def number_steps(self): """Return the number of structures in the trajectory.""" if self._is_trajectory: @@ -355,7 +355,7 @@ def _parse_supercell(self, supercell): raise exception.IncorrectUsage(message) def _topology(self): - return calculation.topology.from_data(self._raw_data.topology) + return calculation._topology.from_data(self._raw_data.topology) def _scale(self): if isinstance(self._raw_data.cell.scale, np.float_): @@ -380,7 +380,7 @@ def _step_string(self): else: return f" (step {self._steps + 1})" - @_base.data_access + @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/src/py4vasp/_combine/base.py b/src/py4vasp/_combine/base.py index 3672a4b5..cabba39a 100644 --- a/src/py4vasp/_combine/base.py +++ b/src/py4vasp/_combine/base.py @@ -4,7 +4,8 @@ import pathlib from typing import Dict, List -from py4vasp import calculation, exception +import py4vasp +from py4vasp import exception def _match_combine_with_refinement(combine_name: str): @@ -13,7 +14,7 @@ def _match_combine_with_refinement(combine_name: str): "Forces": "force", "Stresses": "stress", } - return getattr(calculation, combine_to_refinement_name[combine_name]) + return getattr(py4vasp.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_ diff --git a/src/py4vasp/_control/poscar.py b/src/py4vasp/_control/poscar.py index 3816925b..c0ccfdab 100644 --- a/src/py4vasp/_control/poscar.py +++ b/src/py4vasp/_control/poscar.py @@ -1,6 +1,6 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -from py4vasp import calculation +import py4vasp from py4vasp._control import base from py4vasp._third_party import view @@ -36,5 +36,5 @@ def to_view(self, supercell=None, *, elements=None): View Visualize the structure as a 3d figure. """ - structure = calculation.structure.from_POSCAR(self, elements=elements) + structure = py4vasp.calculation.structure.from_POSCAR(self, elements=elements) return structure.plot(supercell) diff --git a/src/py4vasp/_util/convert.py b/src/py4vasp/_util/convert.py index c97f2944..72e37979 100644 --- a/src/py4vasp/_util/convert.py +++ b/src/py4vasp/_util/convert.py @@ -46,11 +46,10 @@ def _to_snakecase(word: str) -> str: return word.lower() -# NOTE: to_camelcase is the function camelize from the inflection package +# NOTE: to_camelcase is based on 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. + """Convert strings to CamelCase. Examples:: @@ -70,7 +69,7 @@ def to_camelcase(string: str, uppercase_first_letter: bool = True) -> str: lowerCamelCase. Defaults to `True`. """ if uppercase_first_letter: - return re.sub(r"(?:^|_)(.)", lambda m: m.group(1).upper(), string) + return re.sub(r"(?:_|^)(.)", lambda m: m.group(1).upper(), string) else: return string[0].lower() + camelize(string)[1:] diff --git a/src/py4vasp/calculation/__init__.py b/src/py4vasp/calculation/__init__.py deleted file mode 100644 index 2e6857ce..00000000 --- a/src/py4vasp/calculation/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright © VASP Software GmbH, -# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -"""Provide refinement functions for a the raw data of a VASP calculation run in the -current directory. - -Usually one is not directly interested in the raw data that is produced but -wants to produce either a figure for a publication or some post-processing of -the data. This package contains multiple modules that enable these kinds of -workflows by extracting the relevant data from the HDF5 file and transforming -them into an accessible format. The modules also provide plotting functionality -to get a quick insight about the data, which can then be refined either within -python or a different tool to obtain publication-quality figures. - -Generally, all modules provide a `read` function that extracts the data from the -HDF5 file and puts it into a Python dictionary. Where it makes sense in addition -a `plot` function is available that converts the data into a figure for Jupyter -notebooks. In addition, data conversion routines `to_X` may be available -transforming the data into another format or file, which may be useful to -generate plots with tools other than Python. For the specifics, please refer to -the documentation of the individual modules. - -The raw data is read from the current directory. The :class:`~py4vasp.Calculation` -class provides a more flexible interface with which you can determine the source -directory or file for the VASP calculation manually. That class exposes functions -of the modules as methods of attributes, i.e., the two following examples are -equivalent: - -.. rubric:: using :mod:`~py4vasp.calculation` module - ->>> from py4vasp import calculation ->>> calculation.dos.read() - -.. rubric:: using :class:`~py4vasp.Calculation` class - ->>> from py4vasp import Calculation ->>> calc = Calculation.from_path(".") ->>> calc.dos.read() - -In the latter example, you can change the path from which the data is extracted. -""" -import importlib -import pathlib - -from py4vasp import control, exception -from py4vasp._util import convert - -_input_files = ("INCAR", "KPOINTS", "POSCAR") -_quantities = ( - "band", - "bandgap", - "born_effective_charge", - "CONTCAR", - "density", - "dielectric_function", - "dielectric_tensor", - "dos", - "elastic_modulus", - "energy", - "fatband", - "force", - "force_constant", - "internal_strain", - "kpoint", - "magnetism", - "OSZICAR", - "pair_correlation", - "partial_charge", - "phonon_band", - "phonon_dos", - "piezoelectric_tensor", - "polarization", - "potential", - "projector", - "stress", - "structure", - "system", - "topology", - "velocity", - "workfunction", -) -_private = ("dispersion",) -__all__ = _quantities + _input_files - - -path = pathlib.Path(".") - - -def __getattr__(attr): - if attr in (_quantities + _private): - module = importlib.import_module(f"py4vasp.calculation._{attr}") - class_ = getattr(module, convert.to_camelcase(attr)) - return class_.from_path(".") - elif attr in (_input_files): - class_ = getattr(control, attr) - return class_(".") - else: - message = f"Could not find {attr} in the possible attributes, please check the spelling" - raise exception.MissingAttribute(message) diff --git a/src/py4vasp/exception.py b/src/py4vasp/exception.py index d0800864..437d5ae6 100644 --- a/src/py4vasp/exception.py +++ b/src/py4vasp/exception.py @@ -36,11 +36,6 @@ class OutdatedVaspVersion(Py4VaspError): used version of Vasp.""" -class MissingAttribute(Py4VaspError, AttributeError): - """Exception raised when py4vasp attribute of Calculation, Batch, ... is used - that does not exist""" - - class ModuleNotInstalled(Py4VaspError): """Exception raised when a functionality is used that relies on an optional dependency of py4vasp but that dependency is not installed.""" diff --git a/tests/calculation/test_band.py b/tests/calculation/test_band.py index 490656bb..14ecfe9e 100644 --- a/tests/calculation/test_band.py +++ b/tests/calculation/test_band.py @@ -293,7 +293,7 @@ def test_plot_incorrect_width(with_projectors): with_projectors.plot("Sr", width="not a number") -@patch("py4vasp.calculation._band.Band.to_graph") +@patch("py4vasp._calculation.band.Band.to_graph") def test_to_plotly(mock_plot, single_band): fig = single_band.to_plotly("selection", width=0.2) mock_plot.assert_called_once_with("selection", width=0.2) @@ -309,7 +309,7 @@ def test_to_image(single_band): def check_to_image(single_band, filename_argument, expected_filename): - with patch("py4vasp.calculation._band.Band.to_plotly") as plot: + with patch("py4vasp._calculation.band.Band.to_plotly") as plot: single_band.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_bandgap.py b/tests/calculation/test_bandgap.py index 91b27968..9ca30436 100644 --- a/tests/calculation/test_bandgap.py +++ b/tests/calculation/test_bandgap.py @@ -164,8 +164,8 @@ def test_plot_incorrect_selection(bandgap, selection): bandgap.plot(selection) -@patch("py4vasp.calculation._bandgap.Bandgap.to_graph") -def test_energy_to_plotly(mock_plot, bandgap): +@patch("py4vasp._calculation.bandgap.Bandgap.to_graph") +def test_bandgap_to_plotly(mock_plot, bandgap): fig = bandgap.to_plotly() mock_plot.assert_called_once_with() graph = mock_plot.return_value @@ -180,7 +180,7 @@ def test_to_image(bandgap): def check_to_image(bandgap, filename_argument, expected_filename): - with patch("py4vasp.calculation._bandgap.Bandgap.to_plotly") as plot: + with patch("py4vasp._calculation.bandgap.Bandgap.to_plotly") as plot: bandgap.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_base.py b/tests/calculation/test_base.py index 543cf3af..e0e19bbd 100644 --- a/tests/calculation/test_base.py +++ b/tests/calculation/test_base.py @@ -12,7 +12,7 @@ from py4vasp import exception, raw from py4vasp._util import select -from py4vasp.calculation import _base +from py4vasp._calculation import base from .conftest import SELECTION @@ -39,40 +39,40 @@ def mock_behavior(quantity, *, selection=None, path=None, file=None): yield access -class Example(_base.Refinery): +class Example(base.Refinery): def __post_init__(self): self.post_init_called = True - @_base.data_access + @base.data_access def to_dict(self): "to_dict documentation." return self._raw_data.content - @_base.data_access + @base.data_access def wrapper(self): return self.read() - @_base.data_access + @base.data_access def with_arguments(self, mandatory, optional=None): return mandatory, optional - @_base.data_access + @base.data_access def with_variadic_arguments(self, *args, **kwargs): return args, kwargs - @_base.data_access + @base.data_access def with_selection_argument(self, selection=DEFAULT_SELECTION): return self._raw_data.selection, selection - @_base.data_access + @base.data_access def selection_without_default(self, selection): return selection - @_base.data_access + @base.data_access def selection_from_property(self): return self._selection - @_base.data_access + @base.data_access def __str__(self): return self._raw_data.content @@ -255,8 +255,8 @@ def check_mock(example, mock, *args, **kwargs): mock.reset_mock() -class CamelCase(_base.Refinery): - @_base.data_access +class CamelCase(base.Refinery): + @base.data_access def to_dict(self): return "convert CamelCase to snake_case" diff --git a/tests/calculation/test_class.py b/tests/calculation/test_class.py index 501aa89f..2e790981 100644 --- a/tests/calculation/test_class.py +++ b/tests/calculation/test_class.py @@ -6,10 +6,10 @@ import pytest -from py4vasp import Calculation, calculation, control, exception +from py4vasp import Calculation, _calculation, control, exception -@patch("py4vasp.calculation._base.Refinery.from_path", autospec=True) +@patch("py4vasp._calculation.base.Refinery.from_path", autospec=True) @patch("py4vasp.raw.access", autospec=True) def test_creation_from_path(mock_access, mock_from_path): # note: in pytest __file__ defaults to absolute path @@ -25,7 +25,7 @@ def test_creation_from_path(mock_access, mock_from_path): mock_from_path.assert_called() -@patch("py4vasp.calculation._base.Refinery.from_file", autospec=True) +@patch("py4vasp._calculation.base.Refinery.from_file", autospec=True) @patch("py4vasp.raw.access", autospec=True) def test_creation_from_file(mock_access, mock_from_file): # note: in pytest __file__ defaults to absolute path @@ -45,7 +45,7 @@ def test_creation_from_file(mock_access, mock_from_file): @patch("py4vasp.raw.access", autospec=True) def test_all_attributes(mock_access): calc = Calculation.from_path("test_path") - for name in calculation.__all__: + for name in _calculation.QUANTITIES + _calculation.INPUT_FILES: assert hasattr(calc, name) mock_access.assert_not_called() mock_access.return_value.__enter__.assert_not_called() diff --git a/tests/calculation/test_module.py b/tests/calculation/test_default_calculation.py similarity index 68% rename from tests/calculation/test_module.py rename to tests/calculation/test_default_calculation.py index 52065114..f575ab76 100644 --- a/tests/calculation/test_module.py +++ b/tests/calculation/test_default_calculation.py @@ -19,6 +19,10 @@ def attribute_included(attr): return True -def test_nonexisting_attribute(): - with pytest.raises(exception.MissingAttribute): - calculation.does_not_exist +def test_assigning_to_input_file(tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + expected ="SYSTEM = demo INCAR file" + calculation.INCAR = expected + with open("INCAR", "r") as file: + actual = file.read() + assert actual == expected diff --git a/tests/calculation/test_projector.py b/tests/calculation/test_projector.py index 4e9baee9..3eac1e86 100644 --- a/tests/calculation/test_projector.py +++ b/tests/calculation/test_projector.py @@ -7,7 +7,7 @@ from py4vasp import calculation, exception from py4vasp._util import select -from py4vasp.calculation._selection import Selection +from py4vasp._calculation.selection import Selection @pytest.fixture diff --git a/tests/calculation/test_slice_mixin.py b/tests/calculation/test_slice_mixin.py index 81402d21..223c319c 100644 --- a/tests/calculation/test_slice_mixin.py +++ b/tests/calculation/test_slice_mixin.py @@ -6,7 +6,7 @@ from py4vasp import exception from py4vasp._util import documentation -from py4vasp.calculation import _slice +from py4vasp._calculation import slice_ class Other: @@ -16,8 +16,8 @@ def __init__(self, *args, **kwargs): self._kwargs = kwargs -@documentation.format(examples=_slice.examples("example")) -class ExampleSlice(_slice.Mixin, Other): +@documentation.format(examples=slice_.examples("example")) +class ExampleSlice(slice_.Mixin, Other): "{examples}" def steps(self): @@ -175,7 +175,7 @@ def test_incorrect_argument(): def test_documentation(single_step, last_step): - reference = _slice.examples("example") + reference = slice_.examples("example") assert inspect.getdoc(single_step) == reference assert inspect.getdoc(last_step) == reference diff --git a/tests/calculation/test_structure.py b/tests/calculation/test_structure.py index 2ee5bbe7..9b7761ea 100644 --- a/tests/calculation/test_structure.py +++ b/tests/calculation/test_structure.py @@ -97,7 +97,7 @@ def make_structure(raw_structure): scale = 1.0 structure.ref.lattice_vectors = scale * raw_structure.cell.lattice_vectors structure.ref.positions = raw_structure.positions - topology = calculation.topology.from_data(raw_structure.topology) + topology = calculation._topology.from_data(raw_structure.topology) structure.ref.elements = topology.elements() return structure diff --git a/tests/calculation/test_topology.py b/tests/calculation/test_topology.py index 9019fa92..3552ec59 100644 --- a/tests/calculation/test_topology.py +++ b/tests/calculation/test_topology.py @@ -4,7 +4,7 @@ from py4vasp import calculation, exception from py4vasp._util import import_, select -from py4vasp.calculation._selection import Selection +from py4vasp._calculation.selection import Selection ase = import_.optional("ase") pd = import_.optional("pandas") @@ -55,7 +55,7 @@ def test_number_atoms(self): def test_from_ase(self, not_core): structure = ase.Atoms("".join(self.elements)) - topology = calculation.topology.from_ase(structure) + topology = calculation._topology.from_ase(structure) assert topology.elements() == self.elements assert str(topology) == str(self.topology) @@ -71,7 +71,7 @@ def unique_elements(self): class TestSr2TiO4(Base): @pytest.fixture(autouse=True) def _setup(self, raw_data): - self.topology = calculation.topology.from_data(raw_data.topology("Sr2TiO4")) + self.topology = calculation._topology.from_data(raw_data.topology("Sr2TiO4")) self.names = ["Sr_1", "Sr_2", "Ti_1", "O_1", "O_2", "O_3", "O_4"] self.elements = 2 * ["Sr"] + ["Ti"] + 4 * ["O"] @@ -101,7 +101,7 @@ class TestCa3AsBr3(Base): @pytest.fixture(autouse=True) def _setup(self, raw_data): raw_topology = raw_data.topology("Ca2AsBr-CaBr2") - self.topology = calculation.topology.from_data(raw_topology) + self.topology = calculation._topology.from_data(raw_topology) self.names = ["Ca_1", "Ca_2", "As_1", "Br_1", "Ca_3", "Br_2", "Br_3"] self.elements = ["Ca", "Ca", "As", "Br", "Ca", "Br", "Br"] @@ -124,4 +124,4 @@ def test_print(self, format_): def test_factory_methods(raw_data, check_factory_methods): data = raw_data.topology("Sr2TiO4") - check_factory_methods(calculation.topology, data) + check_factory_methods(calculation._topology, data) diff --git a/tests/util/test_convert.py b/tests/util/test_convert.py index 390acee5..9a3dcb66 100644 --- a/tests/util/test_convert.py +++ b/tests/util/test_convert.py @@ -3,7 +3,7 @@ import numpy as np from py4vasp._config import VASP_COLORS -from py4vasp._util.convert import text_to_string, to_complex, to_rgb +from py4vasp._util.convert import text_to_string, to_complex, to_rgb, to_camelcase def test_text_to_string(): @@ -47,3 +47,11 @@ def test_hex_to_rgb(Assert): expected = np.array(colors) / 255 actual = np.array([to_rgb(color) for color in VASP_COLORS]) Assert.allclose(expected, actual) + + +def test_camelcase(): + assert to_camelcase("foo") == "Foo" + assert to_camelcase("foo_bar") == "FooBar" + assert to_camelcase("foo_bar_baz") == "FooBarBaz" + assert to_camelcase("_foo") == "Foo" + assert to_camelcase("_foo_bar") == "FooBar" From 3056a240482804249a2fd7b790acc2c5ea5b443f Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Fri, 7 Jun 2024 16:22:01 +0200 Subject: [PATCH 2/6] WIP: remove input files --- src/py4vasp/_calculation/_CONTCAR.py | 12 +-- src/py4vasp/_calculation/__init__.py | 26 +++--- .../_calculation/{_density.py => density.py} | 26 +++--- ...ric_function.py => dielectric_function.py} | 12 +-- ...lectric_tensor.py => dielectric_tensor.py} | 8 +- src/py4vasp/_calculation/{_dos.py => dos.py} | 24 ++--- ..._elastic_modulus.py => elastic_modulus.py} | 8 +- ..._OSZICAR.py => electronic_minimization.py} | 12 +-- .../_calculation/{_energy.py => energy.py} | 22 ++--- .../_calculation/{_fatband.py => fatband.py} | 10 +- .../_calculation/{_force.py => force.py} | 16 ++-- .../{_force_constant.py => force_constant.py} | 8 +- ..._internal_strain.py => internal_strain.py} | 8 +- .../{_magnetism.py => magnetism.py} | 32 +++---- src/py4vasp/_raw/data.py | 31 +++---- src/py4vasp/_raw/definition.py | 16 ++-- tests/calculation/test_contcar.py | 4 +- tests/calculation/test_dielectric_function.py | 4 +- tests/calculation/test_dos.py | 4 +- .../test_electronic_minimization.py | 92 +++++++++++++++++++ tests/calculation/test_energy.py | 4 +- tests/calculation/test_fatband.py | 2 +- tests/calculation/test_oszicar.py | 89 ------------------ tests/conftest.py | 12 +-- 24 files changed, 242 insertions(+), 240 deletions(-) rename src/py4vasp/_calculation/{_density.py => density.py} (97%) rename src/py4vasp/_calculation/{_dielectric_function.py => dielectric_function.py} (97%) rename src/py4vasp/_calculation/{_dielectric_tensor.py => dielectric_tensor.py} (95%) rename src/py4vasp/_calculation/{_dos.py => dos.py} (90%) rename src/py4vasp/_calculation/{_elastic_modulus.py => elastic_modulus.py} (95%) rename src/py4vasp/_calculation/{_OSZICAR.py => electronic_minimization.py} (96%) rename src/py4vasp/_calculation/{_energy.py => energy.py} (94%) rename src/py4vasp/_calculation/{_fatband.py => fatband.py} (92%) rename src/py4vasp/_calculation/{_force.py => force.py} (90%) rename src/py4vasp/_calculation/{_force_constant.py => force_constant.py} (94%) rename src/py4vasp/_calculation/{_internal_strain.py => internal_strain.py} (93%) rename src/py4vasp/_calculation/{_magnetism.py => magnetism.py} (93%) create mode 100644 tests/calculation/test_electronic_minimization.py delete mode 100644 tests/calculation/test_oszicar.py diff --git a/src/py4vasp/_calculation/_CONTCAR.py b/src/py4vasp/_calculation/_CONTCAR.py index e4eaccc1..a7679e72 100644 --- a/src/py4vasp/_calculation/_CONTCAR.py +++ b/src/py4vasp/_calculation/_CONTCAR.py @@ -3,10 +3,10 @@ from py4vasp import calculation from py4vasp._third_party import view from py4vasp._util import convert -from py4vasp.calculation import _base, _structure +from py4vasp._calculation import base, structure -class CONTCAR(_base.Refinery, view.Mixin, _structure.Mixin): +class CONTCAR(base.Refinery, view.Mixin, structure.Mixin): """CONTCAR contains structural restart-data after a relaxation or MD simulation. The CONTCAR contains the final structure of the VASP calculation. It can be used as @@ -14,7 +14,7 @@ class CONTCAR(_base.Refinery, view.Mixin, _structure.Mixin): CONTCAR might contain additional information about the system such as the ion and lattice velocities.""" - @_base.data_access + @base.data_access def to_dict(self): """Extract the structural data and the available additional data to a dictionary. @@ -36,7 +36,7 @@ def _read(self, key): data = getattr(self._raw_data, key) return {key: data[:]} if not data.is_none() else {} - @_base.data_access + @base.data_access def to_view(self, supercell=None): """Generate a visualization of the final structure. @@ -52,7 +52,7 @@ def to_view(self, supercell=None): """ return self._structure.plot(supercell) - @_base.data_access + @base.data_access def __str__(self): return "\n".join(self._line_generator()) @@ -71,7 +71,7 @@ def _line_generator(self): yield from _ion_velocity_lines(self._raw_data.ion_velocities) def _topology(self): - return calculation.topology.from_data(self._raw_data.structure.topology) + return calculation._topology.from_data(self._raw_data.structure.topology) def _cell_lines(cell): diff --git a/src/py4vasp/_calculation/__init__.py b/src/py4vasp/_calculation/__init__.py index c05c4cc8..16130cce 100644 --- a/src/py4vasp/_calculation/__init__.py +++ b/src/py4vasp/_calculation/__init__.py @@ -49,20 +49,19 @@ class provides a more flexible interface with which you can determine the source "band", "bandgap", "born_effective_charge", - # "CONTCAR", - # "density", - # "dielectric_function", - # "dielectric_tensor", - # "dos", - # "elastic_modulus", - # "energy", - # "fatband", - # "force", - # "force_constant", - # "internal_strain", + "density", + "dielectric_function", + "dielectric_tensor", + "dos", + "elastic_modulus", + "electronic_minimization", + "energy", + "fatband", + "force", + "force_constant", + "internal_strain", "kpoint", - # "magnetism", - # "OSZICAR", + "magnetism", # "pair_correlation", # "partial_charge", # "phonon_band", @@ -76,6 +75,7 @@ class provides a more flexible interface with which you can determine the source # "system", # "velocity", # "workfunction", + "_CONTCAR", "_dispersion", "_topology", ) diff --git a/src/py4vasp/_calculation/_density.py b/src/py4vasp/_calculation/density.py similarity index 97% rename from src/py4vasp/_calculation/_density.py rename to src/py4vasp/_calculation/density.py index 19fb5c46..c3993fd0 100644 --- a/src/py4vasp/_calculation/_density.py +++ b/src/py4vasp/_calculation/density.py @@ -5,7 +5,7 @@ from py4vasp import _config, calculation, exception from py4vasp._third_party import graph, view from py4vasp._util import documentation, import_, index, select, slicing -from py4vasp.calculation import _base, _structure +from py4vasp._calculation import base, structure pretty = import_.optional("IPython.lib.pretty") @@ -68,7 +68,7 @@ def _join_with_emphasis(data): return ", ".join(emph_data) -class Density(_base.Refinery, _structure.Mixin, view.Mixin): +class Density(base.Refinery, structure.Mixin, view.Mixin): """This class accesses various densities (charge, magnetization, ...) of VASP. The charge density is one key quantity optimized by VASP. With this class you @@ -79,11 +79,11 @@ class Density(_base.Refinery, _structure.Mixin, view.Mixin): metaGGA calculations. """ - @_base.data_access + @base.data_access def __str__(self): _raise_error_if_no_data(self._raw_data.charge) grid = self._raw_data.charge.shape[1:] - topology = calculation.topology.from_data(self._raw_data.structure.topology) + topology = calculation._topology.from_data(self._raw_data.structure.topology) if self._selection == "tau": name = "Kinetic energy" elif self.is_nonpolarized(): @@ -102,7 +102,7 @@ def __str__(self): component2=_join_with_emphasis(_COMPONENTS[2]), component3=_join_with_emphasis(_COMPONENTS[3]), ) - @_base.data_access + @base.data_access def selections(self): """Returns possible densities VASP can produce along with all available components. @@ -165,7 +165,7 @@ def selections(self): components = [_COMPONENTS[i][_DEFAULT] for i in range(4)] return {**sources, "component": components} - @_base.data_access + @base.data_access def to_dict(self): """Read the density into a dictionary. @@ -198,7 +198,7 @@ def _read_density(self): elif self.is_noncollinear(): yield "magnetization", density[1:] - @_base.data_access + @base.data_access def to_numpy(self): """Convert the density to a numpy array. @@ -213,7 +213,7 @@ def to_numpy(self): """ return np.moveaxis(self._raw_data.charge, 0, -1).T - @_base.data_access + @base.data_access def to_view(self, selection=None, supercell=None, **user_options): """Plot the selected density as a 3d isosurface within the structure. @@ -328,7 +328,7 @@ def _use_symmetric_isosurface(self, component): _raise_is_collinear_error() return component > 0 - @_base.data_access + @base.data_access @documentation.format(plane=_PLANE, common_parameters=_COMMON_PARAMETERS) def to_contour( self, selection=None, *, a=None, b=None, c=None, supercell=None, normal=None @@ -390,7 +390,7 @@ def _contour(self, selector, selection, plane, fraction, supercell): contour.supercell = np.ones(2, dtype=np.int_) * supercell return contour - @_base.data_access + @base.data_access @documentation.format(plane=_PLANE, common_parameters=_COMMON_PARAMETERS) def to_quiver(self, *, a=None, b=None, c=None, supercell=None, normal=None): """Generate a quiver plot of magnetization density. @@ -450,17 +450,17 @@ def _get_cut(self, a, b, c): return "b", b return "c", c - @_base.data_access + @base.data_access def is_nonpolarized(self): "Returns whether the density is not spin polarized." return len(self._raw_data.charge) == 1 - @_base.data_access + @base.data_access def is_collinear(self): "Returns whether the density has a collinear magnetization." return len(self._raw_data.charge) == 2 - @_base.data_access + @base.data_access def is_noncollinear(self): "Returns whether the density has a noncollinear magnetization." return len(self._raw_data.charge) == 4 diff --git a/src/py4vasp/_calculation/_dielectric_function.py b/src/py4vasp/_calculation/dielectric_function.py similarity index 97% rename from src/py4vasp/_calculation/_dielectric_function.py rename to src/py4vasp/_calculation/dielectric_function.py index eab984ce..9f26db88 100644 --- a/src/py4vasp/_calculation/_dielectric_function.py +++ b/src/py4vasp/_calculation/dielectric_function.py @@ -4,10 +4,10 @@ from py4vasp._third_party import graph from py4vasp._util import convert, index, select -from py4vasp.calculation import _base +from py4vasp._calculation import base -class DielectricFunction(_base.Refinery, graph.Mixin): +class DielectricFunction(base.Refinery, graph.Mixin): """The dielectric function describes the material response to an electric field. The dielectric function is a fundamental concept that describes how a material @@ -28,7 +28,7 @@ class provides a common interface to all of them. You can pass a `selection` one of the six components as selection. """ - @_base.data_access + @base.data_access def __str__(self): energies = self._raw_data.energies return f""" @@ -43,7 +43,7 @@ def _components(self): else: return "" - @_base.data_access + @base.data_access def to_dict(self): """Read the data into a dictionary. @@ -69,7 +69,7 @@ def _add_current_current_if_available(self): def _has_current_component(self): return not self._raw_data.current_current.is_none() - @_base.data_access + @base.data_access def to_graph(self, selection=None): """Read the data and generate a figure with the selected directions. @@ -93,7 +93,7 @@ def to_graph(self, selection=None): ylabel="dielectric function ϵ", ) - @_base.data_access + @base.data_access def selections(self): "Returns a dictionary of possible selections for component, direction, and complex value." components = ( diff --git a/src/py4vasp/_calculation/_dielectric_tensor.py b/src/py4vasp/_calculation/dielectric_tensor.py similarity index 95% rename from src/py4vasp/_calculation/_dielectric_tensor.py rename to src/py4vasp/_calculation/dielectric_tensor.py index a4b734dd..e019b6f7 100644 --- a/src/py4vasp/_calculation/_dielectric_tensor.py +++ b/src/py4vasp/_calculation/dielectric_tensor.py @@ -2,10 +2,10 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) from py4vasp import exception from py4vasp._util import convert -from py4vasp.calculation import _base +from py4vasp._calculation import base -class DielectricTensor(_base.Refinery): +class DielectricTensor(base.Refinery): """The dielectric tensor is the static limit of the :attr:`dielectric function`. The dielectric tensor represents how a material's response to an external electric @@ -14,7 +14,7 @@ class DielectricTensor(_base.Refinery): tensor corresponds to the dielectric function along a specific crystallographic axis.""" - @_base.data_access + @base.data_access def to_dict(self): """Read the dielectric tensor into a dictionary. @@ -31,7 +31,7 @@ def to_dict(self): "method": convert.text_to_string(self._raw_data.method), } - @_base.data_access + @base.data_access def __str__(self): data = self.to_dict() return f""" diff --git a/src/py4vasp/_calculation/_dos.py b/src/py4vasp/_calculation/dos.py similarity index 90% rename from src/py4vasp/_calculation/_dos.py rename to src/py4vasp/_calculation/dos.py index 978eb9ea..0762ae6d 100644 --- a/src/py4vasp/_calculation/_dos.py +++ b/src/py4vasp/_calculation/dos.py @@ -3,13 +3,13 @@ from py4vasp import calculation from py4vasp._third_party import graph from py4vasp._util import documentation, import_ -from py4vasp.calculation import _base, _projector +from py4vasp._calculation import base, projector pd = import_.optional("pandas") pretty = import_.optional("IPython.lib.pretty") -class Dos(_base.Refinery, graph.Mixin): +class Dos(base.Refinery, graph.Mixin): """The density of states (DOS) describes the number of states per energy. The DOS quantifies the distribution of electronic states within an energy range @@ -31,7 +31,7 @@ class Dos(_base.Refinery, graph.Mixin): _missing_data_message = "No DOS data found, please verify that LORBIT flag is set." - @_base.data_access + @base.data_access def __str__(self): energies = self._raw_data.energies return f""" @@ -40,10 +40,10 @@ def __str__(self): {pretty.pretty(self._projectors())} """.strip() - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("dos", "to_dict"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("dos", "to_dict"), ) def to_dict(self, selection=None): """Read the data into a dictionary. @@ -67,10 +67,10 @@ def to_dict(self, selection=None): "fermi_energy": self._raw_data.fermi_energy, } - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("dos", "to_graph"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("dos", "to_graph"), ) def to_graph(self, selection=None): """Generate a graph of the selected data reading it from the VASP output. @@ -96,10 +96,10 @@ def to_graph(self, selection=None): ylabel="DOS (1/eV)", ) - @_base.data_access + @base.data_access @documentation.format( - selection_doc=_projector.selection_doc, - examples=_projector.selection_examples("dos", "to_frame"), + selection_doc=projector.selection_doc, + examples=projector.selection_examples("dos", "to_frame"), ) def to_frame(self, selection=None): """Read the data into a pandas DataFrame. diff --git a/src/py4vasp/_calculation/_elastic_modulus.py b/src/py4vasp/_calculation/elastic_modulus.py similarity index 95% rename from src/py4vasp/_calculation/_elastic_modulus.py rename to src/py4vasp/_calculation/elastic_modulus.py index 5d1feab5..e8af9ea2 100644 --- a/src/py4vasp/_calculation/_elastic_modulus.py +++ b/src/py4vasp/_calculation/elastic_modulus.py @@ -2,10 +2,10 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import numpy as np -from py4vasp.calculation import _base +from py4vasp._calculation import base -class ElasticModulus(_base.Refinery): +class ElasticModulus(base.Refinery): """The elastic modulus is the second derivative of the energy with respect to strain. The elastic modulus, also known as the modulus of elasticity, is a measure of a @@ -18,7 +18,7 @@ class ElasticModulus(_base.Refinery): atoms are allowed to relax when the cell is deformed. """ - @_base.data_access + @base.data_access def to_dict(self): """Read the clamped-ion and relaxed-ion elastic modulus into a dictionary. @@ -32,7 +32,7 @@ def to_dict(self): "relaxed_ion": self._raw_data.relaxed_ion[:], } - @_base.data_access + @base.data_access def __str__(self): return f"""Elastic modulus (kBar) Direction XX YY ZZ XY YZ ZX diff --git a/src/py4vasp/_calculation/_OSZICAR.py b/src/py4vasp/_calculation/electronic_minimization.py similarity index 96% rename from src/py4vasp/_calculation/_OSZICAR.py rename to src/py4vasp/_calculation/electronic_minimization.py index 8aa12e91..5d5148f4 100644 --- a/src/py4vasp/_calculation/_OSZICAR.py +++ b/src/py4vasp/_calculation/electronic_minimization.py @@ -5,10 +5,10 @@ from py4vasp import exception, raw from py4vasp._third_party import graph -from py4vasp.calculation import _base, _slice +from py4vasp._calculation import base, slice_ -class OSZICAR(_slice.Mixin, _base.Refinery, graph.Mixin): +class ElectronicMinimization(slice_.Mixin, base.Refinery, graph.Mixin): """Access the convergence data for each electronic step. The OSZICAR file written out by VASP stores information related to convergence. @@ -18,7 +18,7 @@ class OSZICAR(_slice.Mixin, _base.Refinery, graph.Mixin): def _more_than_one_ionic_step(self, data): return any(isinstance(_data, list) for _data in data) == True - @_base.data_access + @base.data_access def __str__(self): format_rep = "{0:g}\t{1:0.12E}\t{2:0.6E}\t{3:0.6E}\t{4:g}\t{5:0.3E}\t{6:0.3E}\n" label_rep = "{}\t\t{}\t\t{}\t\t{}\t\t{}\t{}\t\t{}\n" @@ -44,7 +44,7 @@ def __str__(self): string += format_rep.format(*_data) return string - @_base.data_access + @base.data_access def to_dict(self, selection=None): """Extract convergence data from the HDF5 file and make it available in a dict @@ -79,7 +79,7 @@ def to_dict(self, selection=None): def _from_bytes_to_utf(self, quantity: list): return [_quantity.decode("utf-8") for _quantity in quantity] - @_base.data_access + @base.data_access def _read(self, key): # data represents all of the electronic steps for all ionic steps data = getattr(self._raw_data, "convergence_data") @@ -121,7 +121,7 @@ def to_graph(self, selection="E"): ylabel=ylabel, ) - @_base.data_access + @base.data_access def is_converged(self): is_elmin_converged = self._raw_data.is_elmin_converged[self._steps] converged = is_elmin_converged == 0 diff --git a/src/py4vasp/_calculation/_energy.py b/src/py4vasp/_calculation/energy.py similarity index 94% rename from src/py4vasp/_calculation/_energy.py rename to src/py4vasp/_calculation/energy.py index 88ca3c47..ddf3300c 100644 --- a/src/py4vasp/_calculation/_energy.py +++ b/src/py4vasp/_calculation/energy.py @@ -4,7 +4,7 @@ from py4vasp._third_party import graph from py4vasp._util import convert, documentation, index, select -from py4vasp.calculation import _base, _slice +from py4vasp._calculation import base, slice_ def _selection_string(default): @@ -31,8 +31,8 @@ def _selection_string(default): } -@documentation.format(examples=_slice.examples("energy")) -class Energy(_slice.Mixin, _base.Refinery, graph.Mixin): +@documentation.format(examples=slice_.examples("energy")) +class Energy(slice_.Mixin, base.Refinery, graph.Mixin): """The energy data for one or several steps of a relaxation or MD simulation. You can use this class to inspect how the ionic relaxation converges or @@ -49,7 +49,7 @@ class Energy(_slice.Mixin, _base.Refinery, graph.Mixin): {examples} """ - @_base.data_access + @base.data_access def __str__(self): text = f"Energies at {self._step_string()}:" values = self._raw_data.values[self._last_step_in_slice] @@ -69,10 +69,10 @@ def _step_string(self): else: return f"step {self._steps + 1}" - @_base.data_access + @base.data_access @documentation.format( selection=_selection_string("all energies"), - examples=_slice.examples("energy", "to_dict"), + examples=slice_.examples("energy", "to_dict"), ) def to_dict(self, selection=None): """Read the energy data and store it in a dictionary. @@ -101,10 +101,10 @@ def _default_dict(self): for label, value in zip(self._raw_data.labels, raw_values) } - @_base.data_access + @base.data_access @documentation.format( selection=_selection_string("the total energy"), - examples=_slice.examples("energy", "to_graph"), + examples=slice_.examples("energy", "to_graph"), ) def to_graph(self, selection="TOTEN"): """Read the energy data and generate a figure of the selected components. @@ -129,10 +129,10 @@ def to_graph(self, selection="TOTEN"): y2label=yaxes.y2label, ) - @_base.data_access + @base.data_access @documentation.format( selection=_selection_string("the total energy"), - examples=_slice.examples("energy", "to_numpy"), + examples=slice_.examples("energy", "to_numpy"), ) def to_numpy(self, selection="TOTEN"): """Read the energy of the selected steps. @@ -153,7 +153,7 @@ def to_numpy(self, selection="TOTEN"): tree = select.Tree.from_selection(selection) return np.squeeze([values for _, values in self._read_data(tree, self._steps)]) - @_base.data_access + @base.data_access def selections(self): """Returns all possible selections you can use for the other routines. diff --git a/src/py4vasp/_calculation/_fatband.py b/src/py4vasp/_calculation/fatband.py similarity index 92% rename from src/py4vasp/_calculation/_fatband.py rename to src/py4vasp/_calculation/fatband.py index 0417f730..699c5d15 100644 --- a/src/py4vasp/_calculation/_fatband.py +++ b/src/py4vasp/_calculation/fatband.py @@ -2,10 +2,10 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) from py4vasp import calculation from py4vasp._util import convert -from py4vasp.calculation import _base +from py4vasp._calculation import base -class Fatband(_base.Refinery): +class Fatband(base.Refinery): """BSE fatbands illustrate the excitonic properties of materials. The Bethe-Salpeter Equation (BSE) accounts for electron-hole interactions @@ -18,7 +18,7 @@ class Fatband(_base.Refinery): in materials. """ - @_base.data_access + @base.data_access def __str__(self): shape = self._raw_data.bse_index.shape return f"""BSE fatband data: @@ -26,7 +26,7 @@ def __str__(self): {shape[3]} valence bands {shape[2]} conduction bands""" - @_base.data_access + @base.data_access def to_dict(self): """Read the data into a dictionary. @@ -55,4 +55,4 @@ def to_dict(self): @property def _dispersion(self): - return calculation.dispersion.from_data(self._raw_data.dispersion) + return calculation._dispersion.from_data(self._raw_data.dispersion) diff --git a/src/py4vasp/_calculation/_force.py b/src/py4vasp/_calculation/force.py similarity index 90% rename from src/py4vasp/_calculation/_force.py rename to src/py4vasp/_calculation/force.py index 39248c1d..b0351474 100644 --- a/src/py4vasp/_calculation/_force.py +++ b/src/py4vasp/_calculation/force.py @@ -5,11 +5,11 @@ from py4vasp import _config from py4vasp._third_party import view from py4vasp._util import documentation, reader -from py4vasp.calculation import _base, _slice, _structure +from py4vasp._calculation import base, slice_, structure -@documentation.format(examples=_slice.examples("force")) -class Force(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): +@documentation.format(examples=slice_.examples("force")) +class Force(slice_.Mixin, base.Refinery, structure.Mixin, view.Mixin): """The forces determine the path of the atoms in a trajectory. You can use this class to analyze the forces acting on the atoms. The forces @@ -27,7 +27,7 @@ class Force(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): force_rescale = 1.5 "Scaling constant to convert forces to Å." - @_base.data_access + @base.data_access def __str__(self): "Convert the forces to a format similar to the OUTCAR file." result = """ @@ -42,8 +42,8 @@ def __str__(self): result += f"\n{position_to_string(position)} {force_to_string(force)}" return result - @_base.data_access - @documentation.format(examples=_slice.examples("force", "to_dict")) + @base.data_access + @documentation.format(examples=slice_.examples("force", "to_dict")) def to_dict(self): """Read the forces and associated structural information for one or more selected steps of the trajectory. @@ -61,8 +61,8 @@ def to_dict(self): "forces": self._force[self._steps], } - @_base.data_access - @documentation.format(examples=_slice.examples("force", "to_view")) + @base.data_access + @documentation.format(examples=slice_.examples("force", "to_view")) def to_view(self, supercell=None): """Visualize the forces showing arrows at the atoms. diff --git a/src/py4vasp/_calculation/_force_constant.py b/src/py4vasp/_calculation/force_constant.py similarity index 94% rename from src/py4vasp/_calculation/_force_constant.py rename to src/py4vasp/_calculation/force_constant.py index 25096579..3e9e6c7b 100644 --- a/src/py4vasp/_calculation/_force_constant.py +++ b/src/py4vasp/_calculation/force_constant.py @@ -2,10 +2,10 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import itertools -from py4vasp.calculation import _base, _structure +from py4vasp._calculation import base, structure -class ForceConstant(_base.Refinery, _structure.Mixin): +class ForceConstant(base.Refinery, structure.Mixin): """Force constants are the 2nd derivatives of the energy with respect to displacement. Force constants quantify the strength of interactions between atoms in a crystal @@ -18,7 +18,7 @@ class ForceConstant(_base.Refinery, _structure.Mixin): a careful relaxation is required to eliminate the first derivative (i.e. forces). """ - @_base.data_access + @base.data_access def __str__(self): result = """ Force constants (eV/Ų): @@ -35,7 +35,7 @@ def __str__(self): result += f"\n{i + 1:6d} {j + 1:6d} {string_representation}" return result - @_base.data_access + @base.data_access def to_dict(self): """Read structure information and force constants into a dictionary. diff --git a/src/py4vasp/_calculation/_internal_strain.py b/src/py4vasp/_calculation/internal_strain.py similarity index 93% rename from src/py4vasp/_calculation/_internal_strain.py rename to src/py4vasp/_calculation/internal_strain.py index d40cc727..601b10c0 100644 --- a/src/py4vasp/_calculation/_internal_strain.py +++ b/src/py4vasp/_calculation/internal_strain.py @@ -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.calculation import _base, _structure +from py4vasp._calculation import base, structure -class InternalStrain(_base.Refinery, _structure.Mixin): +class InternalStrain(base.Refinery, structure.Mixin): """The internal strain is the derivative of energy with respect to displacement and strain. The internal strain tensor characterizes the deformation within a material at @@ -14,7 +14,7 @@ class InternalStrain(_base.Refinery, _structure.Mixin): with linear response and this class provides access to the resulting data. """ - @_base.data_access + @base.data_access def __str__(self): result = """ Internal strain tensor (eV/Å): @@ -28,7 +28,7 @@ def __str__(self): ion_string = " " return result.strip() - @_base.data_access + @base.data_access def to_dict(self): """Read the internal strain to a dictionary. diff --git a/src/py4vasp/_calculation/_magnetism.py b/src/py4vasp/_calculation/magnetism.py similarity index 93% rename from src/py4vasp/_calculation/_magnetism.py rename to src/py4vasp/_calculation/magnetism.py index 2c2152cf..23007e17 100644 --- a/src/py4vasp/_calculation/_magnetism.py +++ b/src/py4vasp/_calculation/magnetism.py @@ -5,7 +5,7 @@ from py4vasp import _config, exception from py4vasp._third_party import view from py4vasp._util import documentation -from py4vasp.calculation import _base, _slice, _structure +from py4vasp._calculation import base, slice_, structure _index_note = """\ Notes @@ -22,8 +22,8 @@ """ -@documentation.format(examples=_slice.examples("magnetism")) -class Magnetism(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): +@documentation.format(examples=slice_.examples("magnetism")) +class Magnetism(slice_.Mixin, base.Refinery, structure.Mixin, view.Mixin): """The local moments describe the charge and magnetization near an atom. The projection on local moments is particularly relevant in the context of @@ -51,7 +51,7 @@ class Magnetism(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): length_moments = 1.5 "Length in Å how a magnetic moment is displayed relative to the largest moment." - @_base.data_access + @base.data_access def __str__(self): magmom = "MAGMOM = " moments_last_step = self.total_moments() @@ -65,9 +65,9 @@ def __str__(self): generator = (moments_to_string(vec) for vec in moments_last_step) return magmom + separator.join(generator) - @_base.data_access + @base.data_access @documentation.format( - index_note=_index_note, examples=_slice.examples("magnetism", "to_dict") + index_note=_index_note, examples=slice_.examples("magnetism", "to_dict") ) def to_dict(self): """Read the charges and magnetization data into a dictionary. @@ -88,9 +88,9 @@ def to_dict(self): **self._add_spin_and_orbital_moments(), } - @_base.data_access + @base.data_access @documentation.format( - selection=_moment_selection, examples=_slice.examples("magnetism", "to_view") + selection=_moment_selection, examples=slice_.examples("magnetism", "to_view") ) def to_view(self, selection="total", supercell=None): """Visualize the magnetic moments as arrows inside the structure. @@ -121,8 +121,8 @@ def to_view(self, selection="total", supercell=None): viewer.ion_arrows = [ion_arrows] return viewer - @_base.data_access - @documentation.format(examples=_slice.examples("magnetism", "charges")) + @base.data_access + @documentation.format(examples=slice_.examples("magnetism", "charges")) def charges(self): """Read the charges of the selected steps. @@ -136,11 +136,11 @@ def charges(self): self._raise_error_if_steps_out_of_bounds() return self._raw_data.spin_moments[self._steps, 0] - @_base.data_access + @base.data_access @documentation.format( selection=_moment_selection, index_note=_index_note, - examples=_slice.examples("magnetism", "moments"), + examples=slice_.examples("magnetism", "moments"), ) def moments(self, selection="total"): """Read the magnetic moments of the selected steps. @@ -168,8 +168,8 @@ def moments(self, selection="total"): else: return self._noncollinear_moments(selection) - @_base.data_access - @documentation.format(examples=_slice.examples("magnetism", "total_charges")) + @base.data_access + @documentation.format(examples=slice_.examples("magnetism", "total_charges")) def total_charges(self): """Read the total charges of the selected steps. @@ -183,11 +183,11 @@ def total_charges(self): """ return _sum_over_orbitals(self.charges()) - @_base.data_access + @base.data_access @documentation.format( selection=_moment_selection, index_note=_index_note, - examples=_slice.examples("magnetism", "total_moments"), + examples=slice_.examples("magnetism", "total_moments"), ) def total_moments(self, selection="total"): """Read the total magnetic moments of the selected steps. diff --git a/src/py4vasp/_raw/data.py b/src/py4vasp/_raw/data.py index 77d7866c..ea91e751 100644 --- a/src/py4vasp/_raw/data.py +++ b/src/py4vasp/_raw/data.py @@ -200,6 +200,21 @@ class ElasticModulus: "Elastic modulus when the position of the ions is relaxed." +@dataclasses.dataclass +class ElectronicMinimization: + """The OSZICAR data as generated by VASP. + + All data generated by VASP and traditionally stored in the OSZICAR file will be + stored here. See https://www.vasp.at/wiki/index.php/OSZICAR for more details about + what quantities to expect.""" + + convergence_data: VaspData + "All columns of the OSZICAR file stored for all ionic steps." + label: VaspData + "Label of all the data from the OSZICAR file." + is_elmin_converged: VaspData + "Is the electronic minimization step converged?" + @dataclasses.dataclass class Energy: """Various energies during ionic relaxation or MD simulation. @@ -312,22 +327,6 @@ class Magnetism: "Contains the orbital magnetization for all atoms" -@dataclasses.dataclass -class OSZICAR: - """The OSZICAR data as generated by VASP. - - All data generated by VASP and traditionally stored in the OSZICAR file will be - stored here. See https://www.vasp.at/wiki/index.php/OSZICAR for more details about - what quantities to expect.""" - - convergence_data: VaspData - "All columns of the OSZICAR file stored for all ionic steps." - label: VaspData - "Label of all the data from the OSZICAR file." - is_elmin_converged: VaspData - "Is the electronic minimization step converged?" - - @dataclasses.dataclass class PairCorrelation: """The pair-correlation function calculated during a MD simulation. diff --git a/src/py4vasp/_raw/definition.py b/src/py4vasp/_raw/definition.py index 6119fabc..c1269296 100644 --- a/src/py4vasp/_raw/definition.py +++ b/src/py4vasp/_raw/definition.py @@ -285,6 +285,14 @@ def selections(quantity): relaxed_ion=f"{group}/relaxed_ion_elastic_modulus", ) # +schema.add( + raw.ElectronicMinimization, + required=raw.Version(6, 5), + label="intermediate/ion_dynamics/oszicar_label", + convergence_data="intermediate/ion_dynamics/oszicar", + is_elmin_converged="/intermediate/ion_dynamics/electronic_step_converged", +) +# group = "results/linear_response" schema.add( raw.Fatband, @@ -378,14 +386,6 @@ def selections(quantity): orbital_moments="intermediate/ion_dynamics/magnetism/orbital_moments/values", ) # -schema.add( - raw.OSZICAR, - required=raw.Version(6, 5), - label="intermediate/ion_dynamics/oszicar_label", - convergence_data="intermediate/ion_dynamics/oszicar", - is_elmin_converged="/intermediate/ion_dynamics/electronic_step_converged", -) -# group = "intermediate/pair_correlation" schema.add( raw.PairCorrelation, diff --git a/tests/calculation/test_contcar.py b/tests/calculation/test_contcar.py index d3006112..66962d97 100644 --- a/tests/calculation/test_contcar.py +++ b/tests/calculation/test_contcar.py @@ -61,7 +61,7 @@ def CONTCAR(raw_data, request): selection = request.param raw_contcar = raw_data.CONTCAR(selection) - contcar = calculation.CONTCAR.from_data(raw_contcar) + contcar = calculation._CONTCAR.from_data(raw_contcar) contcar.ref = types.SimpleNamespace() structure = calculation.structure.from_data(raw_data.structure(selection))[-1] contcar.ref.structure = structure @@ -116,4 +116,4 @@ def test_print(CONTCAR, format_): def test_factory_methods(raw_data, check_factory_methods): raw_contcar = raw_data.CONTCAR("Sr2TiO4") - check_factory_methods(calculation.CONTCAR, raw_contcar) + check_factory_methods(calculation._CONTCAR, raw_contcar) diff --git a/tests/calculation/test_dielectric_function.py b/tests/calculation/test_dielectric_function.py index 69cd110e..d9dd4a7d 100644 --- a/tests/calculation/test_dielectric_function.py +++ b/tests/calculation/test_dielectric_function.py @@ -299,7 +299,7 @@ def check_figure_contains_plots(fig, references, Assert): assert data.name == ref.name -@patch("py4vasp.calculation._dielectric_function.DielectricFunction.to_graph") +@patch("py4vasp._calculation.dielectric_function.DielectricFunction.to_graph") def test_electronic_to_plotly(mock_plot, electronic): fig = electronic.to_plotly("selection") mock_plot.assert_called_once_with("selection") @@ -322,7 +322,7 @@ def test_ionic_to_image(ionic): def check_to_image(dielectric_function, filename_argument, expected_filename): plot_function = ( - "py4vasp.calculation._dielectric_function.DielectricFunction.to_plotly" + "py4vasp._calculation.dielectric_function.DielectricFunction.to_plotly" ) with patch(plot_function) as plot: dielectric_function.to_image("args", filename=filename_argument, key="word") diff --git a/tests/calculation/test_dos.py b/tests/calculation/test_dos.py index b6eb69a9..9f49fb04 100644 --- a/tests/calculation/test_dos.py +++ b/tests/calculation/test_dos.py @@ -206,7 +206,7 @@ def test_plot_combine_projectors(Fe3O4_projectors, Assert): Assert.allclose(data[names.index("Fe_down - p_down")].y, -subtraction_down) -@patch("py4vasp.calculation._dos.Dos.to_graph") +@patch("py4vasp._calculation.dos.Dos.to_graph") def test_Sr2TiO4_to_plotly(mock_plot, Sr2TiO4): fig = Sr2TiO4.to_plotly("selection") mock_plot.assert_called_once_with("selection") @@ -222,7 +222,7 @@ def test_Sr2TiO4_to_image(Sr2TiO4): def check_to_image(Sr2TiO4, filename_argument, expected_filename): - with patch("py4vasp.calculation._dos.Dos.to_plotly") as plot: + with patch("py4vasp._calculation.dos.Dos.to_plotly") as plot: Sr2TiO4.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_electronic_minimization.py b/tests/calculation/test_electronic_minimization.py new file mode 100644 index 00000000..60579698 --- /dev/null +++ b/tests/calculation/test_electronic_minimization.py @@ -0,0 +1,92 @@ +# Copyright © VASP Software GmbH, +# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + +import types + +import numpy as np +import pytest + +from py4vasp import calculation, exception + + +@pytest.fixture +def electronic_minimization(raw_data): + raw_electronic_minimization = raw_data.electronic_minimization() + constructor = calculation.electronic_minimization.from_data + electronic_minimization = constructor(raw_electronic_minimization) + electronic_minimization.ref = types.SimpleNamespace() + convergence_data = raw_electronic_minimization.convergence_data + electronic_minimization.ref.N = np.int64(convergence_data[:, 0]) + electronic_minimization.ref.E = convergence_data[:, 1] + electronic_minimization.ref.dE = convergence_data[:, 2] + electronic_minimization.ref.deps = convergence_data[:, 3] + electronic_minimization.ref.ncg = convergence_data[:, 4] + electronic_minimization.ref.rms = convergence_data[:, 5] + electronic_minimization.ref.rmsc = convergence_data[:, 6] + is_elmin_converged = [raw_electronic_minimization.is_elmin_converged == [0.0]] + electronic_minimization.ref.is_elmin_converged = is_elmin_converged + string_rep = "N\t\tE\t\tdE\t\tdeps\t\tncg\trms\t\trms(c)\n" + format_rep = "{0:g}\t{1:0.12E}\t{2:0.6E}\t{3:0.6E}\t{4:g}\t{5:0.3E}\t{6:0.3E}\n" + for idx in range(len(convergence_data)): + string_rep += format_rep.format(*convergence_data[idx]) + electronic_minimization.ref.string_rep = str(string_rep) + return electronic_minimization + + +def test_read(electronic_minimization, Assert): + actual = electronic_minimization.read() + expected = electronic_minimization.ref + Assert.allclose(actual["N"], expected.N) + Assert.allclose(actual["E"], expected.E) + Assert.allclose(actual["dE"], expected.dE) + Assert.allclose(actual["deps"], expected.deps) + Assert.allclose(actual["ncg"], expected.ncg) + Assert.allclose(actual["rms"], expected.rms) + Assert.allclose(actual["rms(c)"], expected.rmsc) + + +@pytest.mark.parametrize( + "quantity_name", ["N", "E", "dE", "deps", "ncg", "rms", "rms(c)"] +) +def test_read_selection(quantity_name, electronic_minimization, Assert): + actual = electronic_minimization.read(quantity_name) + name_without_parenthesis = quantity_name.replace("(", "").replace(")", "") + expected = getattr(electronic_minimization.ref, name_without_parenthesis) + Assert.allclose(actual[quantity_name], expected) + + +def test_read_incorrect_selection(electronic_minimization): + with pytest.raises(exception.RefinementError): + electronic_minimization.read("forces") + + +def test_slice(electronic_minimization, Assert): + actual = electronic_minimization[0:1].read() + expected = electronic_minimization.ref + Assert.allclose(actual["N"], expected.N) + Assert.allclose(actual["E"], expected.E) + Assert.allclose(actual["dE"], expected.dE) + Assert.allclose(actual["deps"], expected.deps) + Assert.allclose(actual["ncg"], expected.ncg) + Assert.allclose(actual["rms"], expected.rms) + Assert.allclose(actual["rms(c)"], expected.rmsc) + + +def test_plot(electronic_minimization, Assert): + graph = electronic_minimization.plot() + assert graph.xlabel == "Iteration number" + assert graph.ylabel == "E" + assert len(graph.series) == 1 + Assert.allclose(graph.series[0].x, electronic_minimization.ref.N) + Assert.allclose(graph.series[0].y, electronic_minimization.ref.E) + + +def test_print(electronic_minimization, format_): + actual, _ = format_(electronic_minimization) + assert actual["text/plain"] == electronic_minimization.ref.string_rep + + +def test_is_converged(electronic_minimization): + actual = electronic_minimization.is_converged() + expected = electronic_minimization.ref.is_elmin_converged + assert actual == expected diff --git a/tests/calculation/test_energy.py b/tests/calculation/test_energy.py index b301ec6f..2df79aa3 100644 --- a/tests/calculation/test_energy.py +++ b/tests/calculation/test_energy.py @@ -96,7 +96,7 @@ def test_incorrect_label(MD_energy): MD_energy.plot(number_instead_of_string) -@patch("py4vasp.calculation._energy.Energy.to_graph") +@patch("py4vasp._calculation.energy.Energy.to_graph") def test_energy_to_plotly(mock_plot, MD_energy): fig = MD_energy.to_plotly("selection") mock_plot.assert_called_once_with("selection") @@ -112,7 +112,7 @@ def test_to_image(MD_energy): def check_to_image(MD_energy, filename_argument, expected_filename): - with patch("py4vasp.calculation._energy.Energy.to_plotly") as plot: + with patch("py4vasp._calculation.energy.Energy.to_plotly") as plot: MD_energy.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_fatband.py b/tests/calculation/test_fatband.py index f2a07936..d0f1891c 100644 --- a/tests/calculation/test_fatband.py +++ b/tests/calculation/test_fatband.py @@ -13,7 +13,7 @@ def fatband(raw_data): raw_fatband = raw_data.fatband("default") fatband = calculation.fatband.from_data(raw_fatband) fatband.ref = types.SimpleNamespace() - fatband.ref.dispersion = calculation.dispersion.from_data(raw_fatband.dispersion) + fatband.ref.dispersion = calculation._dispersion.from_data(raw_fatband.dispersion) fatbands = raw_fatband.fatbands fatband.ref.fatbands = fatbands[:, :, 0] + fatbands[:, :, 1] * 1j fatband.ref.fermi_energy = raw_fatband.fermi_energy diff --git a/tests/calculation/test_oszicar.py b/tests/calculation/test_oszicar.py deleted file mode 100644 index 4608b056..00000000 --- a/tests/calculation/test_oszicar.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright © VASP Software GmbH, -# Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - -import types - -import numpy as np -import pytest - -from py4vasp import calculation, exception - - -@pytest.fixture -def OSZICAR(raw_data): - raw_oszicar = raw_data.OSZICAR() - oszicar = calculation.OSZICAR.from_data(raw_oszicar) - oszicar.ref = types.SimpleNamespace() - convergence_data = raw_oszicar.convergence_data - oszicar.ref.N = np.int64(convergence_data[:, 0]) - oszicar.ref.E = convergence_data[:, 1] - oszicar.ref.dE = convergence_data[:, 2] - oszicar.ref.deps = convergence_data[:, 3] - oszicar.ref.ncg = convergence_data[:, 4] - oszicar.ref.rms = convergence_data[:, 5] - oszicar.ref.rmsc = convergence_data[:, 6] - oszicar.ref.is_elmin_converged = [raw_oszicar.is_elmin_converged == [0.0]] - string_rep = "N\t\tE\t\tdE\t\tdeps\t\tncg\trms\t\trms(c)\n" - format_rep = "{0:g}\t{1:0.12E}\t{2:0.6E}\t{3:0.6E}\t{4:g}\t{5:0.3E}\t{6:0.3E}\n" - for idx in range(len(convergence_data)): - string_rep += format_rep.format(*convergence_data[idx]) - oszicar.ref.string_rep = str(string_rep) - return oszicar - - -def test_read(OSZICAR, Assert): - actual = OSZICAR.read() - expected = OSZICAR.ref - Assert.allclose(actual["N"], expected.N) - Assert.allclose(actual["E"], expected.E) - Assert.allclose(actual["dE"], expected.dE) - Assert.allclose(actual["deps"], expected.deps) - Assert.allclose(actual["ncg"], expected.ncg) - Assert.allclose(actual["rms"], expected.rms) - Assert.allclose(actual["rms(c)"], expected.rmsc) - - -@pytest.mark.parametrize( - "quantity_name", ["N", "E", "dE", "deps", "ncg", "rms", "rms(c)"] -) -def test_read_selection(quantity_name, OSZICAR, Assert): - actual = OSZICAR.read(quantity_name) - expected = getattr(OSZICAR.ref, quantity_name.replace("(", "").replace(")", "")) - Assert.allclose(actual[quantity_name], expected) - - -def test_read_incorrect_selection(OSZICAR): - with pytest.raises(exception.RefinementError): - OSZICAR.read("forces") - - -def test_slice(OSZICAR, Assert): - actual = OSZICAR[0:1].read() - expected = OSZICAR.ref - Assert.allclose(actual["N"], expected.N) - Assert.allclose(actual["E"], expected.E) - Assert.allclose(actual["dE"], expected.dE) - Assert.allclose(actual["deps"], expected.deps) - Assert.allclose(actual["ncg"], expected.ncg) - Assert.allclose(actual["rms"], expected.rms) - Assert.allclose(actual["rms(c)"], expected.rmsc) - - -def test_plot(OSZICAR, Assert): - graph = OSZICAR.plot() - assert graph.xlabel == "Iteration number" - assert graph.ylabel == "E" - assert len(graph.series) == 1 - Assert.allclose(graph.series[0].x, OSZICAR.ref.N) - Assert.allclose(graph.series[0].y, OSZICAR.ref.E) - - -def test_print(OSZICAR, format_): - actual, _ = format_(OSZICAR) - assert actual["text/plain"] == OSZICAR.ref.string_rep - - -def test_is_converged(OSZICAR): - actual = OSZICAR.is_converged() - expected = OSZICAR.ref.is_elmin_converged - assert actual == expected diff --git a/tests/conftest.py b/tests/conftest.py index 1458b8bb..334c5af7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -131,10 +131,6 @@ def CONTCAR(selection): else: raise exception.NotImplemented() - @staticmethod - def OSZICAR(selection=None): - return _example_OSZICAR() - @staticmethod def density(selection): parts = selection.split() @@ -190,6 +186,10 @@ def dos(selection): def elastic_modulus(selection): return _elastic_modulus() + @staticmethod + def electronic_minimization(selection=None): + return _electronic_minimization() + @staticmethod def energy(selection, randomize: bool = False): if selection == "MD": @@ -665,7 +665,7 @@ def _Sr2TiO4_cell(): ) -def _example_OSZICAR(): +def _electronic_minimization(): random_convergence_data = np.random.rand(9, 3) iteration_number = np.arange(1, 10)[:, np.newaxis] ncg = np.random.randint(4, 10, (9, 1)) @@ -676,7 +676,7 @@ def _example_OSZICAR(): convergence_data = raw.VaspData(convergence_data) label = raw.VaspData([b"N", b"E", b"dE", b"deps", b"ncg", b"rms", b"rms(c)"]) is_elmin_converged = [0] - return raw.OSZICAR( + return raw.ElectronicMinimization( convergence_data=convergence_data, label=label, is_elmin_converged=is_elmin_converged, From d737e52ca66fded7e6c3222e24da79e97c6e66be Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Mon, 17 Jun 2024 14:32:16 +0200 Subject: [PATCH 3/6] Fix calculations tests --- src/py4vasp/__init__.py | 2 +- src/py4vasp/_calculation/_CONTCAR.py | 2 +- src/py4vasp/_calculation/__init__.py | 28 +-- src/py4vasp/_calculation/_topology.py | 2 +- src/py4vasp/_calculation/band.py | 2 +- src/py4vasp/_calculation/bandgap.py | 2 +- src/py4vasp/_calculation/density.py | 2 +- .../_calculation/dielectric_function.py | 2 +- src/py4vasp/_calculation/dielectric_tensor.py | 2 +- src/py4vasp/_calculation/dos.py | 2 +- .../_calculation/electronic_minimization.py | 2 +- src/py4vasp/_calculation/energy.py | 2 +- src/py4vasp/_calculation/fatband.py | 2 +- src/py4vasp/_calculation/force.py | 2 +- src/py4vasp/_calculation/kpoint.py | 2 +- src/py4vasp/_calculation/magnetism.py | 2 +- ...air_correlation.py => pair_correlation.py} | 16 +- ...{_partial_charge.py => partial_density.py} | 20 +- .../_calculation/{_phonon.py => phonon.py} | 6 +- .../{_phonon_band.py => phonon_band.py} | 14 +- .../{_phonon_dos.py => phonon_dos.py} | 14 +- ...tric_tensor.py => piezoelectric_tensor.py} | 8 +- .../{_polarization.py => polarization.py} | 8 +- .../{_potential.py => potential.py} | 12 +- src/py4vasp/_calculation/projector.py | 2 +- .../_calculation/{_stress.py => stress.py} | 12 +- src/py4vasp/_calculation/structure.py | 2 +- .../_calculation/{_system.py => system.py} | 8 +- .../{_velocity.py => velocity.py} | 16 +- .../{_workfunction.py => workfunction.py} | 23 +-- src/py4vasp/_raw/data.py | 3 +- src/py4vasp/_raw/definition.py | 2 +- tests/calculation/test_base.py | 2 +- tests/calculation/test_default_calculation.py | 2 +- tests/calculation/test_dispersion.py | 4 +- tests/calculation/test_pair_correlation.py | 4 +- ...tial_charge.py => test_partial_density.py} | 176 +++++++++--------- tests/calculation/test_phonon_band.py | 6 +- tests/calculation/test_phonon_dos.py | 4 +- tests/calculation/test_projector.py | 2 +- tests/calculation/test_repr.py | 8 +- tests/calculation/test_slice_mixin.py | 2 +- tests/calculation/test_topology.py | 2 +- tests/calculation/test_workfunction.py | 18 +- tests/conftest.py | 8 +- tests/util/test_convert.py | 2 +- 46 files changed, 236 insertions(+), 228 deletions(-) rename src/py4vasp/_calculation/{_pair_correlation.py => pair_correlation.py} (91%) rename src/py4vasp/_calculation/{_partial_charge.py => partial_density.py} (98%) rename src/py4vasp/_calculation/{_phonon.py => phonon.py} (93%) rename src/py4vasp/_calculation/{_phonon_band.py => phonon_band.py} (91%) rename src/py4vasp/_calculation/{_phonon_dos.py => phonon_dos.py} (91%) rename src/py4vasp/_calculation/{_piezoelectric_tensor.py => piezoelectric_tensor.py} (95%) rename src/py4vasp/_calculation/{_polarization.py => polarization.py} (93%) rename src/py4vasp/_calculation/{_potential.py => potential.py} (96%) rename src/py4vasp/_calculation/{_stress.py => stress.py} (91%) rename src/py4vasp/_calculation/{_system.py => system.py} (79%) rename src/py4vasp/_calculation/{_velocity.py => velocity.py} (89%) rename src/py4vasp/_calculation/{_workfunction.py => workfunction.py} (82%) rename tests/calculation/{test_partial_charge.py => test_partial_density.py} (59%) diff --git a/src/py4vasp/__init__.py b/src/py4vasp/__init__.py index 6bdbeb73..efe013c6 100644 --- a/src/py4vasp/__init__.py +++ b/src/py4vasp/__init__.py @@ -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, 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 import calculation, Calculation __version__ = "0.9.0" set_error_handling("Minimal") diff --git a/src/py4vasp/_calculation/_CONTCAR.py b/src/py4vasp/_calculation/_CONTCAR.py index a7679e72..0da5510a 100644 --- a/src/py4vasp/_calculation/_CONTCAR.py +++ b/src/py4vasp/_calculation/_CONTCAR.py @@ -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 import calculation +from py4vasp._calculation import base, structure from py4vasp._third_party import view from py4vasp._util import convert -from py4vasp._calculation import base, structure class CONTCAR(base.Refinery, view.Mixin, structure.Mixin): diff --git a/src/py4vasp/_calculation/__init__.py b/src/py4vasp/_calculation/__init__.py index 16130cce..10cc7ce1 100644 --- a/src/py4vasp/_calculation/__init__.py +++ b/src/py4vasp/_calculation/__init__.py @@ -62,19 +62,19 @@ class provides a more flexible interface with which you can determine the source "internal_strain", "kpoint", "magnetism", - # "pair_correlation", - # "partial_charge", - # "phonon_band", - # "phonon_dos", - # "piezoelectric_tensor", - # "polarization", - # "potential", + "pair_correlation", + "partial_density", + "phonon_band", + "phonon_dos", + "piezoelectric_tensor", + "polarization", + "potential", "projector", - # "stress", + "stress", "structure", - # "system", - # "velocity", - # "workfunction", + "system", + "velocity", + "workfunction", "_CONTCAR", "_dispersion", "_topology", @@ -230,19 +230,25 @@ def _add_to_documentation(calc, name): calc.__doc__ += f" ~py4vasp.calculation.{name}\n " return calc + def _add_input_files(calc): for name in INPUT_FILES: file_ = getattr(control, name)(calc.path()) setattr(calc, f"_{name}", file_) return calc + Calculation = _add_all_refinement_classes(Calculation, _add_to_documentation) + class DefaultCalculationFactory: def __getattr__(self, attr): calc = Calculation.from_path(".") return getattr(calc, attr) + def __setattr__(self, attr, value): calc = Calculation.from_path(".") return setattr(calc, attr, value) + + calculation = DefaultCalculationFactory() diff --git a/src/py4vasp/_calculation/_topology.py b/src/py4vasp/_calculation/_topology.py index 0c37193e..6ba66af8 100644 --- a/src/py4vasp/_calculation/_topology.py +++ b/src/py4vasp/_calculation/_topology.py @@ -5,9 +5,9 @@ import numpy as np from py4vasp import raw -from py4vasp._util import check, convert, import_, select from py4vasp._calculation import base from py4vasp._calculation.selection import Selection +from py4vasp._util import check, convert, import_, select mdtraj = import_.optional("mdtraj") pd = import_.optional("pandas") diff --git a/src/py4vasp/_calculation/band.py b/src/py4vasp/_calculation/band.py index a9e375ec..70284ce5 100644 --- a/src/py4vasp/_calculation/band.py +++ b/src/py4vasp/_calculation/band.py @@ -3,9 +3,9 @@ import numpy as np from py4vasp import calculation +from py4vasp._calculation import base, projector from py4vasp._third_party import graph from py4vasp._util import check, documentation, import_ -from py4vasp._calculation import base, projector pd = import_.optional("pandas") pretty = import_.optional("IPython.lib.pretty") diff --git a/src/py4vasp/_calculation/bandgap.py b/src/py4vasp/_calculation/bandgap.py index 6b1bc72d..5410ebed 100644 --- a/src/py4vasp/_calculation/bandgap.py +++ b/src/py4vasp/_calculation/bandgap.py @@ -6,9 +6,9 @@ import numpy as np from py4vasp import exception +from py4vasp._calculation import base, slice_ from py4vasp._third_party import graph from py4vasp._util import convert, documentation, select -from py4vasp._calculation import base, slice_ class Gap(typing.NamedTuple): diff --git a/src/py4vasp/_calculation/density.py b/src/py4vasp/_calculation/density.py index c3993fd0..d2eb5274 100644 --- a/src/py4vasp/_calculation/density.py +++ b/src/py4vasp/_calculation/density.py @@ -3,9 +3,9 @@ import numpy as np from py4vasp import _config, calculation, exception +from py4vasp._calculation import base, structure from py4vasp._third_party import graph, view from py4vasp._util import documentation, import_, index, select, slicing -from py4vasp._calculation import base, structure pretty = import_.optional("IPython.lib.pretty") diff --git a/src/py4vasp/_calculation/dielectric_function.py b/src/py4vasp/_calculation/dielectric_function.py index 9f26db88..c232c178 100644 --- a/src/py4vasp/_calculation/dielectric_function.py +++ b/src/py4vasp/_calculation/dielectric_function.py @@ -2,9 +2,9 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import numpy as np +from py4vasp._calculation import base from py4vasp._third_party import graph from py4vasp._util import convert, index, select -from py4vasp._calculation import base class DielectricFunction(base.Refinery, graph.Mixin): diff --git a/src/py4vasp/_calculation/dielectric_tensor.py b/src/py4vasp/_calculation/dielectric_tensor.py index e019b6f7..e841a325 100644 --- a/src/py4vasp/_calculation/dielectric_tensor.py +++ b/src/py4vasp/_calculation/dielectric_tensor.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 import exception -from py4vasp._util import convert from py4vasp._calculation import base +from py4vasp._util import convert class DielectricTensor(base.Refinery): diff --git a/src/py4vasp/_calculation/dos.py b/src/py4vasp/_calculation/dos.py index 0762ae6d..04a3e331 100644 --- a/src/py4vasp/_calculation/dos.py +++ b/src/py4vasp/_calculation/dos.py @@ -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 import calculation +from py4vasp._calculation import base, projector from py4vasp._third_party import graph from py4vasp._util import documentation, import_ -from py4vasp._calculation import base, projector pd = import_.optional("pandas") pretty = import_.optional("IPython.lib.pretty") diff --git a/src/py4vasp/_calculation/electronic_minimization.py b/src/py4vasp/_calculation/electronic_minimization.py index 5d5148f4..d5e2acc8 100644 --- a/src/py4vasp/_calculation/electronic_minimization.py +++ b/src/py4vasp/_calculation/electronic_minimization.py @@ -4,8 +4,8 @@ import numpy as np from py4vasp import exception, raw -from py4vasp._third_party import graph from py4vasp._calculation import base, slice_ +from py4vasp._third_party import graph class ElectronicMinimization(slice_.Mixin, base.Refinery, graph.Mixin): diff --git a/src/py4vasp/_calculation/energy.py b/src/py4vasp/_calculation/energy.py index ddf3300c..fa545d83 100644 --- a/src/py4vasp/_calculation/energy.py +++ b/src/py4vasp/_calculation/energy.py @@ -2,9 +2,9 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import numpy as np +from py4vasp._calculation import base, slice_ from py4vasp._third_party import graph from py4vasp._util import convert, documentation, index, select -from py4vasp._calculation import base, slice_ def _selection_string(default): diff --git a/src/py4vasp/_calculation/fatband.py b/src/py4vasp/_calculation/fatband.py index 699c5d15..ef6dafd0 100644 --- a/src/py4vasp/_calculation/fatband.py +++ b/src/py4vasp/_calculation/fatband.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 import calculation -from py4vasp._util import convert from py4vasp._calculation import base +from py4vasp._util import convert class Fatband(base.Refinery): diff --git a/src/py4vasp/_calculation/force.py b/src/py4vasp/_calculation/force.py index b0351474..283dfd66 100644 --- a/src/py4vasp/_calculation/force.py +++ b/src/py4vasp/_calculation/force.py @@ -3,9 +3,9 @@ import numpy as np from py4vasp import _config +from py4vasp._calculation import base, slice_, structure from py4vasp._third_party import view from py4vasp._util import documentation, reader -from py4vasp._calculation import base, slice_, structure @documentation.format(examples=slice_.examples("force")) diff --git a/src/py4vasp/_calculation/kpoint.py b/src/py4vasp/_calculation/kpoint.py index f8eaae5c..838fd2bf 100644 --- a/src/py4vasp/_calculation/kpoint.py +++ b/src/py4vasp/_calculation/kpoint.py @@ -6,8 +6,8 @@ import numpy as np from py4vasp import exception -from py4vasp._util import convert, documentation from py4vasp._calculation import base +from py4vasp._util import convert, documentation _kpoints_selection = """\ selection : str, optional diff --git a/src/py4vasp/_calculation/magnetism.py b/src/py4vasp/_calculation/magnetism.py index 23007e17..09e5159c 100644 --- a/src/py4vasp/_calculation/magnetism.py +++ b/src/py4vasp/_calculation/magnetism.py @@ -3,9 +3,9 @@ import numpy as np from py4vasp import _config, exception +from py4vasp._calculation import base, slice_, structure from py4vasp._third_party import view from py4vasp._util import documentation -from py4vasp._calculation import base, slice_, structure _index_note = """\ Notes diff --git a/src/py4vasp/_calculation/_pair_correlation.py b/src/py4vasp/_calculation/pair_correlation.py similarity index 91% rename from src/py4vasp/_calculation/_pair_correlation.py rename to src/py4vasp/_calculation/pair_correlation.py index 92fbf296..74dcd4af 100644 --- a/src/py4vasp/_calculation/_pair_correlation.py +++ b/src/py4vasp/_calculation/pair_correlation.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._calculation import base, slice_ from py4vasp._third_party import graph from py4vasp._util import convert, documentation, index, select -from py4vasp.calculation import _base, _slice def _selection_string(default): @@ -19,8 +19,8 @@ def _selection_string(default): """ -@documentation.format(examples=_slice.examples("pair_correlation", step="block")) -class PairCorrelation(_slice.Mixin, _base.Refinery, graph.Mixin): +@documentation.format(examples=slice_.examples("pair_correlation", step="block")) +class PairCorrelation(slice_.Mixin, base.Refinery, graph.Mixin): """The pair-correlation function measures the distribution of atoms. A pair-correlation function is a statistical measure to describe the spatial @@ -40,10 +40,10 @@ class PairCorrelation(_slice.Mixin, _base.Refinery, graph.Mixin): {examples} """ - @_base.data_access + @base.data_access @documentation.format( selection=_selection_string("all possibilities are read"), - examples=_slice.examples("pair_correlation", "to_dict", "block"), + examples=slice_.examples("pair_correlation", "to_dict", "block"), ) def to_dict(self, selection=None): """Read the pair-correlation function and store it in a dictionary. @@ -68,10 +68,10 @@ def to_dict(self, selection=None): **self._read_data(selection), } - @_base.data_access + @base.data_access @documentation.format( selection=_selection_string("the total pair correlation is used"), - examples=_slice.examples("pair_correlation", "to_graph", "block"), + examples=slice_.examples("pair_correlation", "to_graph", "block"), ) def to_graph(self, selection="total"): """Plot selected pair-correlation functions. @@ -92,7 +92,7 @@ def to_graph(self, selection="total"): series = self._make_series(self.to_dict(selection)) return graph.Graph(series, xlabel="Distance (Å)", ylabel="Pair correlation") - @_base.data_access + @base.data_access def labels(self): "Return all possible labels for the selection string." return tuple(convert.text_to_string(label) for label in self._raw_data.labels) diff --git a/src/py4vasp/_calculation/_partial_charge.py b/src/py4vasp/_calculation/partial_density.py similarity index 98% rename from src/py4vasp/_calculation/_partial_charge.py rename to src/py4vasp/_calculation/partial_density.py index 6841a879..28d5872c 100644 --- a/src/py4vasp/_calculation/_partial_charge.py +++ b/src/py4vasp/_calculation/partial_density.py @@ -7,11 +7,11 @@ import numpy as np from py4vasp import exception +from py4vasp._calculation import base, structure from py4vasp._third_party.graph import Graph from py4vasp._third_party.graph.contour import Contour from py4vasp._util import import_, select from py4vasp._util.slicing import plane -from py4vasp.calculation import _base, _structure interpolate = import_.optional("scipy.interpolate") ndimage = import_.optional("scipy.ndimage") @@ -50,7 +50,7 @@ class STM_settings: interpolation_factor: int = 10 -class PartialCharge(_base.Refinery, _structure.Mixin): +class PartialDensity(base.Refinery, structure.Mixin): """Partial charges describe the fraction of the charge density in a certain energy, band, or k-point range. @@ -70,7 +70,7 @@ class PartialCharge(_base.Refinery, _structure.Mixin): def stm_settings(self): return STM_settings() - @_base.data_access + @base.data_access def __str__(self): """Return a string representation of the partial charge density.""" return f""" @@ -80,11 +80,11 @@ def __str__(self): {"summed over all contributing k-points" if 0 in self.kpoints() else f" separated for k-points: {self.kpoints()}"} """.strip() - @_base.data_access + @base.data_access def grid(self): return self._raw_data.grid[:] - @_base.data_access + @base.data_access def to_dict(self): """Store the partial charges in a dictionary. @@ -101,10 +101,10 @@ def to_dict(self): "grid": self.grid(), "bands": self.bands(), "kpoints": self.kpoints(), - "partial_charge": parchg, + "partial_density": parchg, } - @_base.data_access + @base.data_access def to_stm( self, selection: str = "constant_height", @@ -290,7 +290,7 @@ def _out_of_plane_vector(self): def _spin_polarized(self): return self._raw_data.partial_charge.shape[2] == 2 - @_base.data_access + @base.data_access def to_numpy(self, selection="total", band=0, kpoint=0): """Return the partial charge density as a 3D array. @@ -324,7 +324,7 @@ def to_numpy(self, selection="total", band=0, kpoint=0): message = f"Spin '{selection}' not understood. Use 'up', 'down' or 'total'." raise exception.IncorrectUsage(message) - @_base.data_access + @base.data_access def bands(self): """Return the band array listing the contributing bands. @@ -348,7 +348,7 @@ def _check_band_index(self, band): Make sure to set IBAND, EINT, and LSEPB correctly in the INCAR file.""" raise exception.NoData(message) - @_base.data_access + @base.data_access def kpoints(self): """Return the k-points array listing the contributing k-points. diff --git a/src/py4vasp/_calculation/_phonon.py b/src/py4vasp/_calculation/phonon.py similarity index 93% rename from src/py4vasp/_calculation/_phonon.py rename to src/py4vasp/_calculation/phonon.py index c65a13bc..522eb85d 100644 --- a/src/py4vasp/_calculation/_phonon.py +++ b/src/py4vasp/_calculation/phonon.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 import calculation +from py4vasp._calculation import base from py4vasp._util import select -from py4vasp.calculation import _base selection_doc = """\ selection : str @@ -27,7 +27,7 @@ class Mixin: "Provide functionality common to Phonon classes." - @_base.data_access + @base.data_access def selections(self): "Return a dictionary specifying which atoms and directions can be used as selection." atoms = self._init_atom_dict().keys() @@ -37,7 +37,7 @@ def selections(self): } def _topology(self): - return calculation.topology.from_data(self._raw_data.topology) + return calculation._topology.from_data(self._raw_data.topology) def _init_atom_dict(self): return { diff --git a/src/py4vasp/_calculation/_phonon_band.py b/src/py4vasp/_calculation/phonon_band.py similarity index 91% rename from src/py4vasp/_calculation/_phonon_band.py rename to src/py4vasp/_calculation/phonon_band.py index 0f02ce8d..9a5d4017 100644 --- a/src/py4vasp/_calculation/_phonon_band.py +++ b/src/py4vasp/_calculation/phonon_band.py @@ -3,12 +3,12 @@ import numpy as np from py4vasp import calculation +from py4vasp._calculation import base, phonon from py4vasp._third_party import graph from py4vasp._util import convert, documentation, index, select -from py4vasp.calculation import _base, _phonon -class PhononBand(_phonon.Mixin, _base.Refinery, graph.Mixin): +class PhononBand(phonon.Mixin, base.Refinery, graph.Mixin): """The phonon band structure contains the **q**-resolved phonon eigenvalues. The phonon band structure is a graphical representation of the phonons. It @@ -26,14 +26,14 @@ class PhononBand(_phonon.Mixin, _base.Refinery, graph.Mixin): of a structural instability. """ - @_base.data_access + @base.data_access def __str__(self): return f"""phonon band data: {self._raw_data.dispersion.eigenvalues.shape[0]} q-points {self._raw_data.dispersion.eigenvalues.shape[1]} modes {self._topology()}""" - @_base.data_access + @base.data_access def to_dict(self): """Read the phonon band structure into a dictionary. @@ -51,8 +51,8 @@ def to_dict(self): "modes": self._modes(), } - @_base.data_access - @documentation.format(selection=_phonon.selection_doc) + @base.data_access + @documentation.format(selection=phonon.selection_doc) def to_graph(self, selection=None, width=1.0): """Generate a graph of the phonon bands. @@ -75,7 +75,7 @@ def to_graph(self, selection=None, width=1.0): return graph def _dispersion(self): - return calculation.dispersion.from_data(self._raw_data.dispersion) + return calculation._dispersion.from_data(self._raw_data.dispersion) def _modes(self): return convert.to_complex(self._raw_data.eigenvectors[:]) diff --git a/src/py4vasp/_calculation/_phonon_dos.py b/src/py4vasp/_calculation/phonon_dos.py similarity index 91% rename from src/py4vasp/_calculation/_phonon_dos.py rename to src/py4vasp/_calculation/phonon_dos.py index 27f08a0a..289e2a63 100644 --- a/src/py4vasp/_calculation/_phonon_dos.py +++ b/src/py4vasp/_calculation/phonon_dos.py @@ -1,11 +1,11 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +from py4vasp._calculation import base, phonon from py4vasp._third_party import graph from py4vasp._util import documentation, index, select -from py4vasp.calculation import _base, _phonon -class PhononDos(_phonon.Mixin, _base.Refinery, graph.Mixin): +class PhononDos(phonon.Mixin, base.Refinery, graph.Mixin): """The phonon density of states (DOS) describes the number of modes per energy. The phonon density of states (DOS) is a representation of the distribution of @@ -23,7 +23,7 @@ class PhononDos(_phonon.Mixin, _base.Refinery, graph.Mixin): with specific atomic species. """ - @_base.data_access + @base.data_access def __str__(self): energies = self._raw_data.energies topology = self._topology() @@ -32,8 +32,8 @@ def __str__(self): {3 * topology.number_atoms()} modes {topology}""" - @_base.data_access - @documentation.format(selection=_phonon.selection_doc) + @base.data_access + @documentation.format(selection=phonon.selection_doc) def to_dict(self, selection=None): """Read the phonon DOS into a dictionary. @@ -54,8 +54,8 @@ def to_dict(self, selection=None): **self._read_data(selection), } - @_base.data_access - @documentation.format(selection=_phonon.selection_doc) + @base.data_access + @documentation.format(selection=phonon.selection_doc) def to_graph(self, selection=None): """Generate a graph of the selected DOS. diff --git a/src/py4vasp/_calculation/_piezoelectric_tensor.py b/src/py4vasp/_calculation/piezoelectric_tensor.py similarity index 95% rename from src/py4vasp/_calculation/_piezoelectric_tensor.py rename to src/py4vasp/_calculation/piezoelectric_tensor.py index b108c8e4..1513d954 100644 --- a/src/py4vasp/_calculation/_piezoelectric_tensor.py +++ b/src/py4vasp/_calculation/piezoelectric_tensor.py @@ -2,10 +2,10 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import numpy as np -from py4vasp.calculation import _base +from py4vasp._calculation import base -class PiezoelectricTensor(_base.Refinery): +class PiezoelectricTensor(base.Refinery): """The piezoelectric tensor is the derivative of the energy with respect to strain and field. The piezoelectric tensor represents the coupling between mechanical stress and @@ -24,7 +24,7 @@ class PiezoelectricTensor(_base.Refinery): the crystal structure. """ - @_base.data_access + @base.data_access def __str__(self): data = self.to_dict() return f"""Piezoelectric tensor (C/m²) @@ -33,7 +33,7 @@ def __str__(self): {_tensor_to_string(data["clamped_ion"], "clamped-ion")} {_tensor_to_string(data["relaxed_ion"], "relaxed-ion")}""" - @_base.data_access + @base.data_access def to_dict(self): """Read the ionic and electronic contribution to the piezoelectric tensor into a dictionary. diff --git a/src/py4vasp/_calculation/_polarization.py b/src/py4vasp/_calculation/polarization.py similarity index 93% rename from src/py4vasp/_calculation/_polarization.py rename to src/py4vasp/_calculation/polarization.py index 57dfabfc..cd344392 100644 --- a/src/py4vasp/_calculation/_polarization.py +++ b/src/py4vasp/_calculation/polarization.py @@ -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.calculation import _base +from py4vasp._calculation import base -class Polarization(_base.Refinery): +class Polarization(base.Refinery): """The static polarization describes the electric dipole moment per unit volume. Static polarization arises in a material in response to a constant external electric @@ -18,7 +18,7 @@ class Polarization(_base.Refinery): side. Therefore you always need to compare changes of polarization. """ - @_base.data_access + @base.data_access def __str__(self): vec_to_string = lambda vec: " ".join(f"{x:11.5f}" for x in vec) return f""" @@ -28,7 +28,7 @@ def __str__(self): electronic dipole moment: {vec_to_string(self._raw_data.electron[:])} """.strip() - @_base.data_access + @base.data_access def to_dict(self): """Read electronic and ionic polarization into a dictionary diff --git a/src/py4vasp/_calculation/_potential.py b/src/py4vasp/_calculation/potential.py similarity index 96% rename from src/py4vasp/_calculation/_potential.py rename to src/py4vasp/_calculation/potential.py index b1a50800..65cbd024 100644 --- a/src/py4vasp/_calculation/_potential.py +++ b/src/py4vasp/_calculation/potential.py @@ -5,14 +5,14 @@ import numpy as np from py4vasp import _config, calculation, exception +from py4vasp._calculation import base, structure from py4vasp._third_party import view from py4vasp._util import select -from py4vasp.calculation import _base, _structure VALID_KINDS = ("total", "ionic", "xc", "hartree") -class Potential(_base.Refinery, _structure.Mixin, view.Mixin): +class Potential(base.Refinery, structure.Mixin, view.Mixin): """The local potential describes the interactions between electrons and ions. In DFT calculations, the local potential consists of various contributions, each @@ -30,7 +30,7 @@ class Potential(_base.Refinery, _structure.Mixin, view.Mixin): average potential, you may also look at the :data:`~py4vasp.calculation.workfunction`. """ - @_base.data_access + @base.data_access def __str__(self): potential = self._raw_data.total_potential if _is_collinear(potential): @@ -39,7 +39,7 @@ def __str__(self): description = "noncollinear potential:" else: description = "nonpolarized potential:" - topology = calculation.topology.from_data(self._raw_data.structure.topology) + topology = calculation._topology.from_data(self._raw_data.structure.topology) structure = f"structure: {topology}" grid = f"grid: {potential.shape[3]}, {potential.shape[2]}, {potential.shape[1]}" available = "available: " + ", ".join( @@ -47,7 +47,7 @@ def __str__(self): ) return "\n ".join([description, structure, grid, available]) - @_base.data_access + @base.data_access def to_dict(self): """Store all available contributions to the potential in a dictionary. @@ -78,7 +78,7 @@ def _generate_items(self, kind): elif _is_noncollinear(potential): yield f"{kind}_magnetization", potential[1:] - @_base.data_access + @base.data_access def to_view(self, selection="total", supercell=None, **user_options): """Plot an isosurface of a selected potential. diff --git a/src/py4vasp/_calculation/projector.py b/src/py4vasp/_calculation/projector.py index 2b5032a7..01367e00 100644 --- a/src/py4vasp/_calculation/projector.py +++ b/src/py4vasp/_calculation/projector.py @@ -5,9 +5,9 @@ from typing import NamedTuple, Union from py4vasp import calculation, exception -from py4vasp._util import convert, documentation, index, select from py4vasp._calculation import base from py4vasp._calculation.selection import Selection +from py4vasp._util import convert, documentation, index, select selection_doc = """\ selection : str diff --git a/src/py4vasp/_calculation/_stress.py b/src/py4vasp/_calculation/stress.py similarity index 91% rename from src/py4vasp/_calculation/_stress.py rename to src/py4vasp/_calculation/stress.py index d2071009..449cae38 100644 --- a/src/py4vasp/_calculation/_stress.py +++ b/src/py4vasp/_calculation/stress.py @@ -2,12 +2,12 @@ # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import numpy as np +from py4vasp._calculation import base, slice_, structure from py4vasp._util import documentation, reader -from py4vasp.calculation import _base, _slice, _structure -@documentation.format(examples=_slice.examples("stress")) -class Stress(_slice.Mixin, _base.Refinery, _structure.Mixin): +@documentation.format(examples=slice_.examples("stress")) +class Stress(slice_.Mixin, base.Refinery, structure.Mixin): """The stress describes the force acting on the shape of the unit cell. The stress refers to the force applied to the cell per unit area. Specifically, @@ -23,7 +23,7 @@ class Stress(_slice.Mixin, _base.Refinery, _structure.Mixin): {examples} """ - @_base.data_access + @base.data_access def __str__(self): "Convert the stress to a format similar to the OUTCAR file." step = self._last_step_in_slice @@ -38,8 +38,8 @@ def __str__(self): in kB {stress_to_string(stress)} """.strip() - @_base.data_access - @documentation.format(examples=_slice.examples("stress", "to_dict")) + @base.data_access + @documentation.format(examples=slice_.examples("stress", "to_dict")) def to_dict(self): """Read the stress and associated structural information for one or more selected steps of the trajectory. diff --git a/src/py4vasp/_calculation/structure.py b/src/py4vasp/_calculation/structure.py index 6286fc7d..413deaa1 100644 --- a/src/py4vasp/_calculation/structure.py +++ b/src/py4vasp/_calculation/structure.py @@ -6,9 +6,9 @@ import numpy as np from py4vasp import calculation, exception, raw +from py4vasp._calculation import _topology, base, slice_ from py4vasp._third_party import view from py4vasp._util import documentation, import_, reader -from py4vasp._calculation import base, slice_, _topology ase = import_.optional("ase") ase_io = import_.optional("ase.io") diff --git a/src/py4vasp/_calculation/_system.py b/src/py4vasp/_calculation/system.py similarity index 79% rename from src/py4vasp/_calculation/_system.py rename to src/py4vasp/_calculation/system.py index 367ee8e6..70e4af04 100644 --- a/src/py4vasp/_calculation/_system.py +++ b/src/py4vasp/_calculation/system.py @@ -1,17 +1,17 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +from py4vasp._calculation import base from py4vasp._util import convert -from py4vasp.calculation import _base -class System(_base.Refinery): +class System(base.Refinery): "The :tag:`SYSTEM` tag in the INCAR file is a title you choose for a VASP calculation." - @_base.data_access + @base.data_access def __str__(self): return convert.text_to_string(self._raw_data.system) - @_base.data_access + @base.data_access def to_dict(self): "Returns a dictionary containing the system tag." return {"system": str(self)} diff --git a/src/py4vasp/_calculation/_velocity.py b/src/py4vasp/_calculation/velocity.py similarity index 89% rename from src/py4vasp/_calculation/_velocity.py rename to src/py4vasp/_calculation/velocity.py index 1426b9f7..7cb012af 100644 --- a/src/py4vasp/_calculation/_velocity.py +++ b/src/py4vasp/_calculation/velocity.py @@ -3,13 +3,13 @@ import numpy as np from py4vasp import _config, exception +from py4vasp._calculation import base, slice_, structure from py4vasp._third_party import view from py4vasp._util import documentation, reader -from py4vasp.calculation import _base, _slice, _structure -@documentation.format(examples=_slice.examples("velocity")) -class Velocity(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): +@documentation.format(examples=slice_.examples("velocity")) +class Velocity(slice_.Mixin, base.Refinery, structure.Mixin, view.Mixin): """The velocities describe the ionic motion during an MD simulation. The velocities of the ions are a metric for the temperature of the system. Most @@ -27,7 +27,7 @@ class Velocity(_slice.Mixin, _base.Refinery, _structure.Mixin, view.Mixin): velocity_rescale = 200 - @_base.data_access + @base.data_access def __str__(self): step = self._last_step_in_slice velocities = self._vectors_to_string(self._velocity[step]) @@ -42,8 +42,8 @@ def _vector_to_string(self, vector): def _element_to_string(self, element): return f"{element:21.16f}" - @_base.data_access - @documentation.format(examples=_slice.examples("velocity", "to_dict")) + @base.data_access + @documentation.format(examples=slice_.examples("velocity", "to_dict")) def to_dict(self): """Return the structure and ion velocities in a dictionary @@ -60,8 +60,8 @@ def to_dict(self): "velocities": self._velocity[self._steps], } - @_base.data_access - @documentation.format(examples=_slice.examples("velocity", "to_view")) + @base.data_access + @documentation.format(examples=slice_.examples("velocity", "to_view")) def to_view(self, supercell=None): """Plot the velocities as vectors in the structure. diff --git a/src/py4vasp/_calculation/_workfunction.py b/src/py4vasp/_calculation/workfunction.py similarity index 82% rename from src/py4vasp/_calculation/_workfunction.py rename to src/py4vasp/_calculation/workfunction.py index d6387919..3124845a 100644 --- a/src/py4vasp/_calculation/_workfunction.py +++ b/src/py4vasp/_calculation/workfunction.py @@ -1,11 +1,11 @@ # 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._calculation import base from py4vasp._third_party import graph -from py4vasp.calculation import _base -class Workfunction(_base.Refinery, graph.Mixin): +class Workfunction(base.Refinery, graph.Mixin): """The workfunction describes the energy required to remove an electron to the vacuum. The workfunction of a material is the minimum energy required to remove an @@ -18,17 +18,16 @@ class Workfunction(_base.Refinery, graph.Mixin): resulting potential. """ - @_base.data_access + @base.data_access def __str__(self): data = self.to_dict() return f"""workfunction along {data["direction"]}: vacuum potential: {data["vacuum_potential"][0]:.3f} {data["vacuum_potential"][1]:.3f} - Fermi energy: {data["fermi_energy"]:.3f}""" + Fermi energy: {data["fermi_energy"]:.3f} + valence band maximum: {data["valence_band_maximum"]:.3f} + conduction band minimum: {data["conduction_band_minimum"]:.3f}""" - # valence band maximum: {data["valence_band_maximum"]:.3f} - # conduction band minimum: {data["conduction_band_minimum"]:.3f} - - @_base.data_access + @base.data_access def to_dict(self): """Reports useful information about the workfunction as a dictionary. @@ -44,19 +43,17 @@ def to_dict(self): within the surface. """ bandgap = calculation.bandgap.from_data(self._raw_data.reference_potential) - # vbm and cbm will be uncommented out when the relevant parts of the - # code are added to VASP 6.5 return { "direction": f"lattice vector {self._raw_data.idipol}", "distance": self._raw_data.distance[:], "average_potential": self._raw_data.average_potential[:], "vacuum_potential": self._raw_data.vacuum_potential[:], - # "valence_band_maximum": bandgap.valence_band_maximum(), - # "conduction_band_minimum": bandgap.conduction_band_minimum(), + "valence_band_maximum": bandgap.valence_band_maximum(), + "conduction_band_minimum": bandgap.conduction_band_minimum(), "fermi_energy": self._raw_data.fermi_energy, } - @_base.data_access + @base.data_access def to_graph(self): """Plot the average potential along the lattice vector selected by IDIPOL. diff --git a/src/py4vasp/_raw/data.py b/src/py4vasp/_raw/data.py index ea91e751..db992385 100644 --- a/src/py4vasp/_raw/data.py +++ b/src/py4vasp/_raw/data.py @@ -215,6 +215,7 @@ class ElectronicMinimization: is_elmin_converged: VaspData "Is the electronic minimization step converged?" + @dataclasses.dataclass class Energy: """Various energies during ionic relaxation or MD simulation. @@ -343,7 +344,7 @@ class PairCorrelation: @dataclasses.dataclass -class PartialCharge: +class PartialDensity: """Electronic partial charge and magnetization density on the fine Fourier grid Possibly not only split by spin, but also by band and kpoint.""" diff --git a/src/py4vasp/_raw/definition.py b/src/py4vasp/_raw/definition.py index c1269296..67f43878 100644 --- a/src/py4vasp/_raw/definition.py +++ b/src/py4vasp/_raw/definition.py @@ -396,7 +396,7 @@ def selections(quantity): ) # schema.add( - raw.PartialCharge, + raw.PartialDensity, required=raw.Version(6, 5), structure=Link("structure", DEFAULT_SOURCE), partial_charge="results/partial_charges/parchg", diff --git a/tests/calculation/test_base.py b/tests/calculation/test_base.py index e0e19bbd..66a6946e 100644 --- a/tests/calculation/test_base.py +++ b/tests/calculation/test_base.py @@ -11,8 +11,8 @@ import pytest from py4vasp import exception, raw -from py4vasp._util import select from py4vasp._calculation import base +from py4vasp._util import select from .conftest import SELECTION diff --git a/tests/calculation/test_default_calculation.py b/tests/calculation/test_default_calculation.py index f575ab76..a69eee4c 100644 --- a/tests/calculation/test_default_calculation.py +++ b/tests/calculation/test_default_calculation.py @@ -21,7 +21,7 @@ def attribute_included(attr): def test_assigning_to_input_file(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) - expected ="SYSTEM = demo INCAR file" + expected = "SYSTEM = demo INCAR file" calculation.INCAR = expected with open("INCAR", "r") as file: actual = file.read() diff --git a/tests/calculation/test_dispersion.py b/tests/calculation/test_dispersion.py index 1f22ad85..d4ca7300 100644 --- a/tests/calculation/test_dispersion.py +++ b/tests/calculation/test_dispersion.py @@ -11,7 +11,7 @@ @pytest.fixture(params=["single_band", "spin_polarized", "line", "phonon"]) def dispersion(raw_data, request): raw_dispersion = raw_data.dispersion(request.param) - dispersion = calculation.dispersion.from_data(raw_dispersion) + dispersion = calculation._dispersion.from_data(raw_dispersion) dispersion.ref = types.SimpleNamespace() dispersion.ref.kpoints = calculation.kpoint.from_data(raw_dispersion.kpoints) dispersion.ref.eigenvalues = raw_dispersion.eigenvalues @@ -98,4 +98,4 @@ def test_print(dispersion, format_): def test_factory_methods(raw_data, check_factory_methods): data = raw_data.dispersion("single_band") - check_factory_methods(calculation.dispersion, data) + check_factory_methods(calculation._dispersion, data) diff --git a/tests/calculation/test_pair_correlation.py b/tests/calculation/test_pair_correlation.py index c3063919..17c3be39 100644 --- a/tests/calculation/test_pair_correlation.py +++ b/tests/calculation/test_pair_correlation.py @@ -71,7 +71,7 @@ def test_plot_nonexisting_label(pair_correlation): pair_correlation.plot("label does exist") -@patch("py4vasp.calculation._pair_correlation.PairCorrelation.to_graph") +@patch("py4vasp._calculation.pair_correlation.PairCorrelation.to_graph") def test_pair_correlation_to_plotly(mock_plot, pair_correlation): fig = pair_correlation.to_plotly("selection") mock_plot.assert_called_once_with("selection") @@ -87,7 +87,7 @@ def test_to_image(pair_correlation): def check_to_image(pair_correlation, filename_argument, expected_filename): - function = "py4vasp.calculation._pair_correlation.PairCorrelation.to_plotly" + function = "py4vasp._calculation.pair_correlation.PairCorrelation.to_plotly" with patch(function) as plot: pair_correlation.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") diff --git a/tests/calculation/test_partial_charge.py b/tests/calculation/test_partial_density.py similarity index 59% rename from tests/calculation/test_partial_charge.py rename to tests/calculation/test_partial_density.py index 4d8770a8..7759da76 100644 --- a/tests/calculation/test_partial_charge.py +++ b/tests/calculation/test_partial_density.py @@ -34,48 +34,48 @@ "split_bands and split_kpoints and spin_polarized Sr2TiO4", ] ) -def PartialCharge(raw_data, request): - return make_reference_partial_charge(raw_data, request.param) +def PartialDensity(raw_data, request): + return make_reference_partial_density(raw_data, request.param) @pytest.fixture -def NonSplitPartialCharge(raw_data): - return make_reference_partial_charge(raw_data, "no splitting no spin") +def NonSplitPartialDensity(raw_data): + return make_reference_partial_density(raw_data, "no splitting no spin") @pytest.fixture -def PolarizedNonSplitPartialCharge(raw_data): - return make_reference_partial_charge(raw_data, "spin_polarized") +def PolarizedNonSplitPartialDensity(raw_data): + return make_reference_partial_density(raw_data, "spin_polarized") @pytest.fixture -def PolarizedNonSplitPartialChargeCa3AsBr3(raw_data): - return make_reference_partial_charge(raw_data, "spin_polarized Ca3AsBr3") +def PolarizedNonSplitPartialDensityCa3AsBr3(raw_data): + return make_reference_partial_density(raw_data, "spin_polarized Ca3AsBr3") @pytest.fixture -def NonSplitPartialChargeCaAs3_110(raw_data): - return make_reference_partial_charge(raw_data, "CaAs3_110") +def NonSplitPartialDensityCaAs3_110(raw_data): + return make_reference_partial_density(raw_data, "CaAs3_110") @pytest.fixture -def NonSplitPartialChargeNi_100(raw_data): - return make_reference_partial_charge(raw_data, "Ni100") +def NonSplitPartialDensityNi_100(raw_data): + return make_reference_partial_density(raw_data, "Ni100") @pytest.fixture -def PolarizedNonSplitPartialChargeSr2TiO4(raw_data): - return make_reference_partial_charge(raw_data, "spin_polarized Sr2TiO4") +def PolarizedNonSplitPartialDensitySr2TiO4(raw_data): + return make_reference_partial_density(raw_data, "spin_polarized Sr2TiO4") @pytest.fixture -def NonPolarizedBandSplitPartialCharge(raw_data): - return make_reference_partial_charge(raw_data, "split_bands") +def NonPolarizedBandSplitPartialDensity(raw_data): + return make_reference_partial_density(raw_data, "split_bands") @pytest.fixture -def PolarizedAllSplitPartialCharge(raw_data): - return make_reference_partial_charge( +def PolarizedAllSplitPartialDensity(raw_data): + return make_reference_partial_density( raw_data, "split_bands and split_kpoints and spin_polarized" ) @@ -85,119 +85,121 @@ def spin(request): return request.param -def make_reference_partial_charge(raw_data, selection): - raw_partial_charge = raw_data.partial_charge(selection=selection) - parchg = calculation.partial_charge.from_data(raw_partial_charge) +def make_reference_partial_density(raw_data, selection): + raw_partial_density = raw_data.partial_density(selection=selection) + parchg = calculation.partial_density.from_data(raw_partial_density) parchg.ref = types.SimpleNamespace() - parchg.ref.structure = calculation.structure.from_data(raw_partial_charge.structure) + parchg.ref.structure = calculation.structure.from_data( + raw_partial_density.structure + ) parchg.ref.plane_vectors = plane( cell=parchg.ref.structure.lattice_vectors(), cut="c", normal="z", ) - parchg.ref.partial_charge = raw_partial_charge.partial_charge - parchg.ref.bands = raw_partial_charge.bands - parchg.ref.kpoints = raw_partial_charge.kpoints - parchg.ref.grid = raw_partial_charge.grid + parchg.ref.partial_density = raw_partial_density.partial_charge + parchg.ref.bands = raw_partial_density.bands + parchg.ref.kpoints = raw_partial_density.kpoints + parchg.ref.grid = raw_partial_density.grid return parchg -def test_read(PartialCharge, Assert): - actual = PartialCharge.read() - expected = PartialCharge.ref +def test_read(PartialDensity, Assert): + actual = PartialDensity.read() + expected = PartialDensity.ref Assert.allclose(actual["bands"], expected.bands) Assert.allclose(actual["kpoints"], expected.kpoints) Assert.allclose(actual["grid"], expected.grid) - expected_charge = np.squeeze(np.asarray(expected.partial_charge).T) - Assert.allclose(actual["partial_charge"], expected_charge) + expected_density = np.squeeze(np.asarray(expected.partial_density).T) + Assert.allclose(actual["partial_density"], expected_density) Assert.same_structure(actual["structure"], expected.structure.read()) -def test_topology(PartialCharge): - actual = PartialCharge._topology() - expected = str(PartialCharge.ref.structure._topology()) +def test_topology(PartialDensity): + actual = PartialDensity._topology() + expected = str(PartialDensity.ref.structure._topology()) assert actual == expected -def test_bands(PartialCharge, Assert): - actual = PartialCharge.bands() - expected = PartialCharge.ref.bands +def test_bands(PartialDensity, Assert): + actual = PartialDensity.bands() + expected = PartialDensity.ref.bands Assert.allclose(actual, expected) -def test_kpoints(PartialCharge, Assert): - actual = PartialCharge.kpoints() - expected = PartialCharge.ref.kpoints +def test_kpoints(PartialDensity, Assert): + actual = PartialDensity.kpoints() + expected = PartialDensity.ref.kpoints Assert.allclose(actual, expected) -def test_grid(PartialCharge, Assert): - actual = PartialCharge.grid() - expected = PartialCharge.ref.grid +def test_grid(PartialDensity, Assert): + actual = PartialDensity.grid() + expected = PartialDensity.ref.grid Assert.allclose(actual, expected) -def test_non_split_to_numpy(PolarizedNonSplitPartialCharge, Assert): - actual = PolarizedNonSplitPartialCharge.to_numpy("total") - expected = PolarizedNonSplitPartialCharge.ref.partial_charge +def test_non_split_to_numpy(PolarizedNonSplitPartialDensity, Assert): + actual = PolarizedNonSplitPartialDensity.to_numpy("total") + expected = PolarizedNonSplitPartialDensity.ref.partial_density Assert.allclose(actual, expected[0, 0, 0].T) - actual = PolarizedNonSplitPartialCharge.to_numpy("up") + actual = PolarizedNonSplitPartialDensity.to_numpy("up") Assert.allclose(actual, 0.5 * (expected[0, 0, 0].T + expected[0, 0, 1].T)) - actual = PolarizedNonSplitPartialCharge.to_numpy("down") + actual = PolarizedNonSplitPartialDensity.to_numpy("down") Assert.allclose(actual, 0.5 * (expected[0, 0, 0].T - expected[0, 0, 1].T)) -def test_split_to_numpy(PolarizedAllSplitPartialCharge, Assert): - bands = PolarizedAllSplitPartialCharge.ref.bands - kpoints = PolarizedAllSplitPartialCharge.ref.kpoints +def test_split_to_numpy(PolarizedAllSplitPartialDensity, Assert): + bands = PolarizedAllSplitPartialDensity.ref.bands + kpoints = PolarizedAllSplitPartialDensity.ref.kpoints for band_index, band in enumerate(bands): for kpoint_index, kpoint in enumerate(kpoints): - actual = PolarizedAllSplitPartialCharge.to_numpy( + actual = PolarizedAllSplitPartialDensity.to_numpy( band=band, kpoint=kpoint, selection="total" ) - expected = PolarizedAllSplitPartialCharge.ref.partial_charge + expected = PolarizedAllSplitPartialDensity.ref.partial_density Assert.allclose(actual, np.asarray(expected)[kpoint_index, band_index, 0].T) msg = f"Band {max(bands) + 1} not found in the bands array." with pytest.raises(NoData) as excinfo: - PolarizedAllSplitPartialCharge.to_numpy( + PolarizedAllSplitPartialDensity.to_numpy( band=max(bands) + 1, kpoint=max(kpoints), selection="up" ) assert msg in str(excinfo.value) msg = f"K-point {min(kpoints) - 1} not found in the kpoints array." with pytest.raises(NoData) as excinfo: - PolarizedAllSplitPartialCharge.to_numpy( + PolarizedAllSplitPartialDensity.to_numpy( band=min(bands), kpoint=min(kpoints) - 1, selection="down" ) assert msg in str(excinfo.value) -def test_non_polarized_to_numpy(NonSplitPartialCharge, spin, Assert): - actual = NonSplitPartialCharge.to_numpy(selection=spin) - expected = NonSplitPartialCharge.ref.partial_charge +def test_non_polarized_to_numpy(NonSplitPartialDensity, spin, Assert): + actual = NonSplitPartialDensity.to_numpy(selection=spin) + expected = NonSplitPartialDensity.ref.partial_density Assert.allclose(actual, np.asarray(expected).T[:, :, :, 0, 0, 0]) -def test_split_bands_to_numpy(NonPolarizedBandSplitPartialCharge, spin, Assert): - bands = NonPolarizedBandSplitPartialCharge.ref.bands +def test_split_bands_to_numpy(NonPolarizedBandSplitPartialDensity, spin, Assert): + bands = NonPolarizedBandSplitPartialDensity.ref.bands for band_index, band in enumerate(bands): - actual = NonPolarizedBandSplitPartialCharge.to_numpy(spin, band=band) - expected = NonPolarizedBandSplitPartialCharge.ref.partial_charge + actual = NonPolarizedBandSplitPartialDensity.to_numpy(spin, band=band) + expected = NonPolarizedBandSplitPartialDensity.ref.partial_density Assert.allclose(actual, np.asarray(expected).T[:, :, :, 0, band_index, 0]) -def test_to_stm_split(PolarizedAllSplitPartialCharge): +def test_to_stm_split(PolarizedAllSplitPartialDensity): msg = "set LSEPK and LSEPB to .FALSE. in the INCAR file." with pytest.raises(NotImplemented) as excinfo: - PolarizedAllSplitPartialCharge.to_stm(selection="constant_current") + PolarizedAllSplitPartialDensity.to_stm(selection="constant_current") assert msg in str(excinfo.value) -def test_to_stm_nonsplit_tip_to_high(NonSplitPartialCharge): - actual = NonSplitPartialCharge +def test_to_stm_nonsplit_tip_to_high(NonSplitPartialDensity): + actual = NonSplitPartialDensity tip_height = 8.4 error = f"""The tip position at {tip_height:.2f} is above half of the estimated vacuum thickness {actual._estimate_vacuum():.2f} Angstrom. @@ -207,44 +209,44 @@ def test_to_stm_nonsplit_tip_to_high(NonSplitPartialCharge): def test_to_stm_nonsplit_not_orthogonal_no_vacuum( - PolarizedNonSplitPartialChargeSr2TiO4, + PolarizedNonSplitPartialDensitySr2TiO4, ): msg = "The vacuum region in your cell is too small for STM simulations." with pytest.raises(IncorrectUsage) as excinfo: - PolarizedNonSplitPartialChargeSr2TiO4.to_stm() + PolarizedNonSplitPartialDensitySr2TiO4.to_stm() assert msg in str(excinfo.value) -def test_to_stm_wrong_spin_nonsplit(PolarizedNonSplitPartialCharge): +def test_to_stm_wrong_spin_nonsplit(PolarizedNonSplitPartialDensity): msg = "'up', 'down', or 'total'" with pytest.raises(IncorrectUsage) as excinfo: - PolarizedNonSplitPartialCharge.to_stm(selection="all") + PolarizedNonSplitPartialDensity.to_stm(selection="all") assert msg in str(excinfo.value) -def test_to_stm_wrong_mode(PolarizedNonSplitPartialCharge): +def test_to_stm_wrong_mode(PolarizedNonSplitPartialDensity): with pytest.raises(IncorrectUsage) as excinfo: - PolarizedNonSplitPartialCharge.to_stm(selection="stm") + PolarizedNonSplitPartialDensity.to_stm(selection="stm") assert "STM mode" in str(excinfo.value) -def test_wrong_vacuum_direction(NonSplitPartialChargeNi_100): +def test_wrong_vacuum_direction(NonSplitPartialDensityNi_100): msg = """The vacuum region in your cell is not located along the third lattice vector.""" with pytest.raises(NotImplemented) as excinfo: - NonSplitPartialChargeNi_100.to_stm() + NonSplitPartialDensityNi_100.to_stm() assert msg in str(excinfo.value) @pytest.mark.parametrize("alias", ("constant_height", "ch", "height")) def test_to_stm_nonsplit_constant_height( - PolarizedNonSplitPartialCharge, alias, spin, Assert, not_core + PolarizedNonSplitPartialDensity, alias, spin, Assert, not_core ): supercell = 3 - actual = PolarizedNonSplitPartialCharge.to_stm( + actual = PolarizedNonSplitPartialDensity.to_stm( selection=f"{alias}({spin})", tip_height=2.0, supercell=supercell ) - expected = PolarizedNonSplitPartialCharge.ref + expected = PolarizedNonSplitPartialDensity.ref assert type(actual.series.data) == np.ndarray assert actual.series.data.shape == (expected.grid[0], expected.grid[1]) Assert.allclose(actual.series.lattice.vectors, expected.plane_vectors.vectors) @@ -261,16 +263,16 @@ def test_to_stm_nonsplit_constant_height( @pytest.mark.parametrize("alias", ("constant_current", "cc", "current")) def test_to_stm_nonsplit_constant_current( - PolarizedNonSplitPartialCharge, alias, spin, Assert, not_core + PolarizedNonSplitPartialDensity, alias, spin, Assert, not_core ): current = 5 supercell = np.asarray([2, 4]) - actual = PolarizedNonSplitPartialCharge.to_stm( + actual = PolarizedNonSplitPartialDensity.to_stm( selection=f"{spin}({alias})", current=current, supercell=supercell, ) - expected = PolarizedNonSplitPartialCharge.ref + expected = PolarizedNonSplitPartialDensity.ref assert type(actual.series.data) == np.ndarray assert actual.series.data.shape == (expected.grid[0], expected.grid[1]) Assert.allclose(actual.series.lattice.vectors, expected.plane_vectors.vectors) @@ -287,16 +289,16 @@ def test_to_stm_nonsplit_constant_current( @pytest.mark.parametrize("alias", ("constant_current", "cc", "current")) def test_to_stm_nonsplit_constant_current_non_ortho( - NonSplitPartialChargeCaAs3_110, alias, spin, Assert, not_core + NonSplitPartialDensityCaAs3_110, alias, spin, Assert, not_core ): current = 5 supercell = np.asarray([2, 4]) - actual = NonSplitPartialChargeCaAs3_110.to_stm( + actual = NonSplitPartialDensityCaAs3_110.to_stm( selection=f"{spin}({alias})", current=current, supercell=supercell, ) - expected = NonSplitPartialChargeCaAs3_110.ref + expected = NonSplitPartialDensityCaAs3_110.ref assert type(actual.series.data) == np.ndarray assert actual.series.data.shape == (expected.grid[0], expected.grid[1]) Assert.allclose(actual.series.lattice.vectors, expected.plane_vectors.vectors) @@ -311,8 +313,8 @@ def test_to_stm_nonsplit_constant_current_non_ortho( assert f"{current:.2f}" in actual.title -def test_stm_default_settings(PolarizedNonSplitPartialCharge): - actual = dataclasses.asdict(PolarizedNonSplitPartialCharge.stm_settings) +def test_stm_default_settings(PolarizedNonSplitPartialDensity): + actual = dataclasses.asdict(PolarizedNonSplitPartialDensity.stm_settings) defaults = { "sigma_xy": 4.0, "sigma_z": 4.0, @@ -324,5 +326,5 @@ def test_stm_default_settings(PolarizedNonSplitPartialCharge): def test_factory_methods(raw_data, check_factory_methods): - data = raw_data.partial_charge("spin_polarized") - check_factory_methods(calculation.partial_charge, data) + data = raw_data.partial_density("spin_polarized") + check_factory_methods(calculation.partial_density, data) diff --git a/tests/calculation/test_phonon_band.py b/tests/calculation/test_phonon_band.py index fbc13002..857c88ac 100644 --- a/tests/calculation/test_phonon_band.py +++ b/tests/calculation/test_phonon_band.py @@ -19,7 +19,7 @@ def phonon_band(raw_data): band.ref.modes = convert.to_complex(raw_band.eigenvectors) raw_qpoints = raw_band.dispersion.kpoints band.ref.qpoints = calculation.kpoint.from_data(raw_qpoints) - band.ref.topology = calculation.topology.from_data(raw_band.topology) + band.ref.topology = calculation._topology.from_data(raw_band.topology) Sr = slice(0, 2) band.ref.Sr = np.sum(np.abs(band.ref.modes[:, :, Sr, :]), axis=(2, 3)) Ti = 2 @@ -88,7 +88,7 @@ def check_series(self, series, projection, label, width): self.Assert.allclose(series.width, width * projection.T) -@patch("py4vasp.calculation._phonon_band.PhononBand.to_graph") +@patch("py4vasp._calculation.phonon_band.PhononBand.to_graph") def test_to_plotly(mock_plot, phonon_band): fig = phonon_band.to_plotly("selection", width=0.2) mock_plot.assert_called_once_with("selection", width=0.2) @@ -104,7 +104,7 @@ def test_to_image(phonon_band): def check_to_image(phonon_band, filename_argument, expected_filename): - with patch("py4vasp.calculation._phonon_band.PhononBand.to_plotly") as plot: + with patch("py4vasp._calculation.phonon_band.PhononBand.to_plotly") as plot: phonon_band.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_phonon_dos.py b/tests/calculation/test_phonon_dos.py index 3cefed00..fd6bac2c 100644 --- a/tests/calculation/test_phonon_dos.py +++ b/tests/calculation/test_phonon_dos.py @@ -65,7 +65,7 @@ def check_series(series, reference, label, Assert): Assert.allclose(series.y, reference) -@patch("py4vasp.calculation._phonon_dos.PhononDos.to_graph") +@patch("py4vasp._calculation.phonon_dos.PhononDos.to_graph") def test_phonon_dos_to_plotly(mock_plot, phonon_dos): fig = phonon_dos.to_plotly("selection") mock_plot.assert_called_once_with("selection") @@ -81,7 +81,7 @@ def test_phonon_dos_to_image(phonon_dos): def check_to_image(phonon_dos, filename_argument, expected_filename): - with patch("py4vasp.calculation._phonon_dos.PhononDos.to_plotly") as plot: + with patch("py4vasp._calculation.phonon_dos.PhononDos.to_plotly") as plot: phonon_dos.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value diff --git a/tests/calculation/test_projector.py b/tests/calculation/test_projector.py index 3eac1e86..2d501369 100644 --- a/tests/calculation/test_projector.py +++ b/tests/calculation/test_projector.py @@ -6,8 +6,8 @@ import pytest from py4vasp import calculation, exception -from py4vasp._util import select from py4vasp._calculation.selection import Selection +from py4vasp._util import select @pytest.fixture diff --git a/tests/calculation/test_repr.py b/tests/calculation/test_repr.py index 205ec3d0..d3db9344 100644 --- a/tests/calculation/test_repr.py +++ b/tests/calculation/test_repr.py @@ -1,16 +1,18 @@ # Copyright © VASP Software GmbH, # Licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) import importlib +from pathlib import PosixPath, WindowsPath -from py4vasp import calculation +from py4vasp import _calculation, calculation from py4vasp._util import convert def test_repr(): - for name in calculation._quantities: + for name in _calculation.QUANTITIES: instance = getattr(calculation, name) class_name = convert.to_camelcase(name) - module = importlib.import_module(f"py4vasp.calculation._{name}") + module = importlib.import_module(f"py4vasp._calculation.{name}") locals()[class_name] = getattr(module, class_name) + print(repr(instance)) copy = eval(repr(instance)) assert copy.__class__ == instance.__class__ diff --git a/tests/calculation/test_slice_mixin.py b/tests/calculation/test_slice_mixin.py index 223c319c..83f24e1e 100644 --- a/tests/calculation/test_slice_mixin.py +++ b/tests/calculation/test_slice_mixin.py @@ -5,8 +5,8 @@ import pytest from py4vasp import exception -from py4vasp._util import documentation from py4vasp._calculation import slice_ +from py4vasp._util import documentation class Other: diff --git a/tests/calculation/test_topology.py b/tests/calculation/test_topology.py index 3552ec59..be7995b7 100644 --- a/tests/calculation/test_topology.py +++ b/tests/calculation/test_topology.py @@ -3,8 +3,8 @@ import pytest from py4vasp import calculation, exception -from py4vasp._util import import_, select from py4vasp._calculation.selection import Selection +from py4vasp._util import import_, select ase = import_.optional("ase") pd = import_.optional("pandas") diff --git a/tests/calculation/test_workfunction.py b/tests/calculation/test_workfunction.py index ceacf08f..4944c17a 100644 --- a/tests/calculation/test_workfunction.py +++ b/tests/calculation/test_workfunction.py @@ -30,8 +30,8 @@ def test_read(workfunction, Assert): Assert.allclose(actual["average_potential"], workfunction.ref.average_potential) Assert.allclose(actual["vacuum_potential"], workfunction.ref.vacuum_potential) # Uncomment out these lines when vbm and cbm are added to VASP 6.5 - # Assert.allclose(actual["valence_band_maximum"], workfunction.ref.vbm) - # Assert.allclose(actual["conduction_band_minimum"], workfunction.ref.cbm) + Assert.allclose(actual["valence_band_maximum"], workfunction.ref.vbm) + Assert.allclose(actual["conduction_band_minimum"], workfunction.ref.cbm) Assert.allclose(actual["fermi_energy"], workfunction.ref.fermi_energy) @@ -44,7 +44,7 @@ def test_plot(workfunction, Assert): assert graph.series.name == "potential" -@patch("py4vasp.calculation._workfunction.Workfunction.to_graph") +@patch("py4vasp._calculation.workfunction.Workfunction.to_graph") def test_to_plotly(mock_plot, workfunction): fig = workfunction.to_plotly() mock_plot.assert_called_once_with() @@ -60,7 +60,7 @@ def test_to_image(workfunction): def check_to_image(workfunction, filename_argument, expected_filename): - with patch("py4vasp.calculation._workfunction.Workfunction.to_plotly") as plot: + with patch("py4vasp._calculation.workfunction.Workfunction.to_plotly") as plot: workfunction.to_image("args", filename=filename_argument, key="word") plot.assert_called_once_with("args", key="word") fig = plot.return_value @@ -72,18 +72,18 @@ def test_print(workfunction, format_): reference = """\ workfunction along {lattice_vector}: vacuum potential: {vacuum1:.3f} {vacuum2:.3f} - Fermi energy: {fermi_energy:.3f}""" + Fermi energy: {fermi_energy:.3f} + valence band maximum: {vbm:.3f} + conduction band minimum: {cbm:.3f}""" reference = reference.format( lattice_vector=workfunction.ref.lattice_vector, vacuum1=workfunction.ref.vacuum_potential[0], vacuum2=workfunction.ref.vacuum_potential[1], fermi_energy=workfunction.ref.fermi_energy, + vbm=workfunction.ref.vbm, + cbm=workfunction.ref.cbm, ) assert actual == {"text/plain": reference} - # valence band maximum: {vbm:.3f} - # conduction band minimum: {cbm:.3f} - # vbm=workfunction.ref.vbm, - # cbm=workfunction.ref.cbm, def test_factory_methods(raw_data, check_factory_methods): diff --git a/tests/conftest.py b/tests/conftest.py index 334c5af7..121dcfcf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -327,8 +327,8 @@ def workfunction(selection): return _workfunction(selection) @staticmethod - def partial_charge(selection): - return _partial_charge(selection) + def partial_density(selection): + return _partial_density(selection) @pytest.fixture @@ -683,7 +683,7 @@ def _electronic_minimization(): ) -def _partial_charge(selection): +def _partial_density(selection): grid_dim = grid_dimensions if "CaAs3_110" in selection: structure = _CaAs3_110_structure() @@ -713,7 +713,7 @@ def _partial_charge(selection): random_charge = raw.VaspData( np.random.rand(len(kpoints), len(bands), spin_dimension, *grid_dim) ) - return raw.PartialCharge( + return raw.PartialDensity( structure=structure, bands=bands, kpoints=kpoints, diff --git a/tests/util/test_convert.py b/tests/util/test_convert.py index 9a3dcb66..44aab33c 100644 --- a/tests/util/test_convert.py +++ b/tests/util/test_convert.py @@ -3,7 +3,7 @@ import numpy as np from py4vasp._config import VASP_COLORS -from py4vasp._util.convert import text_to_string, to_complex, to_rgb, to_camelcase +from py4vasp._util.convert import text_to_string, to_camelcase, to_complex, to_rgb def test_text_to_string(): From 1740d6c96e2efb184f48d5ef3e6f7c7176fbdec9 Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Mon, 17 Jun 2024 15:39:17 +0200 Subject: [PATCH 4/6] Rename Calculations -> Batch --- src/py4vasp/__init__.py | 2 +- src/py4vasp/_analysis/mlff.py | 32 ++++----- src/py4vasp/{_calculations.py => _batch.py} | 16 ++--- tests/analysis/test_mlff.py | 10 +-- .../test_batch.py} | 70 +++++++++---------- 5 files changed, 60 insertions(+), 70 deletions(-) rename src/py4vasp/{_calculations.py => _batch.py} (89%) rename tests/{test_calculations.py => batch/test_batch.py} (71%) diff --git a/src/py4vasp/__init__.py b/src/py4vasp/__init__.py index efe013c6..aae105d1 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._batch import Batch from py4vasp._calculation import Calculation, calculation -from py4vasp._calculations import Calculations 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 a6bdfd29..5fa5dea3 100644 --- a/src/py4vasp/_analysis/mlff.py +++ b/src/py4vasp/_analysis/mlff.py @@ -50,9 +50,9 @@ def __init__(self, *args, **kwargs): self.dft = SimpleNamespace() @classmethod - def _from_data(cls, _calculations): + def _from_data(cls, batch): mlff_error_analysis = cls(_internal=True) - mlff_error_analysis._calculations = _calculations + mlff_error_analysis._batch = batch set_appropriate_attrs(mlff_error_analysis) return mlff_error_analysis @@ -72,10 +72,8 @@ def from_paths(cls, dft_data, mlff_data): Path to the MLFF data. Accepts wildcards. """ mlff_error_analysis = cls(_internal=True) - calculations = py4vasp.Calculations.from_paths( - dft_data=dft_data, mlff_data=mlff_data - ) - mlff_error_analysis._calculations = calculations + batch = py4vasp.Batch.from_paths(dft_data=dft_data, mlff_data=mlff_data) + mlff_error_analysis._batch = batch set_appropriate_attrs(mlff_error_analysis) return mlff_error_analysis @@ -95,10 +93,8 @@ def from_files(cls, dft_data, mlff_data): Path to the MLFF data. Accepts wildcards. """ mlff_error_analysis = cls(_internal=True) - calculations = py4vasp.Calculations.from_files( - dft_data=dft_data, mlff_data=mlff_data - ) - mlff_error_analysis._calculations = calculations + batch = py4vasp.Batch.from_files(dft_data=dft_data, mlff_data=mlff_data) + mlff_error_analysis._batch = batch set_appropriate_attrs(mlff_error_analysis) return mlff_error_analysis @@ -213,7 +209,7 @@ def set_number_of_configurations(cls): cls : MLFFErrorAnalysis An instance of MLFFErrorAnalysis. """ - number_of_calculations = cls._calculations.number_of_calculations() + number_of_calculations = cls._batch.number_of_calculations() cls.dft.nconfig = number_of_calculations["dft_data"] cls.mlff.nconfig = number_of_calculations["mlff_data"] @@ -229,7 +225,7 @@ def set_number_of_ions(cls): cls : MLFFErrorAnalysis An instance of MLFFErrorAnalysis. """ - force_data = cls._calculations.forces.read() + force_data = cls._batch.forces.read() structures_dft = _dict_to_list(force_data["dft_data"], "structure") structures_mlff = _dict_to_list(force_data["mlff_data"], "structure") elements_dft = _dict_to_array(structures_dft, "elements") @@ -252,11 +248,11 @@ def set_paths_and_files(cls): cls : MLFFErrorAnalysis An instance of MLFFErrorAnalysis. """ - paths = cls._calculations.paths() + paths = cls._batch.paths() cls.dft.paths = paths["dft_data"] cls.mlff.paths = paths["mlff_data"] - if hasattr(cls._calculations, "_files"): - files = cls._calculations.files() + if hasattr(cls._batch, "_files"): + files = cls._batch.files() cls.dft.files = files["dft_data"] cls.mlff.files = files["mlff_data"] @@ -273,7 +269,7 @@ def set_energies(cls): An instance of MLFFErrorAnalysis. """ tag = "free energy TOTEN" - energies_data = cls._calculations.energies.read() + energies_data = cls._batch.energies.read() cls.mlff.energies = _dict_to_array(energies_data["mlff_data"], tag) cls.dft.energies = _dict_to_array(energies_data["dft_data"], tag) @@ -298,7 +294,7 @@ def set_force_related_attributes(cls): cls : MLFFErrorAnalysis An instance of MLFFErrorAnalysis. """ - force_data = cls._calculations.forces.read() + force_data = cls._batch.forces.read() cls.dft.forces = _dict_to_array(force_data["dft_data"], "forces") cls.mlff.forces = _dict_to_array(force_data["mlff_data"], "forces") dft_structures = _dict_to_list(force_data["dft_data"], "structure") @@ -320,6 +316,6 @@ def set_stresses(cls): cls : MLFFErrorAnalysis An instance of MLFFErrorAnalysis. """ - stress_data = cls._calculations.stresses.read() + stress_data = cls._batch.stresses.read() cls.dft.stresses = _dict_to_array(stress_data["dft_data"], "stress") cls.mlff.stresses = _dict_to_array(stress_data["mlff_data"], "stress") diff --git a/src/py4vasp/_calculations.py b/src/py4vasp/_batch.py similarity index 89% rename from src/py4vasp/_calculations.py rename to src/py4vasp/_batch.py index 32cb2bf4..8d7d0768 100644 --- a/src/py4vasp/_calculations.py +++ b/src/py4vasp/_batch.py @@ -8,17 +8,17 @@ from py4vasp._util import convert -class Calculations: - """A class to handle multiple Calculations all at once. +class Batch: + """A class to handle batch of multiple calculations at once. This class combines the functionality of the Calculation class for more than one - calculation. Create a Calculations object using either a wildcard for a set of + calculation. Create a Batch object using either a wildcard for a set of paths or files or pass in paths and files directly. Then you can access the properties of all calculations via the attributes of the object. Examples -------- - >>> calcs = Calculations.from_paths(calc1="path_to_calc1", calc2="path_to_calc2") + >>> calcs = Batch.from_paths(calc1="path_to_calc1", calc2="path_to_calc2") >>> calcs.energies.read() # returns a dictionary with the energies of calc1 and calc2 >>> calcs.forces.read() # returns a dictionary with the forces of calc1 and calc2 >>> calcs.stresses.read() # returns a dictionary with the stresses of calc1 and calc2 @@ -34,8 +34,8 @@ class Calculations: def __init__(self, *args, **kwargs): if not kwargs.get("_internal"): message = """\ -Please setup new CompareCalculations instance using the classmethod CompareCalculations.from_paths() -or CompareCalculations.from_files() instead of the constructor CompareCalculations().""" +Please setup new Batch instance using the classmethod Batch.from_paths() +or Batch.from_files() instead of the constructor Batch().""" raise exception.IncorrectUsage(message) def _path_finder(**kwargs): @@ -53,7 +53,7 @@ def _path_finder(**kwargs): @classmethod def from_paths(cls, **kwargs): - """Set up a Calculations object for paths. + """Set up a Batch object for paths. Setup a calculation for paths by passing in a dictionary with the name of the calculation as key and the path to the calculation as value. @@ -75,7 +75,7 @@ def from_paths(cls, **kwargs): @classmethod def from_files(cls, **kwargs): - """Set up a Calculations object from files. + """Set up a Batch object from files. Setup a calculation for files by passing in a dictionary with the name of the calculation as key and the path to the calculation as value. Note that this diff --git a/tests/analysis/test_mlff.py b/tests/analysis/test_mlff.py index 6439de0f..9b5d9aec 100644 --- a/tests/analysis/test_mlff.py +++ b/tests/analysis/test_mlff.py @@ -127,7 +127,7 @@ def mock_calculations_incorrect(raw_data): return _mock_calculations -@patch("py4vasp.calculation._base.Refinery.from_path", autospec=True) +@patch("py4vasp._calculation.base.Refinery.from_path", autospec=True) @patch("py4vasp.raw.access", autospec=True) def test_read_inputs_from_path(mock_access, mock_from_path): absolute_path_dft = Path(__file__) / "dft" @@ -151,7 +151,7 @@ def test_read_inputs_from_path(mock_access, mock_from_path): assert isinstance(error_analysis.dft.stresses, np.ndarray) -@patch("py4vasp.calculation._base.Refinery.from_path", autospec=True) +@patch("py4vasp._calculation.base.Refinery.from_path", autospec=True) @patch("py4vasp.raw.access", autospec=True) def test_read_inputs_from_files(mock_analysis, mock_from_path): absolute_files_dft = Path(__file__) / "dft*.h5" @@ -180,9 +180,9 @@ def test_read_from_data(mock_calculations): expected_forces = mock_calculations.forces.read() expected_stresses = mock_calculations.stresses.read() mlff_error_analysis = MLFFErrorAnalysis._from_data(mock_calculations) - output_energies = mlff_error_analysis._calculations.energies.read() - output_forces = mlff_error_analysis._calculations.forces.read() - output_stresses = mlff_error_analysis._calculations.stresses.read() + output_energies = mlff_error_analysis._batch.energies.read() + output_forces = mlff_error_analysis._batch.forces.read() + output_stresses = mlff_error_analysis._batch.stresses.read() assert output_energies == expected_energies assert output_forces == expected_forces assert output_stresses == expected_stresses diff --git a/tests/test_calculations.py b/tests/batch/test_batch.py similarity index 71% rename from tests/test_calculations.py rename to tests/batch/test_batch.py index 6bfd5af0..aac2a39b 100644 --- a/tests/test_calculations.py +++ b/tests/batch/test_batch.py @@ -6,54 +6,50 @@ import pytest -from py4vasp import Calculations +from py4vasp import Batch def test_error_when_using_constructor(): with pytest.raises(Exception): - Calculations() + Batch() def test_creation_from_paths(): # Test creation from absolute paths absolute_path_1 = Path(__file__) / "path_1" absolute_path_2 = Path(__file__) / "path_2" - calculations = Calculations.from_paths( - path_name_1=absolute_path_1, path_name_2=absolute_path_2 - ) - output_paths = calculations.paths() + batch = Batch.from_paths(path_name_1=absolute_path_1, path_name_2=absolute_path_2) + output_paths = batch.paths() assert output_paths["path_name_1"] == [absolute_path_1] assert output_paths["path_name_2"] == [absolute_path_2] - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["path_name_1"] == 1 assert output_number_of_calculations["path_name_2"] == 1 # Test creation from relative paths relative_path_1 = os.path.relpath(absolute_path_1, Path.cwd()) relative_path_2 = os.path.relpath(absolute_path_2, Path.cwd()) - calculations = Calculations.from_paths( - path_name_1=relative_path_1, path_name_2=relative_path_2 - ) - output_paths = calculations.paths() + batch = Batch.from_paths(path_name_1=relative_path_1, path_name_2=relative_path_2) + output_paths = batch.paths() assert output_paths["path_name_1"] == [absolute_path_1] assert output_paths["path_name_2"] == [absolute_path_2] - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["path_name_1"] == 1 assert output_number_of_calculations["path_name_2"] == 1 # Test creation with string paths - calculations = Calculations.from_paths( + batch = Batch.from_paths( path_name_1=absolute_path_1.as_posix(), path_name_2=absolute_path_2.as_posix() ) - output_paths = calculations.paths() + output_paths = batch.paths() assert output_paths["path_name_1"] == [absolute_path_1] assert output_paths["path_name_2"] == [absolute_path_2] - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["path_name_1"] == 1 assert output_number_of_calculations["path_name_2"] == 1 def test_creation_from_paths_with_incorrect_input(): with pytest.raises(Exception): - Calculations.from_paths(path_name_1=1, path_name_2=2) + Batch.from_paths(path_name_1=1, path_name_2=2) def test_creation_from_paths_with_wildcards(tmp_path): @@ -64,10 +60,10 @@ def test_creation_from_paths_with_wildcards(tmp_path): create_paths = lambda paths: [path.mkdir() for path in paths] create_paths(paths_1) create_paths(paths_2) - calculations = Calculations.from_paths( + batch = Batch.from_paths( path_name_1=tmp_path / "path1_*", path_name_2=tmp_path / "path2_*" ) - output_paths = calculations.paths() + output_paths = batch.paths() assert all( [ output_paths["path_name_1"][i] == absolute_paths_1[i] @@ -80,7 +76,7 @@ def test_creation_from_paths_with_wildcards(tmp_path): for i in range(len(absolute_paths_2)) ] ) - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["path_name_1"] == 2 assert output_number_of_calculations["path_name_2"] == 2 @@ -88,13 +84,11 @@ def test_creation_from_paths_with_wildcards(tmp_path): def test_creation_from_file(): absolute_path_1 = Path(__file__) / "example_1.h5" absolute_path_2 = Path(__file__) / "example_2.h5" - calculations = Calculations.from_files( - path_name_1=absolute_path_1, path_name_2=absolute_path_2 - ) - output_paths = calculations.paths() + batch = Batch.from_files(path_name_1=absolute_path_1, path_name_2=absolute_path_2) + output_paths = batch.paths() assert output_paths["path_name_1"] == [absolute_path_1.parent] assert output_paths["path_name_2"] == [absolute_path_2.parent] - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["path_name_1"] == 1 assert output_number_of_calculations["path_name_2"] == 1 @@ -107,11 +101,11 @@ def test_create_from_files_with_wildcards(tmp_path): create_files = lambda paths: [path.touch() for path in paths] create_files(paths_1) create_files(paths_2) - calculations = Calculations.from_files( + batch = Batch.from_files( file_1=tmp_path / "example1_*.h5", file_2=tmp_path / "example2_*.h5", ) - output_paths = calculations.paths() + output_paths = batch.paths() assert all( [ output_paths["file_1"][i] == absolute_paths_1[i].parent @@ -124,32 +118,32 @@ def test_create_from_files_with_wildcards(tmp_path): for i in range(len(absolute_paths_2)) ] ) - output_number_of_calculations = calculations.number_of_calculations() + output_number_of_calculations = batch.number_of_calculations() assert output_number_of_calculations["file_1"] == 2 assert output_number_of_calculations["file_2"] == 2 -@patch("py4vasp.calculation._base.Refinery.from_path", autospec=True) +@patch("py4vasp._calculation.base.Refinery.from_path", autospec=True) @patch("py4vasp.raw.access", autospec=True) def test_has_attributes(mock_access, mock_from_path): - calculations = Calculations.from_paths(path_name_1="path_1", path_name_2="path_2") - assert hasattr(calculations, "energies") - assert hasattr(calculations.energies, "read") - output_read = calculations.energies.read() + batch = Batch.from_paths(path_name_1="path_1", path_name_2="path_2") + assert hasattr(batch, "energies") + assert hasattr(batch.energies, "read") + output_read = batch.energies.read() assert isinstance(output_read, dict) assert output_read.keys() == {"path_name_1", "path_name_2"} assert isinstance(output_read["path_name_1"], list) assert isinstance(output_read["path_name_2"], list) - assert hasattr(calculations, "forces") - assert hasattr(calculations.forces, "read") - output_read = calculations.forces.read() + assert hasattr(batch, "forces") + assert hasattr(batch.forces, "read") + output_read = batch.forces.read() assert isinstance(output_read, dict) assert output_read.keys() == {"path_name_1", "path_name_2"} assert isinstance(output_read["path_name_1"], list) assert isinstance(output_read["path_name_2"], list) - assert hasattr(calculations, "stresses") - assert hasattr(calculations.stresses, "read") - output_read = calculations.stresses.read() + assert hasattr(batch, "stresses") + assert hasattr(batch.stresses, "read") + output_read = batch.stresses.read() assert isinstance(output_read, dict) assert output_read.keys() == {"path_name_1", "path_name_2"} assert isinstance(output_read["path_name_1"], list) From aee60164a298f113d26d632d3b28264e5b298e40 Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Mon, 17 Jun 2024 17:53:04 +0200 Subject: [PATCH 5/6] Prevent numpy 2 installation --- core/pyproject.toml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index ee78a837..15063423 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -25,7 +25,7 @@ repository = "https://github.com/vasp-dev/py4vasp" [tool.poetry.dependencies] python = ">=3.9" -numpy = ">=1.23" +numpy = "^1.23" h5py = ">=3.7.0" [tool.poetry.group.dev.dependencies] diff --git a/pyproject.toml b/pyproject.toml index f95412e6..7fd1d047 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ repository = "https://github.com/vasp-dev/py4vasp" [tool.poetry.dependencies] python = ">=3.9" -numpy = ">=1.23" +numpy = "^1.23" h5py = ">=3.7.0" pandas = ">=1.4.3" nglview = ">=3.0.5" From ac9f063ccd2823bb0018642a6dc4d3b9fc49dd8b Mon Sep 17 00:00:00 2001 From: Martin Schlipf Date: Mon, 17 Jun 2024 17:59:04 +0200 Subject: [PATCH 6/6] Update lock files --- core/poetry.lock | 32 ++++++++++++++++---------------- poetry.lock | 9 ++++----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/core/poetry.lock b/core/poetry.lock index 1d94d801..a51f3034 100644 --- a/core/poetry.lock +++ b/core/poetry.lock @@ -1,14 +1,14 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "astroid" -version = "3.1.0" +version = "3.2.1" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, + {file = "astroid-3.2.1-py3-none-any.whl", hash = "sha256:b452064132234819f023b94f4bd045b250ea0009f372b4377cfcd87f10806ca5"}, + {file = "astroid-3.2.1.tar.gz", hash = "sha256:902564b36796ba1eab3ad2c7a694861fbd926f574d5dbb5fa1d86778a2ba2d91"}, ] [package.dependencies] @@ -235,13 +235,13 @@ numpy = ">=1.17.3" [[package]] name = "hypothesis" -version = "6.101.0" +version = "6.102.4" description = "A library for property-based testing" optional = false python-versions = ">=3.8" files = [ - {file = "hypothesis-6.101.0-py3-none-any.whl", hash = "sha256:3aeeda59bc7878aa2ad066f71925ab9296a148f7fb7d5395a47795035283dec2"}, - {file = "hypothesis-6.101.0.tar.gz", hash = "sha256:b43242fc2b672b26c81688876976c1c2cc44116050ca12f60eb2671b75eacd45"}, + {file = "hypothesis-6.102.4-py3-none-any.whl", hash = "sha256:013df31b04a4daede13756f497e60e451963d86f426395a79f99c5d692919bbd"}, + {file = "hypothesis-6.102.4.tar.gz", hash = "sha256:59b4d144346d5cffb482cc1bafbd21b13ff31608e8c4b3e4630339aee3e87763"}, ] [package.dependencies] @@ -382,13 +382,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -413,22 +413,22 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pylint" -version = "3.1.0" +version = "3.2.0" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, + {file = "pylint-3.2.0-py3-none-any.whl", hash = "sha256:9f20c05398520474dac03d7abb21ab93181f91d4c110e1e0b32bc0d016c34fa4"}, + {file = "pylint-3.2.0.tar.gz", hash = "sha256:ad8baf17c8ea5502f23ae38d7c1b7ec78bd865ce34af9a0b986282e2611a8ff2"}, ] [package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" +astroid = ">=3.2.0,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -528,4 +528,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "a19e45d9772b6787259404e7542594e210af50e4ff329eafdc8a2363dfa71dbe" +content-hash = "0e9853afabcd6b1e258bb9c2b31aa7a469829940a2f195fefcd625029743416f" diff --git a/poetry.lock b/poetry.lock index 826aa2cf..eb5056a1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -2124,8 +2124,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.22.4", markers = "python_version < \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -2479,8 +2479,8 @@ astroid = ">=3.2.0,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -2646,7 +2646,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -3523,4 +3522,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "1dbe1ec8147ca66d47d5e20b43f33bd2a8941507b9aa3b2a03e6e305968a2dc8" +content-hash = "77a96b6fd125ce33f11c5361874255a95af49da196d543a3de0be1af5b179172"