", @@ -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/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/_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/_raw/data.py b/src/py4vasp/_raw/data.py index 77d7866c..db992385 100644 --- a/src/py4vasp/_raw/data.py +++ b/src/py4vasp/_raw/data.py @@ -200,6 +200,22 @@ 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 +328,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. @@ -344,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 6119fabc..67f43878 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, @@ -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/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/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) 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..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._calculation import base from py4vasp._util import select -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_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_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..a69eee4c 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_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_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_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/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 4e9baee9..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._calculation.selection import Selection from py4vasp._util import select -from py4vasp.calculation._selection import Selection @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 81402d21..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._calculation import slice_ from py4vasp._util import documentation -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..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._calculation.selection import Selection from py4vasp._util import import_, select -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/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 1458b8bb..121dcfcf 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": @@ -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 @@ -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,14 +676,14 @@ 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, ) -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 390acee5..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 +from py4vasp._util.convert import text_to_string, to_camelcase, to_complex, to_rgb 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" |