diff --git a/PQAnalysis/analysis/rdf/api.py b/PQAnalysis/analysis/rdf/api.py index b43e5303..4d6e4ff3 100644 --- a/PQAnalysis/analysis/rdf/api.py +++ b/PQAnalysis/analysis/rdf/api.py @@ -12,6 +12,7 @@ from .rdf_output_file_writer import RDFDataWriter, RDFLogWriter + @runtime_type_checking def rdf(input_file: str, md_format: MDEngineFormat | str = MDEngineFormat.PQ): """ @@ -49,17 +50,24 @@ def rdf(input_file: str, md_format: MDEngineFormat | str = MDEngineFormat.PQ): if input_reader.moldescriptor_file is not None: moldescriptor_reader = MoldescriptorReader( - input_reader.moldescriptor_file) + input_reader.moldescriptor_file + ) reference_residues = moldescriptor_reader.read() else: reference_residues = None if topology is not None: - topology = Topology(atoms=topology.atoms, residue_ids=topology.residue_ids, - reference_residues=reference_residues) + topology = Topology( + atoms=topology.atoms, + residue_ids=topology.residue_ids, + reference_residues=reference_residues + ) traj_reader = TrajectoryReader( - input_reader.traj_files, md_format=md_format, topology=topology) + input_reader.traj_files, + md_format=md_format, + topology=topology + ) _rdf = RDF( traj=traj_reader, diff --git a/PQAnalysis/analysis/rdf/exceptions.py b/PQAnalysis/analysis/rdf/exceptions.py index adf26bab..9ef24a43 100644 --- a/PQAnalysis/analysis/rdf/exceptions.py +++ b/PQAnalysis/analysis/rdf/exceptions.py @@ -6,7 +6,9 @@ from ...exceptions import PQException, PQWarning + class RDFError(PQException): + """ Exception raised if something goes wrong during the RDF setup or calculation. """ @@ -22,7 +24,9 @@ def __init__(self, message: str) -> None: super().__init__(self.message) + class RDFWarning(PQWarning): + """ Warning raised if something goes wrong during the RDF setup or calculation. """ diff --git a/PQAnalysis/analysis/rdf/rdf.py b/PQAnalysis/analysis/rdf/rdf.py index 8e6ab949..99cb4c7b 100644 --- a/PQAnalysis/analysis/rdf/rdf.py +++ b/PQAnalysis/analysis/rdf/rdf.py @@ -15,7 +15,6 @@ from beartype.typing import Tuple from tqdm.auto import tqdm - # local absolute imports from PQAnalysis.config import with_progress_bar from PQAnalysis.types import Np1DNumberArray, PositiveInt, PositiveReal @@ -32,7 +31,9 @@ from .exceptions import RDFError + class RDF: + """ A class for calculating the radial distribution of a reference selection to a target selection. The radial @@ -69,17 +70,18 @@ class RDF: logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - traj: Trajectory | TrajectoryReader, - reference_species: SelectionCompatible, - target_species: SelectionCompatible, - use_full_atom_info: bool = False, - no_intra_molecular: bool = False, - n_bins: PositiveInt | None = None, - delta_r: PositiveReal | None = None, - r_max: PositiveReal | None = None, - r_min: PositiveReal | None = 0.0, - ): + def __init__( + self, + traj: Trajectory | TrajectoryReader, + reference_species: SelectionCompatible, + target_species: SelectionCompatible, + use_full_atom_info: bool = False, + no_intra_molecular: bool = False, + n_bins: PositiveInt | None = None, + delta_r: PositiveReal | None = None, + r_max: PositiveReal | None = None, + r_min: PositiveReal | None = 0.0, + ): """ Parameters ---------- @@ -209,8 +211,12 @@ def __init__(self, self.first_frame = next(self.frame_generator) self.topology = traj.topology - self._setup_bins(n_bins=n_bins, delta_r=delta_r, - r_max=r_max, r_min=self.r_min) + self._setup_bins( + n_bins=n_bins, + delta_r=delta_r, + r_max=r_max, + r_min=self.r_min + ) self.reference_indices = self.reference_selection.select( self.topology, @@ -222,12 +228,13 @@ def __init__(self, self.use_full_atom_info ) - def _setup_bins(self, - n_bins: PositiveInt | None = None, - delta_r: PositiveReal | None = None, - r_max: PositiveReal | None = None, - r_min: PositiveReal = 0.0 - ): + def _setup_bins( + self, + n_bins: PositiveInt | None = None, + delta_r: PositiveReal | None = None, + r_max: PositiveReal | None = None, + r_min: PositiveReal = 0.0 + ): """ Sets up the bins of the RDF analysis. @@ -279,9 +286,9 @@ def _setup_bins(self, elif all([n_bins, delta_r, r_max]): self.logger.error( ( - "It is not possible to specify all of n_bins, " - "delta_r and r_max in the same RDF analysis " - "as this would lead to ambiguous results." + "It is not possible to specify all of n_bins, " + "delta_r and r_max in the same RDF analysis " + "as this would lead to ambiguous results." ), exception=RDFError ) @@ -348,13 +355,14 @@ def _check_trajectory_conditions(self): Meaning that some frames are in vacuum and others are periodic. """ - if not check_trajectory_pbc(self.cells) and not check_trajectory_vacuum(self.cells): + if not check_trajectory_pbc( + self.cells) and not check_trajectory_vacuum(self.cells): self.logger.error( ( - "The provided trajectory is not fully periodic or " - "in vacuum, meaning that some frames are in vacuum " - "and others are periodic. This is not supported by " - "the RDF analysis." + "The provided trajectory is not fully periodic or " + "in vacuum, meaning that some frames are in vacuum " + "and others are periodic. This is not supported by " + "the RDF analysis." ), exception=RDFError ) @@ -365,13 +373,13 @@ def average_volume(self) -> PositiveReal: return np.mean([cell.volume for cell in self.cells]) @timeit_in_class - def run(self) -> Tuple[ - Np1DNumberArray, + def run( + self + ) -> Tuple[Np1DNumberArray, Np1DNumberArray, Np1DNumberArray, Np1DNumberArray, - Np1DNumberArray - ]: + Np1DNumberArray]: """ Runs the RDF analysis. @@ -442,9 +450,12 @@ def _initialize_target_index_combinations(self): if self.no_intra_molecular: for reference_index in self.reference_indices: - residue_indices = self.topology.residue_atom_indices[reference_index] + residue_indices = self.topology.residue_atom_indices[ + reference_index] self.target_index_combinations.append( - np.setdiff1d(self.target_indices, residue_indices)) + np.setdiff1d(self.target_indices, + residue_indices) + ) def _calculate_bins(self): """ @@ -458,11 +469,9 @@ def _calculate_bins(self): calculated from these distances. """ - for frame in tqdm( - self.frame_generator, + for frame in tqdm(self.frame_generator, total=self.n_frames, - disable=not with_progress_bar - ): + disable=not with_progress_bar): for i, reference_index in enumerate(self.reference_indices): if self.no_intra_molecular: @@ -486,13 +495,13 @@ def _calculate_bins(self): self.n_bins ) - def _finalize_run(self) -> Tuple[ + def _finalize_run( + self + ) -> Tuple[Np1DNumberArray, Np1DNumberArray, Np1DNumberArray, Np1DNumberArray, - Np1DNumberArray, - Np1DNumberArray - ]: + Np1DNumberArray]: """ Finalizes the RDF analysis after running. @@ -562,12 +571,13 @@ def n_atoms(self) -> int: return self.topology.n_atoms @classmethod - def _add_to_bins(cls, - distances: Np1DNumberArray, - r_min: PositiveReal, - delta_r: PositiveReal, - n_bins: PositiveInt - ) -> Np1DNumberArray: + def _add_to_bins( + cls, + distances: Np1DNumberArray, + r_min: PositiveReal, + delta_r: PositiveReal, + n_bins: PositiveInt + ) -> Np1DNumberArray: """ Returns the bins of the RDF analysis based on the provided distances. @@ -588,22 +598,20 @@ def _add_to_bins(cls, The bins of the RDF analysis. """ - distances = np.floor_divide( - distances - r_min, - delta_r - ).astype(int) + distances = np.floor_divide(distances - r_min, delta_r).astype(int) distances = distances[(distances < n_bins) & (distances >= 0)] return np.bincount(distances, minlength=n_bins) @classmethod - def _setup_bin_middle_points(cls, - n_bins: PositiveInt, - r_min: PositiveReal, - r_max: PositiveReal, - delta_r: PositiveReal - ) -> Np1DNumberArray: + def _setup_bin_middle_points( + cls, + n_bins: PositiveInt, + r_min: PositiveReal, + r_max: PositiveReal, + delta_r: PositiveReal + ) -> Np1DNumberArray: """ Sets up the middle points of the bins of the RDF analysis for outputting the RDF analysis. @@ -631,12 +639,13 @@ def _setup_bin_middle_points(cls, return bin_middle_points @classmethod - def _calculate_r_max(cls, - n_bins: PositiveInt, - delta_r: PositiveReal, - r_min: PositiveReal, - cells: Cells - ) -> PositiveReal: + def _calculate_r_max( + cls, + n_bins: PositiveInt, + delta_r: PositiveReal, + r_min: PositiveReal, + cells: Cells + ) -> PositiveReal: """ Calculates the maximum radius of the RDF analysis from the provided parameters. @@ -664,10 +673,7 @@ def _calculate_r_max(cls, return r_max @classmethod - def _check_r_max(cls, - r_max: PositiveReal, - cells: Cells - ) -> PositiveReal: + def _check_r_max(cls, r_max: PositiveReal, cells: Cells) -> PositiveReal: """ Checks if the provided maximum radius is larger than the maximum allowed radius according to the box vectors of the trajectory. @@ -698,11 +704,11 @@ def _check_r_max(cls, if check_trajectory_pbc(cells) and r_max > cls._infer_r_max(cells): cls.logger.warning( ( - f"The calculated r_max {r_max} is larger " - "than the maximum allowed radius according " - "to the box vectors of the trajectory " - f"{cls._infer_r_max(cells)}. r_max will be " - "set to the maximum allowed radius." + f"The calculated r_max {r_max} is larger " + "than the maximum allowed radius according " + "to the box vectors of the trajectory " + f"{cls._infer_r_max(cells)}. r_max will be " + "set to the maximum allowed radius." ), ) @@ -711,11 +717,13 @@ def _check_r_max(cls, return r_max @classmethod - def _calculate_n_bins(cls, - delta_r: PositiveReal, - r_max: PositiveReal, - r_min: PositiveReal - ) -> Tuple[PositiveInt, PositiveReal]: + def _calculate_n_bins( + cls, + delta_r: PositiveReal, + r_max: PositiveReal, + r_min: PositiveReal + ) -> Tuple[PositiveInt, + PositiveReal]: """ Calculates the number of bins of the RDF analysis from the provided parameters. @@ -775,10 +783,10 @@ def _infer_r_max(cls, cells: Cells) -> PositiveReal: if not check_trajectory_pbc(cells): cls.logger.error( ( - "To infer r_max of the RDF analysis, " - "the trajectory cannot be a vacuum trajectory. " - "Please specify r_max manually or use the " - "combination n_bins and delta_r." + "To infer r_max of the RDF analysis, " + "the trajectory cannot be a vacuum trajectory. " + "Please specify r_max manually or use the " + "combination n_bins and delta_r." ), exception=RDFError ) @@ -786,13 +794,14 @@ def _infer_r_max(cls, cells: Cells) -> PositiveReal: return np.min([cell.box_lengths for cell in cells]) / 2.0 @classmethod - def _norm(cls, - n_bins: int, - delta_r: PositiveReal, - target_density: PositiveReal, - n_reference_indices: int, - n_frames: int - ) -> Np1DNumberArray: + def _norm( + cls, + n_bins: int, + delta_r: PositiveReal, + target_density: PositiveReal, + n_reference_indices: int, + n_frames: int + ) -> Np1DNumberArray: """ Calculates the normalization of the RDF analysis based on a spherical shell model. @@ -822,17 +831,18 @@ def _norm(cls, large_radius_range = np.arange(1, n_bins + 1) delta_radius_range = large_radius_range**3 - small_radius_range**3 - delta_volume = delta_r ** 3 * delta_radius_range + delta_volume = delta_r**3 * delta_radius_range volume = surface_prefactor * delta_volume return volume * target_density * n_reference_indices * n_frames @classmethod - def _integration(cls, - bins: Np1DNumberArray, - n_reference_indices: int, - n_frames: int - ) -> Np1DNumberArray: + def _integration( + cls, + bins: Np1DNumberArray, + n_reference_indices: int, + n_frames: int + ) -> Np1DNumberArray: """ Calculates the integrated RDF analysis. The integral is calculated using a cumulative sum. diff --git a/PQAnalysis/analysis/rdf/rdf_input_file_reader.py b/PQAnalysis/analysis/rdf/rdf_input_file_reader.py index 7e7abd6d..143992ac 100644 --- a/PQAnalysis/analysis/rdf/rdf_input_file_reader.py +++ b/PQAnalysis/analysis/rdf/rdf_input_file_reader.py @@ -12,7 +12,9 @@ from PQAnalysis.type_checking import runtime_type_checking + class RDFInputFileReader(Reader): + """ A class to read input files to setup the :py:class:`~PQAnalysis.analysis.rdf.rdf.RDF` class. """ @@ -75,17 +77,12 @@ def read(self): super().check_required_keys(self.required_keys) super().check_known_keys(self.required_keys + self.optional_keys) - if ( - self.no_intra_molecular is not None and - ( - self.restart_file is None or - self.moldescriptor_file is None - ) - ): + if (self.no_intra_molecular is not None and + (self.restart_file is None or self.moldescriptor_file is None)): self.logger.error( ( - "The no_intra_molecular key can only be used " - "if both a restart file and a moldescriptor file are given." + "The no_intra_molecular key can only be used " + "if both a restart file and a moldescriptor file are given." ), exception=InputFileError, ) @@ -93,13 +90,14 @@ def read(self): if self.moldescriptor_file is not None and self.restart_file is None: self.logger.error( ( - "The moldescriptor_file key can only be " - "used in a meaningful way if a restart file is given." + "The moldescriptor_file key can only be " + "used in a meaningful way if a restart file is given." ), exception=InputFileError, ) + input_keys_documentation = f""" For the RDF analysis input file several keys are available of which some are required and some are optional. For more details on the grammar and syntax of the input file see :ref:`inputFile`. diff --git a/PQAnalysis/atomic_system/_decorators.py b/PQAnalysis/atomic_system/_decorators.py index 40a26f26..1f25a4d3 100644 --- a/PQAnalysis/atomic_system/_decorators.py +++ b/PQAnalysis/atomic_system/_decorators.py @@ -30,6 +30,7 @@ ) + @decorator def check_atom_number_setters(func, self, arg_to_set: Any) -> None: """ @@ -61,9 +62,9 @@ def check_atom_number_setters(func, self, arg_to_set: Any) -> None: print("test") self.logger.error( ( - "The number of atoms in the AtomicSystem object have " - "to be equal to the number of atoms in the new array " - "in order to set the property." + "The number of atoms in the AtomicSystem object have " + "to be equal to the number of atoms in the new array " + "in order to set the property." ), exception=AtomicSystemError ) @@ -71,6 +72,7 @@ def check_atom_number_setters(func, self, arg_to_set: Any) -> None: return func(self, arg_to_set) + @decorator def check_atoms_pos(func, *args, **kwargs): """ @@ -102,6 +104,7 @@ def check_atoms_pos(func, *args, **kwargs): return func(*args, **kwargs) + @decorator def check_atoms_has_mass(func, *args, **kwargs): """ diff --git a/PQAnalysis/atomic_system/_positions.py b/PQAnalysis/atomic_system/_positions.py index 73f4c624..f0a1123f 100644 --- a/PQAnalysis/atomic_system/_positions.py +++ b/PQAnalysis/atomic_system/_positions.py @@ -20,17 +20,21 @@ from ._decorators import check_atoms_pos + class _PositionsMixin: + """ A mixin class containing methods for related information to the positions of an atomic system. """ @check_atoms_pos - def _nearest_neighbours(self, - n: PositiveInt = 1, - indices: Np1DIntArray | None = None - ) -> Tuple[Np2DIntArray, Np2DNumberArray]: + def _nearest_neighbours( + self, + n: PositiveInt = 1, + indices: Np1DIntArray | None = None + ) -> Tuple[Np2DIntArray, + Np2DNumberArray]: """ Returns the 'n' nearest neighbours of selected atoms in the system. @@ -60,7 +64,7 @@ def _nearest_neighbours(self, distances = distance(self.pos[indices], self.pos, self.cell) - nearest_neighbours = np.argsort(distances, axis=-1)[:, 1:n+1] + nearest_neighbours = np.argsort(distances, axis=-1)[:, 1:n + 1] nearest_neighbours_distances = np.take_along_axis( distances, nearest_neighbours, @@ -70,11 +74,13 @@ def _nearest_neighbours(self, return nearest_neighbours, nearest_neighbours_distances @runtime_type_checking - def nearest_neighbours(self, - n: PositiveInt = 1, - selection: SelectionCompatible = None, - use_full_atom_info: bool = False - ) -> Tuple[Np2DIntArray, Np2DNumberArray]: + def nearest_neighbours( + self, + n: PositiveInt = 1, + selection: SelectionCompatible = None, + use_full_atom_info: bool = False + ) -> Tuple[Np2DIntArray, + Np2DNumberArray]: """ Returns the n nearest neighbours of the given atoms in the system. @@ -127,10 +133,9 @@ def nearest_neighbours(self, to the nearest neighbours. """ - indices = Selection(selection).select( - self.topology, - use_full_atom_info - ) + indices = Selection(selection + ).select(self.topology, + use_full_atom_info) return self._nearest_neighbours(n=n, indices=indices) diff --git a/PQAnalysis/atomic_system/_properties.py b/PQAnalysis/atomic_system/_properties.py index 6a372b8e..67fc6f58 100644 --- a/PQAnalysis/atomic_system/_properties.py +++ b/PQAnalysis/atomic_system/_properties.py @@ -14,10 +14,13 @@ from .exceptions import AtomicSystemError + class _PropertiesMixin: + """ A mixin class containing properties derived from the standard properties of an atomic system. """ + @property def pbc(self) -> bool: """bool: Whether the system has periodic boundary conditions.""" @@ -50,8 +53,8 @@ def n_atoms(self) -> int: if not np.all(n_atoms_list == n_atoms_list[0]): self.logger.error( ( - "The number of atoms (or atoms in the topology), " - "positions, velocities, forces and charges must be equal." + "The number of atoms (or atoms in the topology), " + "positions, velocities, forces and charges must be equal." ), exception=AtomicSystemError ) @@ -80,7 +83,10 @@ def center_of_mass(self) -> Np1DNumberArray: relative_pos = self.cell.image(self.pos - self.pos[0]) + self.pos[0] weighted_average_pos = np.sum( - relative_pos * self.atomic_masses[:, None], axis=0) / self.mass + relative_pos * self.atomic_masses[:, + None], + axis=0 + ) / self.mass center_of_mass = self.cell.image(weighted_average_pos) diff --git a/PQAnalysis/atomic_system/_standard_properties.py b/PQAnalysis/atomic_system/_standard_properties.py index 6a8dc5fe..c5534eb9 100644 --- a/PQAnalysis/atomic_system/_standard_properties.py +++ b/PQAnalysis/atomic_system/_standard_properties.py @@ -16,11 +16,14 @@ from ._decorators import check_atom_number_setters + class _StandardPropertiesMixin: + """ A mixin class containing the standard properties of an atomic system (i.e. standard getter and setter methods). """ + @property def atoms(self) -> Atoms: """Atoms: The atoms in the system.""" diff --git a/PQAnalysis/atomic_system/atomic_system.py b/PQAnalysis/atomic_system/atomic_system.py index 42b07425..7158988f 100644 --- a/PQAnalysis/atomic_system/atomic_system.py +++ b/PQAnalysis/atomic_system/atomic_system.py @@ -20,6 +20,7 @@ from PQAnalysis.utils.random import get_random_seed from PQAnalysis.utils.custom_logging import setup_logger from PQAnalysis import __package_name__ + from PQAnalysis.types import ( Np2DNumberArray, Np1DNumberArray, @@ -33,7 +34,11 @@ from .exceptions import AtomicSystemError -class AtomicSystem(_PropertiesMixin, _StandardPropertiesMixin, _PositionsMixin): + +class AtomicSystem(_PropertiesMixin, + _StandardPropertiesMixin, + _PositionsMixin): + """ A class for storing atomic systems. @@ -103,18 +108,19 @@ class AtomicSystem(_PropertiesMixin, _StandardPropertiesMixin, _PositionsMixin): logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - atoms: Atoms | None = None, - pos: Np2DNumberArray | None = None, - vel: Np2DNumberArray | None = None, - forces: Np2DNumberArray | None = None, - charges: Np1DNumberArray | None = None, - topology: Topology | None = None, - energy: float | None = None, - virial: Np3x3NumberArray | None = None, - stress: Np3x3NumberArray | None = None, - cell: Cell = Cell() - ) -> None: + def __init__( + self, + atoms: Atoms | None = None, + pos: Np2DNumberArray | None = None, + vel: Np2DNumberArray | None = None, + forces: Np2DNumberArray | None = None, + charges: Np1DNumberArray | None = None, + topology: Topology | None = None, + energy: float | None = None, + virial: Np3x3NumberArray | None = None, + stress: Np3x3NumberArray | None = None, + cell: Cell = Cell() + ) -> None: """ For the initialization of an AtomicSystem all parameters are optional. If no value is given for a parameter, the default value is used which @@ -163,8 +169,8 @@ def __init__(self, if topology is not None and atoms is not None: self.logger.error( ( - "Cannot initialize AtomicSystem with both atoms and topology " - "arguments - they are mutually exclusive." + "Cannot initialize AtomicSystem with both atoms and topology " + "arguments - they are mutually exclusive." ), exception=AtomicSystemError ) @@ -185,14 +191,15 @@ def __init__(self, self._cell = cell @runtime_type_checking - def fit_atomic_system(self, - system: "AtomicSystem", - number_of_additions: PositiveInt = 1, - max_iterations: PositiveInt = 100, - distance_cutoff: PositiveReal = 1.0, - max_displacement: PositiveReal | Np1DNumberArray = 0.1, - rotation_angle_step: PositiveInt = 10, - ) -> "List[AtomicSystem] | AtomicSystem": + def fit_atomic_system( + self, + system: "AtomicSystem", + number_of_additions: PositiveInt = 1, + max_iterations: PositiveInt = 100, + distance_cutoff: PositiveReal = 1.0, + max_displacement: PositiveReal | Np1DNumberArray = 0.1, + rotation_angle_step: PositiveInt = 10, + ) -> "List[AtomicSystem] | AtomicSystem": """ Fit the positions of the system to the positions of another system. @@ -216,14 +223,14 @@ def fit_atomic_system(self, First a random center of mass is chosen and a random displacement is applied to the system. Then the system is rotated in all possible - ways and the distances between the atoms are checked. If the + ways and the distances between the atoms are checked. If the distances are larger than the distance cutoff, the system is fitted. Returns ------- List[AtomicSystem] | AtomicSystem The fitted AtomicSystem(s). If number_of_additions is 1, - a single AtomicSystem is returned, otherwise a list of + a single AtomicSystem is returned, otherwise a list of AtomicSystems is returned. Raises @@ -247,35 +254,38 @@ def fit_atomic_system(self, ) self.fitting_logger.info( - f"Performing fitting for { - i + 1}/{number_of_additions} addition(s)." + ( + f"Performing fitting for {i + 1}/{number_of_additions} " + "addition(s)." + ) ) systems.append( self._fit_atomic_system( - positions_to_fit_into=positions_to_fit_into, - system=system, - max_iterations=max_iterations, - distance_cutoff=distance_cutoff, - max_displacement=max_displacement, - rotation_angle_step=rotation_angle_step + positions_to_fit_into=positions_to_fit_into, + system=system, + max_iterations=max_iterations, + distance_cutoff=distance_cutoff, + max_displacement=max_displacement, + rotation_angle_step=rotation_angle_step ) ) return systems if number_of_additions > 1 else systems[0] - def _fit_atomic_system(self, - positions_to_fit_into: Np2DNumberArray, - system: "AtomicSystem", - max_iterations: PositiveInt = 100, - distance_cutoff: PositiveReal = 1.0, - max_displacement: PositiveReal | Np1DNumberArray = 0.1, - rotation_angle_step: PositiveInt = 10, - ) -> "AtomicSystem": + def _fit_atomic_system( + self, + positions_to_fit_into: Np2DNumberArray, + system: "AtomicSystem", + max_iterations: PositiveInt = 100, + distance_cutoff: PositiveReal = 1.0, + max_displacement: PositiveReal | Np1DNumberArray = 0.1, + rotation_angle_step: PositiveInt = 10, + ) -> "AtomicSystem": """ Fit the positions of the system to the positions of another system. - First a random center of mass is chosen and a random displacement + First a random center of mass is chosen and a random displacement is applied to the system. Then the system is rotated in all possible ways and the distances between the atoms are checked. If the distances are larger than the distance cutoff, the system is fitted. @@ -308,7 +318,7 @@ def _fit_atomic_system(self, ValueError If the maximum displacement percentage is negative. AtomicSystemError - If the system could not be fitted into the positions + If the system could not be fitted into the positions of the AtomicSystem within the maximum number of iterations. """ @@ -344,10 +354,7 @@ def _fit_atomic_system(self, rotation = Rotation.random(random_state=rng) for x, y, z in itertools.product(range(0, 360, rotation_angle_step), repeat=3): - rotation_angles = rotation.as_euler( - 'xyz', - degrees=True - ) + rotation_angles = rotation.as_euler('xyz', degrees=True) rotation_angles += np.array([x, y, z]) rotation = Rotation.from_euler( 'xyz', @@ -383,7 +390,7 @@ def _fit_atomic_system(self, # TODO: refactor or discard this method def compute_com_atomic_system(self, group=None) -> "AtomicSystem": """ - Computes a new AtomicSystem with the center of mass of the system or groups of atoms. + Computes a new AtomicSystem with the center of mass of the system or groups of atoms. Parameters ---------- @@ -405,14 +412,18 @@ def compute_com_atomic_system(self, group=None) -> "AtomicSystem": elif self.n_atoms % group != 0: raise AtomicSystemError( - 'Number of atoms in selection is not a multiple of group.') + 'Number of atoms in selection is not a multiple of group.' + ) pos = [] names = [] for i in range(0, self.n_atoms, group): atomic_system = AtomicSystem( - atoms=self.atoms[i:i+group], pos=self.pos[i:i+group], cell=self.cell) + atoms=self.atoms[i:i + group], + pos=self.pos[i:i + group], + cell=self.cell + ) pos.append(atomic_system.center_of_mass) names.append(atomic_system.combined_name) @@ -479,7 +490,10 @@ def __eq__(self, other: Any) -> bool: return True - def __getitem__(self, key: Atom | int | slice | Np1DIntArray) -> "AtomicSystem": + def __getitem__( + self, + key: Atom | int | slice | Np1DIntArray + ) -> "AtomicSystem": """ Returns a new AtomicSystem with the given key. @@ -531,8 +545,8 @@ def __getitem__(self, key: Atom | int | slice | Np1DIntArray) -> "AtomicSystem": if isinstance(key, int): key = np.array([key]) - keys = np.array(range(self.n_atoms))[key] if isinstance( - key, slice) else np.array(key) + keys = np.array(range(self.n_atoms))[key] if isinstance(key, + slice) else np.array(key) pos = self.pos[keys] if np.shape(self.pos)[0] > 0 else None vel = self.vel[keys] if np.shape(self.vel)[0] > 0 else None diff --git a/PQAnalysis/atomic_system/exceptions.py b/PQAnalysis/atomic_system/exceptions.py index 9a1c9668..835079b4 100644 --- a/PQAnalysis/atomic_system/exceptions.py +++ b/PQAnalysis/atomic_system/exceptions.py @@ -5,7 +5,9 @@ from PQAnalysis.exceptions import PQException + class AtomicSystemError(PQException): + """ Exception raised for errors related to the AtomicSystem class """ @@ -21,7 +23,9 @@ def __init__(self, message: str) -> None: super().__init__(self.message) + class AtomicSystemPositionsError(AtomicSystemError): + """ Exception raised if atoms is not of the same length as positions """ @@ -41,7 +45,9 @@ def __init__(self, message: str | None = None) -> None: super().__init__(self.message) + class AtomicSystemMassError(AtomicSystemError): + """ Exception raised if atoms do not contain mass information """ diff --git a/PQAnalysis/cli/_argument_parser.py b/PQAnalysis/cli/_argument_parser.py index 09d9fab8..93ecee49 100644 --- a/PQAnalysis/cli/_argument_parser.py +++ b/PQAnalysis/cli/_argument_parser.py @@ -11,7 +11,6 @@ import argparse import argcomplete - from beartype.typing import Sequence from rich_argparse import ArgumentDefaultsRichHelpFormatter @@ -23,7 +22,9 @@ from PQAnalysis._version import __version__ + class _ArgumentParser(argparse.ArgumentParser): + """ The _ArgumentParser class is a subclass of the argparse.ArgumentParser class. It provides a set of methods that are used in many cli scripts of this package. @@ -63,10 +64,11 @@ def __init__(self, *args, **kwargs): **kwargs ) - def parse_args(self, - args: Sequence[str] | None = None, - namespace: None = None - ) -> argparse.Namespace: + def parse_args( + self, + args: Sequence[str] | None = None, + namespace: None = None + ) -> argparse.Namespace: """ The parse_args method is the same as the parse_args method of the argparse.ArgumentParser class. The only difference is that the progress @@ -103,11 +105,7 @@ def parse_input_file(self): The parse_input_file method adds the input_file argument to the parser. The input_file argument is a positional argument and is required. """ - super().add_argument( - 'input_file', - type=str, - help='The input file.' - ) + super().add_argument('input_file', type=str, help='The input file.') def parse_trajectory_file(self): """ @@ -127,10 +125,12 @@ def parse_output_file(self): The output_file argument is an optional argument and defaults to None. """ super().add_argument( - '-o', '--output', + '-o', + '--output', type=str, default=None, - help='The output file. If not specified, the output is printed to stdout.' + help= + 'The output file. If not specified, the output is printed to stdout.' ) def parse_engine(self): @@ -158,9 +158,9 @@ def parse_mode(self): type=FileWritingMode, default=FileWritingMode("w"), help=( - 'The writing mode. The following modes ' - 'are available: "w": write, "a": append, ' - '"o": overwrite.' + 'The writing mode. The following modes ' + 'are available: "w": write, "a": append, ' + '"o": overwrite.' ) ) @@ -200,14 +200,14 @@ def _parse_log_file(self): const="__LOG_DEFINED_BY_TIMESTAMP__", nargs='?', help=( - "This options can be used to set wether " - "a log file should be used or not. " - "If the option is given without a value, " - "the log will be printed to an " - "automatically generated file. If the option " - "is not given, the log will be only printed " - "to stderr. If the option is given with a " - "value, the log will be printed to the given file." + "This options can be used to set wether " + "a log file should be used or not. " + "If the option is given without a value, " + "the log will be printed to an " + "automatically generated file. If the option " + "is not given, the log will be only printed " + "to stderr. If the option is given with a " + "value, the log will be printed to the given file." ) ) diff --git a/PQAnalysis/cli/_cli_base.py b/PQAnalysis/cli/_cli_base.py index 3dff11b4..679f3ad0 100644 --- a/PQAnalysis/cli/_cli_base.py +++ b/PQAnalysis/cli/_cli_base.py @@ -5,7 +5,9 @@ from abc import ABCMeta, abstractmethod + class CLIBase(metaclass=ABCMeta): + """ Abstract base class for all the CLI classes. """ diff --git a/PQAnalysis/cli/add_molecules.py b/PQAnalysis/cli/add_molecules.py index e410ec95..8ba267b0 100644 --- a/PQAnalysis/cli/add_molecules.py +++ b/PQAnalysis/cli/add_molecules.py @@ -14,7 +14,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool can be used to add molecules to a restart file. @@ -34,10 +33,13 @@ __doc__ += __outputdoc__ + class AddMoleculesCLI(CLIBase): + """ Command Line Tool for Adding Molecules to Restart Files """ + @classmethod def program_name(cls) -> str: """ @@ -71,9 +73,9 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: 'molecule_file', type=str, help=( - "The molecule file that contains the coordinates " - "of the molecule that should be added. Can be in " - "any format that is supported by the PQAnalysis library." + "The molecule file that contains the coordinates " + "of the molecule that should be added. Can be in " + "any format that is supported by the PQAnalysis library." ) ) @@ -87,9 +89,9 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: default=OutputFileFormat.AUTO, choices=OutputFileFormat.__members__.values(), help=( - 'The file format of the molecule file. ' - 'If not specified, the file format will ' - 'be inferred from the file extension.' + 'The file format of the molecule file. ' + 'If not specified, the file format will ' + 'be inferred from the file extension.' ) ) @@ -98,9 +100,9 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: dest='rst_mol_desc_file', type=str, help=( - "The moldescriptor file that is associated with the " - "restart file. If not specified, the moldescriptor " - "file will not be used." + "The moldescriptor file that is associated with the " + "restart file. If not specified, the moldescriptor " + "file will not be used." ), default=None ) @@ -110,10 +112,10 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: dest='molecule_mol_desc_file', type=str, help=( - "The moldescriptor file that is associated with " - "the molecule file. If not specified, the moldescriptor " - "file will not be used. Can only be used if the " - "molecule file is a restart file type." + "The moldescriptor file that is associated with " + "the molecule file. If not specified, the moldescriptor " + "file will not be used. Can only be used if the " + "molecule file is a restart file type." ), default=None ) @@ -123,7 +125,8 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: dest='n_molecules', type=int, default=1, - help="The number of molecules that should be added to the restart file." + help= + "The number of molecules that should be added to the restart file." ) parser.add_argument( @@ -132,75 +135,82 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: type=int, default=100, help=( - "The maximum number of iterations that should " - "be used to fit the molecule to the restart file." + "The maximum number of iterations that should " + "be used to fit the molecule to the restart file." ) ) parser.add_argument( - "-c", "--cut", + "-c", + "--cut", dest='cut', type=float, default=1.0, help=( - "The distance cutoff that should be used " - "to fit the molecule to the restart file in Angstrom." + "The distance cutoff that should be used " + "to fit the molecule to the restart file in Angstrom." ) ) parser.add_argument( - "--max-disp", "--max-displacement", + "--max-disp", + "--max-displacement", dest='max_disp', type=float, default=0.1, help=( - "The maximum displacement that should be applied " - "to the given molecule geometry relative to " - "its center of mass in percentage." + "The maximum displacement that should be applied " + "to the given molecule geometry relative to " + "its center of mass in percentage." ) ) parser.add_argument( - "--rot", "--rotation-angle-step", + "--rot", + "--rotation-angle-step", dest='rot', type=int, default=10, help=( - "If the randomly placed molecule does not " - "fit into the restart file, the molecule " - "is rotated by the given angle step in degrees." + "If the randomly placed molecule does not " + "fit into the restart file, the molecule " + "is rotated by the given angle step in degrees." ) ) parser.add_argument( - "--topology-file", "--top-file", + "--topology-file", + "--top-file", dest='top_file', type=str, help=( - "The topology file that is associated with " - "the restart file. If not specified, " - "the topology file will not be used." + "The topology file that is associated with " + "the restart file. If not specified, " + "the topology file will not be used." ), default=None ) parser.add_argument( - "--added-topology-file", "--added-top-file", + "--added-topology-file", + "--added-top-file", dest='added_top_file', type=str, help=( - "The topology file that is associated with " - "the molecule file. If not specified, the " - "topology file will not be used." + "The topology file that is associated with " + "the molecule file. If not specified, the " + "topology file will not be used." ), default=None ) parser.add_argument( - "--output-topology-file", "--output-top-file", + "--output-topology-file", + "--output-top-file", dest='output_top_file', type=str, - help="The output topology file. If not specified, the output is printed to stdout.", + help= + "The output topology file. If not specified, the output is printed to stdout.", default=None ) @@ -236,6 +246,7 @@ def run(cls, args): ) + def main(): """ Main function of the add_molecules command line tool, diff --git a/PQAnalysis/cli/build_nep_traj.py b/PQAnalysis/cli/build_nep_traj.py index 869cf95c..d9624a66 100644 --- a/PQAnalysis/cli/build_nep_traj.py +++ b/PQAnalysis/cli/build_nep_traj.py @@ -12,7 +12,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool can be used to convert output of PQ of QMCFC simulations to training and test files for the Neuroevolution Potential (NEP) method. The output is written to a xyz file. @@ -27,10 +26,13 @@ __doc__ += __outputdoc__ + class BuildNEPTrajCLI(CLIBase): + """ Command Line Tool for Building Neuroevolution Potential (NEP) training/test trajectories """ + @classmethod def program_name(cls) -> str: """ @@ -67,17 +69,17 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: type=float, default=0.0, help=( - "The ratio of testing frames to the total number of " - "frames, by default 0.0. If the test_ratio is 0.0 no " - "train and test files are created. If the test_ratio " - "is larger not equal to 0.0, the test_ratio is used " - "to determine the number of training and testing frames. " - "The final ratio will be as close to the test_ratio as " - "possible, but if it is not possible to have the exact " - "ratio, always the higher next higher ratio is chosen. " - "As output filenames the original filename is used with " - "the suffix _train or _test appended and the same " - "FileWritingMode as the original file is used." + "The ratio of testing frames to the total number of " + "frames, by default 0.0. If the test_ratio is 0.0 no " + "train and test files are created. If the test_ratio " + "is larger not equal to 0.0, the test_ratio is used " + "to determine the number of training and testing frames. " + "The final ratio will be as close to the test_ratio as " + "possible, but if it is not possible to have the exact " + "ratio, always the higher next higher ratio is chosen. " + "As output filenames the original filename is used with " + "the suffix _train or _test appended and the same " + "FileWritingMode as the original file is used." ) ) @@ -86,30 +88,30 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: type=str, default=None, help=( - "The total_ratios keyword argument is used to " - "describe frame ratios including validation frames " - "in the format train_ratio:test_ratio:validation_ratio. " - "The validation_ratio is optional and if not given, " - "no validation frames are written. The total sum of " - "the integer values provided do not have to add up " - "to the total number of frames in the input trajectory " - "files. The ratios are used to determine the ratios of " - "the training, testing, and validation frames. The final" - "ratio will be as close to the given ratios as possible, " - "but if it is not possible to have the exact ratio, " - "always the next higher ratio is chosen. As output " - "filenames the original filename is used with the suffix " - "_train, _test, or _validation appended and the same " - "FileWritingMode as the original file is used. The " - "validation frames are written to a file with the " - "suffix _validation and a file with the suffix _validation.ref. " - "The _validation file contains only the coordinates and " - "box information to function as crude testing input and " - "the _validation.ref file contains all information " - "additionally provided in the original files. " - "Pay Attention: This keyword argument is mutually exclusive " - "with the test_ratio keyword argument. If both are given, " - "a ValueError is raised." + "The total_ratios keyword argument is used to " + "describe frame ratios including validation frames " + "in the format train_ratio:test_ratio:validation_ratio. " + "The validation_ratio is optional and if not given, " + "no validation frames are written. The total sum of " + "the integer values provided do not have to add up " + "to the total number of frames in the input trajectory " + "files. The ratios are used to determine the ratios of " + "the training, testing, and validation frames. The final" + "ratio will be as close to the given ratios as possible, " + "but if it is not possible to have the exact ratio, " + "always the next higher ratio is chosen. As output " + "filenames the original filename is used with the suffix " + "_train, _test, or _validation appended and the same " + "FileWritingMode as the original file is used. The " + "validation frames are written to a file with the " + "suffix _validation and a file with the suffix _validation.ref. " + "The _validation file contains only the coordinates and " + "box information to function as crude testing input and " + "the _validation.ref file contains all information " + "additionally provided in the original files. " + "Pay Attention: This keyword argument is mutually exclusive " + "with the test_ratio keyword argument. If both are given, " + "a ValueError is raised." ) ) @@ -158,6 +160,7 @@ def run(cls, args): ) + def main(): """ Main function of the build_nep_traj command line tool, which is basically diff --git a/PQAnalysis/cli/continue_input.py b/PQAnalysis/cli/continue_input.py index cec81596..4a018ac1 100644 --- a/PQAnalysis/cli/continue_input.py +++ b/PQAnalysis/cli/continue_input.py @@ -10,7 +10,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool generates n new input files by increasing the number in the filename by one. @@ -28,10 +27,13 @@ __epilog__ += "\n" + class ContinueInputCLI(CLIBase): + """ Command Line Tool for Extending PQ MD Simulation Input Files """ + @classmethod def program_name(cls) -> str: """ @@ -57,7 +59,8 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: parser.parse_input_file() parser.add_argument( - '-n', '--number', + '-n', + '--number', type=int, default=1, help='The number of times the input file should be continued.' @@ -82,11 +85,8 @@ def run(cls, args): """ input_format = InputFileFormat(args.input_format) - continue_input_file( - args.input_file, - args.number, - input_format - ) + continue_input_file(args.input_file, args.number, input_format) + def main(): diff --git a/PQAnalysis/cli/gen2xyz.py b/PQAnalysis/cli/gen2xyz.py index 329e02aa..216b4d1e 100644 --- a/PQAnalysis/cli/gen2xyz.py +++ b/PQAnalysis/cli/gen2xyz.py @@ -12,7 +12,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool can be used to convert gen files to xyz files. @@ -30,10 +29,13 @@ __doc__ += __outputdoc__ + class GEN2XYZCLI(CLIBase): + """ Command Line Tool for Converting GEN Files to XYZ Files """ + @classmethod def program_name(cls) -> str: """ @@ -92,6 +94,7 @@ def run(cls, args): ) + def main(): """ Main function of the gen2xyz command line tool, which is basically just diff --git a/PQAnalysis/cli/main.py b/PQAnalysis/cli/main.py index a76d5603..3bab525d 100644 --- a/PQAnalysis/cli/main.py +++ b/PQAnalysis/cli/main.py @@ -27,15 +27,14 @@ __epilog__ += "\n" + def main(): """ The main function of the PQAnalysis command line interface. """ parser = _ArgumentParser(description=__outputdoc__, epilog=__epilog__) - subparsers = parser.add_subparsers( - dest='cli_command', - ) + subparsers = parser.add_subparsers(dest='cli_command', ) sub_parser_dict = { AddMoleculesCLI.program_name(): AddMoleculesCLI, diff --git a/PQAnalysis/cli/rdf.py b/PQAnalysis/cli/rdf.py index 82c268af..f885a0e5 100644 --- a/PQAnalysis/cli/rdf.py +++ b/PQAnalysis/cli/rdf.py @@ -36,10 +36,13 @@ __doc__ += input_keys_documentation + class RDFCLI(CLIBase): + """ Command Line Tool for RDF Analysis """ + @classmethod def program_name(cls) -> str: """ @@ -78,6 +81,7 @@ def run(cls, args): rdf(args.input_file, args.engine) + def main(): """ The main function of the RDF analysis command line tool, diff --git a/PQAnalysis/cli/rst2xyz.py b/PQAnalysis/cli/rst2xyz.py index b77fb132..89599699 100644 --- a/PQAnalysis/cli/rst2xyz.py +++ b/PQAnalysis/cli/rst2xyz.py @@ -7,13 +7,11 @@ """ - from PQAnalysis.io import rst2xyz from PQAnalysis.config import code_base_url from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool can be used to convert restart files to xyz files. @@ -31,10 +29,13 @@ __doc__ += __outputdoc__ + class Rst2XYZCLI(CLIBase): + """ Command Line Tool for Converting Restart Files to XYZ Files """ + @classmethod def program_name(cls) -> str: """ @@ -93,6 +94,7 @@ def run(cls, args): ) + def main(): """ Main function of the rst2xyz command line tool, which is basically just a wrapper diff --git a/PQAnalysis/cli/traj2box.py b/PQAnalysis/cli/traj2box.py index 7b423673..93cd7311 100644 --- a/PQAnalysis/cli/traj2box.py +++ b/PQAnalysis/cli/traj2box.py @@ -11,7 +11,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ Converts multiple trajectory files to a box file. @@ -34,10 +33,13 @@ __doc__ += __outputdoc__ + class Traj2BoxCLI(CLIBase): + """ Command Line Tool for Converting Trajectory Files to Box Files """ + @classmethod def program_name(cls) -> str: """ @@ -90,6 +92,7 @@ def run(cls, args): traj2box(args.trajectory_file, args.vmd, args.output, args.mode) + def main(): """ Main function of the traj2box command line tool, which is basically just a diff --git a/PQAnalysis/cli/traj2qmcfc.py b/PQAnalysis/cli/traj2qmcfc.py index 8d2764ea..8594e8cc 100644 --- a/PQAnalysis/cli/traj2qmcfc.py +++ b/PQAnalysis/cli/traj2qmcfc.py @@ -11,7 +11,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ Converts a PQ trajectory to a QMCFC trajectory format output. @@ -30,10 +29,13 @@ __doc__ += __outputdoc__ + class Traj2QMCFCCLI(CLIBase): + """ Command Line Tool for Converting PQ to QMCFC Trajectory Files """ + @classmethod def program_name(cls) -> str: """ @@ -79,6 +81,7 @@ def run(cls, args) -> None: traj2qmcfc(args.trajectory_file, args.output) + def main(): """ Main function of the traj2qmcfc command line tool, which is basically just a diff --git a/PQAnalysis/cli/xyz2gen.py b/PQAnalysis/cli/xyz2gen.py index 770336a0..c3f7f1be 100644 --- a/PQAnalysis/cli/xyz2gen.py +++ b/PQAnalysis/cli/xyz2gen.py @@ -12,7 +12,6 @@ from ._argument_parser import _ArgumentParser from ._cli_base import CLIBase - __outputdoc__ = """ This command line tool can be used to convert xyz files to gen files. @@ -27,10 +26,13 @@ __doc__ += __outputdoc__ + class XYZ2GENCLI(CLIBase): + """ Command Line Tool for Converting XYZ Files to GEN Files """ + @classmethod def program_name(cls) -> str: """ @@ -63,11 +65,13 @@ def add_arguments(cls, parser: _ArgumentParser) -> None: parser.add_argument( 'periodic', - choices=[True, False, None], + choices=[True, + False, + None], default=None, help=( - 'If True, the box is printed. If False, the box is not printed. ' - 'If None, the box is printed if it is present in the xyz file.' + 'If True, the box is printed. If False, the box is not printed. ' + 'If None, the box is printed if it is present in the xyz file.' ) ) @@ -93,6 +97,7 @@ def run(cls, args): ) + def main(): """ Main function of the xyz2gen command line tool, which is basically just diff --git a/PQAnalysis/core/api.py b/PQAnalysis/core/api.py index 76de187e..5b214578 100644 --- a/PQAnalysis/core/api.py +++ b/PQAnalysis/core/api.py @@ -11,12 +11,14 @@ from PQAnalysis.type_checking import runtime_type_checking + @runtime_type_checking -def distance(pos1: Np1DNumberArray | Np2DNumberArray, - pos2: Np1DNumberArray | Np2DNumberArray, - cell: _cell.Cell = _cell.Cell(), - **kwargs - ) -> PositiveReal | Np1DNumberArray | Np2DNumberArray: +def distance( + pos1: Np1DNumberArray | Np2DNumberArray, + pos2: Np1DNumberArray | Np2DNumberArray, + cell: _cell.Cell = _cell.Cell(), + **kwargs +) -> PositiveReal | Np1DNumberArray | Np2DNumberArray: """ Returns the distances between all combinations of two position arrays. diff --git a/PQAnalysis/core/atom/element.py b/PQAnalysis/core/atom/element.py index 5ff0bb62..20bc6d31 100644 --- a/PQAnalysis/core/atom/element.py +++ b/PQAnalysis/core/atom/element.py @@ -17,7 +17,9 @@ from ..exceptions import ElementNotFoundError + class Element: + """ A class representing an element. @@ -155,92 +157,239 @@ def mass(self) -> Real | None: return self._mass + #: A type hint for a list of elements Elements = NewType( - "Elements", Annotated[ - list, Is[ - lambda list: all(isinstance(element, Element) for element in list) - ] - ] + "Elements", + Annotated[list, + Is[lambda list: all(isinstance(element, + Element) for element in list)]] ) - -atomicMasses = {"h": 1.00794, "d": 2.014101778, "t": 3.0160492675, - "he": 4.002602, "li": 6.941, "be": 9.012182, - "b": 10.811, "c": 12.0107, "n": 14.0067, - "o": 15.9994, "f": 18.9984032, "ne": 20.1797, - "na": 22.989770, "mg": 24.3050, "al": 26.981538, - "si": 28.0855, "p": 30.973761, "s": 32.065, - "cl": 35.453, "ar": 39.948, "k": 39.0983, - "ca": 40.078, "sc": 44.955910, "ti": 47.880, - "v": 50.9415, "cr": 51.9961, "mn": 54.938049, - "fe": 55.845, "co": 58.933200, "ni": 58.6934, - "cu": 63.546, "zn": 65.399, "ga": 69.723, - "ge": 72.64, "as": 74.92160, "se": 78.96, - "br": 79.904, "kr": 83.798, "rb": 85.4678, - "sr": 87.62, "y": 88.90585, "zr": 91.224, - "nb": 92.90638, "mo": 95.94, "tc": 98.9063, - "ru": 101.07, "rh": 102.9055, "pd": 106.42, - "ag": 107.8682, "cd": 112.411, "in": 114.818, - "sn": 118.71, "sb": 121.76, "te": 127.6, - "i": 126.90447, "xe": 131.293, "cs": 132.90546, - "ba": 137.327, "la": 138.9055, "ce": 140.116, - "pr": 140.90765, "nd": 144.24, "pm": 146.9151, - "sm": 150.36, "eu": 151.964, "gd": 157.25, - "tb": 158.92534, "dy": 162.5, "ho": 164.93032, - "er": 167.259, "tm": 168.93421, "yb": 173.04, - "lu": 174.967, "hf": 178.49, "ta": 180.9479, - "w": 183.84, "re": 186.207, "os": 190.23, - "ir": 192.217, "pt": 195.078, "au": 196.96655, - "hg": 200.59, "tl": 204.3833, "pb": 207.2, - "bi": 208.98038, "po": 208.9824, "at": 209.9871, - "rn": 222.0176, "fr": 223.0197, "ra": 226.0254, - "ac": 227.0278, "th": 232.0381, "pa": 231.03588, - "u": 238.0289, "np": 237.0482, "pu": 244.0642, - "am": 243.0614, "cm": 247.0703, "bk": 247.0703, - "cf": 251.0796, "es": 252.0829, "fm": 257.0951, - "md": 258.0986, "no": 259.1009, "lr": 260.1053, - "q": 999.00000, "x": 999.00000, "cav": 1000.00000, - "sup": 1000000.0, - "dum": 1.0} - -atomicNumbers = {"h": 1, "d": 1, "t": 1, - "he": 2, "li": 3, "be": 4, - "b": 5, "c": 6, "n": 7, - "o": 8, "f": 9, "ne": 10, - "na": 11, "mg": 12, "al": 13, - "si": 14, "p": 15, "s": 16, - "cl": 17, "ar": 18, "k": 19, - "ca": 20, "sc": 21, "ti": 22, - "v": 23, "cr": 24, "mn": 25, - "fe": 26, "co": 27, "ni": 28, - "cu": 29, "zn": 30, "ga": 31, - "ge": 32, "as": 33, "se": 34, - "br": 35, "kr": 36, "rb": 37, - "sr": 38, "y": 39, "zr": 40, - "nb": 41, "mo": 42, "tc": 43, - "ru": 44, "rh": 45, "pd": 46, - "ag": 47, "cd": 48, "in": 49, - "sn": 50, "sb": 51, "te": 52, - "i": 53, "xe": 54, "cs": 55, - "ba": 56, "la": 57, "ce": 58, - "pr": 59, "nd": 60, "pm": 61, - "sm": 62, "eu": 63, "gd": 64, - "tb": 65, "dy": 66, "ho": 67, - "er": 68, "tm": 69, "yb": 70, - "lu": 71, "hf": 72, "ta": 73, - "w": 74, "re": 75, "os": 76, - "ir": 77, "pt": 78, "au": 79, - "hg": 80, "tl": 81, "pb": 82, - "bi": 83, "po": 84, "at": 85, - "rn": 86, "fr": 87, "ra": 88, - "ac": 89, "th": 90, "pa": 91, - "u": 92, "np": 93, "pu": 94, - "am": 95, "cm": 96, "bk": 97, - "cf": 98, "es": 99, "fm": 100, - "md": 101, "no": 102, "lr": 103, - "q": 999, "x": 999, "cav": 1000, - "sup": 1000000, - "dum": 1} +atomicMasses = { + "h": 1.00794, + "d": 2.014101778, + "t": 3.0160492675, + "he": 4.002602, + "li": 6.941, + "be": 9.012182, + "b": 10.811, + "c": 12.0107, + "n": 14.0067, + "o": 15.9994, + "f": 18.9984032, + "ne": 20.1797, + "na": 22.989770, + "mg": 24.3050, + "al": 26.981538, + "si": 28.0855, + "p": 30.973761, + "s": 32.065, + "cl": 35.453, + "ar": 39.948, + "k": 39.0983, + "ca": 40.078, + "sc": 44.955910, + "ti": 47.880, + "v": 50.9415, + "cr": 51.9961, + "mn": 54.938049, + "fe": 55.845, + "co": 58.933200, + "ni": 58.6934, + "cu": 63.546, + "zn": 65.399, + "ga": 69.723, + "ge": 72.64, + "as": 74.92160, + "se": 78.96, + "br": 79.904, + "kr": 83.798, + "rb": 85.4678, + "sr": 87.62, + "y": 88.90585, + "zr": 91.224, + "nb": 92.90638, + "mo": 95.94, + "tc": 98.9063, + "ru": 101.07, + "rh": 102.9055, + "pd": 106.42, + "ag": 107.8682, + "cd": 112.411, + "in": 114.818, + "sn": 118.71, + "sb": 121.76, + "te": 127.6, + "i": 126.90447, + "xe": 131.293, + "cs": 132.90546, + "ba": 137.327, + "la": 138.9055, + "ce": 140.116, + "pr": 140.90765, + "nd": 144.24, + "pm": 146.9151, + "sm": 150.36, + "eu": 151.964, + "gd": 157.25, + "tb": 158.92534, + "dy": 162.5, + "ho": 164.93032, + "er": 167.259, + "tm": 168.93421, + "yb": 173.04, + "lu": 174.967, + "hf": 178.49, + "ta": 180.9479, + "w": 183.84, + "re": 186.207, + "os": 190.23, + "ir": 192.217, + "pt": 195.078, + "au": 196.96655, + "hg": 200.59, + "tl": 204.3833, + "pb": 207.2, + "bi": 208.98038, + "po": 208.9824, + "at": 209.9871, + "rn": 222.0176, + "fr": 223.0197, + "ra": 226.0254, + "ac": 227.0278, + "th": 232.0381, + "pa": 231.03588, + "u": 238.0289, + "np": 237.0482, + "pu": 244.0642, + "am": 243.0614, + "cm": 247.0703, + "bk": 247.0703, + "cf": 251.0796, + "es": 252.0829, + "fm": 257.0951, + "md": 258.0986, + "no": 259.1009, + "lr": 260.1053, + "q": 999.00000, + "x": 999.00000, + "cav": 1000.00000, + "sup": 1000000.0, + "dum": 1.0 +} + +atomicNumbers = { + "h": 1, + "d": 1, + "t": 1, + "he": 2, + "li": 3, + "be": 4, + "b": 5, + "c": 6, + "n": 7, + "o": 8, + "f": 9, + "ne": 10, + "na": 11, + "mg": 12, + "al": 13, + "si": 14, + "p": 15, + "s": 16, + "cl": 17, + "ar": 18, + "k": 19, + "ca": 20, + "sc": 21, + "ti": 22, + "v": 23, + "cr": 24, + "mn": 25, + "fe": 26, + "co": 27, + "ni": 28, + "cu": 29, + "zn": 30, + "ga": 31, + "ge": 32, + "as": 33, + "se": 34, + "br": 35, + "kr": 36, + "rb": 37, + "sr": 38, + "y": 39, + "zr": 40, + "nb": 41, + "mo": 42, + "tc": 43, + "ru": 44, + "rh": 45, + "pd": 46, + "ag": 47, + "cd": 48, + "in": 49, + "sn": 50, + "sb": 51, + "te": 52, + "i": 53, + "xe": 54, + "cs": 55, + "ba": 56, + "la": 57, + "ce": 58, + "pr": 59, + "nd": 60, + "pm": 61, + "sm": 62, + "eu": 63, + "gd": 64, + "tb": 65, + "dy": 66, + "ho": 67, + "er": 68, + "tm": 69, + "yb": 70, + "lu": 71, + "hf": 72, + "ta": 73, + "w": 74, + "re": 75, + "os": 76, + "ir": 77, + "pt": 78, + "au": 79, + "hg": 80, + "tl": 81, + "pb": 82, + "bi": 83, + "po": 84, + "at": 85, + "rn": 86, + "fr": 87, + "ra": 88, + "ac": 89, + "th": 90, + "pa": 91, + "u": 92, + "np": 93, + "pu": 94, + "am": 95, + "cm": 96, + "bk": 97, + "cf": 98, + "es": 99, + "fm": 100, + "md": 101, + "no": 102, + "lr": 103, + "q": 999, + "x": 999, + "cav": 1000, + "sup": 1000000, + "dum": 1 +} atomicNumbersReverse = {v: k for k, v in atomicNumbers.items()} diff --git a/PQAnalysis/core/cell/_standard_properties.py b/PQAnalysis/core/cell/_standard_properties.py index 6b0dcdb4..d149e96d 100644 --- a/PQAnalysis/core/cell/_standard_properties.py +++ b/PQAnalysis/core/cell/_standard_properties.py @@ -10,11 +10,14 @@ from PQAnalysis.type_checking import runtime_type_checking_setter + class _StandardPropertiesMixin: + """ A mixin class containing the standard properties of a Cell class (i.e. standard getter and setter methods). """ + @property def box_lengths(self) -> Np1DNumberArray: """ diff --git a/PQAnalysis/core/exceptions.py b/PQAnalysis/core/exceptions.py index 0cc81b12..c2731ca1 100644 --- a/PQAnalysis/core/exceptions.py +++ b/PQAnalysis/core/exceptions.py @@ -7,7 +7,9 @@ from PQAnalysis.exceptions import PQException, PQWarning + class ElementNotFoundError(PQException): + """ Exception raised if the given element id is not valid """ @@ -24,7 +26,9 @@ def __init__(self, element_id: Any) -> None: super().__init__(self.message) + class ResidueError(PQException): + """ Exception raised for errors related to the Residue class """ @@ -40,7 +44,9 @@ def __init__(self, message: str): super().__init__(self.message) + class ResidueWarning(PQWarning): + """ Warning raised for problems related to the Residue class """ @@ -56,7 +62,9 @@ def __init__(self, message: str): super().__init__(self.message) + class AtomError(PQException): + """ Exception raised for errors related to the Atom class """ diff --git a/PQAnalysis/core/residue.py b/PQAnalysis/core/residue.py index 93baa3ef..8187f085 100644 --- a/PQAnalysis/core/residue.py +++ b/PQAnalysis/core/residue.py @@ -28,12 +28,13 @@ from .atom import Elements, Element from .exceptions import ResidueError - #: A type hint for a list of residues (mol types). Residues = TypeVar("Residues", bound=List["PQAnalysis.core.Residue"]) + class Residue: + """ A class for representing a residue type (mol type). @@ -46,14 +47,15 @@ class Residue: logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - name: str, - residue_id: int, - total_charge: Real, - elements: Element | Elements | str | List[str], - atom_types: int | Np1DIntArray, - partial_charges: Real | Np1DNumberArray, - ) -> None: + def __init__( + self, + name: str, + residue_id: int, + total_charge: Real, + elements: Element | Elements | str | List[str], + atom_types: int | Np1DIntArray, + partial_charges: Real | Np1DNumberArray, + ) -> None: """ Initializes the Residue with the given parameters. @@ -88,7 +90,9 @@ def __init__(self, self._elements = [elements] elif isinstance(elements, str): self._elements = [Element(elements)] - elif isinstance(elements, list) and len(elements) > 0 and isinstance(elements[0], Element): + elif isinstance(elements, + list) and len(elements) > 0 and isinstance(elements[0], + Element): self._elements = elements else: self._elements = [Element(element) for element in elements] @@ -96,7 +100,8 @@ def __init__(self, self._atom_types = np.atleast_1d(atom_types) self._partial_charges = np.atleast_1d(partial_charges) - if not len(self.elements) == len(self.atom_types) == len(self.partial_charges): + if not len(self.elements) == len(self.atom_types) == len( + self.partial_charges): self.logger.error( "The number of elements, atom_types and partial_charges must be the same.", exception=ResidueError @@ -349,7 +354,9 @@ def __eq__(self, other: Any) -> bool: return is_equal + class QMResidue(Residue): + """ A class for representing a QM residue type (mol type). diff --git a/PQAnalysis/io/__init__.py b/PQAnalysis/io/__init__.py index c8fc6f25..909aac52 100644 --- a/PQAnalysis/io/__init__.py +++ b/PQAnalysis/io/__init__.py @@ -48,11 +48,5 @@ from .input_file_reader import InputFileFormat from .api import continue_input_file -from .conversion_api import ( - gen2xyz, - xyz2gen, - rst2xyz, - traj2box, - traj2qmcfc -) +from .conversion_api import (gen2xyz, xyz2gen, rst2xyz, traj2box, traj2qmcfc) from .write_api import write, write_box diff --git a/PQAnalysis/io/api.py b/PQAnalysis/io/api.py index 2a253cbf..30d1d2dc 100644 --- a/PQAnalysis/io/api.py +++ b/PQAnalysis/io/api.py @@ -18,11 +18,13 @@ logger = setup_logger(logger) + @runtime_type_checking -def continue_input_file(input_file: str, - n: PositiveReal = 1, - input_format: InputFileFormat | str = InputFileFormat.PQ - ) -> None: +def continue_input_file( + input_file: str, + n: PositiveReal = 1, + input_format: InputFileFormat | str = InputFileFormat.PQ +) -> None: """ API function for continuing an input file. @@ -50,8 +52,8 @@ def continue_input_file(input_file: str, if input_format != InputFileFormat.PQ: logger.error( ( - f"Format {input_format} not implemented " - "yet for continuing input file." + f"Format {input_format} not implemented " + "yet for continuing input file." ), exception=PQNotImplementedError ) diff --git a/PQAnalysis/io/base.py b/PQAnalysis/io/base.py index 1bd7e224..7ba0a90b 100644 --- a/PQAnalysis/io/base.py +++ b/PQAnalysis/io/base.py @@ -16,7 +16,9 @@ from .exceptions import FileWritingModeError + class BaseWriter: + """ A base class for all writers. @@ -45,10 +47,11 @@ class BaseWriter: logger = logging.getLogger(__package_name__).getChild(__qualname__) logger = setup_logger(logger) - def __init__(self, - filename: str | None = None, - mode: str | FileWritingMode = 'w' - ) -> None: + def __init__( + self, + filename: str | None = None, + mode: str | FileWritingMode = 'w' + ) -> None: """ Parameters ---------- @@ -141,16 +144,13 @@ class :py:class:`~PQAnalysis.io.formats.FileWritingMode`. """ mode = FileWritingMode(mode) - if ( - mode == FileWritingMode.WRITE and - self.filename is not None and - os.path.isfile(self.filename) - ): + if (mode == FileWritingMode.WRITE and self.filename is not None + and os.path.isfile(self.filename)): self.logger.error( ( - f"File {self.filename} already exists. " - "Use mode \'a\' to append to the file or mode " - "\'o\' to overwrite the file." + f"File {self.filename} already exists. " + "Use mode \'a\' to append to the file or mode " + "\'o\' to overwrite the file." ), exception=FileWritingModeError ) @@ -161,7 +161,9 @@ class :py:class:`~PQAnalysis.io.formats.FileWritingMode`. self._mode = mode + class BaseReader: + """ A base class for all readers. @@ -209,8 +211,8 @@ def __init__(self, filename: str | List[str]) -> None: if not os.path.isfile(_filename): self.logger.error( ( - "At least one of the given files does not exist. " - f"File {_filename} not found." + "At least one of the given files does not exist. " + f"File {_filename} not found." ), exception=PQFileNotFoundError ) diff --git a/PQAnalysis/io/box_writer.py b/PQAnalysis/io/box_writer.py index 06728252..d6281f2f 100644 --- a/PQAnalysis/io/box_writer.py +++ b/PQAnalysis/io/box_writer.py @@ -15,7 +15,9 @@ from .exceptions import BoxWriterError + class BoxWriter(BaseWriter): + """ A class for writing a trajectory to a box file. Inherits from BaseWriter @@ -29,10 +31,12 @@ class BoxWriter(BaseWriter): logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - filename: str | None = None, - output_format: str | BoxFileFormat = 'data', - mode: str | FileWritingMode = 'w') -> None: + def __init__( + self, + filename: str | None = None, + output_format: str | BoxFileFormat = 'data', + mode: str | FileWritingMode = 'w' + ) -> None: """ Parameters ---------- @@ -96,9 +100,9 @@ def write_vmd(self, traj: Trajectory) -> None: print("8", file=self.file) print( ( - f"Box " - f"{cell.x} {cell.y} {cell.z} " - f"{cell.alpha} {cell.beta} {cell.gamma}" + f"Box " + f"{cell.x} {cell.y} {cell.z} " + f"{cell.alpha} {cell.beta} {cell.gamma}" ), file=self.file ) @@ -110,10 +114,11 @@ def write_vmd(self, traj: Trajectory) -> None: @instance_function_count_decorator @runtime_type_checking - def write_box_file(self, - traj: Trajectory, - reset_counter: bool = True # pylint: disable=unused-argument # is needed for the decorator - ) -> None: + def write_box_file( + self, + traj: Trajectory, + reset_counter: bool = True # pylint: disable=unused-argument # is needed for the decorator + ) -> None: """ Writes the given trajectory to the file in data file format. @@ -137,15 +142,15 @@ def write_box_file(self, self.__check_pbc__(traj) counter = self.counter[BoxWriter.write_box_file.__name__] # pylint: disable=no-member # is added via decorator - counter = len(traj)*(counter - 1) + counter = len(traj) * (counter - 1) for i, frame in enumerate(traj): cell = frame.cell print( ( - f"{counter + i+1} " - f"{cell.x} {cell.y} {cell.z} " - f"{cell.alpha} {cell.beta} {cell.gamma}" + f"{counter + i+1} " + f"{cell.x} {cell.y} {cell.z} " + f"{cell.alpha} {cell.beta} {cell.gamma}" ), file=self.file ) diff --git a/PQAnalysis/io/energy_file_reader.py b/PQAnalysis/io/energy_file_reader.py index 3eda4f55..071e2917 100644 --- a/PQAnalysis/io/energy_file_reader.py +++ b/PQAnalysis/io/energy_file_reader.py @@ -17,7 +17,9 @@ from .info_file_reader import InfoFileReader + class EnergyFileReader(BaseReader): + """ A class to read energy files from molecular dynamics simulations. """ @@ -26,12 +28,13 @@ class EnergyFileReader(BaseReader): logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - filename: str, - info_filename: str | None = None, - use_info_file: bool = True, - engine_format: MDEngineFormat | str = MDEngineFormat.PQ - ) -> None: + def __init__( + self, + filename: str, + info_filename: str | None = None, + use_info_file: bool = True, + engine_format: MDEngineFormat | str = MDEngineFormat.PQ + ) -> None: """ For the initialization of the EnergyFileReader, the filename of the energy file has to be given. The info_filename can be given as well. If no diff --git a/PQAnalysis/io/exceptions.py b/PQAnalysis/io/exceptions.py index 5e935a8b..4564f1b0 100644 --- a/PQAnalysis/io/exceptions.py +++ b/PQAnalysis/io/exceptions.py @@ -5,31 +5,41 @@ from PQAnalysis.exceptions import PQException, BaseEnumFormatError + class BoxWriterError(PQException): + """ Exception raised for errors related to the BoxWriter class """ + class MoldescriptorReaderError(PQException): + """ Exception raised for errors related to the MoldescriptorReader class """ + class BoxFileFormatError(BaseEnumFormatError): + """ Exception raised if the given enum is not valid """ + class FileWritingModeError(BaseEnumFormatError): + """ Exception raised if the given enum is not valid """ + class OutputFileFormatError(BaseEnumFormatError): + """ Exception raised if the given enum is not valid """ diff --git a/PQAnalysis/io/info_file_reader.py b/PQAnalysis/io/info_file_reader.py index d27bd076..40dd8f3d 100644 --- a/PQAnalysis/io/info_file_reader.py +++ b/PQAnalysis/io/info_file_reader.py @@ -14,7 +14,9 @@ from .base import BaseReader + class InfoFileReader(BaseReader): + """ This is a class to read info files from molecular dynamics simulations. The info file is a specific output file related to the energy file of PQ and QMCFC simulations. @@ -34,10 +36,11 @@ class InfoFileReader(BaseReader): logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - filename: str, - engine_format: MDEngineFormat | str = MDEngineFormat.PQ - ) -> None: + def __init__( + self, + filename: str, + engine_format: MDEngineFormat | str = MDEngineFormat.PQ + ) -> None: """ Parameters ---------- diff --git a/PQAnalysis/io/input_file_reader/pq_analysis/_positions_mixin.py b/PQAnalysis/io/input_file_reader/pq_analysis/_positions_mixin.py index 44c43951..5216b161 100644 --- a/PQAnalysis/io/input_file_reader/pq_analysis/_positions_mixin.py +++ b/PQAnalysis/io/input_file_reader/pq_analysis/_positions_mixin.py @@ -7,7 +7,9 @@ from ._parse import _parse_positive_int, _parse_positive_real + class _PositionsMixin: + """ A mixin class to read all position related keywords from the input dictionary. @@ -17,6 +19,7 @@ class _PositionsMixin: - delta_r - n_bins """ + @property def r_max(self) -> PositiveReal | None: """PositiveReal | None: The maximum radius of the PQAnalysis.""" diff --git a/PQAnalysis/io/input_file_reader/pq_analysis/_selection_mixin.py b/PQAnalysis/io/input_file_reader/pq_analysis/_selection_mixin.py index 95ba4bca..45894fe7 100644 --- a/PQAnalysis/io/input_file_reader/pq_analysis/_selection_mixin.py +++ b/PQAnalysis/io/input_file_reader/pq_analysis/_selection_mixin.py @@ -6,7 +6,9 @@ from ._parse import _parse_bool, _parse_string + class _SelectionMixin: + """ A mixin class to read all selection related keywords from the input dictionary. @@ -16,6 +18,7 @@ class _SelectionMixin: - target_selection - use_full_atom_info """ + @property def selection(self) -> str | None: """str | None: The selection of the simulation.""" diff --git a/PQAnalysis/io/moldescriptor_reader.py b/PQAnalysis/io/moldescriptor_reader.py index b51b261f..fdda37eb 100644 --- a/PQAnalysis/io/moldescriptor_reader.py +++ b/PQAnalysis/io/moldescriptor_reader.py @@ -17,7 +17,9 @@ from .exceptions import MoldescriptorReaderError + class MoldescriptorReader(BaseReader): + """ This is a class to read moldescriptor files. Moldescriptor files are used by the PQ and QMCFC MD engines to store the mol types of @@ -92,9 +94,9 @@ def read(self) -> Residues: line = line.replace('\n', '') self.logger.error( ( - "The number of columns in the header of a mol " - f"type must be 3.\nGot {len(splitted_line)} " - f"columns instead in text: '{line}'\n" + "The number of columns in the header of a mol " + f"type must be 3.\nGot {len(splitted_line)} " + f"columns instead in text: '{line}'\n" ), exception=MoldescriptorReaderError ) @@ -103,8 +105,8 @@ def read(self) -> Residues: mol_types.append( self._read_mol_type( - lines[counter:counter+n_atoms+1], - len(mol_types) + 1 + lines[counter:counter + n_atoms + 1], + len(mol_types) + 1 ) ) @@ -150,9 +152,9 @@ def _read_mol_type(cls, lines: List[str], mol_type_id: int) -> Residue: line = line.replace('\n', '') cls.logger.error( ( - "The number of columns in the body of a mol " - f"type must be 3 or 4.\nGot {len(splitted_line)} " - f"columns instead in text: '{line}'\n" + "The number of columns in the body of a mol " + f"type must be 3 or 4.\nGot {len(splitted_line)} " + f"columns instead in text: '{line}'\n" ), exception=MoldescriptorReaderError ) diff --git a/PQAnalysis/io/restart_file/api.py b/PQAnalysis/io/restart_file/api.py index f37f6ef7..61c6826c 100644 --- a/PQAnalysis/io/restart_file/api.py +++ b/PQAnalysis/io/restart_file/api.py @@ -10,12 +10,14 @@ from .restart_reader import RestartFileReader + @runtime_type_checking -def read_restart_file(filename: str, - moldescriptor_filename: str | None = None, - reference_residues: Residues | None = None, - md_engine_format: MDEngineFormat | str = MDEngineFormat.PQ - ) -> AtomicSystem: +def read_restart_file( + filename: str, + moldescriptor_filename: str | None = None, + reference_residues: Residues | None = None, + md_engine_format: MDEngineFormat | str = MDEngineFormat.PQ +) -> AtomicSystem: """ API function for reading a restart file. diff --git a/PQAnalysis/io/restart_file/exceptions.py b/PQAnalysis/io/restart_file/exceptions.py index b8094293..e73c4af7 100644 --- a/PQAnalysis/io/restart_file/exceptions.py +++ b/PQAnalysis/io/restart_file/exceptions.py @@ -5,13 +5,17 @@ from PQAnalysis.exceptions import PQException + class RestartFileReaderError(PQException): + """ Exception raised for errors related to the RestartFileReader class """ + class RestartFileWriterError(PQException): + """ Exception raised for errors related to the RestartFileWriter class """ diff --git a/PQAnalysis/io/topology_file/api.py b/PQAnalysis/io/topology_file/api.py index f8f1f2d2..9fa792b1 100644 --- a/PQAnalysis/io/topology_file/api.py +++ b/PQAnalysis/io/topology_file/api.py @@ -10,11 +10,13 @@ from .topology_file_reader import TopologyFileReader + @runtime_type_checking -def write_topology_file(bonded_topology: Topology | BondedTopology, - filename: str | None = None, - mode: FileWritingMode | str = "w" - ) -> None: +def write_topology_file( + bonded_topology: Topology | BondedTopology, + filename: str | None = None, + mode: FileWritingMode | str = "w" +) -> None: """ Wrapper function to write a topology file. """ @@ -22,6 +24,7 @@ def write_topology_file(bonded_topology: Topology | BondedTopology, TopologyFileWriter(filename, mode=mode).write(bonded_topology) + @runtime_type_checking def read_topology_file(filename: str) -> BondedTopology: """ diff --git a/PQAnalysis/io/topology_file/exceptions.py b/PQAnalysis/io/topology_file/exceptions.py index c55d5040..6ac655bd 100644 --- a/PQAnalysis/io/topology_file/exceptions.py +++ b/PQAnalysis/io/topology_file/exceptions.py @@ -5,7 +5,9 @@ from PQAnalysis.exceptions import PQException + class TopologyFileError(PQException): + """ A class to represent a topology file exception. """ diff --git a/PQAnalysis/io/topology_file/topology_file_reader.py b/PQAnalysis/io/topology_file/topology_file_reader.py index 77e8e5eb..77f35d1f 100644 --- a/PQAnalysis/io/topology_file/topology_file_reader.py +++ b/PQAnalysis/io/topology_file/topology_file_reader.py @@ -17,7 +17,9 @@ from .exceptions import TopologyFileError + class TopologyFileReader(BaseReader): + """ A class to read a topology file for the bonded topology of the PQ or QMCFC project and return a BondedTopology object. The topology file can have the @@ -215,10 +217,10 @@ def _parse_bonds(self, block: List[str]) -> List[Bond]: bonds.append( Bond( - index1=int(index), - index2=int(target_index), - bond_type=int(bond_type), - is_linker=is_linker, + index1=int(index), + index2=int(target_index), + bond_type=int(bond_type), + is_linker=is_linker, ) ) @@ -265,11 +267,11 @@ def _parse_angles(self, block: List[str]) -> List[Angle]: angles.append( Angle( - index1=int(index1), - index2=int(index2), - index3=int(index3), - angle_type=int(angle_type), - is_linker=is_linker, + index1=int(index1), + index2=int(index2), + index3=int(index3), + angle_type=int(angle_type), + is_linker=is_linker, ) ) @@ -316,12 +318,12 @@ def _parse_dihedrals(self, block: List[str]) -> List[Dihedral]: dihedrals.append( Dihedral( - index1=int(index1), - index2=int(index2), - index3=int(index3), - index4=int(index4), - dihedral_type=int(dihedral_type), - is_linker=is_linker, + index1=int(index1), + index2=int(index2), + index3=int(index3), + index4=int(index4), + dihedral_type=int(dihedral_type), + is_linker=is_linker, ) ) @@ -368,13 +370,13 @@ def _parse_impropers(self, block: List[str]) -> List[Dihedral]: dihedrals.append( Dihedral( - index1=int(index1), - index2=int(index2), - index3=int(index3), - index4=int(index4), - dihedral_type=int(dihedral_type), - is_linker=is_linker, - is_improper=True, + index1=int(index1), + index2=int(index2), + index3=int(index3), + index4=int(index4), + dihedral_type=int(dihedral_type), + is_linker=is_linker, + is_improper=True, ) ) @@ -416,11 +418,11 @@ def _parse_shake(self, block: List[str]) -> List[Bond]: shake_bonds.append( Bond( - index1=int(index), - index2=int(target_index), - equilibrium_distance=float(distance), - is_linker=is_linker, - is_shake=True, + index1=int(index), + index2=int(target_index), + equilibrium_distance=float(distance), + is_linker=is_linker, + is_shake=True, ) ) diff --git a/PQAnalysis/io/topology_file/topology_file_writer.py b/PQAnalysis/io/topology_file/topology_file_writer.py index 3e212dd3..1c55bea1 100644 --- a/PQAnalysis/io/topology_file/topology_file_writer.py +++ b/PQAnalysis/io/topology_file/topology_file_writer.py @@ -20,7 +20,9 @@ from .exceptions import TopologyFileError + class TopologyFileWriter(BaseWriter): + """ Class for writing topology files containing bonded information for the PQ and QMCFC MD software packages. For more information about the topology file @@ -31,10 +33,11 @@ class TopologyFileWriter(BaseWriter): logger = setup_logger(logger) @runtime_type_checking - def __init__(self, - filename: str, - mode: str | FileWritingMode = "w" - ) -> None: + def __init__( + self, + filename: str, + mode: str | FileWritingMode = "w" + ) -> None: """ Parameters ---------- @@ -76,7 +79,8 @@ def write(self, bonded_topology: Topology | BondedTopology) -> None: If the bonded topology is not a Topology or BondedTopology object. """ - if isinstance(bonded_topology, Topology) and bonded_topology.bonded_topology is not None: + if isinstance(bonded_topology, + Topology) and bonded_topology.bonded_topology is not None: bonded_topology = bonded_topology.bonded_topology elif not isinstance(bonded_topology, BondedTopology): self.logger.error( @@ -97,10 +101,11 @@ def write(self, bonded_topology: Topology | BondedTopology) -> None: self.close() @classmethod - def _write_bond_info(cls, - bonded_topology: BondedTopology, - file: File - ) -> None: + def _write_bond_info( + cls, + bonded_topology: BondedTopology, + file: File + ) -> None: """ Determines if the bonded topology contains bonds and writes the bond information to the file. @@ -118,10 +123,11 @@ def _write_bond_info(cls, print(line, file=file) @classmethod - def _write_angle_info(cls, - bonded_topology: BondedTopology, - file: File - ) -> None: + def _write_angle_info( + cls, + bonded_topology: BondedTopology, + file: File + ) -> None: """ Determines if the bonded topology contains angles and writes the angle information to the file. @@ -139,10 +145,11 @@ def _write_angle_info(cls, print(line, file=file) @classmethod - def _write_dihedral_info(cls, - bonded_topology: BondedTopology, - file: File - ) -> None: + def _write_dihedral_info( + cls, + bonded_topology: BondedTopology, + file: File + ) -> None: """ Determines if the bonded topology contains dihedrals and writes the dihedral information to the file. @@ -160,10 +167,11 @@ def _write_dihedral_info(cls, print(line, file=file) @classmethod - def _write_improper_info(cls, - bonded_topology: BondedTopology, - file: File - ) -> None: + def _write_improper_info( + cls, + bonded_topology: BondedTopology, + file: File + ) -> None: """ Writes the improper information to the file. @@ -180,10 +188,11 @@ def _write_improper_info(cls, print(line, file=file) @classmethod - def _write_shake_info(cls, - bonded_topology: BondedTopology, - file: File - ) -> None: + def _write_shake_info( + cls, + bonded_topology: BondedTopology, + file: File + ) -> None: """ Writes the shake information to the file. @@ -233,9 +242,7 @@ def _get_bond_lines(bonded_topology: BondedTopology) -> List[str]: ) for bond in bonded_topology.bonds: - lines.append( - f"{bond.index1} {bond.index2} {bond.bond_type}" - ) + lines.append(f"{bond.index1} {bond.index2} {bond.bond_type}") lines.append("END") @@ -272,14 +279,14 @@ def _get_angle_lines(bonded_topology: BondedTopology) -> List[str]: lines = [] lines.append( - f"ANGLES {n_unique_indices1} {n_unique_indices2} { - n_unique_indices3} {n_linkers}" + f"ANGLES {n_unique_indices1} {n_unique_indices2} " + f"{n_unique_indices3} {n_linkers}" ) for angle in bonded_topology.angles: lines.append( - f"{angle.index1} {angle.index2} { - angle.index3} {angle.angle_type}" + f"{angle.index1} {angle.index2} " + f"{angle.index3} {angle.angle_type}" ) lines.append("END") @@ -317,14 +324,14 @@ def _get_dihedral_lines(bonded_topology: BondedTopology) -> List[str]: lines = [] lines.append( - f"DIHEDRALS {n_unique_indices1} {n_unique_indices2} { - n_unique_indices3} {n_unique_indices4}" + f"DIHEDRALS {n_unique_indices1} {n_unique_indices2} " + f"{n_unique_indices3} {n_unique_indices4}" ) for dihedral in bonded_topology.dihedrals: lines.append( - f"{dihedral.index1} {dihedral.index2} {dihedral.index3} { - dihedral.index4} {dihedral.dihedral_type}" + f"{dihedral.index1} {dihedral.index2} {dihedral.index3} " + f"{dihedral.index4} {dihedral.dihedral_type}" ) lines.append("END") @@ -362,14 +369,14 @@ def _get_improper_lines(bonded_topology: BondedTopology) -> List[str]: lines = [] lines.append( - f"IMPROPERS {n_unique_indices1} {n_unique_indices2} { - n_unique_indices3} {n_unique_indices4}" + f"IMPROPERS {n_unique_indices1} {n_unique_indices2} " + f"{n_unique_indices3} {n_unique_indices4}" ) for improper in bonded_topology.impropers: lines.append( - f"{improper.index1} {improper.index2} {improper.index3} { - improper.index4} {improper.improper_type}" + f"{improper.index1} {improper.index2} {improper.index3} " + f"{improper.index4} {improper.improper_type}" ) lines.append("END") @@ -401,7 +408,8 @@ def _get_shake_lines(bonded_topology: BondedTopology) -> List[str]: """ n_unique_indices = len(bonded_topology.unique_shake_indices) n_unique_target_indices = len( - bonded_topology.unique_shake_target_indices) + bonded_topology.unique_shake_target_indices + ) n_linkers = len(bonded_topology.shake_linkers) lines = [] @@ -413,8 +421,8 @@ def _get_shake_lines(bonded_topology: BondedTopology) -> List[str]: for bond in bonded_topology.shake_bonds: linker = "*" if bond.is_linker else "" lines.append( - f"{bond.index1} {bond.index2} { - bond.equilibrium_distance} {linker}" + f"{bond.index1} {bond.index2} " + f"{bond.equilibrium_distance} {linker}" ) lines.append("END") diff --git a/PQAnalysis/io/traj_file/api.py b/PQAnalysis/io/traj_file/api.py index 563cab9a..c565ffa7 100644 --- a/PQAnalysis/io/traj_file/api.py +++ b/PQAnalysis/io/traj_file/api.py @@ -21,13 +21,15 @@ ) + @runtime_type_checking -def write_trajectory(traj: Trajectory | AtomicSystem, - filename: str | None = None, - engine_format: MDEngineFormat | str = MDEngineFormat.PQ, - traj_type: TrajectoryFormat | str = TrajectoryFormat.XYZ, - mode: FileWritingMode | str = FileWritingMode.WRITE, - ) -> None: +def write_trajectory( + traj: Trajectory | AtomicSystem, + filename: str | None = None, + engine_format: MDEngineFormat | str = MDEngineFormat.PQ, + traj_type: TrajectoryFormat | str = TrajectoryFormat.XYZ, + mode: FileWritingMode | str = FileWritingMode.WRITE, +) -> None: """Wrapper for TrajectoryWriter to write a trajectory to a file. if format is None, the default PQ format is used @@ -57,13 +59,15 @@ def write_trajectory(traj: Trajectory | AtomicSystem, writer.write(traj, traj_type=traj_type) + @runtime_type_checking -def read_trajectory(filename: str, - md_format: MDEngineFormat | str = MDEngineFormat.PQ, - traj_format: TrajectoryFormat | str = TrajectoryFormat.AUTO, - topology: Topology | None = None, - constant_topology: bool = True - ) -> Trajectory: +def read_trajectory( + filename: str, + md_format: MDEngineFormat | str = MDEngineFormat.PQ, + traj_format: TrajectoryFormat | str = TrajectoryFormat.AUTO, + topology: Topology | None = None, + constant_topology: bool = True +) -> Trajectory: """ API function for reading a trajectory from a file. @@ -98,6 +102,7 @@ def read_trajectory(filename: str, return reader.read() + @runtime_type_checking def calculate_frames_of_trajectory_file(filename: str): """ @@ -117,13 +122,15 @@ def calculate_frames_of_trajectory_file(filename: str): return reader.calculate_number_of_frames() + @runtime_type_checking -def read_trajectory_generator(filename: str, - md_format: MDEngineFormat | str = MDEngineFormat.PQ, - traj_format: TrajectoryFormat | str = TrajectoryFormat.AUTO, - topology: Topology | None = None, - constant_topology: bool = True - ) -> Generator[AtomicSystem]: +def read_trajectory_generator( + filename: str, + md_format: MDEngineFormat | str = MDEngineFormat.PQ, + traj_format: TrajectoryFormat | str = TrajectoryFormat.AUTO, + topology: Topology | None = None, + constant_topology: bool = True +) -> Generator[AtomicSystem]: """ API function for building a frame generator from a trajectory file. diff --git a/PQAnalysis/io/traj_file/exceptions.py b/PQAnalysis/io/traj_file/exceptions.py index a3fd2092..137da508 100644 --- a/PQAnalysis/io/traj_file/exceptions.py +++ b/PQAnalysis/io/traj_file/exceptions.py @@ -5,13 +5,17 @@ from PQAnalysis.exceptions import PQException + class TrajectoryReaderError(PQException): + """ Exception raised for errors related to the TrajectoryReader class """ + class FrameReaderError(PQException): + """ Exception raised for errors related to the FrameReader class """ diff --git a/PQAnalysis/io/traj_file/trajectory_writer.py b/PQAnalysis/io/traj_file/trajectory_writer.py index 66043b66..43b7d025 100644 --- a/PQAnalysis/io/traj_file/trajectory_writer.py +++ b/PQAnalysis/io/traj_file/trajectory_writer.py @@ -13,7 +13,9 @@ from PQAnalysis.type_checking import runtime_type_checking, runtime_type_checking_setter + class TrajectoryWriter(BaseWriter): + """ A class for writing a trajectory to a file. Inherits from BaseWriter. See BaseWriter for more information. @@ -22,11 +24,12 @@ class TrajectoryWriter(BaseWriter): """ @runtime_type_checking - def __init__(self, - filename: str | None = None, - engine_format: MDEngineFormat | str = MDEngineFormat.PQ, - mode: str | FileWritingMode = 'w' - ) -> None: + def __init__( + self, + filename: str | None = None, + engine_format: MDEngineFormat | str = MDEngineFormat.PQ, + mode: str | FileWritingMode = 'w' + ) -> None: """ Parameters ---------- @@ -44,10 +47,11 @@ def __init__(self, self.format = MDEngineFormat(engine_format) @runtime_type_checking - def write(self, - trajectory: Trajectory | AtomicSystem, - traj_type: TrajectoryFormat | str = TrajectoryFormat.XYZ - ) -> None: + def write( + self, + trajectory: Trajectory | AtomicSystem, + traj_type: TrajectoryFormat | str = TrajectoryFormat.XYZ + ) -> None: """ Writes the trajectory to the file. @@ -156,9 +160,9 @@ def _write_header(self, n_atoms: int, cell: Cell = Cell()) -> None: if cell != Cell(): print( ( - f"{n_atoms} " - f"{cell.x} {cell.y} {cell.z} " - f"{cell.alpha} {cell.beta} {cell.gamma}" + f"{n_atoms} " + f"{cell.x} {cell.y} {cell.z} " + f"{cell.alpha} {cell.beta} {cell.gamma}" ), file=self.file ) @@ -179,8 +183,8 @@ def _write_comment(self, frame: AtomicSystem) -> None: sum_forces = sum(frame.forces) print( ( - f"sum of forces: {sum_forces[0]:e} " - f"{sum_forces[1]:e} {sum_forces[2]:e}" + f"sum of forces: {sum_forces[0]:e} " + f"{sum_forces[1]:e} {sum_forces[2]:e}" ), file=self.file ) @@ -208,21 +212,25 @@ def _write_xyz(self, xyz: Np2DNumberArray, atoms: List[Atom]) -> None: if self.type == TrajectoryFormat.VEL: print( ( - f"{atom.name} {xyz[i][0]:16.12e} " - f"{xyz[i][1]:16.12e} {xyz[i][2]:16.12e}" + f"{atom.name} {xyz[i][0]:16.12e} " + f"{xyz[i][1]:16.12e} {xyz[i][2]:16.12e}" ), file=self.file ) else: print( ( - f"{atom.name} {xyz[i][0]:16.10f} " - f"{xyz[i][1]:16.10f} {xyz[i][2]:16.10f}" + f"{atom.name} {xyz[i][0]:16.10f} " + f"{xyz[i][1]:16.10f} {xyz[i][2]:16.10f}" ), file=self.file ) - def _write_scalar(self, scalar: Np1DNumberArray, atoms: List[Atom]) -> None: + def _write_scalar( + self, + scalar: Np1DNumberArray, + atoms: List[Atom] + ) -> None: """ Writes the charges of the frame to the file. @@ -235,9 +243,7 @@ def _write_scalar(self, scalar: Np1DNumberArray, atoms: List[Atom]) -> None: """ for i, atom in enumerate(atoms): - print( - f"{atom.name} {scalar[i]}", file=self.file - ) + print(f"{atom.name} {scalar[i]}", file=self.file) @property def format(self) -> MDEngineFormat: diff --git a/PQAnalysis/io/virial/api.py b/PQAnalysis/io/virial/api.py index 3e6ae9cc..d72c8f6d 100644 --- a/PQAnalysis/io/virial/api.py +++ b/PQAnalysis/io/virial/api.py @@ -9,6 +9,7 @@ from .virial_reader import VirialFileReader, StressFileReader + @runtime_type_checking def read_virial_file(filename: str) -> List: """ @@ -28,6 +29,7 @@ def read_virial_file(filename: str) -> List: return reader.read() + @runtime_type_checking def read_stress_file(filename: str) -> List: """ diff --git a/PQAnalysis/io/virial/virial_reader.py b/PQAnalysis/io/virial/virial_reader.py index aca92a5c..8a2c2e17 100644 --- a/PQAnalysis/io/virial/virial_reader.py +++ b/PQAnalysis/io/virial/virial_reader.py @@ -9,7 +9,9 @@ from PQAnalysis.type_checking import runtime_type_checking + class _BaseReader(BaseReader): + """ A base class for reading virial and stress files. """ @@ -56,13 +58,17 @@ def read(self) -> list[Np3x3NumberArray]: return data + class VirialFileReader(_BaseReader): + """ A class to read virial files. """ + class StressFileReader(_BaseReader): + """ A class to read stress files. """ diff --git a/PQAnalysis/io/write_api.py b/PQAnalysis/io/write_api.py index fba122e7..4874a3bd 100644 --- a/PQAnalysis/io/write_api.py +++ b/PQAnalysis/io/write_api.py @@ -21,11 +21,13 @@ logger = setup_logger(logger) + @runtime_type_checking -def write(object_to_write: Any, - filename: str | None = None, - mode: FileWritingMode | str = FileWritingMode.WRITE, - ) -> None: +def write( + object_to_write: Any, + filename: str | None = None, + mode: FileWritingMode | str = FileWritingMode.WRITE, +) -> None: """ API write wrapper function for writing different objects to a file. @@ -59,18 +61,20 @@ def write(object_to_write: Any, logger.error( ( - f"Writing object of type {type(object_to_write)} " - "is not implemented yet." + f"Writing object of type {type(object_to_write)} " + "is not implemented yet." ), exception=PQNotImplementedError ) -@ runtime_type_checking -def write_box(traj: Trajectory, - filename: str | None = None, - output_format: str | None = None - ) -> None: + +@runtime_type_checking +def write_box( + traj: Trajectory, + filename: str | None = None, + output_format: str | None = None +) -> None: """ Writes the given trajectory to the file in a selected box file format. diff --git a/PQAnalysis/physical_data/energy.py b/PQAnalysis/physical_data/energy.py index 0f19f7f2..661878aa 100644 --- a/PQAnalysis/physical_data/energy.py +++ b/PQAnalysis/physical_data/energy.py @@ -12,7 +12,9 @@ from .exceptions import EnergyError + class Energy(): + """ A class to store the data of an energy file. @@ -22,11 +24,12 @@ class Energy(): was found. """ - def __init__(self, - data: Np1DNumberArray | Np2DNumberArray, - info: Dict | None = None, - units: Dict | None = None - ) -> None: + def __init__( + self, + data: Np1DNumberArray | Np2DNumberArray, + info: Dict | None = None, + units: Dict | None = None + ) -> None: """ Parameters ---------- @@ -76,10 +79,11 @@ def __init__(self, self._make_attributes() - def _setup_info_dictionary(self, - info: Dict | None = None, - units: Dict | None = None - ) -> None: + def _setup_info_dictionary( + self, + info: Dict | None = None, + units: Dict | None = None + ) -> None: """ Sets up the info dictionary. @@ -118,7 +122,8 @@ def _setup_info_dictionary(self, self.info_given = True if len(info) != len(self.data): raise EnergyError( - "The length of info dictionary has to be equal to the length of data.") + "The length of info dictionary has to be equal to the length of data." + ) if units is None: self.units_given = False @@ -127,14 +132,17 @@ def _setup_info_dictionary(self, self.units_given = True if len(units) != len(self.data): raise EnergyError( - "The length of units dictionary has to be equal to the length of data.") + "The length of units dictionary has to be equal to the length of data." + ) self.info = info self.units = units - if self.info_given and self.units_given and units.keys() != info.keys(): + if self.info_given and self.units_given and units.keys() != info.keys( + ): raise EnergyError( - "The keys of the info and units dictionary do not match.") + "The keys of the info and units dictionary do not match." + ) def _make_attributes(self) -> None: """ @@ -162,23 +170,13 @@ def _make_attributes(self) -> None: for attribute, value in self.__data_attributes__.items(): info_string = attribute if info_string in self.info or info_string in self.units: - setattr( - self, - value, - self.data[self.info[attribute]] - ) - setattr( - self, - value + "_unit", - self.units[attribute] - ) + setattr(self, value, self.data[self.info[attribute]]) + setattr(self, value + "_unit", self.units[attribute]) setattr( self, value + "_with_unit", - ( - self.data[self.info[attribute]], - self.units[attribute] - ) + (self.data[self.info[attribute]], + self.units[attribute]) ) ################################################ diff --git a/PQAnalysis/physical_data/exceptions.py b/PQAnalysis/physical_data/exceptions.py index 91af7454..b6ad529a 100644 --- a/PQAnalysis/physical_data/exceptions.py +++ b/PQAnalysis/physical_data/exceptions.py @@ -5,7 +5,9 @@ from PQAnalysis.exceptions import PQException + class EnergyError(PQException): + """ Exception raised for errors related to the Energy class """ diff --git a/PQAnalysis/tools/traj_to_com_traj.py b/PQAnalysis/tools/traj_to_com_traj.py index 857a74be..8a03f7d2 100644 --- a/PQAnalysis/tools/traj_to_com_traj.py +++ b/PQAnalysis/tools/traj_to_com_traj.py @@ -5,9 +5,14 @@ from PQAnalysis.traj import Trajectory + # TODO: add atom to element mapper if atom name not element names # TODO rethink concept of selection/getitem/slices -def traj_to_com_traj(trajectory: Trajectory, selection=None, group=None) -> Trajectory: +def traj_to_com_traj( + trajectory: Trajectory, + selection=None, + group=None +) -> Trajectory: """ Function that computes the center of mass trajectory for a given selection. diff --git a/PQAnalysis/topology/api.py b/PQAnalysis/topology/api.py index f5f292a8..b2a5f36d 100644 --- a/PQAnalysis/topology/api.py +++ b/PQAnalysis/topology/api.py @@ -11,12 +11,14 @@ from .shake_topology import ShakeTopologyGenerator -def generate_shake_topology_file(trajectory: Trajectory, - selection: SelectionCompatible = None, - output: str | None = None, - mode: FileWritingMode | str = "w", - use_full_atom_info: bool = False, - ) -> None: + +def generate_shake_topology_file( + trajectory: Trajectory, + selection: SelectionCompatible = None, + output: str | None = None, + mode: FileWritingMode | str = "w", + use_full_atom_info: bool = False, +) -> None: """ Wrapper function to generate a shake topology file for a given trajectory. @@ -45,11 +47,13 @@ def generate_shake_topology_file(trajectory: Trajectory, generator.write_topology(output, mode) -def select_from_restart_file(selection: SelectionCompatible, - restart_file: str, - moldescriptor_file: str | None = None, - use_full_atom_info: bool = False, - ) -> Np1DIntArray: + +def select_from_restart_file( + selection: SelectionCompatible, + restart_file: str, + moldescriptor_file: str | None = None, + use_full_atom_info: bool = False, +) -> Np1DIntArray: """ Selects atoms from a restart file and writes them to a new file. @@ -72,14 +76,19 @@ def select_from_restart_file(selection: SelectionCompatible, selection = Selection(selection) - return selection.select(system.topology, use_full_atom_info=use_full_atom_info) + return selection.select( + system.topology, + use_full_atom_info=use_full_atom_info + ) + -def selection_from_restart_file_as_list(selection: SelectionCompatible, - restart_file: str, - moldescriptor_file: str | None = None, - use_full_atom_info: bool = False, - ) -> List[int]: +def selection_from_restart_file_as_list( + selection: SelectionCompatible, + restart_file: str, + moldescriptor_file: str | None = None, + use_full_atom_info: bool = False, +) -> List[int]: """ Selects atoms from a restart file and writes them to a new file. @@ -100,9 +109,9 @@ def selection_from_restart_file_as_list(selection: SelectionCompatible, return list( select_from_restart_file( - selection, - restart_file, - moldescriptor_file, - use_full_atom_info + selection, + restart_file, + moldescriptor_file, + use_full_atom_info ) ) diff --git a/PQAnalysis/topology/bonded_topology/__init__.py b/PQAnalysis/topology/bonded_topology/__init__.py index e69de29b..8b137891 100644 --- a/PQAnalysis/topology/bonded_topology/__init__.py +++ b/PQAnalysis/topology/bonded_topology/__init__.py @@ -0,0 +1 @@ + diff --git a/PQAnalysis/topology/bonded_topology/_topology_properties.py b/PQAnalysis/topology/bonded_topology/_topology_properties.py index 8dfe872b..ceed062a 100644 --- a/PQAnalysis/topology/bonded_topology/_topology_properties.py +++ b/PQAnalysis/topology/bonded_topology/_topology_properties.py @@ -10,10 +10,13 @@ from .dihedral import Dihedral + class TopologyPropertiesMixin: + """ A mixin class to add the most common properties of a topology. """ + @property def unique_bond1_indices(self) -> Set[PositiveInt]: """Set[PositiveInt]: The unique indices of the first atoms in the bonds.""" diff --git a/PQAnalysis/topology/bonded_topology/bonded_topology.py b/PQAnalysis/topology/bonded_topology/bonded_topology.py index fe68c7f1..9409736c 100644 --- a/PQAnalysis/topology/bonded_topology/bonded_topology.py +++ b/PQAnalysis/topology/bonded_topology/bonded_topology.py @@ -16,7 +16,9 @@ from ._topology_properties import TopologyPropertiesMixin + class BondedTopology(TopologyPropertiesMixin): + """ A class to represent a bonded topology in a molecular topology. @@ -24,14 +26,15 @@ class BondedTopology(TopologyPropertiesMixin): A mixin class to add the most common properties of a topology. """ - def __init__(self, - bonds: List[Bond] | None = None, - angles: List[Angle] | None = None, - dihedrals: List[Dihedral] | None = None, - impropers: List[Dihedral] | None = None, - shake_bonds: List[Bond] | None = None, - ordering_keys: List[str] | None = None - ) -> None: + def __init__( + self, + bonds: List[Bond] | None = None, + angles: List[Angle] | None = None, + dihedrals: List[Dihedral] | None = None, + impropers: List[Dihedral] | None = None, + shake_bonds: List[Bond] | None = None, + ordering_keys: List[str] | None = None + ) -> None: """ Parameters ---------- @@ -58,12 +61,13 @@ def __init__(self, self.ordering_keys = ordering_keys - def extend_shake_bonds(self, - shake_bonds: List[Bond], - n_atoms: PositiveInt, - n_extensions: PositiveInt = 1, - n_atoms_per_extension: PositiveInt | None = None - ) -> None: + def extend_shake_bonds( + self, + shake_bonds: List[Bond], + n_atoms: PositiveInt, + n_extensions: PositiveInt = 1, + n_atoms_per_extension: PositiveInt | None = None + ) -> None: """ Extend the shake bonds in the bonded topology. @@ -95,8 +99,8 @@ def extend_shake_bonds(self, ) max_index_shake_bonds = max( - [bond.index1 for bond in shake_bonds] + - [bond.index2 for bond in shake_bonds] + [bond.index1 + for bond in shake_bonds] + [bond.index2 for bond in shake_bonds] ) if n_atoms_per_extension is None: diff --git a/PQAnalysis/topology/exceptions.py b/PQAnalysis/topology/exceptions.py index f789223c..92eb5473 100644 --- a/PQAnalysis/topology/exceptions.py +++ b/PQAnalysis/topology/exceptions.py @@ -5,7 +5,9 @@ from PQAnalysis.exceptions import PQException + class TopologyError(PQException): + """ Exception raised for errors related to the Topology class """ diff --git a/PQAnalysis/topology/shake_topology.py b/PQAnalysis/topology/shake_topology.py index 23aedd93..962d8102 100644 --- a/PQAnalysis/topology/shake_topology.py +++ b/PQAnalysis/topology/shake_topology.py @@ -12,15 +12,18 @@ from .selection import SelectionCompatible, Selection + class ShakeTopologyGenerator: + """ A class for generating the shake topology for a given trajectory """ - def __init__(self, - selection: SelectionCompatible = None, - use_full_atom_info: bool = False - ) -> None: + def __init__( + self, + selection: SelectionCompatible = None, + use_full_atom_info: bool = False + ) -> None: """ Parameters ---------- @@ -62,7 +65,9 @@ def generate_topology(self, trajectory: Trajectory) -> None: self._topology = trajectory.topology indices = self.selection.select( - self._topology, self._use_full_atom_info) + self._topology, + self._use_full_atom_info + ) target_indices, distances = atomic_system.nearest_neighbours( n=1, selection=indices, use_full_atom_info=self._use_full_atom_info) @@ -84,7 +89,10 @@ def generate_topology(self, trajectory: Trajectory) -> None: self.target_indices = target_indices self.distances = np.mean(np.array(distances), axis=0) - def average_equivalents(self, indices: List[Np1DIntArray] | Np2DIntArray) -> None: + def average_equivalents( + self, + indices: List[Np1DIntArray] | Np2DIntArray + ) -> None: """ Averages the distances for equivalent atoms. @@ -105,10 +113,11 @@ def average_equivalents(self, indices: List[Np1DIntArray] | Np2DIntArray) -> Non self.distances[_indices] = mean_distance - def write_topology(self, - filename: str | None = None, - mode: FileWritingMode | str = "w" - ) -> None: + def write_topology( + self, + filename: str | None = None, + mode: FileWritingMode | str = "w" + ) -> None: """ Writes the topology to a file. @@ -132,8 +141,8 @@ def write_topology(self, print( ( - f"SHAKE {len(self.indices)} " - f"{len(np.unique(self.target_indices))} 0" + f"SHAKE {len(self.indices)} " + f"{len(np.unique(self.target_indices))} 0" ), file=writer.file ) diff --git a/PQAnalysis/traj/api.py b/PQAnalysis/traj/api.py index f7873421..d2596a76 100644 --- a/PQAnalysis/traj/api.py +++ b/PQAnalysis/traj/api.py @@ -5,6 +5,7 @@ from PQAnalysis.core import Cells + def check_trajectory_pbc(cells: Cells) -> bool: """ Checks no cell in the trajectory is Cell() i.e. @@ -27,6 +28,7 @@ def check_trajectory_pbc(cells: Cells) -> bool: return all(not cell.is_vacuum for cell in cells) + def check_trajectory_vacuum(cells: Cells) -> bool: """ Checks if all cells of the trajectory are in vacuum i.e. cell = Cell(). diff --git a/PQAnalysis/traj/exceptions.py b/PQAnalysis/traj/exceptions.py index 43c22157..e4d5a1d1 100644 --- a/PQAnalysis/traj/exceptions.py +++ b/PQAnalysis/traj/exceptions.py @@ -5,19 +5,25 @@ from PQAnalysis.exceptions import PQException, BaseEnumFormatError + class TrajectoryFormatError(BaseEnumFormatError): + """ Exception raised if the given enum is not valid """ + class MDEngineFormatError(BaseEnumFormatError): + """ Exception raised if the given enum is not valid """ + class TrajectoryError(PQException): + """ Exception raised for errors related to the Trajectory class """ diff --git a/PQAnalysis/traj/trajectory.py b/PQAnalysis/traj/trajectory.py index bf6abf0d..f5c8e0e4 100644 --- a/PQAnalysis/traj/trajectory.py +++ b/PQAnalysis/traj/trajectory.py @@ -16,7 +16,9 @@ from PQAnalysis import __package_name__ + class Trajectory: + """ A trajectory object is a sequence of frames. @@ -30,7 +32,10 @@ class Trajectory: logger = logging.getLogger(__package_name__).getChild(__qualname__) - def __init__(self, frames: List[AtomicSystem] | AtomicSystem | None = None) -> None: + def __init__( + self, + frames: List[AtomicSystem] | AtomicSystem | None = None + ) -> None: """ Parameters ---------- @@ -270,14 +275,17 @@ def window( # Check if all frames are included in the windows # Length of the trajectory - window_size should be divisible by window_gap - if ((trajectory_stop - trajectory_start) - window_size) % window_gap != 0: + if ((trajectory_stop - trajectory_start) - + window_size) % window_gap != 0: self.logger.warning( "Not all frames are included in the windows. Check the window size and gap." ) # generate the window of the trajectory - for i in range(trajectory_start, trajectory_stop - window_size + 1, window_gap): - yield self[i: i + window_size] + for i in range(trajectory_start, + trajectory_stop - window_size + 1, + window_gap): + yield self[i:i + window_size] def __contains__(self, item: AtomicSystem) -> bool: """ diff --git a/PQAnalysis/types.py b/PQAnalysis/types.py index 2f53255b..20fb3809 100644 --- a/PQAnalysis/types.py +++ b/PQAnalysis/types.py @@ -14,91 +14,64 @@ # :A type hint for positive real numbers PositiveReal = NewType( - "PositiveReal", Annotated[Real, Is[lambda real: real >= 0.0]]) + "PositiveReal", + Annotated[Real, + Is[lambda real: real >= 0.0]] +) #: A type variable for a 1D np.ndarray with dtype np.number Np1DNumberArray = NewType( "Np1DNumberArray", - Annotated[ - np.ndarray, - Is[ - lambda array:array.ndim == 1 and - (np.issubdtype(array.dtype, np.number)) or - len(array) == 0 - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim == 1 and + (np.issubdtype(array.dtype, + np.number)) or len(array) == 0]] ) #: A type hint for a 2D np.ndarray with dtype np.number Np2DNumberArray = NewType( "Np2DNumberArray", - Annotated[ - np.ndarray, - Is[ - lambda array:array.ndim == 2 and - (np.issubdtype(array.dtype, np.number)) - - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim == 2 and + (np.issubdtype(array.dtype, + np.number))]] ) #: A type hint for a nD np.ndarray with dtype np.number NpnDNumberArray = NewType( "NpnDNumberArray", - Annotated[ - np.ndarray, - Is[ - lambda array: array.ndim > 0 and - (np.issubdtype(array.dtype, np.number)) - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim > 0 and + (np.issubdtype(array.dtype, + np.number))]] ) #: A type hint for a 3x3 np.ndarray matrix with dtype np.number Np3x3NumberArray = NewType( "Np2x2NumberArray", - Annotated[ - np.ndarray, - Is[ - lambda array: array.ndim == 2 and - array.shape == (3, 3) and - (np.issubdtype(array.dtype, np.number)) - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim == 2 and array.shape == (3, + 3) and (np.issubdtype(array.dtype, + np.number))]] ) #: A type hint for a 2D np.ndarray with dtype np.integer Np2DIntArray = NewType( "Np2DIntArray", - Annotated[ - np.ndarray, - Is[ - lambda array: array.ndim == 2 and - (np.issubdtype(array.dtype, np.integer)) - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim == 2 and + (np.issubdtype(array.dtype, + np.integer))]] ) #: A type hint for a 1D np.ndarray with dtype np.integer Np1DIntArray = NewType( "Np1DIntArray", - Annotated[ - np.ndarray, - Is[ - lambda array: array.ndim == 1 and - (np.issubdtype(array.dtype, np.integer)) or - len(array) == 0 - ] - ] + Annotated[np.ndarray, + Is[lambda array: array.ndim == 1 and + (np.issubdtype(array.dtype, + np.integer)) or len(array) == 0]] ) #: A type hint for a range object -Range = NewType( - "Range", - Annotated[ - range, - Is[ - lambda r: isinstance(r, range) - ] - ] -) +Range = NewType("Range", Annotated[range, Is[lambda r: isinstance(r, range)]]) diff --git a/PQAnalysis/utils/common.py b/PQAnalysis/utils/common.py index d935ae50..6d6295b3 100644 --- a/PQAnalysis/utils/common.py +++ b/PQAnalysis/utils/common.py @@ -27,6 +27,7 @@ """ + def print_header(file: str = None) -> None: """ A function to print the header of the program. diff --git a/PQAnalysis/utils/decorators.py b/PQAnalysis/utils/decorators.py index fe690a10..5b34c4d9 100644 --- a/PQAnalysis/utils/decorators.py +++ b/PQAnalysis/utils/decorators.py @@ -11,6 +11,7 @@ from beartype.typing import Any + def count_decorator(func, *args, **kwargs) -> Any: # pylint: disable=unused-argument, missing-function-docstring """ Decorator which counts the number of times a function is called. @@ -24,6 +25,7 @@ def count_decorator(func, *args, **kwargs) -> Any: # pylint: disable=unused-arg kwargs : The keyword arguments of the function. """ + def wrapper(*args, **kwargs): """ A wrapper function to count the number of times a function is called. @@ -40,6 +42,7 @@ def wrapper(*args, **kwargs): return wrapper + @decorator def instance_function_count_decorator(func, *args, **kwargs): """ @@ -57,12 +60,14 @@ def instance_function_count_decorator(func, *args, **kwargs): return func(*args, **kwargs) + @decorator def timeit_in_class(func, *args, **kwargs) -> Any: """ Decorator which measures the time a function of a class takes to execute and sets elapsed time as an attribute of the class. """ + def wrapper(*args, **kwargs) -> Any: """ A wrapper function to measure the time a function takes to execute. @@ -76,6 +81,7 @@ def wrapper(*args, **kwargs) -> Any: return wrapper(*args, **kwargs) + def get_arg_var_by_name(func, args, arg_name): """ Returns the value of the argument with the given name. diff --git a/PQAnalysis/utils/files.py b/PQAnalysis/utils/files.py index 2513fa8e..4c507b58 100644 --- a/PQAnalysis/utils/files.py +++ b/PQAnalysis/utils/files.py @@ -10,6 +10,7 @@ from beartype.typing import List + def find_files_with_prefix(file_prefixes: List[str] | str) -> List[str]: """ Finds files with a given prefix. @@ -32,9 +33,7 @@ def find_files_with_prefix(file_prefixes: List[str] | str) -> List[str]: file_prefixes = [glob.glob(prefix + "*") for prefix in file_prefixes] files = [ - file - for sublist in file_prefixes - for file in sublist + file for sublist in file_prefixes for file in sublist if Path(file).is_file() ] diff --git a/PQAnalysis/utils/random.py b/PQAnalysis/utils/random.py index 51e78bd9..f145945f 100644 --- a/PQAnalysis/utils/random.py +++ b/PQAnalysis/utils/random.py @@ -6,6 +6,7 @@ import os + def get_random_seed() -> int: """ Get a random seed for the simulation. diff --git a/pyproject.toml b/pyproject.toml index 694826d0..2511e60f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,6 @@ dependencies = [ "tqdm", "decorator", "argcomplete", - "unum", "yapf", "rich-argparse", ] diff --git a/tests/analysis/rdf/__init__.py b/tests/analysis/rdf/__init__.py index e69de29b..8b137891 100644 --- a/tests/analysis/rdf/__init__.py +++ b/tests/analysis/rdf/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/analysis/rdf/test_api.py b/tests/analysis/rdf/test_api.py index f82b4db1..e4b29039 100644 --- a/tests/analysis/rdf/test_api.py +++ b/tests/analysis/rdf/test_api.py @@ -12,14 +12,18 @@ from ...conftest import assert_logging_with_exception + class TestRDFAPI: + def test_wrong_param_types(self, caplog): assert_logging_with_exception( caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "input_file", 1, str, + "input_file", + 1, + str, ), exception=PQTypeError, function=rdf, @@ -31,7 +35,9 @@ def test_wrong_param_types(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "md_format", 1, "PQAnalysis.traj.formats.MDEngineFormat | str", + "md_format", + 1, + "PQAnalysis.traj.formats.MDEngineFormat | str", ), exception=PQTypeError, function=rdf, diff --git a/tests/analysis/rdf/test_rdf.py b/tests/analysis/rdf/test_rdf.py index 23af1f3a..2c73065c 100644 --- a/tests/analysis/rdf/test_rdf.py +++ b/tests/analysis/rdf/test_rdf.py @@ -18,6 +18,7 @@ # pylint: disable=protected-access + def test__calculate_n_bins(): r_min = 1.0 r_max = 101.5 @@ -29,6 +30,7 @@ def test__calculate_n_bins(): assert np.isclose(r_max, 101.0) + def test__infer_r_max(caplog): system1 = AtomicSystem(cell=Cell(10, 10, 10, 90, 90, 90)) @@ -48,10 +50,10 @@ def test__infer_r_max(caplog): logging_name=RDF.__qualname__, logging_level="ERROR", message_to_test=( - "To infer r_max of the RDF analysis, the " - "trajectory cannot be a vacuum trajectory. " - "Please specify r_max manually or use " - "the combination n_bins and delta_r." + "To infer r_max of the RDF analysis, the " + "trajectory cannot be a vacuum trajectory. " + "Please specify r_max manually or use " + "the combination n_bins and delta_r." ), exception=RDFError, function=RDF._infer_r_max, @@ -59,6 +61,7 @@ def test__infer_r_max(caplog): ) + def test__check_r_max(caplog): r_max = 5.0 traj = Trajectory() @@ -79,10 +82,10 @@ def test__check_r_max(caplog): logging_name=RDF.__qualname__, logging_level="WARNING", message_to_test=( - f"The calculated r_max {r_max} " - "is larger than the maximum allowed radius " - "according to the box vectors of the trajectory 5.0. " - "r_max will be set to the maximum allowed radius." + f"The calculated r_max {r_max} " + "is larger than the maximum allowed radius " + "according to the box vectors of the trajectory 5.0. " + "r_max will be set to the maximum allowed radius." ), exception=None, function=RDF._check_r_max, @@ -93,6 +96,7 @@ def test__check_r_max(caplog): assert np.isclose(r_max, 5.0) + def test__calculate_r_max(caplog): n_bins = 50 delta_r = 0.1 @@ -109,6 +113,7 @@ def test__calculate_r_max(caplog): assert np.isclose(r_max, 8.0) + def test__setup_bin_middle_points(): n_bins = 5 r_min = 3.0 @@ -125,6 +130,7 @@ def test__setup_bin_middle_points(): assert np.allclose(bin_middle_points, np.array([3.5, 4.5, 5.5, 6.5, 7.5])) + def test__integration(): bins = np.array([1, 2, 3, 4, 5]) len_reference_indices = 3 @@ -136,17 +142,16 @@ def test__integration(): assert np.allclose( integration, np.array( - [ - 1 / n_total, - 3 / n_total, - 6 / n_total, - 10 / n_total, - 15 / n_total - ] + [1 / n_total, + 3 / n_total, + 6 / n_total, + 10 / n_total, + 15 / n_total] ) ) + def test__norm(): n_bins = 5 n_frames = 10 @@ -164,10 +169,13 @@ def test__norm(): help_1 = np.arange(0, n_bins) help_2 = np.arange(1, n_bins + 1) - norm_ref = (help_2**3 - help_1**3)*delta_r**3 * 4 / 3 * np.pi + norm_ref = (help_2**3 - help_1**3) * delta_r**3 * 4 / 3 * np.pi + + assert np.allclose( + norm, + norm_ref * target_density * n_reference_indices * n_frames + ) - assert np.allclose(norm, norm_ref * target_density * - n_reference_indices * n_frames) def test__add_to_bins(): @@ -178,24 +186,30 @@ def test__add_to_bins(): distances = np.array([1.5, 2.5, 3.5, 3.6, 3.7, 4.5, 4.6, 5.5, 6.5, 8.5]) assert np.allclose( - RDF._add_to_bins( - distances, - r_min, - delta_r, - n_bins - ), - np.array([3, 2, 1, 1, 0]) + RDF._add_to_bins(distances, + r_min, + delta_r, + n_bins), + np.array([3, + 2, + 1, + 1, + 0]) ) + class TestRDF: + def test__init__type_checking(self, caplog): assert_logging_with_exception( caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "traj", 1, Trajectory | TrajectoryReader + "traj", + 1, + Trajectory | TrajectoryReader ), exception=PQTypeError, function=RDF, @@ -209,7 +223,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "reference_species", Trajectory(), SelectionCompatible + "reference_species", + Trajectory(), + SelectionCompatible ), exception=PQTypeError, function=RDF, @@ -223,7 +239,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "target_species", Trajectory(), SelectionCompatible + "target_species", + Trajectory(), + SelectionCompatible ), exception=PQTypeError, function=RDF, @@ -237,7 +255,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "use_full_atom_info", 1, bool + "use_full_atom_info", + 1, + bool ), exception=PQTypeError, function=RDF, @@ -252,7 +272,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "no_intra_molecular", 1, bool + "no_intra_molecular", + 1, + bool ), exception=PQTypeError, function=RDF, @@ -267,7 +289,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "r_max", -1, PositiveReal | None + "r_max", + -1, + PositiveReal | None ), exception=PQTypeError, function=RDF, @@ -282,7 +306,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "r_min", -1, PositiveReal | None + "r_min", + -1, + PositiveReal | None ), exception=PQTypeError, function=RDF, @@ -297,7 +323,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "delta_r", -1, PositiveReal | None + "delta_r", + -1, + PositiveReal | None ), exception=PQTypeError, function=RDF, @@ -312,7 +340,9 @@ def test__init__type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "n_bins", -1, PositiveInt | None + "n_bins", + -1, + PositiveInt | None ), exception=PQTypeError, function=RDF, @@ -347,10 +377,10 @@ def test__init__(self, caplog): logging_name=RDF.__qualname__, logging_level="ERROR", message_to_test=( - "The provided trajectory is not fully periodic " - "or in vacuum, meaning that some frames are in " - "vacuum and others are periodic. This is not " - "supported by the RDF analysis." + "The provided trajectory is not fully periodic " + "or in vacuum, meaning that some frames are in " + "vacuum and others are periodic. This is not " + "supported by the RDF analysis." ), exception=RDFError, function=RDF, @@ -385,9 +415,9 @@ def test__init__(self, caplog): logging_name=RDF.__qualname__, logging_level="ERROR", message_to_test=( - "It is not possible to specify all of n_bins, " - "delta_r and r_max in the same RDF analysis as " - "this would lead to ambiguous results." + "It is not possible to specify all of n_bins, " + "delta_r and r_max in the same RDF analysis as " + "this would lead to ambiguous results." ), exception=RDFError, function=RDF, @@ -405,20 +435,18 @@ def test__init__(self, caplog): n_bins = 5 delta_r = 1.0 - rdf = RDF( - traj, - ["h"], - ["h"], - delta_r=delta_r, - n_bins=n_bins - ) + rdf = RDF(traj, ["h"], ["h"], delta_r=delta_r, n_bins=n_bins) assert np.isclose(rdf.r_max, 5.0) assert np.isclose(rdf.r_min, 0.0) assert len(rdf.bins) == 5 assert np.allclose( rdf.bin_middle_points, - np.array([0.5, 1.5, 2.5, 3.5, 4.5]) + np.array([0.5, + 1.5, + 2.5, + 3.5, + 4.5]) ) assert rdf.n_bins == 5 assert np.isclose(rdf.delta_r, 1.0) @@ -432,9 +460,9 @@ def test__init__(self, caplog): logging_name=RDF.__qualname__, logging_level="WARNING", message_to_test=( - "The calculated r_max 10.0 is larger than the maximum allowed " - "radius according to the box vectors of the trajectory 5.0. " - "r_max will be set to the maximum allowed radius." + "The calculated r_max 10.0 is larger than the maximum allowed " + "radius according to the box vectors of the trajectory 5.0. " + "r_max will be set to the maximum allowed radius." ), exception=None, function=RDF, @@ -457,9 +485,9 @@ def test__init__(self, caplog): logging_name=RDF.__qualname__, logging_level="ERROR", message_to_test=( - "To infer r_max of the RDF analysis, the trajectory cannot " - "be a vacuum trajectory. Please specify r_max manually or " - "use the combination n_bins and delta_r." + "To infer r_max of the RDF analysis, the trajectory cannot " + "be a vacuum trajectory. Please specify r_max manually or " + "use the combination n_bins and delta_r." ), exception=RDFError, function=RDF, @@ -474,9 +502,9 @@ def test__init__(self, caplog): logging_name=RDF.__qualname__, logging_level="ERROR", message_to_test=( - "To infer r_max of the RDF analysis, the trajectory cannot be " - "a vacuum trajectory. Please specify r_max manually or use the " - "combination n_bins and delta_r." + "To infer r_max of the RDF analysis, the trajectory cannot be " + "a vacuum trajectory. Please specify r_max manually or use the " + "combination n_bins and delta_r." ), exception=RDFError, function=RDF, @@ -488,26 +516,36 @@ def test__init__(self, caplog): r_max = 5.0 - rdf = RDF( - traj, ["h"], ["h"], delta_r=delta_r, r_max=r_max) + rdf = RDF(traj, ["h"], ["h"], delta_r=delta_r, r_max=r_max) assert np.isclose(rdf.r_max, 5.0) assert np.isclose(rdf.r_min, 0.0) assert len(rdf.bins) == 5 - assert np.allclose(rdf.bin_middle_points, - np.array([0.5, 1.5, 2.5, 3.5, 4.5])) + assert np.allclose( + rdf.bin_middle_points, + np.array([0.5, + 1.5, + 2.5, + 3.5, + 4.5]) + ) assert rdf.n_bins == 5 assert np.isclose(rdf.delta_r, 1.0) n_bins = 5 - rdf = RDF( - traj, ["h"], ["h"], n_bins=n_bins, r_max=r_max) + rdf = RDF(traj, ["h"], ["h"], n_bins=n_bins, r_max=r_max) assert np.isclose(rdf.r_max, 5.0) assert np.isclose(rdf.r_min, 0.0) assert len(rdf.bins) == 5 - assert np.allclose(rdf.bin_middle_points, - np.array([0.5, 1.5, 2.5, 3.5, 4.5])) + assert np.allclose( + rdf.bin_middle_points, + np.array([0.5, + 1.5, + 2.5, + 3.5, + 4.5]) + ) assert rdf.n_bins == 5 assert np.isclose(rdf.delta_r, 1.0) diff --git a/tests/analysis/rdf/test_rdfOutputFileReader.py b/tests/analysis/rdf/test_rdfOutputFileReader.py index 9976f704..e80800c9 100644 --- a/tests/analysis/rdf/test_rdfOutputFileReader.py +++ b/tests/analysis/rdf/test_rdfOutputFileReader.py @@ -9,16 +9,18 @@ from ...conftest import assert_logging_with_exception + class TestRDFLogWriter: + def test__type_checking(self, caplog): assert_logging_with_exception( caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "filename", - 1.0, - str | None + "filename", + 1.0, + str | None ), exception=PQTypeError, function=RDFLogWriter, @@ -29,7 +31,9 @@ def test__type_checking(self, caplog): caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", - message_to_test=get_type_error_message("rdf", 1.0, RDF), + message_to_test=get_type_error_message("rdf", + 1.0, + RDF), exception=PQTypeError, function=RDFLogWriter("test.out").write_before_run, rdf=1.0, @@ -39,7 +43,9 @@ def test__type_checking(self, caplog): caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", - message_to_test=get_type_error_message("rdf", 1.0, RDF), + message_to_test=get_type_error_message("rdf", + 1.0, + RDF), exception=PQTypeError, function=RDFLogWriter("test.out").write_after_run, rdf=1.0, diff --git a/tests/atomicSystem/test_decorators.py b/tests/atomicSystem/test_decorators.py index 6220525c..25e35910 100644 --- a/tests/atomicSystem/test_decorators.py +++ b/tests/atomicSystem/test_decorators.py @@ -10,6 +10,7 @@ from ..conftest import assert_logging_with_exception + def test_check_atom_number_setter(caplog): system = AtomicSystem() @@ -18,14 +19,16 @@ def test_check_atom_number_setter(caplog): AtomicSystem.__qualname__, "ERROR", ( - "The number of atoms in the AtomicSystem object " - "have to be equal to the number of atoms " - "in the new array in order to set the property." + "The number of atoms in the AtomicSystem object " + "have to be equal to the number of atoms " + "in the new array in order to set the property." ), AtomicSystemError, AtomicSystem.pos.__set__, system, - np.array([[0, 0, 0]]) + np.array([[0, + 0, + 0]]) ) system = AtomicSystem(atoms=[Atom('C'), Atom('H')]) diff --git a/tests/atomicSystem/test_positions.py b/tests/atomicSystem/test_positions.py index 7837038e..768c89f8 100644 --- a/tests/atomicSystem/test_positions.py +++ b/tests/atomicSystem/test_positions.py @@ -5,7 +5,6 @@ import numpy as np import pytest - from PQAnalysis.atomic_system import AtomicSystem from PQAnalysis.atomic_system.exceptions import AtomicSystemPositionsError from PQAnalysis.core import Atom, Cell @@ -18,16 +17,18 @@ from ..conftest import assert_logging_with_exception + class TestPositionsMixin: + def test_nearest_neighbours_type_checking(self, caplog): assert_logging_with_exception( caplog=caplog, logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "n", - -1, - PositiveInt, + "n", + -1, + PositiveInt, ), exception=PQTypeError, function=AtomicSystem().nearest_neighbours, @@ -39,9 +40,9 @@ def test_nearest_neighbours_type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "selection", - AtomicSystem(), - SelectionCompatible, + "selection", + AtomicSystem(), + SelectionCompatible, ), exception=PQTypeError, function=AtomicSystem().nearest_neighbours, @@ -53,9 +54,9 @@ def test_nearest_neighbours_type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "use_full_atom_info", - "2", - bool, + "use_full_atom_info", + "2", + bool, ), exception=PQTypeError, function=AtomicSystem().nearest_neighbours, @@ -71,18 +72,21 @@ def test_nearest_neighbours(self, caplog): indices, distances = system.nearest_neighbours() assert np.allclose(indices, [[3], [2], [3], [0]]) assert np.allclose( - distances, [[1.0], [np.sqrt(8*8+1*1)], [np.sqrt(2)], [1.0]]) + distances, + [[1.0], + [np.sqrt(8 * 8 + 1 * 1)], + [np.sqrt(2)], + [1.0]] + ) indices, distances = system._nearest_neighbours() assert np.allclose(indices, [[3], [2], [3], [0]]) assert np.allclose( distances, - [ - [1.0], - [np.sqrt(8*8+1*1)], - [np.sqrt(2)], - [1.0] - ] + [[1.0], + [np.sqrt(8 * 8 + 1 * 1)], + [np.sqrt(2)], + [1.0]] ) indices, distances = system.nearest_neighbours(n=2) @@ -90,10 +94,14 @@ def test_nearest_neighbours(self, caplog): assert np.allclose( distances, [ - [1.0, np.sqrt(5)], - [np.sqrt(8*8+1), np.sqrt(9*9)], - [np.sqrt(2), np.sqrt(5)], - [1.0, np.sqrt(2)] + [1.0, + np.sqrt(5)], + [np.sqrt(8 * 8 + 1), + np.sqrt(9 * 9)], + [np.sqrt(2), + np.sqrt(5)], + [1.0, + np.sqrt(2)] ] ) @@ -105,10 +113,7 @@ def test_nearest_neighbours(self, caplog): assert np.allclose(indices, [[2], [0]]) assert np.allclose(distances, [[np.sqrt(65)], [1.0]]) - indices, distances = system.nearest_neighbours( - n=1, - selection=['H1'] - ) + indices, distances = system.nearest_neighbours(n=1, selection=['H1']) assert np.allclose(indices, [[2], [0]]) assert np.allclose(distances, [[np.sqrt(65)], [1.0]]) @@ -140,9 +145,18 @@ def test_nearest_neighbours(self, caplog): def test_image(self): system = AtomicSystem( - atoms=[Atom('C'), Atom('H1', 1)], - pos=np.array([[0, 0, 0], [10, 0, 0]]), - cell=Cell(8, 8, 8) + atoms=[Atom('C'), + Atom('H1', + 1)], + pos=np.array([[0, + 0, + 0], + [10, + 0, + 0]]), + cell=Cell(8, + 8, + 8) ) system.image() @@ -154,9 +168,9 @@ def test_center_type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "position", - "2", - Np1DNumberArray, + "position", + "2", + Np1DNumberArray, ), exception=PQTypeError, function=AtomicSystem().center, @@ -168,21 +182,32 @@ def test_center_type_checking(self, caplog): logging_name="TypeChecking", logging_level="ERROR", message_to_test=get_type_error_message( - "image", - "2", - bool, + "image", + "2", + bool, ), exception=PQTypeError, function=AtomicSystem().center, - position=np.array([1, 0, 0]), + position=np.array([1, + 0, + 0]), image="2" ) def test_center(self): system = AtomicSystem( - atoms=[Atom('C'), Atom('H1', 1)], - pos=np.array([[0, 0, 0], [10, 0, 0]]), - cell=Cell(8, 8, 8) + atoms=[Atom('C'), + Atom('H1', + 1)], + pos=np.array([[0, + 0, + 0], + [10, + 0, + 0]]), + cell=Cell(8, + 8, + 8) ) system.center(np.array([1, 0, 0]), image=False) diff --git a/tests/cli/__init__.py b/tests/cli/__init__.py index 30c4de72..7fb7dc3e 100644 --- a/tests/cli/__init__.py +++ b/tests/cli/__init__.py @@ -3,7 +3,9 @@ from PQAnalysis._version import __version__ + class ArgparseNamespace(argparse.Namespace): + def __init__(self, **kwargs): self.__dict__.update(progress=False, version=__version__) self.__dict__.update(kwargs) diff --git a/tests/cli/test_rst2xyz.py b/tests/cli/test_rst2xyz.py index b0827a28..9865ed04 100644 --- a/tests/cli/test_rst2xyz.py +++ b/tests/cli/test_rst2xyz.py @@ -7,6 +7,7 @@ from PQAnalysis.cli.rst2xyz import rst2xyz, main + @pytest.mark.parametrize("example_dir", ["rst2xyz"], indirect=False) def test_rst2xyz(test_with_data_dir, capsys): print() @@ -36,6 +37,7 @@ def test_rst2xyz(test_with_data_dir, capsys): """ + @pytest.mark.parametrize("example_dir", ["rst2xyz"], indirect=False) def test_main(test_with_data_dir, capsys): main_rst2xyz() @@ -64,16 +66,17 @@ def test_main(test_with_data_dir, capsys): """ + @mock.patch( 'argparse.ArgumentParser.parse_args', return_value=ArgparseNamespace( - restart_file="md-01.rst", - output=None, - nobox=False, - engine="PQ", - mode="w", - log_file=None, - logging_level="INFO", + restart_file="md-01.rst", + output=None, + nobox=False, + engine="PQ", + mode="w", + log_file=None, + logging_level="INFO", ) ) def main_rst2xyz(mock_args): @@ -81,16 +84,17 @@ def main_rst2xyz(mock_args): main() + @mock.patch( 'argparse.ArgumentParser.parse_args', return_value=ArgparseNamespace( - restart_file="md-01.rst", - output=None, - nobox=False, - engine="qmcfc", - mode="w", - log_file=None, - logging_level="INFO", + restart_file="md-01.rst", + output=None, + nobox=False, + engine="qmcfc", + mode="w", + log_file=None, + logging_level="INFO", ) ) def main_rst2xyz_qmcfc(mock_args): diff --git a/tests/cli/test_traj2box.py b/tests/cli/test_traj2box.py index 987484ab..5b4098ac 100644 --- a/tests/cli/test_traj2box.py +++ b/tests/cli/test_traj2box.py @@ -8,33 +8,39 @@ from . import ArgparseNamespace + @pytest.mark.parametrize("example_dir", ["traj2box"], indirect=False) def test_traj2box(test_with_data_dir): traj2box(trajectory_files=["test.xyz"], vmd=False, output="test_box.dat") assert filecmp("box.dat", "test_box.dat") - traj2box(trajectory_files=["test.xyz"], - vmd=True, output="test_box.vmd.xyz") + traj2box( + trajectory_files=["test.xyz"], + vmd=True, + output="test_box.vmd.xyz" + ) assert filecmp("box.vmd.xyz", "test_box.vmd.xyz") + @pytest.mark.parametrize("example_dir", ["traj2box"], indirect=False) def test_main(test_with_data_dir): main_box_file() main_vmd() + @mock.patch( 'argparse.ArgumentParser.parse_args', return_value=ArgparseNamespace( - trajectory_file=["test.xyz"], - vmd=False, - output="test_box.dat", - log_file=None, - logging_level="INFO", - mode='w' + trajectory_file=["test.xyz"], + vmd=False, + output="test_box.dat", + log_file=None, + logging_level="INFO", + mode='w' ) ) def main_box_file(mock_args): @@ -42,15 +48,16 @@ def main_box_file(mock_args): assert filecmp("box.dat", "test_box.dat") + @mock.patch( 'argparse.ArgumentParser.parse_args', return_value=ArgparseNamespace( - trajectory_file=["test.xyz"], - vmd=True, - output="test_box.vmd.xyz", - log_file=None, - logging_level="INFO", - mode='w' + trajectory_file=["test.xyz"], + vmd=True, + output="test_box.vmd.xyz", + log_file=None, + logging_level="INFO", + mode='w' ) ) def main_vmd(mock_args): diff --git a/tests/cli/test_traj2qmcfc.py b/tests/cli/test_traj2qmcfc.py index c776d0ad..dee38371 100644 --- a/tests/cli/test_traj2qmcfc.py +++ b/tests/cli/test_traj2qmcfc.py @@ -8,27 +8,34 @@ from . import ArgparseNamespace + @pytest.mark.parametrize("example_dir", ["traj2qmcfc"], indirect=False) def test_traj2qmcfc(test_with_data_dir): - traj2qmcfc(trajectory_files=[ - "acof_triclinic.xyz", "acof_triclinic_2.xyz"], output="test_traj.qmcfc.xyz") + traj2qmcfc( + trajectory_files=["acof_triclinic.xyz", + "acof_triclinic_2.xyz"], + output="test_traj.qmcfc.xyz" + ) assert filecmp("traj.qmcfc.xyz", "test_traj.qmcfc.xyz") + @pytest.mark.parametrize("example_dir", ["traj2qmcfc"], indirect=False) def test_main(test_with_data_dir): main_traj2qmcfc() + @mock.patch( 'argparse.ArgumentParser.parse_args', return_value=ArgparseNamespace( - trajectory_file=["acof_triclinic.xyz", "acof_triclinic_2.xyz"], - vmd=False, - output="test_traj.qmcfc.xyz", - log_file=None, - logging_level="INFO", + trajectory_file=["acof_triclinic.xyz", + "acof_triclinic_2.xyz"], + vmd=False, + output="test_traj.qmcfc.xyz", + log_file=None, + logging_level="INFO", ) ) def main_traj2qmcfc(mock_args): diff --git a/tests/core/atom/__init__.py b/tests/core/atom/__init__.py index e69de29b..8b137891 100644 --- a/tests/core/atom/__init__.py +++ b/tests/core/atom/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/core/atom/test_atom.py b/tests/core/atom/test_atom.py index afdd52fc..619b0483 100644 --- a/tests/core/atom/test_atom.py +++ b/tests/core/atom/test_atom.py @@ -12,7 +12,9 @@ from ...conftest import assert_logging_with_exception + class TestAtom: + def test__init__(self, caplog): element = Atom('C') assert element.symbol == 'c' @@ -56,11 +58,9 @@ def test__init__(self, caplog): caplog, "TypeChecking", "ERROR", - get_type_error_message( - "name", - 1.2, - str | int - ), + get_type_error_message("name", + 1.2, + str | int), PQTypeError, Atom, 1.2 diff --git a/tests/core/atom/test_element.py b/tests/core/atom/test_element.py index 46960a95..b05753d4 100644 --- a/tests/core/atom/test_element.py +++ b/tests/core/atom/test_element.py @@ -1,7 +1,6 @@ import pytest import numpy as np - from PQAnalysis.core import Element, ElementNotFoundError from PQAnalysis.type_checking import get_type_error_message from PQAnalysis.exceptions import PQTypeError @@ -10,7 +9,9 @@ from ...conftest import assert_logging_with_exception + class TestElement: + def test__init__(self, caplog): assert_logging_with_exception( caplog, @@ -26,11 +27,9 @@ def test__init__(self, caplog): caplog, "TypeChecking", "ERROR", - get_type_error_message( - "element_id", - 1.2, - int | str | None - ), + get_type_error_message("element_id", + 1.2, + int | str | None), PQTypeError, Element, 1.2 diff --git a/tests/core/test_cell.py b/tests/core/test_cell.py index 2ee2fd8e..f3a6db41 100644 --- a/tests/core/test_cell.py +++ b/tests/core/test_cell.py @@ -6,7 +6,9 @@ from PQAnalysis.core import Cell + class TestCell: + def test__init__(self): cell = Cell(1, 2, 3) assert cell.x == 1 @@ -15,8 +17,18 @@ def test__init__(self): assert cell.alpha == 90 assert cell.beta == 90 assert cell.gamma == 90 - assert np.allclose(cell.box_matrix, np.array( - [[1, 0, 0], [0, 2, 0], [0, 0, 3]])) + assert np.allclose( + cell.box_matrix, + np.array([[1, + 0, + 0], + [0, + 2, + 0], + [0, + 0, + 3]]) + ) cell = Cell(1, 2, 3, 60, 90, 120) assert cell.x == 1 @@ -25,8 +37,20 @@ def test__init__(self): assert cell.alpha == 60 assert cell.beta == 90 assert cell.gamma == 120 - assert np.allclose(cell.box_matrix, np.array( - [[1, -1, 0], [0, 1.73205081, 1.73205081], [0, 0, 2.44948974]])) + assert np.allclose( + cell.box_matrix, + np.array( + [[1, + -1, + 0], + [0, + 1.73205081, + 1.73205081], + [0, + 0, + 2.44948974]] + ) + ) def test_box_lengths(self): cell = Cell(1, 2, 3) @@ -36,8 +60,18 @@ def test_set_box_lengths(self): cell = Cell(1, 2, 3) cell.box_lengths = np.array([2, 3, 4]) assert np.allclose(cell.box_lengths, np.array([2, 3, 4])) - assert np.allclose(cell.box_matrix, np.array( - [[2, 0, 0], [0, 3, 0], [0, 0, 4]])) + assert np.allclose( + cell.box_matrix, + np.array([[2, + 0, + 0], + [0, + 3, + 0], + [0, + 0, + 4]]) + ) def test_box_angles(self): cell = Cell(1, 2, 3) @@ -47,14 +81,30 @@ def test_set_box_angles(self): cell = Cell(1, 2, 3) cell.box_angles = np.array([60, 90, 120]) assert np.allclose(cell.box_angles, np.array([60, 90, 120])) - assert np.allclose(cell.box_matrix, np.array( - [[1, -1, 0], [0, 1.73205081, 1.73205081], [0, 0, 2.44948974]])) + assert np.allclose( + cell.box_matrix, + np.array( + [[1, + -1, + 0], + [0, + 1.73205081, + 1.73205081], + [0, + 0, + 2.44948974]] + ) + ) def test_volume(self): cell = Cell(1, 2, 3, 60, 90, 120) box_angles = np.deg2rad(cell.box_angles) - assert np.isclose(cell.volume, np.prod(cell.box_lengths)*np.sqrt(1 - - sum(np.cos(box_angles)**2) + 2 * np.prod(np.cos(box_angles)))) + assert np.isclose( + cell.volume, + np.prod(cell.box_lengths) * np.sqrt( + 1 - sum(np.cos(box_angles)**2) + 2 * np.prod(np.cos(box_angles)) + ) + ) def test_bounding_edges(self): cell = Cell(1, 2, 3, 60, 90, 120) @@ -82,14 +132,38 @@ def test__eq__(self): def test_image(self): cell = Cell(1, 2, 3, 60, 90, 120) - assert np.allclose(cell.image( - np.array([0, 0, 0])), np.array([0, 0, 0])) - assert np.allclose(cell.image( - np.array([0.75, 0.5, 0.5])), np.array([-0.25, 0.5, 0.5])) - assert np.allclose(cell.image( - np.array([1, 2, 3])), np.array([0., 0.267949192, 0.550510257])) - assert np.allclose(cell.image( - np.array([-1, -2, -3])), np.array([0., -0.267949192, -0.550510257])) + assert np.allclose( + cell.image(np.array([0, + 0, + 0])), + np.array([0, + 0, + 0]) + ) + assert np.allclose( + cell.image(np.array([0.75, + 0.5, + 0.5])), + np.array([-0.25, + 0.5, + 0.5]) + ) + assert np.allclose( + cell.image(np.array([1, + 2, + 3])), + np.array([0., + 0.267949192, + 0.550510257]) + ) + assert np.allclose( + cell.image(np.array([-1, + -2, + -3])), + np.array([0., + -0.267949192, + -0.550510257]) + ) def test__str__(self): cell = Cell(1, 2, 3, 60, 90, 120) @@ -115,11 +189,15 @@ def test_init_from_box_matrix(self): assert np.isclose(cell.volume, 6) box_matrix = np.array( - [ - [1, -1, 0], - [0, 1.73205081, 1.73205081], - [0, 0, 2.44948974] - ] + [[1, + -1, + 0], + [0, + 1.73205081, + 1.73205081], + [0, + 0, + 2.44948974]] ) cell = Cell.init_from_box_matrix(box_matrix) assert np.isclose(cell.x, 1) diff --git a/tests/core/test_residue.py b/tests/core/test_residue.py index a364d2de..ac0e476a 100644 --- a/tests/core/test_residue.py +++ b/tests/core/test_residue.py @@ -6,7 +6,9 @@ from PQAnalysis.core import Element, Residue, ResidueError + class TestResidue: + def test__init__(self): residue = Residue( name="name", @@ -28,9 +30,15 @@ def test__init__(self): name="name", residue_id=0, total_charge=0.0, - elements=[Element("C"), Element("H"), Element("H")], - atom_types=np.array([0, 1, 1]), - partial_charges=np.array([0.0, 0.1, 0.1]) + elements=[Element("C"), + Element("H"), + Element("H")], + atom_types=np.array([0, + 1, + 1]), + partial_charges=np.array([0.0, + 0.1, + 0.1]) ) assert residue.name == "name" @@ -38,8 +46,7 @@ def test__init__(self): assert np.allclose(residue.total_charge, 0.0) assert residue.elements == [Element("C"), Element("H"), Element("H")] assert np.allclose(residue.atom_types, np.array([0, 1, 1])) - assert np.allclose(residue.partial_charges, - np.array([0.0, 0.1, 0.1])) + assert np.allclose(residue.partial_charges, np.array([0.0, 0.1, 0.1])) assert residue.n_atoms == 3 residue.elements = [Element("C"), Element("H"), Element("O")] @@ -49,30 +56,43 @@ def test__init__(self): assert np.allclose(residue.atom_types, np.array([0, 1, 2])) residue.partial_charges = np.array([0.0, 0.1, 0.2]) - assert np.allclose(residue.partial_charges, - np.array([0.0, 0.1, 0.2])) + assert np.allclose(residue.partial_charges, np.array([0.0, 0.1, 0.2])) with pytest.raises(ResidueError) as exception: residue.elements = [Element("C"), Element("H")] assert str( - exception.value) == "The number of elements must be the same as the number of atoms." + exception.value + ) == "The number of elements must be the same as the number of atoms." with pytest.raises(ResidueError) as exception: residue.atom_types = np.array([0, 1]) assert str( - exception.value) == "The number of atom_types must be the same as the number of atoms." + exception.value + ) == "The number of atom_types must be the same as the number of atoms." with pytest.raises(ResidueError) as exception: residue.partial_charges = np.array([0.0, 0.1]) assert str( - exception.value) == "The number of partial_charges must be the same as the number of atoms." + exception.value + ) == "The number of partial_charges must be the same as the number of atoms." with pytest.raises(ResidueError) as exception: - Residue(name="name", residue_id=0, total_charge=0.0, - elements=[Element("C"), Element("H"), Element("H")], atom_types=np.array([0, 1]), - partial_charges=np.array([0.0, 0.1, 0.1])) + Residue( + name="name", + residue_id=0, + total_charge=0.0, + elements=[Element("C"), + Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.0, + 0.1, + 0.1]) + ) assert str( - exception.value) == "The number of elements, atom_types and partial_charges must be the same." + exception.value + ) == "The number of elements, atom_types and partial_charges must be the same." residue = Residue( name="name", @@ -89,10 +109,22 @@ def test__init__(self): assert residue.n_atoms == 1 def test__str__(self): - residue = Residue(name="name", residue_id=0, total_charge=0.0, - elements=[Element("C"), Element("H"), Element("H")], atom_types=np.array([0, 1, 1]), - partial_charges=np.array([0.0, 0.1, 0.1])) + residue = Residue( + name="name", + residue_id=0, + total_charge=0.0, + elements=[Element("C"), + Element("H"), + Element("H")], + atom_types=np.array([0, + 1, + 1]), + partial_charges=np.array([0.0, + 0.1, + 0.1]) + ) assert str( - residue) == "Residue(name='name', id=0, total_charge=0.0, n_atoms=3)" + residue + ) == "Residue(name='name', id=0, total_charge=0.0, n_atoms=3)" assert str(residue) == repr(residue) diff --git a/tests/io/inputFileReader/PQ/__init__.py b/tests/io/inputFileReader/PQ/__init__.py index e69de29b..8b137891 100644 --- a/tests/io/inputFileReader/PQ/__init__.py +++ b/tests/io/inputFileReader/PQ/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/io/inputFileReader/PQAnalysis/__init__.py b/tests/io/inputFileReader/PQAnalysis/__init__.py index e69de29b..8b137891 100644 --- a/tests/io/inputFileReader/PQAnalysis/__init__.py +++ b/tests/io/inputFileReader/PQAnalysis/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/io/inputFileReader/PQAnalysis/test_parse.py b/tests/io/inputFileReader/PQAnalysis/test_parse.py index c2bb29a1..41d1f2e1 100644 --- a/tests/io/inputFileReader/PQAnalysis/test_parse.py +++ b/tests/io/inputFileReader/PQAnalysis/test_parse.py @@ -9,6 +9,7 @@ from PQAnalysis.traj import MDEngineFormat + def test_parse_real(): input_dict = InputDictionary() input_dict["a"] = (2.0, "float", "1") @@ -25,7 +26,9 @@ def test_parse_real(): with pytest.raises(InputFileError) as exception: parse._parse_real(input_dict, "c") assert str( - exception.value) == "The \"c\" value has to be of float type - actually it is parsed as a str" + exception.value + ) == "The \"c\" value has to be of float type - actually it is parsed as a str" + def test_parse_positive_real(): @@ -44,14 +47,17 @@ def test_parse_positive_real(): with pytest.raises(InputFileError) as exception: parse._parse_positive_real(input_dict, "c") assert str( - exception.value) == "The \"c\" value has to be of float type - actually it is parsed as a str" + exception.value + ) == "The \"c\" value has to be of float type - actually it is parsed as a str" input_dict["d"] = (-2.0, "float", "1") with pytest.raises(InputFileError) as exception: parse._parse_positive_real(input_dict, "d") assert str( - exception.value) == "The \"d\" value has to be a positive real number - It actually is -2.0!" + exception.value + ) == "The \"d\" value has to be a positive real number - It actually is -2.0!" + def test_parse_files(): @@ -73,7 +79,10 @@ def test_parse_files(): with pytest.raises(InputFileError) as exception: parse._parse_files(input_dict, "d") - assert str(exception.value) == "The \"d\" value has to be either a string, glob or a list of strings - actually it is parsed as a int" + assert str( + exception.value + ) == "The \"d\" value has to be either a string, glob or a list of strings - actually it is parsed as a int" + def test_parse_int(): @@ -88,7 +97,9 @@ def test_parse_int(): with pytest.raises(InputFileError) as exception: parse._parse_int(input_dict, "d") assert str( - exception.value) == "The \"d\" value has to be of int type - actually it is parsed as a str" + exception.value + ) == "The \"d\" value has to be of int type - actually it is parsed as a str" + def test_parse_positive_int(): @@ -103,7 +114,9 @@ def test_parse_positive_int(): with pytest.raises(InputFileError) as exception: parse._parse_positive_int(input_dict, "b") assert str( - exception.value) == "The \"b\" value has to be a positive integer - It actually is -2!" + exception.value + ) == "The \"b\" value has to be a positive integer - It actually is -2!" + def test_parse_string(): @@ -118,7 +131,9 @@ def test_parse_string(): with pytest.raises(InputFileError) as exception: parse._parse_string(input_dict, "b") assert str( - exception.value) == "The \"b\" value has to be of string type - actually it is parsed as a int" + exception.value + ) == "The \"b\" value has to be of string type - actually it is parsed as a int" + def test_parse_bool(): @@ -137,4 +152,5 @@ def test_parse_bool(): with pytest.raises(InputFileError) as exception: parse._parse_bool(input_dict, "c") assert str( - exception.value) == "The \"c\" value has to be of bool type - actually it is parsed as a int" + exception.value + ) == "The \"c\" value has to be of bool type - actually it is parsed as a int" diff --git a/tests/io/inputFileReader/__init__.py b/tests/io/inputFileReader/__init__.py index e69de29b..8b137891 100644 --- a/tests/io/inputFileReader/__init__.py +++ b/tests/io/inputFileReader/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/io/inputFileReader/test_inputFileParser.py b/tests/io/inputFileReader/test_inputFileParser.py index e22e4b09..03cc3738 100644 --- a/tests/io/inputFileReader/test_inputFileParser.py +++ b/tests/io/inputFileReader/test_inputFileParser.py @@ -7,13 +7,18 @@ from PQAnalysis.exceptions import PQFileNotFoundError + class TestInputFileParser: - @pytest.mark.parametrize("example_dir", ["inputFileReader"], indirect=False) + + @pytest.mark.parametrize( + "example_dir", + ["inputFileReader"], + indirect=False + ) def test__init__(self, test_with_data_dir): with pytest.raises(PQFileNotFoundError) as exception: InputFileParser("non-existent-file") - assert str( - exception.value) == "File non-existent-file not found." + assert str(exception.value) == "File non-existent-file not found." input_file = "input.in" @@ -43,7 +48,11 @@ def test__init__(self, test_with_data_dir): "PQANALYSIS, PQ, QMCFC" ) - @ pytest.mark.parametrize("example_dir", ["inputFileReader"], indirect=False) + @pytest.mark.parametrize( + "example_dir", + ["inputFileReader"], + indirect=False + ) def test_parse(self, test_with_data_dir): input_file_parser = InputFileParser("input.in") input_dictionary = input_file_parser.parse() diff --git a/tests/io/inputFileReader/test_visitors.py b/tests/io/inputFileReader/test_visitors.py index 5188f5bf..961b48ba 100644 --- a/tests/io/inputFileReader/test_visitors.py +++ b/tests/io/inputFileReader/test_visitors.py @@ -5,7 +5,9 @@ from PQAnalysis.io.input_file_reader.input_file_parser import InputFileVisitor, InputDictionary + class TestInputFileVisitor: + def test__init__(self): visitor = InputFileVisitor() assert visitor.dict == InputDictionary() @@ -25,8 +27,17 @@ def test_multiline_statement(self): token.end_line = 4 key_token = Token("WORD", "key") key_token.end_line = 1 - tree = Tree("multiline_statement", [ - key_token, ("1", "float", "1"), ("4", "int", "4"), token]) + tree = Tree( + "multiline_statement", + [key_token, + ("1", + "float", + "1"), + ("4", + "int", + "4"), + token] + ) return_tree = visitor.multiline_statement(tree) @@ -35,8 +46,14 @@ def test_multiline_statement(self): def test_visit(self): visitor = InputFileVisitor() - tree = Tree("input_file", [ - Tree("assign", ["key", ("value", "word", "1")])]) + tree = Tree( + "input_file", + [Tree("assign", + ["key", + ("value", + "word", + "1")])] + ) return_dict = visitor.visit(tree) diff --git a/tests/io/test_base.py b/tests/io/test_base.py index 1a4c1639..0a15ea06 100644 --- a/tests/io/test_base.py +++ b/tests/io/test_base.py @@ -9,6 +9,7 @@ from PQAnalysis.exceptions import PQFileNotFoundError + class TestBaseWriter: @pytest.mark.usefixtures("tmpdir") @@ -19,7 +20,8 @@ def test__init__(self): with pytest.raises(FileWritingModeError) as exception: BaseWriter(filename, "r") assert str( - exception.value) == """" + exception.value + ) == """" 'r' is not a valid FileWritingMode. Possible values are: FileWritingMode.OVERWRITE, FileWritingMode.APPEND, FileWritingMode.WRITE or their case insensitive string representation: o, a, w""" @@ -29,7 +31,8 @@ def test__init__(self): with pytest.raises(FileWritingModeError) as exception: BaseWriter(filename, "w") assert str( - exception.value) == "File tmp already exists. Use mode \'a\' to append to the file or mode 'o' to overwrite the file." + exception.value + ) == "File tmp already exists. Use mode \'a\' to append to the file or mode 'o' to overwrite the file." writer = BaseWriter(filename, "o") assert writer.file is None @@ -92,7 +95,9 @@ def test_close(self): assert writer.file is None + class TestBaseReader: + @pytest.mark.usefixtures("tmpdir") def test__init__(self): with pytest.raises(PQFileNotFoundError) as exception: @@ -111,7 +116,8 @@ def test__init__(self): with pytest.raises(PQFileNotFoundError) as exception: BaseReader([filename, "tmp2"]) assert str( - exception.value) == "At least one of the given files does not exist. File tmp2 not found." + exception.value + ) == "At least one of the given files does not exist. File tmp2 not found." filename2 = "tmp2" file = open(filename2, "w") diff --git a/tests/io/test_boxWriter.py b/tests/io/test_boxWriter.py index 69c3a38c..2844104b 100644 --- a/tests/io/test_boxWriter.py +++ b/tests/io/test_boxWriter.py @@ -12,6 +12,7 @@ from PQAnalysis.atomic_system import AtomicSystem + class TestBoxWriter: def test__init__(self): @@ -49,7 +50,10 @@ def test__init__(self): def test__check_PBC__(self): system1 = AtomicSystem( - atoms=self.atoms1, pos=self.pos1, cell=self.cell1) + atoms=self.atoms1, + pos=self.pos1, + cell=self.cell1 + ) system2 = AtomicSystem(atoms=self.atoms1, pos=self.pos1) frame1 = system1 @@ -68,7 +72,8 @@ def test__check_PBC__(self): with pytest.raises(BoxWriterError) as exception: writer.__check_pbc__(traj2) assert str( - exception.value) == "At least on cell of the trajectory is None. Cannot write box file." + exception.value + ) == "At least on cell of the trajectory is None. Cannot write box file." def test_write_box_file(self, capsys: CaptureFixture): writer = BoxWriter() @@ -76,16 +81,8 @@ def test_write_box_file(self, capsys: CaptureFixture): cell1 = Cell(10, 10, 10, 90, 90, 90) cell2 = Cell(10, 10, 11, 90, 90, 120) - frame1 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell1 - ) - frame2 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell2 - ) + frame1 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell1) + frame2 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell2) traj = Trajectory([frame1, frame2]) @@ -100,16 +97,8 @@ def test_write_vmd(self, capsys: CaptureFixture): cell1 = Cell(10, 10, 10, 90, 90, 90) cell2 = Cell(10, 10, 11, 90, 90, 90) - frame1 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell1 - ) - frame2 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell2 - ) + frame1 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell1) + frame2 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell2) traj = Trajectory([frame1, frame2]) @@ -146,16 +135,8 @@ def test_write(self, capsys: CaptureFixture): cell1 = Cell(10, 10, 10, 90, 90, 90) cell2 = Cell(10, 10, 11, 90, 90, 90) - frame1 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell1 - ) - frame2 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell2 - ) + frame1 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell1) + frame2 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell2) traj = Trajectory([frame1, frame2]) @@ -198,16 +179,8 @@ def test_write_box(self, capsys: CaptureFixture): cell1 = Cell(10, 10, 10, 90, 90, 90) cell2 = Cell(10, 10, 11, 90, 90, 90) - frame1 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell1 - ) - frame2 = AtomicSystem( - atoms=self.atoms1, - pos=self.pos1, - cell=cell2 - ) + frame1 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell1) + frame2 = AtomicSystem(atoms=self.atoms1, pos=self.pos1, cell=cell2) traj = Trajectory([frame1, frame2]) diff --git a/tests/io/test_energyFileReader.py b/tests/io/test_energyFileReader.py index 2779818c..85f46593 100644 --- a/tests/io/test_energyFileReader.py +++ b/tests/io/test_energyFileReader.py @@ -11,7 +11,9 @@ from PQAnalysis.exceptions import PQFileNotFoundError + class TestEnergyReader: + @pytest.mark.parametrize("example_dir", ["readEnergyFile"], indirect=False) def test__init__(self, test_with_data_dir): with pytest.raises(PQFileNotFoundError) as exception: @@ -38,12 +40,16 @@ def test__init__(self, test_with_data_dir): with pytest.raises(PQFileNotFoundError) as exception: EnergyFileReader( - "md-01_noinfo.en", info_filename="md-01_noinfo.info", use_info_file=True) - assert str( - exception.value) == "Info File md-01_noinfo.info not found." + "md-01_noinfo.en", + info_filename="md-01_noinfo.info", + use_info_file=True + ) + assert str(exception.value) == "Info File md-01_noinfo.info not found." reader = EnergyFileReader( - "md-01_noinfo.en", info_filename="md-01.info") + "md-01_noinfo.en", + info_filename="md-01.info" + ) assert reader.filename == "md-01_noinfo.en" assert reader.info_filename == "md-01.info" assert reader.with_info_file == True @@ -74,30 +80,49 @@ def test__info_file_found__(self, test_with_data_dir): assert reader.__info_file_found__() == False reader = EnergyFileReader( - "md-01_noinfo.en", info_filename="md-01.info") + "md-01_noinfo.en", + info_filename="md-01.info" + ) assert reader.__info_file_found__() == True with pytest.raises(PQFileNotFoundError) as exception: EnergyFileReader( - "md-01_noinfo.en", info_filename="md-01_noinfo.info", use_info_file=True) + "md-01_noinfo.en", + info_filename="md-01_noinfo.info", + use_info_file=True + ) assert str(exception.value) == "Info File md-01_noinfo.info not found." @pytest.mark.parametrize("example_dir", ["readEnergyFile"], indirect=False) def test_read(self, test_with_data_dir): - data_ref = np.array([[1.00000000e+00, 2.00000000e+00], - [2.98066020e+02, 2.98442926e+02], - [2.32566666e+04, 2.39839997e+04], - [-1.85898221e+05, - - 1.85719252e+05], - [-1.85903822e+05, - - 1.86024132e+05], - [0.00000000e+00, 0.00000000e+00], - [5.60049937e+00, 3.04879280e+02], - [0.00000000e+00, 0.00000000e+00], - [3.72913867e+03, 3.72146189e+03], - [8.34545285e-01, 8.34545285e-01], - [1.12687000e-16, 5.63381000e-17], - [1.07571000e+00, 9.97330000e-01]]) + data_ref = np.array( + [ + [1.00000000e+00, + 2.00000000e+00], + [2.98066020e+02, + 2.98442926e+02], + [2.32566666e+04, + 2.39839997e+04], + [-1.85898221e+05, + -1.85719252e+05], + [-1.85903822e+05, + -1.86024132e+05], + [0.00000000e+00, + 0.00000000e+00], + [5.60049937e+00, + 3.04879280e+02], + [0.00000000e+00, + 0.00000000e+00], + [3.72913867e+03, + 3.72146189e+03], + [8.34545285e-01, + 8.34545285e-01], + [1.12687000e-16, + 5.63381000e-17], + [1.07571000e+00, + 9.97330000e-01] + ] + ) infoFileReader = InfoFileReader("md-01.info") info, units = infoFileReader.read() diff --git a/tests/io/test_frameReader.py b/tests/io/test_frameReader.py index 3b756686..5deb61f9 100644 --- a/tests/io/test_frameReader.py +++ b/tests/io/test_frameReader.py @@ -11,6 +11,7 @@ from . import pytestmark + class TestFrameReader: def test__read_header_line(self): @@ -19,10 +20,10 @@ def test__read_header_line(self): with pytest.raises(FrameReaderError) as exception: reader._read_header_line("1 2.0 3.0") assert str( - exception.value) == "Invalid file format in header line of Frame." + exception.value + ) == "Invalid file format in header line of Frame." - n_atoms, cell = reader._read_header_line( - "1 2.0 3.0 4.0 5.0 6.0 7.0") + n_atoms, cell = reader._read_header_line("1 2.0 3.0 4.0 5.0 6.0 7.0") assert n_atoms == 1 assert np.allclose(cell.box_lengths, [2.0, 3.0, 4.0]) assert np.allclose(cell.box_angles, [5.0, 6.0, 7.0]) @@ -40,10 +41,10 @@ def test__read_xyz(self): reader = _FrameReader() with pytest.raises(FrameReaderError) as exception: - reader._read_xyz( - ["", "", "h 1.0 2.0 3.0", "o 2.0 2.0"], n_atoms=2) + reader._read_xyz(["", "", "h 1.0 2.0 3.0", "o 2.0 2.0"], n_atoms=2) assert str( - exception.value) == "Invalid file format in xyz coordinates of Frame." + exception.value + ) == "Invalid file format in xyz coordinates of Frame." xyz, atoms = reader._read_xyz( ["", "", "h 1.0 2.0 3.0", "o 2.0 2.0 2.0"], n_atoms=2) @@ -56,7 +57,8 @@ def test__read_scalar(self): with pytest.raises(FrameReaderError) as exception: reader._read_scalar(["", "", "h 1.0 2.0 3.0"], n_atoms=1) assert str( - exception.value) == "Invalid file format in scalar values of Frame." + exception.value + ) == "Invalid file format in scalar values of Frame." scalar, atoms = reader._read_scalar(["", "", "h 1.0"], n_atoms=1) assert np.allclose(scalar, [1.0]) @@ -66,45 +68,61 @@ def test_read(self): reader = _FrameReader() frame = reader.read( - "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no 2.0 2.0 2.0") + "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no 2.0 2.0 2.0" + ) assert frame.n_atoms == 2 assert frame.atoms == [Atom(atom) for atom in ["h", "o"]] - assert np.allclose(frame.pos, [ - [1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) + assert np.allclose(frame.pos, [[1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) assert frame.cell == Cell(2.0, 3.0, 4.0, 5.0, 6.0, 7.0) frame = reader.read( - "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0") + "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0" + ) assert frame.n_atoms == 2 - assert frame.atoms == [Atom(atom, use_guess_element=False) - for atom in ["h", "o1"]] - assert np.allclose(frame.pos, [ - [1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) + assert frame.atoms == [ + Atom(atom, + use_guess_element=False) for atom in ["h", + "o1"] + ] + assert np.allclose(frame.pos, [[1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) assert frame.cell == Cell(2.0, 3.0, 4.0, 5.0, 6.0, 7.0) frame = reader.read( - "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0", traj_format="vel") + "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0", + traj_format="vel" + ) assert frame.n_atoms == 2 - assert frame.atoms == [Atom(atom, use_guess_element=False) - for atom in ["h", "o1"]] - assert np.allclose(frame.vel, [ - [1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) + assert frame.atoms == [ + Atom(atom, + use_guess_element=False) for atom in ["h", + "o1"] + ] + assert np.allclose(frame.vel, [[1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) assert frame.cell == Cell(2.0, 3.0, 4.0, 5.0, 6.0, 7.0) frame = reader.read( - "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0", traj_format="force") + "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0 2.0 3.0\no1 2.0 2.0 2.0", + traj_format="force" + ) assert frame.n_atoms == 2 - assert frame.atoms == [Atom(atom, use_guess_element=False) - for atom in ["h", "o1"]] - assert np.allclose(frame.forces, [ - [1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) + assert frame.atoms == [ + Atom(atom, + use_guess_element=False) for atom in ["h", + "o1"] + ] + assert np.allclose(frame.forces, [[1.0, 2.0, 3.0], [2.0, 2.0, 2.0]]) assert frame.cell == Cell(2.0, 3.0, 4.0, 5.0, 6.0, 7.0) frame = reader.read( - "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0\no1 2.0", traj_format="charge") + "2 2.0 3.0 4.0 5.0 6.0 7.0\n\nh 1.0\no1 2.0", + traj_format="charge" + ) assert frame.n_atoms == 2 - assert frame.atoms == [Atom(atom, use_guess_element=False) - for atom in ["h", "o1"]] + assert frame.atoms == [ + Atom(atom, + use_guess_element=False) for atom in ["h", + "o1"] + ] assert np.allclose(frame.charges, [1.0, 2.0]) assert frame.cell == Cell(2.0, 3.0, 4.0, 5.0, 6.0, 7.0) diff --git a/tests/io/test_infoFileReader.py b/tests/io/test_infoFileReader.py index 25e3d86a..31054cc9 100644 --- a/tests/io/test_infoFileReader.py +++ b/tests/io/test_infoFileReader.py @@ -8,6 +8,7 @@ from . import pytestmark + @pytest.mark.parametrize("example_dir", ["readInfoFile"], indirect=False) def test__init__(test_with_data_dir): with pytest.raises(PQFileNotFoundError) as exception: @@ -15,8 +16,7 @@ def test__init__(test_with_data_dir): assert str(exception.value) == "File tmp not found." with pytest.raises(MDEngineFormatError) as exception: - InfoFileReader( - "md-01.info", engine_format="tmp") + InfoFileReader("md-01.info", engine_format="tmp") assert str(exception.value) == ( "\n" "'tmp' is not a valid MDEngineFormat.\n" @@ -34,6 +34,7 @@ def test__init__(test_with_data_dir): assert reader.format == MDEngineFormat.QMCFC + @pytest.mark.parametrize("example_dir", ["readInfoFile"], indirect=False) def test_read(test_with_data_dir): reader = InfoFileReader("md-01.info") @@ -107,9 +108,12 @@ def test_read(test_with_data_dir): with pytest.raises(MDEngineFormatError) as exception: reader.read() assert str( - exception.value) == "Info file md-01.qmcfc.info is not in PQ format." + exception.value + ) == "Info file md-01.qmcfc.info is not in PQ format." reader = InfoFileReader("md-01.info", engine_format="qmcfc") with pytest.raises(MDEngineFormatError) as exception: reader.read() - assert str(exception.value) == "Info file md-01.info is not in qmcfc format." + assert str( + exception.value + ) == "Info file md-01.info is not in qmcfc format." diff --git a/tests/io/test_moldescriptorReader.py b/tests/io/test_moldescriptorReader.py index 47257dde..fec8fdae 100644 --- a/tests/io/test_moldescriptorReader.py +++ b/tests/io/test_moldescriptorReader.py @@ -9,8 +9,14 @@ from PQAnalysis.exceptions import PQFileNotFoundError + class TestMoldescriptorReader: - @pytest.mark.parametrize("example_dir", ["readMoldescriptor"], indirect=False) + + @pytest.mark.parametrize( + "example_dir", + ["readMoldescriptor"], + indirect=False + ) def test__init__(self, test_with_data_dir): reader = MoldescriptorReader("moldescriptor.dat") assert reader.filename == "moldescriptor.dat" @@ -33,8 +39,12 @@ def test_read_mol_type(self): assert mol_type.total_charge == 1.0 assert mol_type.elements == [Element("H"), Element("H"), Element("O")] assert np.allclose(mol_type.atom_types, np.array([1, 1, 2])) - assert np.allclose(mol_type.partial_charges, - np.array([2.3, 2.3, -4.6])) + assert np.allclose( + mol_type.partial_charges, + np.array([2.3, + 2.3, + -4.6]) + ) assert mol_type.id == 1 mol_type = f"""H2o 3 1.0 @@ -46,9 +56,14 @@ def test_read_mol_type(self): with pytest.raises(MoldescriptorReaderError) as exception: MoldescriptorReader._read_mol_type(mol_type.splitlines(), 1) assert str( - exception.value) == "The number of columns in the body of a mol type must be 3 or 4.\nGot 2 columns instead in text: 'H 1'\n" - - @pytest.mark.parametrize("example_dir", ["readMoldescriptor"], indirect=False) + exception.value + ) == "The number of columns in the body of a mol type must be 3 or 4.\nGot 2 columns instead in text: 'H 1'\n" + + @pytest.mark.parametrize( + "example_dir", + ["readMoldescriptor"], + indirect=False + ) def test_read(self, test_with_data_dir): reader = MoldescriptorReader("moldescriptor.dat") mol_types = reader.read() @@ -60,10 +75,17 @@ def test_read(self, test_with_data_dir): assert water_type.n_atoms == 3 assert np.allclose(water_type.total_charge, 0.0) assert water_type.elements == [ - Element("O"), Element("H"), Element("H")] + Element("O"), + Element("H"), + Element("H") + ] assert np.allclose(water_type.atom_types, np.array([0, 1, 1])) - assert np.allclose(water_type.partial_charges, - np.array([-0.65966, 0.32983, 0.32983])) + assert np.allclose( + water_type.partial_charges, + np.array([-0.65966, + 0.32983, + 0.32983]) + ) assert water_type.id == 1 ammonia_type = mol_types[1] @@ -72,14 +94,24 @@ def test_read(self, test_with_data_dir): assert ammonia_type.n_atoms == 4 assert np.allclose(ammonia_type.total_charge, 0.0) assert ammonia_type.elements == [ - Element("N"), Element("H"), Element("H"), Element("H")] + Element("N"), + Element("H"), + Element("H"), + Element("H") + ] assert np.allclose(ammonia_type.atom_types, np.array([0, 1, 1, 1])) - assert np.allclose(ammonia_type.partial_charges, - np.array([-0.8022, 0.2674, 0.2674, 0.2674])) + assert np.allclose( + ammonia_type.partial_charges, + np.array([-0.8022, + 0.2674, + 0.2674, + 0.2674]) + ) assert ammonia_type.id == 2 reader = MoldescriptorReader("moldescriptor_withError.dat") with pytest.raises(MoldescriptorReaderError) as exception: reader.read() assert str( - exception.value) == "The number of columns in the header of a mol type must be 3.\nGot 2 columns instead in text: ' H2O 3'\n" + exception.value + ) == "The number of columns in the header of a mol type must be 3.\nGot 2 columns instead in text: ' H2O 3'\n" diff --git a/tests/io/test_restartReader.py b/tests/io/test_restartReader.py index 407a338c..9e34a3ac 100644 --- a/tests/io/test_restartReader.py +++ b/tests/io/test_restartReader.py @@ -10,8 +10,14 @@ from PQAnalysis.exceptions import PQFileNotFoundError + class Test_RestartFileReader: - @pytest.mark.parametrize("example_dir", ["readRestartFile"], indirect=False) + + @pytest.mark.parametrize( + "example_dir", + ["readRestartFile"], + indirect=False + ) def test__init__(self, test_with_data_dir): with pytest.raises(PQFileNotFoundError) as exception: RestartFileReader("tmp") @@ -54,7 +60,8 @@ def test__init__(self, test_with_data_dir): reference_residues=[residue] ) assert str( - exception.value) == "Both moldescriptor_filename and reference_residues are given. They are mutually exclusive." + exception.value + ) == "Both moldescriptor_filename and reference_residues are given. They are mutually exclusive." def test__parse_box(self): line = ["box"] @@ -65,7 +72,13 @@ def test__parse_box(self): line = ["box", "1.0", "2.0", "3.0", "90.0", "90.0", "120.0"] assert RestartFileReader._parse_box(line) == Cell( - 1.0, 2.0, 3.0, 90.0, 90.0, 120.0) + 1.0, + 2.0, + 3.0, + 90.0, + 90.0, + 120.0 + ) with pytest.raises(RestartFileReaderError) as exception: line = ["box", "1.0", "2.0"] @@ -81,29 +94,61 @@ def test__parse_atoms(self): with pytest.raises(RestartFileReaderError) as exception: lines = ["C 0 1 1.0 1.0 1.0"] RestartFileReader._parse_atoms(lines, Cell()) - assert str(exception.value) == "Invalid number of arguments for atom: 6" + assert str( + exception.value + ) == "Invalid number of arguments for atom: 6" - lines = ["C 0 1 1.0 1.0 1.0 1.1 1.2 1.3 1.4 1.5 1.6", - "H 0 2 2.0 2.0 2.0 2.1 2.2 2.3 2.4 2.5 2.6"] + lines = [ + "C 0 1 1.0 1.0 1.0 1.1 1.2 1.3 1.4 1.5 1.6", + "H 0 2 2.0 2.0 2.0 2.1 2.2 2.3 2.4 2.5 2.6" + ] frame = RestartFileReader._parse_atoms(lines, Cell()) system = frame residue_ids = frame.topology.residue_ids assert system.n_atoms == 2 - assert system.atoms == [Atom(name, use_guess_element=False) - for name in ["C", "H"]] + assert system.atoms == [ + Atom(name, + use_guess_element=False) for name in ["C", + "H"] + ] assert system.pos.shape == (2, 3) - assert np.allclose(system.pos, np.array( - [[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]])) + assert np.allclose( + system.pos, + np.array([[1.0, + 1.0, + 1.0], + [2.0, + 2.0, + 2.0]]) + ) assert system.vel.shape == (2, 3) - assert np.allclose(system.vel, np.array( - [[1.1, 1.2, 1.3], [2.1, 2.2, 2.3]])) + assert np.allclose( + system.vel, + np.array([[1.1, + 1.2, + 1.3], + [2.1, + 2.2, + 2.3]]) + ) assert system.forces.shape == (2, 3) - assert np.allclose(system.forces, np.array( - [[1.4, 1.5, 1.6], [2.4, 2.5, 2.6]])) + assert np.allclose( + system.forces, + np.array([[1.4, + 1.5, + 1.6], + [2.4, + 2.5, + 2.6]]) + ) assert np.allclose(residue_ids, np.array([1, 2])) assert system.cell == Cell() - @pytest.mark.parametrize("example_dir", ["readRestartFile"], indirect=False) + @pytest.mark.parametrize( + "example_dir", + ["readRestartFile"], + indirect=False + ) def test_read(self, test_with_data_dir): frame = RestartFileReader( "md-01.rst", @@ -114,19 +159,81 @@ def test_read(self, test_with_data_dir): assert np.allclose(residue_ids, np.array([1, 1, 2, 2])) assert system.n_atoms == 4 - assert system.atoms == [Atom(name, use_guess_element=False) - for name in ["C", "H", "N", "N"]] + assert system.atoms == [ + Atom(name, + use_guess_element=False) for name in ["C", + "H", + "N", + "N"] + ] assert system.pos.shape == (4, 3) - assert np.allclose(system.pos, np.array( - [[1.0, 1.1, 1.2], [2.0, 2.1, 2.2], [3.0, 3.1, 3.2], [4.0, 4.1, 4.2]])) + assert np.allclose( + system.pos, + np.array( + [ + [1.0, + 1.1, + 1.2], + [2.0, + 2.1, + 2.2], + [3.0, + 3.1, + 3.2], + [4.0, + 4.1, + 4.2] + ] + ) + ) assert system.vel.shape == (4, 3) - assert np.allclose(system.vel, np.array( - [[1.3, 1.4, 1.5], [2.3, 2.4, 2.5], [3.3, 3.4, 3.5], [4.3, 4.4, 4.5]])) + assert np.allclose( + system.vel, + np.array( + [ + [1.3, + 1.4, + 1.5], + [2.3, + 2.4, + 2.5], + [3.3, + 3.4, + 3.5], + [4.3, + 4.4, + 4.5] + ] + ) + ) assert system.forces.shape == (4, 3) - assert np.allclose(system.forces, np.array( - [[1.6, 1.7, 1.8], [2.6, 2.7, 2.8], [3.6, 3.7, 3.8], [4.6, 4.7, 4.8]])) + assert np.allclose( + system.forces, + np.array( + [ + [1.6, + 1.7, + 1.8], + [2.6, + 2.7, + 2.8], + [3.6, + 3.7, + 3.8], + [4.6, + 4.7, + 4.8] + ] + ) + ) assert system.cell == Cell( - 15.0623, 15.0964, 20.0232, 89.9232, 90.2261, 120.324) + 15.0623, + 15.0964, + 20.0232, + 89.9232, + 90.2261, + 120.324 + ) assert np.allclose(system.topology.residue_ids, np.array([1, 1, 2, 2])) assert len(system.topology.reference_residues) == 2 diff --git a/tests/io/test_restartWriter.py b/tests/io/test_restartWriter.py index 23ab34ef..61260f55 100644 --- a/tests/io/test_restartWriter.py +++ b/tests/io/test_restartWriter.py @@ -12,7 +12,9 @@ from PQAnalysis.io.restart_file.exceptions import RestartFileWriterError + class TestRestartWriter: + def test__init__(self): writer = RestartFileWriter("restart.dat") assert writer.filename == "restart.dat" @@ -28,15 +30,23 @@ def test__write_box(self, capsys): def test__get_atom_lines(self, capsys): writer = RestartFileWriter() atoms = [Atom("C"), Atom("H"), Atom("H")] - positions = np.array([[0.0, 0.0, 0.0], - [1.0, 1.0, 1.0], - [2.0, 2.0, 2.0]]) + positions = np.array( + [[0.0, + 0.0, + 0.0], + [1.0, + 1.0, + 1.0], + [2.0, + 2.0, + 2.0]] + ) frame = AtomicSystem( - topology=Topology( - atoms=atoms, - residue_ids=np.array([1, 2, 3]) - ), + topology=Topology(atoms=atoms, + residue_ids=np.array([1, + 2, + 3])), pos=positions ) @@ -52,10 +62,18 @@ def test__get_atom_lines(self, capsys): ] system = AtomicSystem( - atoms=[Atom("C"), Atom("H"), Atom("H")], - pos=np.array([[0.0, 0.0, 0.0], - [1.0, 1.0, 1.0], - [2.0, 2.0, 2.0]]), + atoms=[Atom("C"), + Atom("H"), + Atom("H")], + pos=np.array([[0.0, + 0.0, + 0.0], + [1.0, + 1.0, + 1.0], + [2.0, + 2.0, + 2.0]]), ) atom_counter = np.array([0, 1]) @@ -68,28 +86,52 @@ def test__get_atom_lines(self, capsys): ) atom_lines = RestartFileWriter._get_atom_lines(system, 0) - assert atom_lines[0] == "C 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0" - assert atom_lines[1] == "H 0 0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0" - assert atom_lines[2] == "H 0 0 2.0 2.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0" + assert atom_lines[ + 0] == "C 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0" + assert atom_lines[ + 1] == "H 0 0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0" + assert atom_lines[ + 2] == "H 0 0 2.0 2.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0" atom_lines = RestartFileWriter._get_atom_lines( - system, np.array([0, 1, 2])) - assert atom_lines[0] == "C 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0" - assert atom_lines[1] == "H 1 0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0" - assert atom_lines[2] == "H 2 0 2.0 2.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0" + system, + np.array([0, + 1, + 2]) + ) + assert atom_lines[ + 0] == "C 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0" + assert atom_lines[ + 1] == "H 1 0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0" + assert atom_lines[ + 2] == "H 2 0 2.0 2.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0" def test_write(self, capsys): writer = RestartFileWriter() atoms = [Atom("C"), Atom("H"), Atom("H")] - positions = np.array([[0.0, 0.0, 0.0], - [1.0, 1.0, 1.0], - [2.0, 2.0, 2.0]]) - velocities = np.array([[0.0, 0.0, 0.0], - [1.0, 1.0, 1.0], - [2.0, 2.0, 2.0]]) - forces = np.array([[0.0, 0.0, 0.0], - [1.0, 1.0, 1.0], - [2.0, 2.0, 2.0]]) + positions = np.array( + [[0.0, + 0.0, + 0.0], + [1.0, + 1.0, + 1.0], + [2.0, + 2.0, + 2.0]] + ) + velocities = np.array( + [[0.0, + 0.0, + 0.0], + [1.0, + 1.0, + 1.0], + [2.0, + 2.0, + 2.0]] + ) + forces = np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0], [2.0, 2.0, 2.0]]) cell = Cell(10.0, 10.0, 10.0) frame = AtomicSystem( atoms, diff --git a/tests/io/test_trajectoryWriter.py b/tests/io/test_trajectoryWriter.py index 8575cb60..91f1085c 100644 --- a/tests/io/test_trajectoryWriter.py +++ b/tests/io/test_trajectoryWriter.py @@ -11,20 +11,13 @@ from PQAnalysis.traj.exceptions import MDEngineFormatError + def test_write_trajectory(capsys): atoms = [Atom(atom) for atom in ['h', 'o']] coordinates1 = np.array([[0, 0, 0], [0, 0, 1]]) coordinates2 = np.array([[0, 0, 0], [0, 0, 1]]) - frame1 = AtomicSystem( - atoms=atoms, - pos=coordinates1, - cell=Cell(10, 10, 10) - ) - frame2 = AtomicSystem( - atoms=atoms, - pos=coordinates2, - cell=Cell(11, 10, 10) - ) + frame1 = AtomicSystem(atoms=atoms, pos=coordinates1, cell=Cell(10, 10, 10)) + frame2 = AtomicSystem(atoms=atoms, pos=coordinates2, cell=Cell(11, 10, 10)) traj = Trajectory([frame1, frame2]) print() @@ -43,7 +36,9 @@ def test_write_trajectory(capsys): """ + class TestTrajectoryWriter: + def test__init__(self): with pytest.raises(MDEngineFormatError) as exception: @@ -87,8 +82,11 @@ def test__write_comment(self, capsys): writer.type = TrajectoryFormat.XYZ writer._write_comment( AtomicSystem( - atoms=[Atom(atom) for atom in ["h", "o"]], - cell=Cell(10, 10, 10) + atoms=[Atom(atom) for atom in ["h", + "o"]], + cell=Cell(10, + 10, + 10) ) ) @@ -99,9 +97,12 @@ def test__write_comment(self, capsys): writer.type = TrajectoryFormat.FORCE writer._write_comment( AtomicSystem( - atoms=[Atom(atom) for atom in ["h", "o"]], - cell=Cell(10, 10, 10), - forces=forces + atoms=[Atom(atom) for atom in ["h", + "o"]], + cell=Cell(10, + 10, + 10), + forces=forces ) ) @@ -115,7 +116,15 @@ def test__write_xyz(self, capsys): print() writer._write_xyz( - atoms=[Atom(atom) for atom in ["h", "o"]], xyz=np.array([[0, 0, 0], [0, 0, 1]])) + atoms=[Atom(atom) for atom in ["h", + "o"]], + xyz=np.array([[0, + 0, + 0], + [0, + 0, + 1]]) + ) captured = capsys.readouterr() assert captured.out == """ @@ -127,7 +136,15 @@ def test__write_xyz(self, capsys): print() writer._write_xyz( - atoms=[Atom(atom) for atom in ["h", "o"]], xyz=np.array([[0, 0, 0], [0, 0, 1]])) + atoms=[Atom(atom) for atom in ["h", + "o"]], + xyz=np.array([[0, + 0, + 0], + [0, + 0, + 1]]) + ) captured = capsys.readouterr() assert captured.out == """ @@ -140,7 +157,11 @@ def test__write_scalar(self, capsys): writer = TrajectoryWriter() writer._write_scalar( - atoms=[Atom(atom) for atom in ["h", "o"]], scalar=np.array([1, 2])) + atoms=[Atom(atom) for atom in ["h", + "o"]], + scalar=np.array([1, + 2]) + ) captured = capsys.readouterr() assert captured.out == "h 1\no 2\n" @@ -154,12 +175,16 @@ def test_write(self, capsys): frame1 = AtomicSystem( atoms=atoms, pos=coordinates1, - cell=Cell(10, 10, 10) + cell=Cell(10, + 10, + 10) ) frame2 = AtomicSystem( atoms=atoms, pos=coordinates2, - cell=Cell(11, 10, 10) + cell=Cell(11, + 10, + 10) ) traj = Trajectory([frame1, frame2]) @@ -197,12 +222,16 @@ def test_write(self, capsys): frame1 = AtomicSystem( atoms=atoms, vel=coordinates1, - cell=Cell(10, 10, 10) + cell=Cell(10, + 10, + 10) ) frame2 = AtomicSystem( atoms=atoms, vel=coordinates2, - cell=Cell(11, 10, 10) + cell=Cell(11, + 10, + 10) ) traj = Trajectory([frame1, frame2]) @@ -226,12 +255,16 @@ def test_write(self, capsys): frame1 = AtomicSystem( atoms=atoms, forces=coordinates1, - cell=Cell(10, 10, 10) + cell=Cell(10, + 10, + 10) ) frame2 = AtomicSystem( atoms=atoms, forces=coordinates2, - cell=Cell(11, 10, 10) + cell=Cell(11, + 10, + 10) ) traj = Trajectory([frame1, frame2]) @@ -258,12 +291,16 @@ def test_write(self, capsys): frame1 = AtomicSystem( atoms=atoms, charges=charges1, - cell=Cell(10, 10, 10) + cell=Cell(10, + 10, + 10) ) frame2 = AtomicSystem( atoms=atoms, charges=charges2, - cell=Cell(11, 10, 10) + cell=Cell(11, + 10, + 10) ) traj = Trajectory([frame1, frame2]) diff --git a/tests/io/test_write_api.py b/tests/io/test_write_api.py index 06a926a6..54f846b5 100644 --- a/tests/io/test_write_api.py +++ b/tests/io/test_write_api.py @@ -10,16 +10,18 @@ from ..conftest import assert_logging_with_exception + class TestWriteAPI: + def test_write_type_checking(self, caplog): assert_logging_with_exception( caplog=caplog, logging_level="ERROR", logging_name="TypeChecking", message_to_test=get_type_error_message( - "filename", - 1, - "str | None" + "filename", + 1, + "str | None" ), exception=PQTypeError, function=write, @@ -32,9 +34,9 @@ def test_write_type_checking(self, caplog): logging_level="ERROR", logging_name="TypeChecking", message_to_test=get_type_error_message( - "mode", - 1, - FileWritingMode | str + "mode", + 1, + FileWritingMode | str ), exception=PQTypeError, function=write, @@ -49,7 +51,7 @@ def test_write_not_implemented(self, caplog): logging_level="ERROR", logging_name="io.write_api", message_to_test=( - "Writing object of type is not implemented yet." + "Writing object of type is not implemented yet." ), exception=PQNotImplementedError, function=write, @@ -62,11 +64,9 @@ def test_write_box_type_checking(self, caplog): caplog=caplog, logging_level="ERROR", logging_name="TypeChecking", - message_to_test=get_type_error_message( - "traj", - None, - Trajectory - ), + message_to_test=get_type_error_message("traj", + None, + Trajectory), exception=PQTypeError, function=write_box, traj=None @@ -77,9 +77,9 @@ def test_write_box_type_checking(self, caplog): logging_level="ERROR", logging_name="TypeChecking", message_to_test=get_type_error_message( - "filename", - 1, - "str | None" + "filename", + 1, + "str | None" ), exception=PQTypeError, function=write_box, @@ -92,9 +92,9 @@ def test_write_box_type_checking(self, caplog): logging_level="ERROR", logging_name="TypeChecking", message_to_test=get_type_error_message( - "output_format", - 1, - "str | None" + "output_format", + 1, + "str | None" ), exception=PQTypeError, function=write_box, diff --git a/tests/physicalData/__init__.py b/tests/physicalData/__init__.py index e69de29b..8b137891 100644 --- a/tests/physicalData/__init__.py +++ b/tests/physicalData/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/physicalData/test_energy.py b/tests/physicalData/test_energy.py index 0692655b..318e208a 100644 --- a/tests/physicalData/test_energy.py +++ b/tests/physicalData/test_energy.py @@ -1,4 +1,3 @@ - from collections import defaultdict import pytest @@ -7,7 +6,9 @@ from PQAnalysis.physical_data import Energy, EnergyError + class TestEnergy: + def test__init__(self): data = np.array([1, 2, 3]) energy = Energy(data) @@ -36,14 +37,17 @@ def test__setup_info_dictionary(self): with pytest.raises(EnergyError) as exception: Energy(data, info={1: 0}) assert str( - exception.value) == "The length of info dictionary has to be equal to the length of data." + exception.value + ) == "The length of info dictionary has to be equal to the length of data." with pytest.raises(EnergyError) as exception: Energy(data, units={1: 0}) assert str( - exception.value) == "The length of units dictionary has to be equal to the length of data." + exception.value + ) == "The length of units dictionary has to be equal to the length of data." with pytest.raises(EnergyError) as exception: Energy(data, info={1: 0, 2: 0}, units={1: 0, 3: 0}) assert str( - exception.value) == "The keys of the info and units dictionary do not match." + exception.value + ) == "The keys of the info and units dictionary do not match." diff --git a/tests/test_formats.py b/tests/test_formats.py index 35c9dcc5..2ebc36c7 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -2,7 +2,9 @@ from PQAnalysis.formats import BaseEnumFormat + def test_BaseEnumFormat(): + class TestEnum(BaseEnumFormat): A = "a" B = "b" diff --git a/tests/tools/__init__.py b/tests/tools/__init__.py index e69de29b..8b137891 100644 --- a/tests/tools/__init__.py +++ b/tests/tools/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/tools/test_traj_to_com_traj.py b/tests/tools/test_traj_to_com_traj.py index e6e50d90..747b33e9 100644 --- a/tests/tools/test_traj_to_com_traj.py +++ b/tests/tools/test_traj_to_com_traj.py @@ -7,6 +7,7 @@ from PQAnalysis.atomic_system import AtomicSystem + def test_traj_to_com_traj(): traj = Trajectory() @@ -56,9 +57,17 @@ def test_traj_to_com_traj(): assert np.allclose(traj_output[0].pos, [[0.5, 0.5, 1.5], [2.5, 2.5, 3.5]]) assert traj_output[0].atoms == [ - Atom('CC', use_guess_element=False), Atom('HH', use_guess_element=False)] + Atom('CC', + use_guess_element=False), + Atom('HH', + use_guess_element=False) + ] assert traj_output[0].combined_name == 'CCHH' assert np.allclose(traj_output[1].pos, [[0.5, 1.5, 1.5], [2.5, 3.5, 3.5]]) assert traj_output[1].atoms == [ - Atom('CC', use_guess_element=False), Atom('HH', use_guess_element=False)] + Atom('CC', + use_guess_element=False), + Atom('HH', + use_guess_element=False) + ] assert traj_output[1].combined_name == 'CCHH' diff --git a/tests/topology/bonded_topology/__init__.py b/tests/topology/bonded_topology/__init__.py index e69de29b..8b137891 100644 --- a/tests/topology/bonded_topology/__init__.py +++ b/tests/topology/bonded_topology/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/topology/bonded_topology/test_angle.py b/tests/topology/bonded_topology/test_angle.py index dbc85cac..037914c2 100644 --- a/tests/topology/bonded_topology/test_angle.py +++ b/tests/topology/bonded_topology/test_angle.py @@ -5,7 +5,9 @@ from PQAnalysis.topology import Angle + class TestAngle: + def test__init__(self): angle = Angle(index1=1, index2=2, index3=3) assert angle.index1 == 1 diff --git a/tests/topology/bonded_topology/test_bond.py b/tests/topology/bonded_topology/test_bond.py index ca1521cd..385682c9 100644 --- a/tests/topology/bonded_topology/test_bond.py +++ b/tests/topology/bonded_topology/test_bond.py @@ -5,7 +5,9 @@ from PQAnalysis.topology import Bond + class TestBond: + def test__init__(self): bond = Bond(index1=1, index2=2) assert bond.index1 == 1 diff --git a/tests/topology/bonded_topology/test_bondedTopology.py b/tests/topology/bonded_topology/test_bondedTopology.py index 200a8340..bfe89906 100644 --- a/tests/topology/bonded_topology/test_bondedTopology.py +++ b/tests/topology/bonded_topology/test_bondedTopology.py @@ -11,7 +11,9 @@ ) + class TestBondedTopology: + def test__init__(self): bonded_topology = BondedTopology() assert bonded_topology.bonds == [] @@ -31,8 +33,13 @@ def test__init__(self): dihedrals=[dihedral], impropers=[dihedral], shake_bonds=[bond], - ordering_keys=["bonds", "angles", - "dihedrals", "impropers", "shake_bonds"] + ordering_keys=[ + "bonds", + "angles", + "dihedrals", + "impropers", + "shake_bonds" + ] ) assert bonded_topology.bonds == [bond] assert bonded_topology.angles == [angle] @@ -40,7 +47,12 @@ def test__init__(self): assert bonded_topology.impropers == [dihedral] assert bonded_topology.shake_bonds == [bond] assert bonded_topology.ordering_keys == [ - "bonds", "angles", "dihedrals", "impropers", "shake_bonds"] + "bonds", + "angles", + "dihedrals", + "impropers", + "shake_bonds" + ] def test_extend_shake_bonds(self): bond = Bond(index1=1, index2=2) @@ -51,8 +63,10 @@ def test_extend_shake_bonds(self): assert all( [ - a == b - for a, b in zip(bonded_topology.shake_bonds, [Bond(index1=5, index2=6)]) + a == b for a, + b in zip(bonded_topology.shake_bonds, + [Bond(index1=5, + index2=6)]) ] ) @@ -60,26 +74,42 @@ def test_extend_shake_bonds(self): with pytest.raises(ValueError) as exception: bonded_topology.extend_shake_bonds( - shake_bonds=[shake_bond], n_atoms=3, n_extensions=2 + shake_bonds=[shake_bond], + n_atoms=3, + n_extensions=2 ) assert str( - exception.value) == "n_atoms_per_extension must be provided if n_extensions is not 1." + exception.value + ) == "n_atoms_per_extension must be provided if n_extensions is not 1." with pytest.raises(ValueError) as exception: bonded_topology.extend_shake_bonds( - shake_bonds=[shake_bond], n_atoms=3, n_extensions=2, n_atoms_per_extension=1 + shake_bonds=[shake_bond], + n_atoms=3, + n_extensions=2, + n_atoms_per_extension=1 ) assert str( - exception.value) == "n_atoms_per_extension must be greater or equal than the highest index in the provided shake bonds." + exception.value + ) == "n_atoms_per_extension must be greater or equal than the highest index in the provided shake bonds." bonded_topology.extend_shake_bonds( - shake_bonds=[shake_bond], n_atoms=3, n_extensions=2, n_atoms_per_extension=3 + shake_bonds=[shake_bond], + n_atoms=3, + n_extensions=2, + n_atoms_per_extension=3 ) assert all( [ - a == b - for a, b in zip(bonded_topology.shake_bonds, [Bond(index1=5, index2=6), Bond(index1=8, index2=9)]) + a == b for a, + b in zip( + bonded_topology.shake_bonds, + [Bond(index1=5, + index2=6), + Bond(index1=8, + index2=9)] + ) ] ) @@ -150,8 +180,13 @@ def test_unique_dihedral4_indices(self): assert bonded_topology.unique_dihedral4_indices == {4} def test_dihedral_linkers(self): - dihedral = Dihedral(index1=1, index2=2, index3=3, - index4=4, is_linker=True) + dihedral = Dihedral( + index1=1, + index2=2, + index3=3, + index4=4, + is_linker=True + ) bonded_topology = BondedTopology(dihedrals=[dihedral, dihedral]) assert bonded_topology.dihedral_linkers == [dihedral, dihedral] @@ -181,8 +216,13 @@ def test_unique_improper4_indices(self): assert bonded_topology.unique_improper4_indices == {4} def test_improper_linkers(self): - dihedral = Dihedral(index1=1, index2=2, index3=3, - index4=4, is_linker=True) + dihedral = Dihedral( + index1=1, + index2=2, + index3=3, + index4=4, + is_linker=True + ) bonded_topology = BondedTopology(impropers=[dihedral, dihedral]) assert bonded_topology.improper_linkers == [dihedral, dihedral] diff --git a/tests/topology/bonded_topology/test_dihedral.py b/tests/topology/bonded_topology/test_dihedral.py index c310355f..d5af0e8a 100644 --- a/tests/topology/bonded_topology/test_dihedral.py +++ b/tests/topology/bonded_topology/test_dihedral.py @@ -5,7 +5,9 @@ from PQAnalysis.topology import Dihedral + class TestDihedral: + def test__init__(self): dihedral = Dihedral(index1=1, index2=2, index3=3, index4=4) assert dihedral.index1 == 1 diff --git a/tests/topology/test_selection.py b/tests/topology/test_selection.py index 5a9053da..b84e3a26 100644 --- a/tests/topology/test_selection.py +++ b/tests/topology/test_selection.py @@ -7,6 +7,7 @@ from PQAnalysis.core import Atom, Element, Residue + class TestSelection: topology = Topology([Atom("C1", 6), Atom("C2", 6), Atom("C1", 6)]) @@ -57,7 +58,8 @@ def test__selection_elements(self): with pytest.raises(ValueError) as exception: selection.select(self.topology, use_full_atom_info=True) assert str( - exception.value) == "The use_full_atom_info parameter is not supported for Element objects." + exception.value + ) == "The use_full_atom_info parameter is not supported for Element objects." def test_selection_lark_grammar(self): @@ -149,10 +151,24 @@ def test_grammar_residue(self): residue_ids = np.array([0, 1, 1, 0]) atoms = [Atom('C'), Atom('H'), Atom('H'), Atom('H')] - reference_residues = [Residue(name="ALA", residue_id=1, total_charge=0.0, elements=[Element( - "H"), Element("H")], atom_types=np.array([0, 1]), partial_charges=np.array([0.1, 0.1]))] - topology = Topology(atoms=atoms, residue_ids=residue_ids, - reference_residues=reference_residues) + reference_residues = [ + Residue( + name="ALA", + residue_id=1, + total_charge=0.0, + elements=[Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.1, + 0.1]) + ) + ] + topology = Topology( + atoms=atoms, + residue_ids=residue_ids, + reference_residues=reference_residues + ) selection = Selection("res~1") indices = selection.select(topology) assert np.all(indices == np.array([1, 2])) diff --git a/tests/topology/test_selectionTransformer.py b/tests/topology/test_selectionTransformer.py index 46a36275..c5b53097 100644 --- a/tests/topology/test_selectionTransformer.py +++ b/tests/topology/test_selectionTransformer.py @@ -5,7 +5,9 @@ from . import pytestmark + class TestSelectionTransformer: + def test__init__(self): transformer = SelectionTransformer() assert transformer.__visit_tokens__ == False diff --git a/tests/topology/test_shakeTopology.py b/tests/topology/test_shakeTopology.py index 128510ca..6b66a7e3 100644 --- a/tests/topology/test_shakeTopology.py +++ b/tests/topology/test_shakeTopology.py @@ -8,7 +8,9 @@ from PQAnalysis.traj import Trajectory + class TestShakeTopologyGenerator: + def test__init__(self): generator = ShakeTopologyGenerator() assert generator.selection_object is None @@ -28,10 +30,40 @@ def test__init__(self): def test_generate_topology(self): atoms = [Atom('C'), Atom('H'), Atom('H'), Atom('O'), Atom('H')] - pos = np.array([[0.1, 0, 0], [1, 0, 0], [2.1, 0, 0], - [3, 0, 0], [4, 0, 0]]) - pos2 = np.array([[0.5, 0, 0], [1, 0.5, 0], [2.5, 0, 0], - [3, 0.5, 0], [4.5, 0, 0]]) + pos = np.array( + [[0.1, + 0, + 0], + [1, + 0, + 0], + [2.1, + 0, + 0], + [3, + 0, + 0], + [4, + 0, + 0]] + ) + pos2 = np.array( + [[0.5, + 0, + 0], + [1, + 0.5, + 0], + [2.5, + 0, + 0], + [3, + 0.5, + 0], + [4.5, + 0, + 0]] + ) system = AtomicSystem(pos=pos, atoms=atoms) system2 = AtomicSystem(pos=pos2, atoms=atoms) @@ -47,11 +79,41 @@ def test_generate_topology(self): def test_average_equivalents(self): atoms = [Atom('C'), Atom('H'), Atom('H'), Atom('O'), Atom('H')] - pos = np.array([[0.1, 0, 0], [1, 0, 0], [2.1, 0, 0], - [3, 0, 0], [4, 0, 0]]) - - pos2 = np.array([[0.5, 0, 0], [1, 0.5, 0], [2.5, 0, 0], - [3, 0.5, 0], [4.5, 0, 0]]) + pos = np.array( + [[0.1, + 0, + 0], + [1, + 0, + 0], + [2.1, + 0, + 0], + [3, + 0, + 0], + [4, + 0, + 0]] + ) + + pos2 = np.array( + [[0.5, + 0, + 0], + [1, + 0.5, + 0], + [2.5, + 0, + 0], + [3, + 0.5, + 0], + [4.5, + 0, + 0]] + ) system = AtomicSystem(pos=pos, atoms=atoms) system2 = AtomicSystem(pos=pos2, atoms=atoms) @@ -70,11 +132,41 @@ def test_average_equivalents(self): def test_write_topology(self, capsys): atoms = [Atom('C'), Atom('H'), Atom('H'), Atom('O'), Atom('H')] - pos = np.array([[0.1, 0, 0], [1, 0, 0], [2.1, 0, 0], - [3, 0, 0], [4, 0, 0]]) - - pos2 = np.array([[0.5, 0, 0], [1, 0.5, 0], [2.5, 0, 0], - [3, 0.5, 0], [4.5, 0, 0]]) + pos = np.array( + [[0.1, + 0, + 0], + [1, + 0, + 0], + [2.1, + 0, + 0], + [3, + 0, + 0], + [4, + 0, + 0]] + ) + + pos2 = np.array( + [[0.5, + 0, + 0], + [1, + 0.5, + 0], + [2.5, + 0, + 0], + [3, + 0.5, + 0], + [4.5, + 0, + 0]] + ) system = AtomicSystem(pos=pos, atoms=atoms) system2 = AtomicSystem(pos=pos2, atoms=atoms) diff --git a/tests/topology/test_topology.py b/tests/topology/test_topology.py index 7c02aec2..2e3ea401 100644 --- a/tests/topology/test_topology.py +++ b/tests/topology/test_topology.py @@ -24,52 +24,88 @@ from ..conftest import assert_logging + def test_find_residue_by_id(caplog): - residue1 = Residue(name="name", residue_id=0, total_charge=0.0, elements=[ - ], atom_types=np.array([]), partial_charges=np.array([])) - residue2 = Residue(name="name", residue_id=1, total_charge=0.0, elements=[ - ], atom_types=np.array([]), partial_charges=np.array([])) - residue3 = Residue(name="name", residue_id=0, total_charge=0.0, elements=[ - ], atom_types=np.array([]), partial_charges=np.array([])) + residue1 = Residue( + name="name", + residue_id=0, + total_charge=0.0, + elements=[], + atom_types=np.array([]), + partial_charges=np.array([]) + ) + residue2 = Residue( + name="name", + residue_id=1, + total_charge=0.0, + elements=[], + atom_types=np.array([]), + partial_charges=np.array([]) + ) + residue3 = Residue( + name="name", + residue_id=0, + total_charge=0.0, + elements=[], + atom_types=np.array([]), + partial_charges=np.array([]) + ) assert_logging( caplog=caplog, logging_name=topology_module.__name__, logging_level="ERROR", - message_to_test=( - "The residue id 2 was not found." - ), + message_to_test=("The residue id 2 was not found."), function=_find_residue_by_id, res_id=2, - residues=[residue1, residue2, residue3] + residues=[residue1, + residue2, + residue3] ) assert_logging( caplog=caplog, logging_name=topology_module.__name__, logging_level="ERROR", - message_to_test=( - "The residue id 0 is not unique." - ), + message_to_test=("The residue id 0 is not unique."), function=_find_residue_by_id, res_id=0, - residues=[residue1, residue2, residue3] + residues=[residue1, + residue2, + residue3] ) residue = _find_residue_by_id(1, [residue1, residue2, residue3]) assert residue == residue2 + def test_unique_residues(): residues = [] assert _unique_residues_(residues) == [] - residues = [Residue(name="name", residue_id=0, total_charge=0.0, elements=[Element( - "C")], atom_types=np.array([0]), partial_charges=np.array([0.0]))] + residues = [ + Residue( + name="name", + residue_id=0, + total_charge=0.0, + elements=[Element("C")], + atom_types=np.array([0]), + partial_charges=np.array([0.0]) + ) + ] assert _unique_residues_(residues) == residues - residues.append(Residue(name="name", residue_id=1, total_charge=0.0, elements=[ - Element("C")], atom_types=np.array([0]), partial_charges=np.array([0.0]))) + residues.append( + Residue( + name="name", + residue_id=1, + total_charge=0.0, + elements=[Element("C")], + atom_types=np.array([0]), + partial_charges=np.array([0.0]) + ) + ) assert _unique_residues_(residues) == residues residues.append(residues[0]) @@ -82,6 +118,7 @@ def test_unique_residues(): assert _unique_residues_(residues) == [residues[0], residues[2]] + class TestTopology: atoms = [Atom('C'), Atom('H'), Atom('H')] @@ -91,7 +128,8 @@ def test__init__(self, caplog): with pytest.raises(TopologyError) as exception: Topology(atoms=self.atoms, residue_ids=residue_ids) assert str( - exception.value) == "The number of atoms does not match the number of residue ids." + exception.value + ) == "The number of atoms does not match the number of residue ids." topology = Topology(atoms=self.atoms) assert topology.n_atoms == 3 @@ -103,17 +141,34 @@ def test__init__(self, caplog): residue_ids = np.array([0, 1, 1, 0]) atoms = [Atom('C'), Atom('H'), Atom('H'), Atom('H')] - reference_residues = [Residue(name="ALA", residue_id=1, total_charge=0.0, elements=[Element( - "H"), Element("H")], atom_types=np.array([0, 1]), partial_charges=np.array([0.1, 0.1]))] - topology = Topology(atoms=atoms, residue_ids=residue_ids, - reference_residues=reference_residues) + reference_residues = [ + Residue( + name="ALA", + residue_id=1, + total_charge=0.0, + elements=[Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.1, + 0.1]) + ) + ] + topology = Topology( + atoms=atoms, + residue_ids=residue_ids, + reference_residues=reference_residues + ) assert topology.n_atoms == 4 assert topology.atoms == atoms assert np.all(topology.residue_ids == np.array([0, 1, 1, 0])) assert topology.atomtype_names == ['C', 'H', 'H', 'H'] assert topology.reference_residues == reference_residues assert topology.residues == [ - QMResidue(Element('C')), reference_residues[0], QMResidue(Element('H'))] + QMResidue(Element('C')), + reference_residues[0], + QMResidue(Element('H')) + ] assert np.all(topology.residue_numbers == [0, 1, 1, 2]) assert_logging( @@ -142,8 +197,19 @@ def test_setup_residues(self, caplog): atoms = [Atom('C', use_guess_element=False), Atom('H'), Atom('H')] topology = Topology(atoms=atoms, residue_ids=residue_ids) - reference_residues = [Residue(name="ALA", residue_id=1, total_charge=0.0, elements=[Element( - "H"), Element("H")], atom_types=np.array([0, 1]), partial_charges=np.array([0.1, 0.1]))] + reference_residues = [ + Residue( + name="ALA", + residue_id=1, + total_charge=0.0, + elements=[Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.1, + 0.1]) + ) + ] topology.reference_residues = reference_residues assert_logging( @@ -151,11 +217,11 @@ def test_setup_residues(self, caplog): logging_name=Topology.__qualname__, logging_level="ERROR", message_to_test=( - "The element of atom 0 is not set. If any reference residues are given the " - "program tries to automatically deduce the residues from the residue ids and " - "the reference residues. This means that any atom with an unknown element " - "raises an error. To avoid deducing residue information please set 'check_residues' " - "to False" + "The element of atom 0 is not set. If any reference residues are given the " + "program tries to automatically deduce the residues from the residue ids and " + "the reference residues. This means that any atom with an unknown element " + "raises an error. To avoid deducing residue information please set 'check_residues' " + "to False" ), function=topology._setup_residues, residue_ids=residue_ids, @@ -228,8 +294,11 @@ def test_setup_residues(self, caplog): assert residues[0] == reference_residues[0] assert residues[1] == QMResidue(Element('C')) - topology = Topology(atoms=atoms, residue_ids=residue_ids, - reference_residues=reference_residues) + topology = Topology( + atoms=atoms, + residue_ids=residue_ids, + reference_residues=reference_residues + ) residues, new_atoms = topology._setup_residues(residue_ids, atoms) assert new_atoms == atoms assert residues[0] == reference_residues[0] @@ -239,14 +308,35 @@ def test__eq__(self): assert Topology() != Atom('C') assert Topology() != Topology(atoms=self.atoms) assert Topology(atoms=self.atoms) == Topology( - atoms=self.atoms, residue_ids=np.array([0, 0, 0])) + atoms=self.atoms, + residue_ids=np.array([0, + 0, + 0]) + ) assert Topology(atoms=self.atoms) != Topology( - atoms=self.atoms, residue_ids=np.array([0, 0, 1])) + atoms=self.atoms, + residue_ids=np.array([0, + 0, + 1]) + ) - reference_residues = [Residue(name="ALA", residue_id=1, total_charge=0.0, elements=[Element( - "H"), Element("H")], atom_types=np.array([0, 1]), partial_charges=np.array([0.1, 0.1]))] + reference_residues = [ + Residue( + name="ALA", + residue_id=1, + total_charge=0.0, + elements=[Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.1, + 0.1]) + ) + ] assert Topology(atoms=self.atoms) == Topology( - atoms=self.atoms, reference_residues=reference_residues) + atoms=self.atoms, + reference_residues=reference_residues + ) def test__getitem__(self): topology = Topology() @@ -254,31 +344,63 @@ def test__getitem__(self): topology = Topology(atoms=self.atoms, residue_ids=np.array([0, 1, 0])) assert topology[0] == Topology( - atoms=[Atom('C')], residue_ids=np.array([0])) + atoms=[Atom('C')], + residue_ids=np.array([0]) + ) assert topology[1] == Topology( - atoms=[Atom('H')], residue_ids=np.array([1])) - assert topology[np.array([0, 1])] == Topology( - atoms=[Atom('C'), Atom('H')], residue_ids=np.array([0, 1])) + atoms=[Atom('H')], + residue_ids=np.array([1]) + ) + assert topology[np.array([0, + 1])] == Topology( + atoms=[Atom('C'), + Atom('H')], + residue_ids=np.array([0, + 1]) + ) def test__str__(self): - reference_residues = [Residue(name="ALA", residue_id=1, total_charge=0.0, elements=[Element( - "H"), Element("H")], atom_types=np.array([0, 1]), partial_charges=np.array([0.1, 0.1]))] + reference_residues = [ + Residue( + name="ALA", + residue_id=1, + total_charge=0.0, + elements=[Element("H"), + Element("H")], + atom_types=np.array([0, + 1]), + partial_charges=np.array([0.1, + 0.1]) + ) + ] topology = Topology(atoms=self.atoms, residue_ids=np.array([0, 1, 1])) assert str( - topology) == "Topology with 3 atoms and 0 residues and 0 unique residues." + topology + ) == "Topology with 3 atoms and 0 residues and 0 unique residues." assert str(topology) == repr(topology) assert topology.n_mm_residues == 0 - topology = Topology(atoms=self.atoms, residue_ids=np.array( - [0, 1, 1]), reference_residues=reference_residues) + topology = Topology( + atoms=self.atoms, + residue_ids=np.array([0, + 1, + 1]), + reference_residues=reference_residues + ) assert str( - topology) == "Topology with 3 atoms and 2 residues (1 QM residues) and 2 unique residues." + topology + ) == "Topology with 3 atoms and 2 residues (1 QM residues) and 2 unique residues." assert str(topology) == repr(topology) assert topology.n_mm_residues == 1 - topology = Topology(atoms=self.atoms, residue_ids=np.array( - [0, 0, 0]), reference_residues=reference_residues) + topology = Topology( + atoms=self.atoms, + residue_ids=np.array([0, + 0, + 0]), + reference_residues=reference_residues + ) topology_str = ( "Topology with 3 atoms and 3 residues" diff --git a/tests/traj/test_api.py b/tests/traj/test_api.py index bc3f4127..bbf44883 100644 --- a/tests/traj/test_api.py +++ b/tests/traj/test_api.py @@ -6,6 +6,7 @@ from PQAnalysis.traj import check_trajectory_pbc, check_trajectory_vacuum + def test_check_trajectory_PBC(): cells = [Cell(10, 10, 10), Cell(10, 10, 10)] @@ -20,6 +21,7 @@ def test_check_trajectory_PBC(): assert check_trajectory_pbc(cells) == False + def test_check_trajectory_vacuum(): cells = [Cell(), Cell(), Cell()] diff --git a/tests/traj/test_trajectory.py b/tests/traj/test_trajectory.py index ea8a2f67..8a95d95d 100644 --- a/tests/traj/test_trajectory.py +++ b/tests/traj/test_trajectory.py @@ -15,6 +15,7 @@ from PQAnalysis.exceptions import PQIndexError + class TestTrajectory: atoms1 = [Atom("H")] @@ -49,10 +50,22 @@ def test_check_PBC(self): assert traj.check_pbc() == False system1 = AtomicSystem( - atoms=self.atoms1, pos=np.array([[0, 1, 2]]), cell=Cell(10, 10, 10) + atoms=self.atoms1, + pos=np.array([[0, + 1, + 2]]), + cell=Cell(10, + 10, + 10) ) system2 = AtomicSystem( - atoms=self.atoms2, pos=np.array([[1, 1, 2]]), cell=Cell(10, 10, 10) + atoms=self.atoms2, + pos=np.array([[1, + 1, + 2]]), + cell=Cell(10, + 10, + 10) ) frame1 = system1 frame2 = system2 @@ -77,10 +90,22 @@ def test_check_vacuum(self): assert traj.check_vacuum() == True system1 = AtomicSystem( - atoms=self.atoms1, pos=np.array([[0, 1, 2]]), cell=Cell(10, 10, 10) + atoms=self.atoms1, + pos=np.array([[0, + 1, + 2]]), + cell=Cell(10, + 10, + 10) ) system2 = AtomicSystem( - atoms=self.atoms2, pos=np.array([[1, 1, 2]]), cell=Cell(10, 10, 10) + atoms=self.atoms2, + pos=np.array([[1, + 1, + 2]]), + cell=Cell(10, + 10, + 10) ) frame1 = system1 frame2 = system2 @@ -104,10 +129,22 @@ def test_box_volumes(self): assert traj.box_volumes[2] > 10**10 system1 = AtomicSystem( - atoms=self.atoms1, pos=np.array([[0, 1, 2]]), cell=Cell(10, 10, 10) + atoms=self.atoms1, + pos=np.array([[0, + 1, + 2]]), + cell=Cell(10, + 10, + 10) ) system2 = AtomicSystem( - atoms=self.atoms2, pos=np.array([[1, 1, 2]]), cell=Cell(11, 11, 11) + atoms=self.atoms2, + pos=np.array([[1, + 1, + 2]]), + cell=Cell(11, + 11, + 11) ) frame1 = system1 frame2 = system2 @@ -146,11 +183,19 @@ def test_window(self, caplog): test_frames = [traj.frames for traj in traj.window(2, 1)] assert test_frames == [ - [self.frame1, self.frame2], [self.frame2, self.frame3]] + [self.frame1, + self.frame2], + [self.frame2, + self.frame3] + ] test_frames = [traj.frames for traj in traj.window(2)] assert test_frames == [ - [self.frame1, self.frame2], [self.frame2, self.frame3]] + [self.frame1, + self.frame2], + [self.frame2, + self.frame3] + ] test_frames = [traj.frames for traj in traj.window(1)] assert test_frames == [[self.frame1], [self.frame2], [self.frame3]] @@ -163,7 +208,8 @@ def test_window(self, caplog): Trajectory.__qualname__, "WARNING", "Not all frames are included in the windows. Check the window size and gap.", - traj.window(2, 2).__next__, + traj.window(2, + 2).__next__, ) assert_logging_with_exception( @@ -172,7 +218,7 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "window size can not be less than 1 or greater than the length of the trajectory" + "window size can not be less than 1 or greater than the length of the trajectory" ), function=traj.window(0).__next__, ) @@ -183,7 +229,7 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "window size can not be less than 1 or greater than the length of the trajectory" + "window size can not be less than 1 or greater than the length of the trajectory" ), function=traj.window(4).__next__, ) @@ -194,9 +240,10 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "window gap can not be less than 1 or greater than the length of the trajectory" + "window gap can not be less than 1 or greater than the length of the trajectory" ), - function=traj.window(1, 0).__next__, + function=traj.window(1, + 0).__next__, ) assert_logging_with_exception( @@ -205,9 +252,10 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "window gap can not be less than 1 or greater than the length of the trajectory" + "window gap can not be less than 1 or greater than the length of the trajectory" ), - function=traj.window(1, 4).__next__, + function=traj.window(1, + 4).__next__, ) assert_logging_with_exception( @@ -216,9 +264,11 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "start index is less than 0 or greater than the length of the trajectory" + "start index is less than 0 or greater than the length of the trajectory" ), - function=traj.window(1, 1, trajectory_start=-1).__next__, + function=traj.window(1, + 1, + trajectory_start=-1).__next__, ) assert_logging_with_exception( @@ -227,9 +277,11 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "stop index is less than 0 or greater than the length of the trajectory" + "stop index is less than 0 or greater than the length of the trajectory" ), - function=traj.window(1, 1, trajectory_stop=-1).__next__, + function=traj.window(1, + 1, + trajectory_stop=-1).__next__, ) assert_logging_with_exception( @@ -238,9 +290,12 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "start index is greater than or equal to the stop index"), - function=traj.window(1, 1, trajectory_start=2, - trajectory_stop=1).__next__, + "start index is greater than or equal to the stop index" + ), + function=traj.window(1, + 1, + trajectory_start=2, + trajectory_stop=1).__next__, ) assert_logging_with_exception( @@ -249,10 +304,12 @@ def test_window(self, caplog): exception=PQIndexError, logging_level="ERROR", message_to_test=( - "window size is greater than the trajectory_stop - trajectory_start" + "window size is greater than the trajectory_stop - trajectory_start" ), - function=traj.window(3, 1, trajectory_start=1, - trajectory_stop=3).__next__, + function=traj.window(3, + 1, + trajectory_start=1, + trajectory_stop=3).__next__, ) def test__iter__(self): @@ -337,8 +394,14 @@ def test_property_box_lengths(self): assert np.allclose( traj.box_lengths, np.array( - [[max_float, max_float, max_float], [ - max_float, max_float, max_float]] + [ + [max_float, + max_float, + max_float], + [max_float, + max_float, + max_float] + ] ), ) @@ -346,8 +409,15 @@ def test_property_box_lengths(self): frame2 = AtomicSystem(cell=Cell(11, 11, 11)) traj = Trajectory([frame1, frame2]) - assert np.allclose(traj.box_lengths, np.array( - [[10, 10, 10], [11, 11, 11]])) + assert np.allclose( + traj.box_lengths, + np.array([[10, + 10, + 10], + [11, + 11, + 11]]) + ) def test_property_cells(self): frame1 = AtomicSystem() diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index e69de29b..8b137891 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/utils/test_common.py b/tests/utils/test_common.py index 1e6d05c9..ae78be17 100644 --- a/tests/utils/test_common.py +++ b/tests/utils/test_common.py @@ -6,6 +6,7 @@ from PQAnalysis._version import __version__ + def test_print_header(capsys: CaptureFixture): print_header() diff --git a/tests/utils/test_decorators.py b/tests/utils/test_decorators.py index 6e457aed..0509531b 100644 --- a/tests/utils/test_decorators.py +++ b/tests/utils/test_decorators.py @@ -1,7 +1,9 @@ from PQAnalysis.utils import count_decorator, instance_function_count_decorator + def test_count_decorator(): + @count_decorator def test_func(reset_counter=False): pass @@ -16,8 +18,11 @@ def test_func(reset_counter=False): assert test_func.counter == 2 + def test_instance_function_count_decorator(): + class Class: + def __init__(self): self.instance_counter = 0